webNotifyDebug.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. from direct.task import Task
  2. from direct.http import WebRequest
  3. from direct.directnotify import DirectNotifyGlobal
  4. import random, string
  5. class webNotifyDebug:
  6. def __init__(self, portNumber = 8888, username = None, password = None):
  7. self.portNumber = portNumber
  8. self.username = username
  9. self.password = password
  10. self.passwordProtect = False
  11. self.pageToHit = 'debug'
  12. self.authTokens = []
  13. self.web = WebRequest.WebRequestDispatcher()
  14. self.web.listenOnPort(int(self.portNumber))
  15. # 'debug' will be the name of the page we have to hit
  16. # If both a username and password should be specified, then
  17. # we will need to present a username and password prompt to the user
  18. if self.username and self.password:
  19. # set self.passwordProtect to True
  20. self.passwordProtect = True
  21. # Register 'debug' with the password prompt
  22. self.web.registerGETHandler('debug', self.passwordPrompt)
  23. self.web.registerGETHandler('authDebug', self.authDebug)
  24. self.pageToHit = 'authDebug'
  25. else:
  26. self.web.registerGETHandler('debug', self.debug)
  27. self.startCheckingIncomingHTTP()
  28. def passwordPrompt(self, replyTo, **kw):
  29. # This should get called if we need to prompt the user for
  30. # a username and password.
  31. try:
  32. username = kw['username']
  33. password = kw['password']
  34. except KeyError:
  35. # the user is probably making their initial connection to the
  36. # password protected site. Present them with the login page
  37. replyTo.respond('<HTML>\n<HEAD><TITLE>Direct Notify Web Interface - Username and Password Required</TITLE></HEAD>\n<BODY>\n<FONT SIZE=4>Username/Password authentication has been enabled. You must provide the following before gaining access to the system:<P><FORM action="debug" method="get">\nUsername: <INPUT type="text" name="username"><BR>\nPassword: <INPUT type="password" name="password"><BR>\n<input type=submit name="Submit" text="Login"></form>\n</BODY></HTML>')
  38. return
  39. # If the username and password are correct, we need to generate an
  40. # auth token and place it in self.authTokens. If the username and
  41. # password are incorrect. Return an error message indicating such.
  42. if username == self.username and password == self.password:
  43. # Username and Password match
  44. # Generate auth token
  45. authToken = self.genToken()
  46. # Place the authToken in the list of valid auth tokens
  47. self.authTokens.append(authToken)
  48. replyTo.respond('<HTML><HEAD><TITLE>Username and Password Good</TITLE></HEAD><BODY>Username and Password are good, please remember to logout when done. <A HREF=authDebug?authToken=%s>Click here to continue</a></BODY></HTML>' % (authToken))
  49. return
  50. else:
  51. replyTo.respond('Username and/or password are incorrect')
  52. return
  53. def listAllCategories(self, replyTo, optionalMessage = None, authToken = None):
  54. # Return a web page with a list of all registered notify categories
  55. # along with an HTML widget to chage their debug state
  56. completeList = DirectNotifyGlobal.directNotify.getCategories()
  57. # Define the static head of the response
  58. if not optionalMessage:
  59. head = '<html>\n<head>\n<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">\n<title>DirectNotify - List All Categories</title>\n</head>\n<body>\n<h1 style="text-align: center;">DirectNotify - Listing All Categories</h1>\n<CENTER><table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">\n<tbody>\n<tr><th>Category</th><th>Debug Status</th></tr>\n'
  60. else:
  61. head = '<html>\n<head>\n<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">\n<title>DirectNotify - List All Categories</title>\n</head>\n<body>\n<h1 style="text-align: center;">DirectNotify - Listing All Categories</h1>\n<CENTER><HR>%s<HR><BR><table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">\n<tbody>\n<tr><th>Category</th><th>Debug Status</th></tr>\n' % (optionalMessage)
  62. # define the static foot
  63. if authToken:
  64. foot = '</tbody></table></CENTER><BR><A HREF="%s?authToken=%s">Main Menu</a></body></html>' % (self.pageToHit, authToken)
  65. else:
  66. foot = '</tbody></table></CENTER><BR><A HREF="%s">Main Menu</a></body></html>' % self.pageToHit
  67. # Sort our catagory list into alpha order
  68. completeList.sort()
  69. # Now generate the body of the page response
  70. body = ''
  71. for item in completeList:
  72. select = '<tr><td>%s</td><td style="text-align: center;">' % (item)
  73. tempCategory = DirectNotifyGlobal.directNotify.getCategory(item)
  74. debugStatus = tempCategory.getDebug()
  75. if debugStatus == 0:
  76. if authToken:
  77. body = '%s%s<A HREF="%s?command=on&item=%s&authToken=%s">Off</a></td></tr>' % (body, select, self.pageToHit, item, authToken)
  78. else:
  79. body = '%s%s<A HREF="%s?command=on&item=%s">Off</a></td></tr>' % (body, select, self.pageToHit, item)
  80. else:
  81. if authToken:
  82. body = '%s%s<A HREF="%s?command=off&item=%s&authToken=%s">On</a></td></tr>' % (body, select, self.pageToHit, item, authToken)
  83. else:
  84. body = '%s%s<A HREF="%s?command=off&item=%s">On</a></td></tr>' % (body, select, self.pageToHit, item)
  85. replyTo.respond('%s\n%s\n%s\n' % (head, body, foot))
  86. def turnCatOn(self, item, replyTo, sString = None, authToken = None):
  87. # Used to turn a catagory (item), to the on state
  88. try:
  89. notifyItem = DirectNotifyGlobal.directNotify.getCategory(item)
  90. notifyItem.setDebug(1)
  91. updateMessage = 'Category <b>%s</b>, has been turned on' % (item)
  92. if not sString:
  93. self.listAllCategories(replyTo, updateMessage, authToken)
  94. else:
  95. self.searchForCat(sString, replyTo, updateMessage, authToken)
  96. except AttributeError:
  97. replyTo.respond('Invalid Category Passed')
  98. def turnCatOff(self, item, replyTo, sString = None, authToken = None):
  99. # Used to turn a catagory (item), to the off state
  100. try:
  101. notifyItem = DirectNotifyGlobal.directNotify.getCategory(item)
  102. notifyItem.setDebug(0)
  103. updateMessage = 'Category <b>%s</b>, has been turned off' % (item)
  104. if not sString:
  105. self.listAllCategories(replyTo, updateMessage, authToken)
  106. else:
  107. self.searchForCat(sString, replyTo, updateMessage, authToken)
  108. except AttributeError:
  109. replyTo.respond('Invalid Category Passed')
  110. def searchForCat(self, searchString, replyTo, toggle = None, authToken = None):
  111. # Used to execute a substring search for a category
  112. completeList = DirectNotifyGlobal.directNotify.getCategories()
  113. resultList = []
  114. while completeList:
  115. item = completeList.pop()
  116. if item.find(searchString) != -1:
  117. resultList.append(item)
  118. # Now that we have the results, present them
  119. # First, sort the list
  120. resultList.sort()
  121. if not toggle:
  122. head = '<html>\n<head>\n<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">\n<title>DirectNotify - Search Results</title>\n</head>\n<body>\n<h1 style="text-align: center;">DirectNotify - Listing All Categories</h1>\n<CENTER><table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">\n<tbody>\n<tr><th>Category</th><th>Debug Status</th></tr>\n'
  123. else:
  124. head = '<html>\n<head>\n<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">\n<title>DirectNotify - Search Results</title>\n</head>\n<body>\n<h1 style="text-align: center;">DirectNotify - Listing All Categories</h1>\n<CENTER><HR>%s<HR><br><table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">\n<tbody>\n<tr><th>Category</th><th>Debug Status</th></tr>\n' % (toggle)
  125. if authToken:
  126. foot = '</tbody></table></CENTER><BR><A HREF="authDebug?authToken=%s">Main Menu</a></body></html>' % (authToken)
  127. else:
  128. foot = '</tbody></table></CENTER><BR><A HREF="debug">Main Menu</a></body></html>'
  129. body = ''
  130. for item in resultList:
  131. select = '<tr><td>%s</td><td style="text-align: center;">' % (item)
  132. tempCategory = DirectNotifyGlobal.directNotify.getCategory(item)
  133. debugStatus = tempCategory.getDebug()
  134. if debugStatus == 0:
  135. if authToken:
  136. body = '%s%s<A HREF="%s?command=on&item=%s&sString=%s&authToken=%s">Off</a></td></tr>' % (body, select, self.pageToHit, item, searchString, authToken)
  137. else:
  138. body = '%s%s<A HREF="%s?command=on&item=%s&sString=%s">Off</a></td></tr>' % (body, select, self.pageToHit, item, searchString)
  139. else:
  140. if authToken:
  141. body = '%s%s<A HREF="%s?command=off&item=%s&sString=%s&authToken=%s">On</a></td></tr>' % (body, select, self.pageToHit, item, searchString, authToken)
  142. else:
  143. body = '%s%s<A HREF="%s?command=off&item=%s&sString=%s">On</a></td></tr>' % (body, select, self.pageToHit, item, searchString)
  144. replyTo.respond('%s\n%s\n%s\n' % (head, body, foot))
  145. def debug(self, replyTo, **kw):
  146. try:
  147. authToken = kw['authToken']
  148. except KeyError:
  149. authToken = None
  150. try:
  151. command = kw['command']
  152. if command == 'listAll':
  153. if self.passwordProtect:
  154. self.listAllCategories(replyTo, None, authToken)
  155. else:
  156. self.listAllCategories(replyTo)
  157. elif command == 'on':
  158. item = kw['item']
  159. try:
  160. sString = kw['sString']
  161. if self.passwordProtect:
  162. self.turnCatOn(item, replyTo, sString, authToken)
  163. else:
  164. self.turnCatOn(item, replyTo, sString)
  165. except KeyError:
  166. if self.passwordProtect:
  167. self.turnCatOn(item, replyTo, None, authToken)
  168. else:
  169. self.turnCatOn(item, replyTo)
  170. elif command == 'off':
  171. item = kw['item']
  172. try:
  173. sString = kw['sString']
  174. if self.passwordProtect:
  175. self.turnCatOff(item, replyTo, sString, authToken)
  176. else:
  177. self.turnCatOff(item, replyTo, sString)
  178. except KeyError:
  179. if self.passwordProtect:
  180. self.turnCatOff(item, replyTo, None, authToken)
  181. else:
  182. self.turnCatOff(item, replyTo)
  183. elif command == 'search':
  184. searchString = kw['searchString']
  185. if self.passwordProtect:
  186. self.searchForCat(searchString, replyTo, None, authToken)
  187. else:
  188. self.searchForCat(searchString, replyTo)
  189. elif command == 'logOff' and authToken:
  190. self.logOut(replyTo, authToken)
  191. else:
  192. replyTo.respond('Error: Invalid args')
  193. return
  194. except KeyError:
  195. pass
  196. # Basic Index Page
  197. if not authToken:
  198. replyTo.respond('<html><head>\n<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">\n<title>DirectNotify Web Interface</title>\n</head>\n<body>\n<div style="text-align: center;">\n<h1>DirectNotify Web Interface</h1>\n</div>\n<hr style="height: 2px;">\n<form method="get" action="debug" name="searchfom"><INPUT TYPE=HIDDEN NAME="command" VALUE="search">Search for a DirectNotify Category: <input name="searchString"> <input type=submit name="Submit"></button><br>\n</form>\n<br>\n<A HREF="%s?command=listAll">Display all DirectNotify Categories</a>\n</body>\n</html>' % (self.pageToHit))
  199. else:
  200. replyTo.respond('<html><head>\n<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">\n<title>DirectNotify Web Interface</title>\n</head>\n<body>\n<div style="text-align: center;">\n<h1>DirectNotify Web Interface</h1>\n</div>\n<hr style="height: 2px;">\n<form method="get" action="authDebug" name="searchfom"><INPUT TYPE=HIDDEN NAME="command" VALUE="search"><INPUT TYPE=HIDDEN NAME="authToken" VALUE="%s">Search for a DirectNotify Category: <input name="searchString"> <input type=submit name="Submit"></button><br>\n</form>\n<br>\n<A HREF="%s?command=listAll&authToken=%s">Display all DirectNotify Categories</a><BR>\n<A HREF="authDebug?command=logOff&authToken=%s">Log Off</a></body>\n</html>' % (authToken, self.pageToHit, authToken, authToken))
  201. def logOut(self, replyTo, authToken):
  202. # Delete token from auth list
  203. self.authTokens.remove(authToken)
  204. replyTo.respond('<HTML><HEAD><TITLE>Logout Sucessful</TITLE></HEAD>\n<BODY>Logout complete. You will need to login again to use the system</BODY>\n</HTML>')
  205. def authDebug(self, replyTo, **kw):
  206. try:
  207. authToken = kw['authToken']
  208. try:
  209. pos = self.authTokens.index(authToken)
  210. except ValueError:
  211. # authToken passed is not in the list
  212. replyTo.respond('Error: Client not authorized')
  213. return
  214. except (ValueError, KeyError):
  215. # authToken not passed in GET. Return an error
  216. replyTo.respond('Error: No auth token passed from client')
  217. return
  218. # If we've gotten this far, we have determined that an auth token was
  219. # passed in the HTTP GET and it is on the list of auth tokens.
  220. # Now we can pass this to the normal debug URI
  221. kw['authToken'] = authToken
  222. self.debug(replyTo, **kw)
  223. def startCheckingIncomingHTTP(self):
  224. taskMgr.remove('pollDirectDebugHTTPTask')
  225. taskMgr.doMethodLater(0.3,self.pollDirectDebugHTTPTask,'pollDirectDebugHTTPTask')
  226. def stopCheckingIncomingHTTP(self):
  227. taskMgr.remove('pollDirectDebugHTTPTask')
  228. def pollDirectDebugHTTPTask(self,task):
  229. self.web.poll()
  230. return Task.again
  231. def genToken(self):
  232. alpha = string.letters.upper()
  233. num = string.digits
  234. ranNumber = ''
  235. ranAlpha = ''
  236. for i in range(3):
  237. ranNumberOne = ranNumber + random.choice(num)
  238. for i in range(3):
  239. ranAlphaOne = ranAlpha + random.choice(alpha)
  240. for i in range(3):
  241. ranNumberTwo = ranNumber + random.choice(num)
  242. for i in range(3):
  243. ranAlphaTwo = ranAlpha + random.choice(alpha)
  244. token = "%s%s%s%s" % (ranAlphaOne, ranNumberOne, ranAlphaTwo, ranNumberTwo)
  245. return token