]> code.delx.au - pymsnt/blob - src/contact.py
MSN contacts now have resources.
[pymsnt] / src / contact.py
1 # Copyright 2005 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
3
4 import utils
5 from twisted.internet import reactor
6 from tlib.xmlw import Element
7 from debug import LogEvent, INFO, WARN, ERROR
8 import disco
9 import legacy
10 import jabw
11 import config
12 import lang
13 import sha
14
15
16 class Contact:
17 """ Represents a Jabber contact """
18 def __init__(self, jid, sub, contactList):
19 self.jid = jid
20 self.contactList = contactList
21 self.groups = []
22 self.origsub = sub
23 self.sub = sub
24 self.nickname = ""
25 self.avatar = None
26 self.show = ""
27 self.status = ""
28 self.ptype = "unavailable"
29
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
35 def syncContactGrantedAuth(self):
36 """ Since last using the transport the user has been granted authorisation by this contact.
37 Call this to synchronise the user's Jabber list with their legacy list after logon. """
38 if(self.sub == "none"):
39 self.sub = "to"
40 elif(self.sub == "from"):
41 self.sub = "both"
42 else:
43 return
44
45 def syncContactRemovedAuth(self):
46 """ Since last using the transport the user has been blocked by this contact.
47 Call this to synchronise the user's Jabber list with their legacy list after logon. """
48 if(self.sub == "to"):
49 self.sub = "none"
50 elif(self.sub == "both"):
51 self.sub = "from"
52 else:
53 return
54
55 def syncUserGrantedAuth(self):
56 """ Since last using the transport the user has granted authorisation to this contact.
57 Call this to synchronise the user's Jabber list with their legacy list after logon. """
58 if(self.sub == "none"):
59 self.sub = "from"
60 elif(self.sub == "to"):
61 self.sub = "both"
62 else:
63 return
64
65 def syncUserRemovedAuth(self):
66 """ Since last using the transport the user has removed this contact's authorisation.
67 Call this to synchronise the user's Jabber list with their legacy list after logon. """
68 if(self.sub == "from"):
69 self.sub = "none"
70 elif(self.sub == "both"):
71 self.sub = "to"
72 else:
73 return
74
75 def syncGroups(self, groups, push=True):
76 """ Set the groups that this contact is in on the legacy service.
77 By default this pushes the groups out with a presence subscribed packet. """
78 self.groups = groups
79 if push: self.syncRoster(ptype="subscribed");
80
81 syncChoice = {
82 ("none", "none") : "", #
83 ("none", "to" ) : "subscribe", # User+ Contact
84 ("none", "from") : "subscribe", # User Contact+
85 ("none", "both") : "subscribe", # User+ Contact+
86 ("to" , "none") : "unsubscribed", # User- Contact
87 ("to" , "to" ) : "", #
88 ("to" , "from") : "unsubscribe", # User- Contact+ **
89 ("to" , "both") : "subscribe", # User Contact+
90 ("from", "none") : "unsubscribe", # User Contact-
91 ("from", "to" ) : "subscribe", # User+ Contact- *
92 ("from", "from") : "", #
93 ("from", "both") : "subscribe", # User+ Contact
94 ("both", "none") : "unsubscribed", # User- Contact- *
95 ("both", "to" ) : "unsubscribe", # User Contact-
96 ("both", "from") : "unsubscribed", # User- Contact
97 ("both", "both") : "" #
98 }
99
100 def syncRoster(self, ptype=""):
101 if not ptype:
102 ptype = self.syncChoice.get((self.origsub, self.sub))
103 if ptype:
104 self.contactList.session.sendRosterImport(jid=self.jid, ptype=ptype, sub=self.sub, groups=self.groups, name=self.nickname)
105
106 def contactGrantsAuth(self):
107 """ Live roster event """
108 if(self.sub == "none"):
109 self.sub = "to"
110 elif(self.sub == "from"):
111 self.sub = "both"
112 self.sendSub("subscribed")
113 self.sendPresence()
114
115 def contactRemovesAuth(self):
116 """ Live roster event """
117 if(self.sub == "to"):
118 self.sub = "none"
119 elif(self.sub == "both"):
120 self.sub = "from"
121 self.sendSub("unsubscribed")
122
123 def contactRequestsAuth(self):
124 """ Live roster event """
125 self.sendSub("subscribe")
126
127 def contactDerequestsAuth(self):
128 """ Live roster event """
129 self.sendSub("unsubscribe")
130
131 def jabberSubscriptionReceived(self, subtype):
132 """ Updates the subscription state internally and pushes the update to the legacy server """
133 if subtype == "subscribe":
134 if self.sub == "to" or self.sub == "both":
135 self.sendSub("subscribed")
136 else:
137 self.contactList.legacyList.addContact(self.jid)
138
139 elif subtype == "subscribed":
140 if self.sub == "none":
141 self.sub = "from"
142 if self.sub == "to":
143 self.sub = "both"
144 self.contactList.legacyList.authContact(self.jid)
145
146 elif subtype == "unsubscribe":
147 if self.sub == "none" or self.sub == "from":
148 self.sendSub("unsubscribed")
149 if self.sub == "both":
150 self.sub = "from"
151 if self.sub == "to":
152 self.sub = "none"
153 self.contactList.legacyList.removeContact(self.jid)
154
155 elif(subtype == "unsubscribed"):
156 if(self.sub == "both"):
157 self.sub = "to"
158 if(self.sub == "from"):
159 self.sub = "none"
160 self.contactList.legacyList.deauthContact(self.jid)
161
162 def updateNickname(self, nickname, push=True):
163 if(self.nickname != nickname):
164 self.nickname = nickname
165 if(push): self.sendPresence()
166
167 def updatePresence(self, show, status, ptype, force=False):
168 updateFlag = (self.show != show or self.status != status or self.ptype != ptype or force)
169 self.show = show
170 self.status = status
171 self.ptype = ptype
172 if(updateFlag):
173 self.sendPresence()
174
175 def updateAvatar(self, avatar=None, push=True):
176 if(self.avatar == avatar): return
177 self.avatar = avatar
178 if(push): self.sendPresence()
179
180 def sendSub(self, ptype):
181 self.contactList.session.sendPresence(to=self.contactList.session.jabberID, fro=self.jid + "/" + legacy.id, ptype=ptype)
182
183 def sendPresence(self, tojid=""):
184 avatarHash = ""
185 if(self.avatar):
186 avatarHash = self.avatar.getImageHash()
187 caps = Element((None, "c"))
188 caps.attributes["xmlns"] = disco.CAPS
189 caps.attributes["node"] = legacy.url + "/protocol/caps"
190 caps.attributes["ver"] = legacy.version
191 if not tojid:
192 tojid=self.contactList.session.jabberID
193 self.contactList.session.sendPresence(to=tojid, fro=self.jid + "/" + legacy.id, ptype=self.ptype, show=self.show, status=self.status, avatarHash=avatarHash, nickname=self.nickname, payload=[caps])
194
195
196
197 class ContactList:
198 """ Represents the Jabber contact list """
199 def __init__(self, session):
200 LogEvent(INFO, session.jabberID)
201 self.session = session
202 self.contacts = {}
203
204 def removeMe(self):
205 """ Cleanly removes the object """
206 LogEvent(INFO, self.session.jabberID)
207 for jid in self.contacts:
208 self.contacts[jid].updatePresence("", "", "unavailable")
209 self.contacts[jid].removeMe()
210 self.contacts = {}
211 self.session = None
212 self.legacyList = None
213
214 def resendLists(self, tojid=""):
215 for jid in self.contacts:
216 if(self.contacts[jid].status != "unavailable"):
217 self.contacts[jid].sendPresence(tojid)
218 LogEvent(INFO, self.session.jabberID)
219
220 def createContact(self, jid, sub):
221 """ Creates a contact object. Use this to initialise the contact list
222 Returns a Contact object which you can call sync* methods on to synchronise
223 the user's legacy contact list with their Jabber list """
224 LogEvent(INFO, self.session.jabberID)
225 c = Contact(jid, sub, self)
226 self.contacts[jid] = c
227 return c
228
229 def getContact(self, jid):
230 """ Finds the contact. If one doesn't exist then a new one is created, with sub set to "none" """
231 if(not self.contacts.has_key(jid)):
232 self.contacts[jid] = Contact(jid, "none", self)
233 return self.contacts[jid]
234
235 def findContact(self, jid):
236 if(self.contacts.has_key(jid)):
237 return self.contacts[jid]
238 return None
239
240 def jabberSubscriptionReceived(self, jid, subtype):
241 self.getContact(jid).jabberSubscriptionReceived(subtype)
242
243