root/tags/gajim-0.11.4/src/common/contacts.py

Revision 9085, 17.1 kB (checked in by asterix, 13 months ago)

typo contact1 -> contact2

Line 
1## common/contacts.py
2##
3## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org>
4## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com>
5##
6##
7## This program is free software; you can redistribute it and/or modify
8## it under the terms of the GNU General Public License as published
9## by the Free Software Foundation; version 2 only.
10##
11## This program is distributed in the hope that it will be useful,
12## but WITHOUT ANY WARRANTY; without even the implied warranty of
13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14## GNU General Public License for more details.
15##
16
17import common.gajim
18
19class Contact:
20        '''Information concerning each contact'''
21        def __init__(self, jid='', name='', groups=[], show='', status='', sub='',
22        ask='', resource='', priority=0, keyID='', our_chatstate=None,
23        chatstate=None, last_status_time=None, msg_id = None, composing_xep = None):
24                self.jid = jid
25                self.name = name
26                self.contact_name = '' # nick choosen by contact
27                self.groups = groups
28                self.show = show
29                self.status = status
30                self.sub = sub
31                self.ask = ask
32                self.resource = resource
33                self.priority = priority
34                self.keyID = keyID
35
36                # please read jep-85 http://www.jabber.org/jeps/jep-0085.html
37                # we keep track of jep85 support with the peer by three extra states:
38                # None, False and 'ask'
39                # None if no info about peer
40                # False if peer does not support jep85
41                # 'ask' if we sent the first 'active' chatstate and are waiting for reply
42                # this holds what WE SEND to contact (our current chatstate)
43                self.our_chatstate = our_chatstate
44                self.msg_id = msg_id
45                # tell which JEP we're using for composing state
46                # None = have to ask, XEP-0022 = use this jep,
47                # XEP-0085 = use this jep, False = no composing support
48                self.composing_xep = composing_xep
49                # this is contact's chatstate
50                self.chatstate = chatstate
51                self.last_status_time = last_status_time
52
53        def get_full_jid(self):
54                if self.resource:
55                        return self.jid + '/' + self.resource
56                return self.jid
57
58        def get_shown_name(self):
59                if self.name:
60                        return self.name
61                if self.contact_name:
62                        return self.contact_name
63                return self.jid.split('@')[0]
64
65        def is_hidden_from_roster(self):
66                '''if contact should not be visible in roster'''
67                # XEP-0162: http://www.xmpp.org/extensions/xep-0162.html
68                if self.is_transport():
69                        return False
70                if self.sub in ('both', 'to'):
71                        return False
72                if self.sub in ('none', 'from') and self.ask == 'subscribe':
73                        return False
74                if self.sub in ('none', 'from') and (self.name or len(self.groups)):
75                        return False
76                if _('Not in Roster') in self.groups:
77                        return False
78                return True
79
80        def is_observer(self):
81                # XEP-0162: http://www.xmpp.org/extensions/xep-0162.html
82                is_observer = False
83                if self.sub == 'from' and not self.is_transport()\
84                and self.is_hidden_from_roster():
85                        is_observer = True
86                return is_observer
87
88        def is_transport(self):
89                # if not '@' or '@' starts the jid then contact is transport
90                if self.jid.find('@') <= 0:
91                        return True
92                return False
93
94
95class GC_Contact:
96        '''Information concerning each groupchat contact'''
97        def __init__(self, room_jid='', name='', show='', status='', role='',
98        affiliation='', jid = '', resource = ''):
99                self.room_jid = room_jid
100                self.name = name
101                self.show = show
102                self.status = status
103                self.role = role
104                self.affiliation = affiliation
105                self.jid = jid
106                self.resource = resource
107
108        def get_full_jid(self):
109                return self.room_jid + '/' + self.name
110
111        def get_shown_name(self):
112                return self.name
113
114class Contacts:
115        '''Information concerning all contacts and groupchat contacts'''
116        def __init__(self):
117                self._contacts = {} # list of contacts {acct: {jid1: [C1, C2]}, } one Contact per resource
118                self._gc_contacts = {} # list of contacts that are in gc {acct: {room_jid: {nick: C}}}
119
120                # For meta contacts:
121                self._metacontacts_tags = {}
122
123        def change_account_name(self, old_name, new_name):
124                self._contacts[new_name] = self._contacts[old_name]
125                self._gc_contacts[new_name] = self._gc_contacts[old_name]
126                self._metacontacts_tags[new_name] = self._metacontacts_tags[old_name]
127                del self._contacts[old_name]
128                del self._gc_contacts[old_name]
129                del self._metacontacts_tags[old_name]
130
131        def add_account(self, account):
132                self._contacts[account] = {}
133                self._gc_contacts[account] = {}
134                if not self._metacontacts_tags.has_key(account):
135                        self._metacontacts_tags[account] = {}
136
137        def get_accounts(self):
138                return self._contacts.keys()
139
140        def remove_account(self, account):
141                del self._contacts[account]
142                del self._gc_contacts[account]
143                del self._metacontacts_tags[account]
144
145        def create_contact(self, jid='', name='', groups=[], show='', status='',
146                sub='', ask='', resource='', priority=0, keyID='', our_chatstate=None,
147                chatstate=None, last_status_time=None, composing_xep=None):
148                return Contact(jid, name, groups, show, status, sub, ask, resource,
149                        priority, keyID, our_chatstate, chatstate, last_status_time,
150                        composing_xep)
151       
152        def copy_contact(self, contact):
153                return self.create_contact(jid = contact.jid, name = contact.name,
154                        groups = contact.groups, show = contact.show, status = contact.status,
155                        sub = contact.sub, ask = contact.ask, resource = contact.resource,
156                        priority = contact.priority, keyID = contact.keyID,
157                        our_chatstate = contact.our_chatstate, chatstate = contact.chatstate,
158                        last_status_time = contact.last_status_time)
159
160        def add_contact(self, account, contact):
161                # No such account before ?
162                if not self._contacts.has_key(account):
163                        self._contacts[account] = {contact.jid : [contact]}
164                        return
165                # No such jid before ?
166                if not self._contacts[account].has_key(contact.jid):
167                        self._contacts[account][contact.jid] = [contact]
168                        return
169                contacts = self._contacts[account][contact.jid]
170                # We had only one that was offline, remove it
171                if len(contacts) == 1 and contacts[0].show == 'offline':
172                        # Do not use self.remove_contact: it deteles
173                        # self._contacts[account][contact.jid]
174                        contacts.remove(contacts[0])
175                # If same JID with same resource already exists, use the new one
176                for c in contacts:
177                        if c.resource == contact.resource:
178                                self.remove_contact(account, c)
179                                break
180                contacts.append(contact)
181
182        def remove_contact(self, account, contact):
183                if not self._contacts.has_key(account):
184                        return
185                if not self._contacts[account].has_key(contact.jid):
186                        return
187                if contact in self._contacts[account][contact.jid]:
188                        self._contacts[account][contact.jid].remove(contact)
189                if len(self._contacts[account][contact.jid]) == 0:
190                        del self._contacts[account][contact.jid]
191
192        def clear_contacts(self, account):
193                self._contacts[account] = {}
194
195        def remove_jid(self, account, jid):
196                '''Removes all contacts for a given jid'''
197                if not self._contacts.has_key(account):
198                        return
199                if not self._contacts[account].has_key(jid):
200                        return
201                del self._contacts[account][jid]
202                # remove metacontacts info
203                self.remove_metacontact(account, jid)
204
205        def get_contact(self, account, jid, resource = None):
206                '''Returns the list of contact instances for this jid (one per resource)
207                or [] if no resource is given
208                returns the contact instance for the given resource if it's given
209                or None if there is not'''
210                if jid in self._contacts[account]:
211                        contacts = self._contacts[account][jid]
212                        if not resource:
213                                return contacts
214                        for c in contacts:
215                                if c.resource == resource:
216                                        return c
217                if resource:
218                        return None
219                return []
220
221        def get_contacts_from_jid(self, account, jid):
222                '''we may have two or more resources on that jid'''
223                if jid in self._contacts[account]:
224                        contacts_instances = self._contacts[account][jid]
225                        return contacts_instances
226                return []
227
228        def get_highest_prio_contact_from_contacts(self, contacts):
229                if not contacts:
230                        return None
231                prim_contact = contacts[0]
232                for contact in contacts[1:]:
233                        if int(contact.priority) > int(prim_contact.priority):
234                                prim_contact = contact
235                return prim_contact
236
237        def get_contact_with_highest_priority(self, account, jid):
238                contacts = self.get_contacts_from_jid(account, jid)
239                if not contacts and '/' in jid:
240                        # jid may be a fake jid, try it
241                        room, nick = jid.split('/', 1)
242                        contact = self.get_gc_contact(account, room, nick)
243                        return contact
244                return self.get_highest_prio_contact_from_contacts(contacts)
245
246        def get_first_contact_from_jid(self, account, jid):
247                if jid in self._contacts[account]:
248                        return self._contacts[account][jid][0]
249                return None
250
251        def get_contacts_from_group(self, account, group):
252                '''Returns all contacts in the given group'''
253                group_contacts = []
254                for jid in self._contacts[account]:
255                        contacts = self.get_contacts_from_jid(account, jid)
256                        if group in contacts[0].groups:
257                                group_contacts += contacts
258                return group_contacts
259
260        def get_nb_online_total_contacts(self, accounts = [], groups = []):
261                '''Returns the number of online contacts and the total number of
262                contacts'''
263                if accounts == []:
264                        accounts = self.get_accounts()
265                nbr_online = 0
266                nbr_total = 0
267                for account in accounts:
268                        our_jid = common.gajim.get_jid_from_account(account)
269                        for jid in self.get_jid_list(account):
270                                if jid == our_jid:
271                                        continue
272                                if common.gajim.jid_is_transport(jid) and not \
273                                        _('Transports') in groups:
274                                        # do not count transports
275                                        continue
276                                contact = self.get_contact_with_highest_priority(account, jid)
277                                if _('Not in roster') in contact.groups:
278                                        continue
279                                in_groups = False
280                                if groups == []:
281                                        in_groups = True
282                                else:
283                                        contact_groups = contact.groups
284                                        if not contact_groups:
285                                                # Contact is not in a group, so count it in General or
286                                                # Transports group
287                                                if common.gajim.jid_is_transport(jid):
288                                                        contact_groups = [_('Transports')]
289                                                else:
290                                                        contact_groups = [_('General')]
291                                        for group in groups:
292                                                if group in contact_groups:
293                                                        in_groups = True
294                                                        break
295
296                                if in_groups:
297                                        if contact.show not in ('offline', 'error'):
298                                                nbr_online += 1
299                                        nbr_total += 1
300                return nbr_online, nbr_total
301
302        def define_metacontacts(self, account, tags_list):
303                self._metacontacts_tags[account] = tags_list
304
305        def get_new_metacontacts_tag(self, jid):
306                if not jid in self._metacontacts_tags.keys():
307                        return jid
308                #FIXME: can this append ?
309                assert False
310
311        def get_metacontacts_tag(self, account, jid):
312                '''Returns the tag of a jid'''
313                if not self._metacontacts_tags.has_key(account):
314                        return None
315                for tag in self._metacontacts_tags[account]:
316                        for data in self._metacontacts_tags[account][tag]:
317                                if data['jid'] == jid:
318                                        return tag
319                return None
320
321        def add_metacontact(self, brother_account, brother_jid, account, jid):
322                tag = self.get_metacontacts_tag(brother_account, brother_jid)
323                if not tag:
324                        tag = self.get_new_metacontacts_tag(brother_jid)
325                        self._metacontacts_tags[brother_account][tag] = [{'jid': brother_jid,
326                                'tag': tag}]
327                        if brother_account != account:
328                                common.gajim.connections[brother_account].store_metacontacts(
329                                        self._metacontacts_tags[brother_account])
330                # be sure jid has no other tag
331                old_tag = self.get_metacontacts_tag(account, jid)
332                while old_tag:
333                        self.remove_metacontact(account, jid)
334                        old_tag = self.get_metacontacts_tag(account, jid)
335                if not self._metacontacts_tags[account].has_key(tag):
336                        self._metacontacts_tags[account][tag] = [{'jid': jid, 'tag': tag}]
337                else:
338                        self._metacontacts_tags[account][tag].append({'jid': jid,
339                                'tag': tag})
340                common.gajim.connections[account].store_metacontacts(
341                        self._metacontacts_tags[account])
342
343        def remove_metacontact(self, account, jid):
344                found = None
345                for tag in self._metacontacts_tags[account]:
346                        for data in self._metacontacts_tags[account][tag]:
347                                if data['jid'] == jid:
348                                        found = data
349                                        break
350                        if found:
351                                self._metacontacts_tags[account][tag].remove(data)
352                                break
353                common.gajim.connections[account].store_metacontacts(
354                        self._metacontacts_tags[account])
355
356        def has_brother(self, account, jid):
357                for account in self._metacontacts_tags:
358                        tag = self.get_metacontacts_tag(account, jid)
359                        if tag and len(self._metacontacts_tags[account][tag]) > 1:
360                                return True
361                return False
362
363        def get_metacontacts_jids(self, tag):
364                '''Returns all jid for the given tag in the form {acct: [jid1, jid2],.}'''
365                answers = {}
366                for account in self._metacontacts_tags:
367                        if self._metacontacts_tags[account].has_key(tag):
368                                answers[account] = []
369                                for data in self._metacontacts_tags[account][tag]:
370                                        answers[account].append(data['jid'])
371                return answers
372
373        def get_metacontacts_family(self, account, jid):
374                '''return the family of the given jid, including jid in the form:
375                [{'account': acct, 'jid': jid, 'order': order}, ]
376                'order' is optional'''
377                tag = self.get_metacontacts_tag(account, jid)
378                if not tag:
379                        return []
380                answers = []
381                for account in self._metacontacts_tags:
382                        if self._metacontacts_tags[account].has_key(tag):
383                                for data in self._metacontacts_tags[account][tag]:
384                                        data['account'] = account
385                                        answers.append(data)
386                return answers
387
388        def compare_metacontacts(self, data1, data2):
389                '''compare 2 metacontacts.
390                Data is {'jid': jid, 'account': account, 'order': order}
391                order is optional'''
392                jid1 = data1['jid']
393                jid2 = data2['jid']
394                account1 = data1['account']
395                account2 = data2['account']
396                contact1 = self.get_contact_with_highest_priority(account1, jid1)
397                contact2 = self.get_contact_with_highest_priority(account2, jid2)
398                show_list = ['not in roster', 'error', 'offline', 'invisible', 'dnd',
399                        'xa', 'away', 'chat', 'online', 'requested', 'message']
400                # contact can be null when we fill the roster on connection
401                if not contact1:
402                        show1 = 0
403                        priority1 = 0
404                else:
405                        show1 = show_list.index(contact1.show)
406                        priority1 = contact1.priority
407                if not contact2:
408                        show2 = 0
409                        priority2 = 0
410                else:
411                        show2 = show_list.index(contact2.show)
412                        priority2 = contact2.priority
413                # If only one is offline, it's always second
414                if show1 > 2 and show2 < 3:
415                        return 1
416                if show2 > 2 and show1 < 3:
417                        return -1
418                if 'order' in data1 and 'order' in data2:
419                        if data1['order'] > data2['order']:
420                                return 1
421                        if data1['order'] < data2['order']:
422                                return -1
423                transport1 = common.gajim.get_transport_name_from_jid(jid1)
424                transport2 = common.gajim.get_transport_name_from_jid(jid2)
425                if transport2 and not transport1:
426                        return 1
427                if transport1 and not transport2:
428                        return -1
429                if priority1 > priority2:
430                        return 1
431                if priority2 > priority1:
432                        return -1
433                if show1 > show2:
434                        return 1
435                if show2 > show1:
436                        return -1
437                if jid1 > jid2:
438                        return 1
439                if jid2 > jid1:
440                        return -1
441                # If all is the same, compare accounts, they can't be the same
442                if account1 > account2:
443                        return 1
444                if account2 > account1:
445                        return -1
446                return 0
447
448        def get_metacontacts_big_brother(self, family):
449                '''which of the family will be the big brother under wich all
450                others will be ?'''
451                family.sort(cmp=self.compare_metacontacts)
452                return family[-1]
453
454        def is_pm_from_jid(self, account, jid):
455                '''Returns True if the given jid is a private message jid'''
456                if jid in self._contacts[account]:
457                        return False
458                return True
459
460        def is_pm_from_contact(self, account, contact):
461                '''Returns True if the given contact is a private message contact'''
462                if isinstance(contact, Contact):
463                        return False
464                return True
465
466        def get_jid_list(self, account):
467                return self._contacts[account].keys()
468
469        def contact_from_gc_contact(self, gc_contact):
470                '''Create a Contact instance from a GC_Contact instance'''
471                jid = gc_contact.get_full_jid()
472                return Contact(jid = jid, resource = '', name = gc_contact.name,
473                        groups = [], show = gc_contact.show, status = gc_contact.status,
474                        sub = 'none')
475
476        def create_gc_contact(self, room_jid='', name='', show='', status='',
477                role='', affiliation='', jid='', resource=''):
478                return GC_Contact(room_jid, name, show, status, role, affiliation, jid,
479                        resource)
480       
481        def add_gc_contact(self, account, gc_contact):
482                # No such account before ?
483                if not self._gc_contacts.has_key(account):
484                        self._contacts[account] = {gc_contact.room_jid : {gc_contact.name: \
485                                gc_contact}}
486                        return
487                # No such room_jid before ?
488                if not self._gc_contacts[account].has_key(gc_contact.room_jid):
489                        self._gc_contacts[account][gc_contact.room_jid] = {gc_contact.name: \
490                                gc_contact}
491                        return
492                self._gc_contacts[account][gc_contact.room_jid][gc_contact.name] = \
493                                gc_contact
494
495        def remove_gc_contact(self, account, gc_contact):
496                if not self._gc_contacts.has_key(account):
497                        return
498                if not self._gc_contacts[account].has_key(gc_contact.room_jid):
499                        return
500                if not self._gc_contacts[account][gc_contact.room_jid].has_key(
501                        gc_contact.name):
502                        return
503                del self._gc_contacts[account][gc_contact.room_jid][gc_contact.name]
504                # It was the last nick in room ?
505                if not len(self._gc_contacts[account][gc_contact.room_jid]):
506                        del self._gc_contacts[account][gc_contact.room_jid]
507
508        def remove_room(self, account, room_jid):
509                if not self._gc_contacts.has_key(account):
510                        return
511                if not self._gc_contacts[account].has_key(room_jid):
512                        return
513                del self._gc_contacts[account][room_jid]
514
515        def get_gc_list(self, account):
516                if not self._gc_contacts.has_key(account):
517                        return []
518                return self._gc_contacts[account].keys()
519
520        def get_nick_list(self, account, room_jid):
521                gc_list = self.get_gc_list(account)
522                if not room_jid in gc_list:
523                        return []
524                return self._gc_contacts[account][room_jid].keys()
525
526        def get_gc_contact(self, account, room_jid, nick):
527                nick_list = self.get_nick_list(account, room_jid)
528                if not nick in nick_list:
529                        return None
530                return self._gc_contacts[account][room_jid][nick]
Note: See TracBrowser for help on using the browser.