PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/dumbhippo/branches/production/client/mugshot-python/mugshot/TwistedModel.py

https://gitlab.com/manoj-makkuboy/magnetism
Python | 325 lines | 316 code | 9 blank | 0 comment | 15 complexity | c972774a27afe9d1f5eaf33fa722d97b MD5 | raw file
  1. import os
  2. import re
  3. from twisted.words.protocols.jabber import client, jid, xmlstream
  4. from twisted.words.protocols.jabber.client import IQ
  5. from twisted.words.xish import domish
  6. from twisted.internet import reactor
  7. from mugshot.AbstractModel import *
  8. from mugshot.Resource import *
  9. from mugshot.NotificationSet import *
  10. from mugshot.Query import *
  11. class MustLoginException(Exception):
  12. """Raised if the username and password can't be found"""
  13. def __init__(self, message):
  14. Exception.__init__(self, message)
  15. class TwistedModel(AbstractModel):
  16. """An implementation of the Mugshot data model for testing purposes
  17. Twisted model uses the networking and main-loop functions of the 'twisted' framework.
  18. """
  19. def __init__(self, server="mugshot.org:5222", username=None, password=None):
  20. """Create a new TwistedModel instance that connects to the specified server.
  21. Arguments:
  22. server: Server to connect to. The port may be specified by appending it after
  23. a colon. If the port is not specified, a default value will be used (default='mugshot.org')
  24. username: GUID of the user to connect as (if not specified, found from cookies.txt)
  25. password: Password of the user (if not specified, found from cookies.txt)
  26. """
  27. AbstractModel.__init__(self)
  28. colon = server.find(":")
  29. if (colon >= 0):
  30. self.server = server[:colon]
  31. self.port = int(server[colon + 1:])
  32. else:
  33. self.server = server
  34. if server == "mugshot.org":
  35. self.port = 5222
  36. else:
  37. self.port = 21020
  38. if username == None and password == None:
  39. username, password = _parse_cookies(self.server)
  40. elif username != None or password != None:
  41. raise MustLoginException("Either both user and password must be specified or neither")
  42. # FIXME: Use a legal resource
  43. self.username = username
  44. user_jid = jid.JID("%s@%s/mugshot-python" % (_guid_to_jabber_id(username), self.server))
  45. self.__factory = client.basicClientFactory(user_jid, password)
  46. self._xmlstream = None
  47. def run(self):
  48. """Connect to the server and start the twisted main loop.
  49. Note that to use this for anything but testing purposes, you'd want to break
  50. the 'connect to server' and 'start the main loop' parts, since the caller would
  51. presumbaly want to start the main loop itself.
  52. """
  53. global reactor
  54. self.__factory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self.__on_auth_succeeded)
  55. self.__factory.addBootstrap(client.BasicAuthenticator.AUTH_FAILED_EVENT, self.__on_auth_failed)
  56. reactor.connectTCP(self.server, self.port, self.__factory)
  57. reactor.run()
  58. def query(self, method, fetch=None, single_result=False, **kwargs):
  59. return TwistedQuery(self, method, fetch, single_result, kwargs)
  60. def __on_auth_succeeded(self, xmlstream):
  61. # Nothing happens until we actually send a presence
  62. presence = domish.Element(('jabber:client', 'presence'))
  63. presence.addElement('status').addContent('Online')
  64. xmlstream.send(presence)
  65. # add a callback for the messages
  66. xmlstream.addObserver('/message', self.__on_message)
  67. self._xmlstream = xmlstream
  68. self._on_connected()
  69. def __on_auth_failed(self, xmlstream):
  70. global reactor
  71. print 'Auth failed!'
  72. reactor.stop()
  73. def __get_resource_id(self, resource_element):
  74. resource_id = resource_element.getAttribute(("http://mugshot.org/p/system", "resourceId"))
  75. if resource_id == None:
  76. return None
  77. resource_base = None
  78. parent = resource_element
  79. while resource_base == None and parent != None:
  80. resource_base = parent.getAttribute(("http://mugshot.org/p/system", "resourceBase"))
  81. parent = parent.parent
  82. if resource_base != None:
  83. return resource_base + resource_id
  84. else:
  85. return resource_id
  86. def __property_value(self, element, type_char):
  87. value = self.__get_resource_id(element)
  88. if value != None:
  89. if type_char != 'r':
  90. raise Exception("Resource-valued property must have type char 'r'")
  91. try:
  92. value = self._get_resource(value)
  93. except KeyError:
  94. raise Exception("Resource-valued property points to a resource we don't know about")
  95. return value
  96. value = u""
  97. for n in element.children:
  98. if isinstance(n, basestring):
  99. value += n
  100. if type_char == 'b':
  101. return value.lower.equals("true")
  102. elif type_char == 'i':
  103. return int(value)
  104. elif type_char == 'l':
  105. return long(value)
  106. elif type_char == 'f':
  107. return float(type_char)
  108. elif type_char == 's':
  109. return value
  110. elif type_char == 'u':
  111. return value
  112. elif type_char == 'r':
  113. raise Exception("resource_id attribute missing for resource-valued property")
  114. def __update_property_from_element(self, resource, property_element, seen_types, notifications):
  115. type_attr = property_element.getAttribute(("http://mugshot.org/p/system", "type"))
  116. if (type_attr == None):
  117. try:
  118. type_attr = seen_types[(property_element.uri, property_element.name)]
  119. except KeyError:
  120. raise Exception("Type attribute missing")
  121. else:
  122. seen_types[(property_element.uri, property_element.name)] = type_attr
  123. m = re.match(r"^(\+?)([bilfsru])([*?]?)$", type_attr)
  124. if (m == None):
  125. raise Exception("Unrecognized type string '%s'" % type_attr)
  126. if (m.group(3) == '*'):
  127. cardinality = CARDINALITY_N
  128. elif (m.group(3) == '?'):
  129. cardinality = CARDINALITY_01
  130. else:
  131. cardinality = CARDINALITY_1
  132. update_attr = property_element.getAttribute(("http://mugshot.org/p/system", "update"))
  133. if update_attr == None:
  134. update = UPDATE_REPLACE
  135. elif update_attr == "add":
  136. update = UPDATE_ADD
  137. elif update_attr == "replace":
  138. update = UPDATE_REPLACE
  139. elif update_attr == "delete":
  140. update = UPDATE_DELETE
  141. elif update_attr == "clear":
  142. update = UPDATE_CLEAR
  143. else:
  144. raise Exception("Bad update value: " + update)
  145. if update == UPDATE_CLEAR:
  146. value = None
  147. else:
  148. value = self.__property_value(property_element, m.group(2))
  149. resource._update_property((property_element.uri, property_element.name), update, cardinality, value, notifications=notifications)
  150. def _update_resource_from_element(self, resource_element, notifications=None):
  151. resource_id = self.__get_resource_id(resource_element)
  152. if resource_id == None:
  153. return
  154. resource = self._ensure_resource(resource_id, resource_element.uri)
  155. seen_types = {}
  156. for property_element in resource_element.elements():
  157. self.__update_property_from_element(resource, property_element, seen_types, notifications=notifications)
  158. return resource
  159. def __on_message(self, message):
  160. child = message.firstChildElement()
  161. if child.uri == "http://mugshot.org/p/system" and child.name == "notify":
  162. notifications = NotificationSet(self)
  163. for resource_element in child.elements():
  164. self._update_resource_from_element(resource_element, notifications=notifications)
  165. notifications.send()
  166. class TwistedQuery(Query):
  167. """Implementation of Query for TwistedModel"""
  168. def __init__(self, model, method, fetch, single_result, params):
  169. Query.__init__(self, single_result)
  170. self.__model = model
  171. self.__iq = IQ(model._xmlstream, "get")
  172. self.__single_result = single_result
  173. element = self.__iq.addElement(method)
  174. for name in params:
  175. param = element.addElement(("http://mugshot.org/p/system", "param"))
  176. param["name"] = name
  177. param.addContent(params[name])
  178. if fetch != None:
  179. element[("http://mugshot.org/p/system", "fetch")] = fetch
  180. self.__iq.addCallback(self.__on_reply)
  181. def __is_indirect(self, resource_element):
  182. indirect_attr = resource_element.getAttribute(("http://mugshot.org/p/system", "indirect"))
  183. return indirect_attr != None and indirect_attr.lower() == "true"
  184. def __on_reply(self, iq):
  185. child = iq.firstChildElement()
  186. result = []
  187. for resource_element in child.elements():
  188. resource = self.__model._update_resource_from_element(resource_element)
  189. if resource != None and not self.__is_indirect(resource_element):
  190. result.append(resource)
  191. self._on_success(result)
  192. def execute(self):
  193. self.__iq.send()
  194. ################################## Utility functions #########################################3
  195. def _guid_to_jabber_id(guid):
  196. return re.sub(r"([a-z])", r"\1_", guid)
  197. def _file_exists(f):
  198. try:
  199. os.stat(f)
  200. return True;
  201. except OSError:
  202. return False
  203. def _find_cookies_file():
  204. home = os.environ['HOME']
  205. cookies_file = None
  206. try:
  207. for subdir in os.listdir(os.path.join(home, ".mozilla", "firefox")):
  208. if (subdir.endswith(".default")):
  209. possible = os.path.join(home, ".mozilla", "firefox", subdir, "cookies.txt")
  210. if (_file_exists(possible)):
  211. cookies_file = possible
  212. break
  213. except OSError:
  214. pass
  215. if not cookies_file:
  216. raise MustLoginException("Can't find your browser cookies file")
  217. return cookies_file
  218. _COOKIE_REGEXP = re.compile(r"^(\S+)(?:\s+\S+){4}\s+(\S+)\s+(.*)$")
  219. def _parse_cookies(server):
  220. cookies_file = _find_cookies_file()
  221. f = open(cookies_file)
  222. username = None
  223. password = None
  224. for l in f:
  225. if re.match(r"^\s*#", l):
  226. continue
  227. if re.match(r"^\s*$", l):
  228. continue
  229. l = l.rstrip()
  230. m = _COOKIE_REGEXP.match(l)
  231. if m:
  232. host = m.group(1)
  233. cookie_name = m.group(2)
  234. value = m.group(3)
  235. if host.endswith(server) and cookie_name == "auth":
  236. cookie_username = None
  237. cookie_password = None
  238. fields = value.split("&")
  239. for field in fields:
  240. (k,v) = field.split("=")
  241. if k == "host":
  242. cookie_host = v
  243. elif k == "name":
  244. cookie_username = v
  245. elif k == "password":
  246. cookie_password = v
  247. if cookie_host == server and cookie_username != None and cookie_password != None:
  248. username = cookie_username
  249. password = cookie_password
  250. break
  251. f.close()
  252. if username == None or password == None:
  253. raise MustLoginException("Can't find the Mugshot authentication cookie for %s, do you need to log in?" % server)
  254. return (username, password)