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