PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/amsn2/ui/front_ends/web/bend.py

https://github.com/lcontreras/amsn2
Python | 228 lines | 212 code | 9 blank | 7 comment | 9 complexity | dce3af805347a7d9faccc454ef8b6806 MD5 | raw file
Possible License(s): GPL-2.0
  1. import os
  2. import socket
  3. import errno
  4. import logging
  5. import re
  6. import gobject
  7. import sys
  8. if sys.hexversion < 0x020600f0:
  9. from cgi import parse_qs
  10. else:
  11. from urlparse import parse_qs
  12. from constants import BASEPATH
  13. from tinyhttpserver import TinyHTTPServer
  14. from time import time
  15. def uri_path_is_safe(path):
  16. if not BASEPATH and path[0] == '/':
  17. return False
  18. elif path[0:1] == '..':
  19. return False
  20. l = path.split('/')
  21. b = [d for d in l if d == '..']
  22. if len(b) >= len(l):
  23. return False
  24. return True
  25. class Backend(object):
  26. """
  27. This is the main comunication module,
  28. all comunication to the JS frontend will be issued from here
  29. """
  30. def __init__(self, core):
  31. self._core = core
  32. self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  33. self._socket.setblocking(False)
  34. self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  35. self._socket.bind((self._options.host, self._options.port))
  36. self._socket.listen(1)
  37. #self._workers = []
  38. self._rules = (
  39. (re.compile('/$'), self.get_index, None),
  40. (re.compile('/static/(.*)'), self.get_static_file, None),
  41. (re.compile('/out$'), self.out, self.out),
  42. (re.compile('/signin$'), None, self.post_signin),
  43. (re.compile('/contactClicked$'), None, self.post_contact_clicked),
  44. (re.compile('/sendMsg$'), None, self.post_send_msg),
  45. (re.compile('/closeCW$'), None, self.post_close_cw),
  46. (re.compile('/logout$'), None, self.post_logout),
  47. (re.compile('/changeMe$'), None, self.post_changeme),
  48. (re.compile('/changePresence$'), None, self.post_change_presence),
  49. #TODO: set dp, get (dp, dps),
  50. # add/remove group/contact
  51. )
  52. gobject.io_add_watch(self._socket, gobject.IO_IN, self.on_accept)
  53. self._q = ""
  54. self.login_window = None
  55. self.cl_window = None
  56. self.chat_windows = {}
  57. self.chat_widgets = {}
  58. self._logged_in = False
  59. self._last_poll = 0
  60. #TODO: on log out
  61. def cb():
  62. if self._logged_in:
  63. if time() - self._last_poll >= 30:
  64. self._core.sign_out_of_account()
  65. self._logged_in = False
  66. return True
  67. self._core._loop.timer_add(30000, cb)
  68. print "Now, open your favorite browser and go to http://%s:%d/" % (self._options.host, self._options.port)
  69. def on_accept(self, s, c):
  70. w = s.accept()
  71. t = TinyHTTPServer(self, *w, rules = self._rules)
  72. #self._workers.append(t)
  73. return True
  74. def out(self, w, uri, headers, body = None):
  75. if self._logged_in:
  76. if len(self._q):
  77. print ">>> %s" % (self._q,)
  78. self._last_poll = time()
  79. w.send_javascript(self._q)
  80. self._q = ""
  81. else:
  82. w.send_javascript("loggedOut();")
  83. def _args2JS(self, *args):
  84. call = ""
  85. for value in args:
  86. t = type(value).__name__
  87. if (t == 'tuple' or t == 'list'):
  88. call += '['+ self._args2JS(*value)+']'
  89. elif (t == 'unicode'):
  90. call += "'" + value.encode('unicode_escape').replace("'","\\'") + "',"
  91. elif (t == 'str'):
  92. call += "'" + value.encode('string_escape') + "',"
  93. elif (t == 'int'):
  94. call += str(value) + ","
  95. else:
  96. print t
  97. call += "'" + str(value).encode('string_escape') + "',"
  98. return call.rstrip(",")
  99. def send(self, event, *args):
  100. # The backend sent a message to the JS client
  101. # select the JS function to call depending on the type of event
  102. if args:
  103. self._q += event + '(' + self._args2JS(*args) + ');'
  104. else:
  105. self._q += event + '();'
  106. def get_index(self, w, uri, headers, body = None):
  107. self._core.main_window_shown()
  108. w.send_file(BASEPATH + "/static/amsn2.html", {'Content-Type':
  109. 'text/html; charset=utf-8'})
  110. def get_static_file(self, w, uri, headers, body = None):
  111. path = uri[2]
  112. if uri_path_is_safe(path):
  113. w.send_file(BASEPATH + path)
  114. else:
  115. w._404()
  116. def post_signin(self, w, uri, headers, body = None):
  117. if self.login_window is None:
  118. w._400()
  119. return
  120. if (body and 'content-type' in headers
  121. and headers['content-type'].startswith('application/x-www-form-urlencoded')):
  122. args = parse_qs(body)
  123. print "<<< signin: {'username': %s, 'password': ******}" %(args['username'],)
  124. self.login_window.signin(args['username'][0], args['password'][0])
  125. self._logged_in = True
  126. self._last_poll = time()
  127. w.write("HTTP/1.1 200 OK\r\n\r\n")
  128. w.close()
  129. return
  130. w._400()
  131. def post_contact_clicked(self, w, uri, headers, body = None):
  132. if self.cl_window is None:
  133. w._400()
  134. return
  135. if (body and 'content-type' in headers
  136. and headers['content-type'].startswith('application/x-www-form-urlencoded')):
  137. args = parse_qs(body)
  138. print "<<< contactClicked: %s" %(args,)
  139. self.cl_window.get_contactlist_widget().contact_clicked(args['uid'][0])
  140. w.write("HTTP/1.1 200 OK\r\n\r\n")
  141. w.close()
  142. return
  143. w._400()
  144. def post_send_msg(self, w, uri, headers, body = None):
  145. if (body and 'content-type' in headers
  146. and headers['content-type'].startswith('application/x-www-form-urlencoded')):
  147. args = parse_qs(body)
  148. print "<<< sendMsg: %s" %(args,)
  149. uid = args['uid'][0]
  150. if uid not in self.chat_widgets:
  151. w._400()
  152. return
  153. cw = self.chat_widgets[uid]
  154. cw.send_message(uid, args['msg'])
  155. w.write("HTTP/1.1 200 OK\r\n\r\n")
  156. w.close()
  157. return
  158. w._400()
  159. def post_close_cw(self, w, uri, headers, body = None):
  160. if (body and 'content-type' in headers
  161. and headers['content-type'].startswith('application/x-www-form-urlencoded')):
  162. args = parse_qs(body)
  163. print "<<< closeCW: %s" %(args,)
  164. uid = args['uid'][0]
  165. if uid not in self.chat_windows:
  166. w._400()
  167. return
  168. cw = self.chat_windows[uid]
  169. cw.close()
  170. w.write("HTTP/1.1 200 OK\r\n\r\n")
  171. w.close()
  172. return
  173. w._400()
  174. def post_logout(self, w, uri, headers, body = None):
  175. if self._core._account:
  176. self._core.sign_out_of_account()
  177. print "<<< logout"
  178. w.write("HTTP/1.1 200 OK\r\n\r\n")
  179. w.close()
  180. return
  181. w._400()
  182. def post_changeme(self, w, uri, headers, body = None):
  183. if (body and 'content-type' in headers
  184. and headers['content-type'].startswith('application/x-www-form-urlencoded')):
  185. args = parse_qs(body)
  186. print "<<< changeMe: %s" %(args,)
  187. print "TODO"
  188. w.write("HTTP/1.1 200 OK\r\n\r\n")
  189. w.close()
  190. return
  191. w._400()
  192. def post_change_presence(self, w, uri, headers, body = None):
  193. if (body and 'content-type' in headers
  194. and headers['content-type'].startswith('application/x-www-form-urlencoded')):
  195. args = parse_qs(body)
  196. print "<<< changePresence: %s" %(args,)
  197. print "TODO"
  198. w.write("HTTP/1.1 200 OK\r\n\r\n")
  199. w.close()
  200. return
  201. w._400()