/src/contact.py

http://pyaimt.googlecode.com/ · Python · 279 lines · 229 code · 20 blank · 30 comment · 23 complexity · 56ac1004e1bce515bc2e5630a6edb8e0 MD5 · raw file

  1. # Copyright 2005-2006 Daniel Henninger <jadestorm@nc.rr.com>
  2. # Licensed for distribution under the GPL version 2, check COPYING for details
  3. import utils
  4. from twisted.internet import reactor
  5. from twisted.words.xish.domish import Element
  6. import jabw
  7. import config
  8. from debug import LogEvent, INFO, WARN, ERROR
  9. import lang
  10. import sha
  11. import legacy
  12. import globals
  13. import base64
  14. if not config.disableAvatars:
  15. import Image
  16. import StringIO
  17. class Contact:
  18. """ Represents a Jabber contact """
  19. def __init__(self, jid, sub, contactList):
  20. self.jid = jid
  21. self.contactList = contactList
  22. self.groups = []
  23. self.sub = sub
  24. self.nickname = ""
  25. self.avatar = None
  26. self.show = ""
  27. self.status = ""
  28. self.url = ""
  29. self.ptype = "unavailable"
  30. def removeMe(self):
  31. """ Destroys this object. Does not remove the contact from the server's list. """
  32. self.contactList = None
  33. self.avatar = None
  34. def syncContactGrantedAuth(self):
  35. """ Since last using the transport the user has been granted authorisation by this contact.
  36. Call this to synchronise the user's Jabber list with their legacy list after logon. """
  37. if self.sub == "none":
  38. self.sub = "to"
  39. elif self.sub == "from":
  40. self.sub = "both"
  41. else:
  42. return
  43. self.updateRoster("subscribe")
  44. def syncContactRemovedAuth(self):
  45. """ Since last using the transport the user has been blocked by this contact.
  46. Call this to synchronise the user's Jabber list with their legacy list after logon. """
  47. if self.sub == "to":
  48. self.sub = "none"
  49. elif self.sub == "both":
  50. self.sub = "from"
  51. else:
  52. return
  53. self.updateRoster("unsubscribed")
  54. def syncUserGrantedAuth(self):
  55. """ Since last using the transport the user has granted authorisation to this contact.
  56. Call this to synchronise the user's Jabber list with their legacy list after logon. """
  57. if self.sub == "none":
  58. self.sub = "from"
  59. elif self.sub == "to":
  60. self.sub = "both"
  61. else:
  62. return
  63. self.updateRoster("subscribe")
  64. def syncUserRemovedAuth(self):
  65. """ Since last using the transport the user has removed this contact's authorisation.
  66. Call this to synchronise the user's Jabber list with their legacy list after logon. """
  67. if self.sub == "from":
  68. self.sub = "none"
  69. elif self.sub == "both":
  70. self.sub = "to"
  71. else:
  72. return
  73. self.updateRoster("unsubscribe")
  74. def syncGroups(self, groups, push=True):
  75. """ Set the groups that this contact is in on the legacy service.
  76. By default this pushes the groups out with a presence subscribed packet. """
  77. self.groups = groups
  78. if push: self.updateRoster("subscribed");
  79. def contactGrantsAuth(self):
  80. """ Live roster event """
  81. if self.sub == "none":
  82. self.sub = "to"
  83. elif self.sub == "from":
  84. self.sub = "both"
  85. self.sendSub("subscribed")
  86. self.sendPresence()
  87. def contactRemovesAuth(self):
  88. """ Live roster event """
  89. if self.sub == "to":
  90. self.sub = "none"
  91. elif self.sub == "both":
  92. self.sub = "from"
  93. self.sendSub("unsubscribed")
  94. def contactRequestsAuth(self):
  95. """ Live roster event """
  96. self.sendSub("subscribe")
  97. def contactDerequestsAuth(self):
  98. """ Live roster event """
  99. self.sendSub("unsubscribe")
  100. def jabberSubscriptionReceived(self, subtype):
  101. """ Updates the subscription state internally and pushes the update to the legacy server """
  102. if subtype == "subscribe":
  103. if self.sub == "to" or self.sub == "both":
  104. self.sendSub("subscribed")
  105. self.contactList.legacyList.addContact(self.jid)
  106. elif subtype == "subscribed":
  107. if self.sub == "none":
  108. self.sub = "from"
  109. if self.sub == "to":
  110. self.sub = "both"
  111. self.contactList.legacyList.authContact(self.jid)
  112. elif(subtype == "unsubscribe"):
  113. if self.sub == "none" and self.sub == "from":
  114. self.sendSub("unsubscribed")
  115. if self.sub == "both":
  116. self.sub = "from"
  117. if self.sub == "to":
  118. self.sub = "none"
  119. self.contactList.legacyList.removeContact(self.jid)
  120. elif(subtype == "unsubscribed"):
  121. if self.sub == "both":
  122. self.sub = "to"
  123. if self.sub == "from":
  124. self.sub = "none"
  125. self.contactList.legacyList.deauthContact(self.jid)
  126. def updateNickname(self, nickname, push=True):
  127. if self.nickname != nickname:
  128. self.nickname = nickname
  129. # will re-remove this if it's removed from JEP-0172.
  130. #self.sendNickname()
  131. if push: self.sendPresence()
  132. #n = Element((None, "nick"))
  133. #n.attributes["xmlns"] = globals.NICK
  134. #n.addContent(nickname)
  135. #self.contactList.session.pytrans.pubsub.localPublish(self.jid, globals.NICK, "current", n)
  136. def updatePresence(self, show, status, ptype, force=False, tojid=None, url=None):
  137. updateFlag = (self.show != show or self.status != status or self.ptype != ptype or force)
  138. self.show = show
  139. self.status = status
  140. self.ptype = ptype
  141. self.url = url
  142. if updateFlag:
  143. self.sendPresence(tojid)
  144. def updateAvatar(self, avatar=None, push=True):
  145. if config.disableAvatars: return
  146. if self.avatar == avatar: return
  147. self.avatar = avatar
  148. if push: self.sendPresence()
  149. #if self.avatar and not config.disableAvatars and not config.disablePEPAvatars:
  150. #avatarHash = self.avatar.getImageHash()
  151. #avatarData = self.avatar.getImageData()
  152. #inbuff = StringIO.StringIO(avatarData)
  153. #img = Image.open(inbuff)
  154. #d = Element((None, "data"))
  155. #d.attributes["xmlns"] = globals.AVATARDATA
  156. #d.addContent(base64.encodestring(avatarData).replace("\n",""))
  157. #self.contactList.session.pytrans.pubsub.localPublish(self.jid, globals.AVATARDATA, avatarHash, d)
  158. #m = Element((None, "metadata"))
  159. #m.attributes["xmlns"] = globals.AVATARMETADATA
  160. #mi = m.addElement("info")
  161. #mi.attributes["id"] = avatarHash
  162. #mi.attributes["type"] = "image/png"
  163. #mi.attributes["bytes"] = str(len(avatarData))
  164. #mi.attributes["height"] = str(img.size[0])
  165. #mi.attributes["width"] = str(img.size[1])
  166. #self.contactList.session.pytrans.pubsub.localPublish(self.jid, globals.AVATARMETADATA, avatarHash, m)
  167. def sendSub(self, ptype):
  168. self.contactList.session.sendPresence(to=self.contactList.session.jabberID, fro=self.jid, ptype=ptype)
  169. def sendNickname(self, tojid=None):
  170. if not tojid:
  171. tojid=self.contactList.session.jabberID
  172. if self.nickname:
  173. el = Element((None, "message"))
  174. el.attributes["to"] = tojid
  175. el.attributes["from"] = self.jid
  176. nick = el.addElement("nick")
  177. nick.attributes["xmlns"] = globals.NICK
  178. nick.addContent(self.nickname)
  179. self.contactList.session.pytrans.send(el)
  180. def sendPresence(self, tojid=None):
  181. avatarHash = ""
  182. if self.avatar and not config.disableAvatars:
  183. avatarHash = self.avatar.getImageHash()
  184. caps = Element((None, "c"))
  185. caps.attributes["xmlns"] = globals.CAPS
  186. caps.attributes["node"] = legacy.url + "/protocol/caps"
  187. caps.attributes["ver"] = legacy.version
  188. if not tojid:
  189. tojid=self.contactList.session.jabberID
  190. self.contactList.session.sendPresence(to=tojid, fro=self.jid, ptype=self.ptype, show=self.show, status=self.status, avatarHash=avatarHash, nickname=self.nickname, payload=[caps], url=self.url)
  191. def updateRoster(self, ptype):
  192. self.contactList.session.sendRosterImport(jid=self.jid, ptype=ptype, sub=self.sub, groups=self.groups)
  193. def fillvCard(self, vCard, jid):
  194. if self.nickname:
  195. NICKNAME = vCard.addElement("NICKNAME")
  196. NICKNAME.addContent(self.nickname)
  197. if self.avatar and not config.disableAvatars and not config.disableVCardAvatars:
  198. PHOTO = self.avatar.makePhotoElement()
  199. vCard.addChild(PHOTO)
  200. user = jid.split('@')[0]
  201. return self.contactList.session.legacycon.jabberVCardRequest(vCard, user)
  202. class ContactList:
  203. """ Represents the Jabber contact list """
  204. def __init__(self, session):
  205. LogEvent(INFO, session.jabberID)
  206. self.session = session
  207. self.contacts = {}
  208. def removeMe(self):
  209. """ Cleanly removes the object """
  210. LogEvent(INFO, self.session.jabberID)
  211. for jid in self.contacts:
  212. self.contacts[jid].updatePresence("", "", "unavailable")
  213. self.contacts[jid].removeMe()
  214. self.contacts = {}
  215. self.session = None
  216. self.legacyList = None
  217. def resendLists(self, tojid=None):
  218. for jid in self.contacts:
  219. if self.contacts[jid].status != "unavailable":
  220. self.contacts[jid].sendPresence(tojid)
  221. LogEvent(INFO, self.session.jabberID)
  222. def createContact(self, jid, sub):
  223. """ Creates a contact object. Use this to initialise the contact list
  224. Returns a Contact object which you can call sync* methods on to synchronise
  225. the user's legacy contact list with their Jabber list """
  226. LogEvent(INFO, self.session.jabberID)
  227. c = Contact(jid, sub, self)
  228. self.contacts[jid] = c
  229. return c
  230. def getContact(self, jid):
  231. """ Finds the contact. If one doesn't exist then a new one is created, with sub set to "none" """
  232. if not self.contacts.has_key(jid):
  233. self.contacts[jid] = Contact(jid, "none", self)
  234. return self.contacts[jid]
  235. def findContact(self, jid):
  236. if self.contacts.has_key(jid):
  237. return self.contacts[jid]
  238. return None
  239. def jabberSubscriptionReceived(self, jid, subtype):
  240. self.getContact(jid).jabberSubscriptionReceived(subtype)