Newer
Older
## common/contacts.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
##
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <kourem@gmail.com>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
import common.gajim
'''Information concerning each contact'''
def __init__(self, jid='', name='', groups=[], show='', status='', sub='',

Yann Leboulanger
committed
ask='', resource='', priority=0, keyID='', our_chatstate=None,

Yann Leboulanger
committed
chatstate=None, last_status_time=None, msg_id = None, composing_jep = None):
self.sub = sub
self.ask = ask
self.resource = resource
self.priority = priority
self.keyID = keyID
# please read jep-85 http://www.jabber.org/jeps/jep-0085.html
# we keep track of jep85 support with the peer by three extra states:
# None, False and 'ask'
# None if no info about peer
# False if peer does not support jep85
# 'ask' if we sent the first 'active' chatstate and are waiting for reply
# this holds what WE SEND to contact (our current chatstate)
self.our_chatstate = our_chatstate

Yann Leboulanger
committed
self.msg_id = msg_id
# tell which JEP we're using for composing state
# None = have to ask, JEP-0022 = use this jep,
# JEP-0085 = use this jep, False = no composing support
self.composing_jep = composing_jep
# this is contact's chatstate
self.chatstate = chatstate

Yann Leboulanger
committed
self.last_status_time = last_status_time
def get_full_jid(self):
if self.resource:
return self.jid + '/' + self.resource
return self.jid

Yann Leboulanger
committed
def get_shown_name(self):
if self.name:
return self.name
return self.jid.split('@')[0]
def is_hidden_from_roster(self):
'''if roster should not be visible in roster'''
# JEP-0162
hide = True
if contact.sub in ('both', 'to', 'from'):
hide = False
elif contact.ask == 'subscribe':
hide = False
elif contact.name or len(contact.groups):
hide = False
return hide
def is_observer(self):
# XEP-0162: http://www.xmpp.org/extensions/xep-0162.html
is_observer = False
if is_hidden_in_roster() and if contact.sub == 'from':
is_observer = True
return is_observer
'''Information concerning each groupchat contact'''
def __init__(self, room_jid='', name='', show='', status='', role='',
affiliation='', jid = '', resource = ''):
self.name = name
self.show = show
self.status = status
self.role = role
self.affiliation = affiliation
self.resource = resource
def get_full_jid(self):
return self.room_jid + '/' + self.name

Yann Leboulanger
committed
def get_shown_name(self):
return self.name
class Contacts:
'''Information concerning all contacts and groupchat contacts'''
def __init__(self):
self._contacts = {} # list of contacts {acct: {jid1: [C1, C2]}, } one Contact per resource
self._gc_contacts = {} # list of contacts that are in gc {acct: {room_jid: {nick: C}}}
# For meta contacts:

Yann Leboulanger
committed
self._metacontacts_tags = {}
def change_account_name(self, old_name, new_name):
self._contacts[new_name] = self._contacts[old_name]
self._gc_contacts[new_name] = self._gc_contacts[old_name]

Yann Leboulanger
committed
self._metacontacts_tags[new_name] = self._metacontacts_tags[old_name]
del self._contacts[old_name]
del self._gc_contacts[old_name]

Yann Leboulanger
committed
del self._metacontacts_tags[old_name]
def add_account(self, account):
self._contacts[account] = {}
self._gc_contacts[account] = {}

Yann Leboulanger
committed
if not self._metacontacts_tags.has_key(account):
self._metacontacts_tags[account] = {}
def get_accounts(self):
return self._contacts.keys()
def remove_account(self, account):
del self._contacts[account]
del self._gc_contacts[account]

Yann Leboulanger
committed
del self._metacontacts_tags[account]
def create_contact(self, jid='', name='', groups=[], show='', status='',

Yann Leboulanger
committed
sub='', ask='', resource='', priority=0, keyID='', our_chatstate=None,

Yann Leboulanger
committed
chatstate=None, last_status_time=None, composing_jep=None):
return Contact(jid, name, groups, show, status, sub, ask, resource,

Yann Leboulanger
committed
priority, keyID, our_chatstate, chatstate, last_status_time,
composing_jep)
def copy_contact(self, contact):
return self.create_contact(jid = contact.jid, name = contact.name,
groups = contact.groups, show = contact.show, status = contact.status,
sub = contact.sub, ask = contact.ask, resource = contact.resource,
priority = contact.priority, keyID = contact.keyID,

Yann Leboulanger
committed
our_chatstate = contact.our_chatstate, chatstate = contact.chatstate,
last_status_time = contact.last_status_time)
def add_contact(self, account, contact):
# No such account before ?
if not self._contacts.has_key(account):
self._contacts[account] = {contact.jid : [contact]}
return
# No such jid before ?
if not self._contacts[account].has_key(contact.jid):
self._contacts[account][contact.jid] = [contact]
return
contacts = self._contacts[account][contact.jid]
# We had only one that was offline, remove it
if len(contacts) == 1 and contacts[0].show == 'offline':
# Do not use self.remove_contact: it deteles
# self._contacts[account][contact.jid]
contacts.remove(contacts[0])
# If same JID with same resource already exists, use the new one
if c.resource == contact.resource:
self.remove_contact(account, c)
break
def remove_contact(self, account, contact):
if not self._contacts.has_key(account):
return
if not self._contacts[account].has_key(contact.jid):
return
if contact in self._contacts[account][contact.jid]:
self._contacts[account][contact.jid].remove(contact)
if len(self._contacts[account][contact.jid]) == 0:
del self._contacts[account][contact.jid]
def remove_jid(self, account, jid):
'''Removes all contacts for a given jid'''
if not self._contacts.has_key(account):
return
if not self._contacts[account].has_key(jid):
return
del self._contacts[account][jid]

Yann Leboulanger
committed
# remove metacontacts info
self.remove_metacontact(account, jid)
def get_contact(self, account, jid, resource = None):
'''Returns the list of contact instances for this jid (one per resource)
returns the contact instance for the given resource if it's given
or None if there is not'''
if jid in self._contacts[account]:
contacts = self._contacts[account][jid]
if not resource:
return contacts
for c in contacts:
if c.resource == resource:
return c

Yann Leboulanger
committed
if resource:
return None
return []
def get_contacts_from_jid(self, account, jid):
contacts_instances = self._contacts[account][jid]
return contacts_instances
return []
def get_highest_prio_contact_from_contacts(self, contacts):
if not contacts:
return None
prim_contact = contacts[0]
for contact in contacts[1:]:
if int(contact.priority) > int(prim_contact.priority):
prim_contact = contact
return prim_contact
def get_contact_with_highest_priority(self, account, jid):
contacts = self.get_contacts_from_jid(account, jid)

Yann Leboulanger
committed
return contact
return self.get_highest_prio_contact_from_contacts(contacts)
def get_first_contact_from_jid(self, account, jid):
if jid in self._contacts[account]:
return self._contacts[account][jid][0]
return None

Yann Leboulanger
committed
def define_metacontacts(self, account, tags_list):
self._metacontacts_tags[account] = tags_list
def get_new_metacontacts_tag(self, jid):
if not jid in self._metacontacts_tags.keys():
return jid
#FIXME: can this append ?
assert False
def get_metacontacts_tag(self, account, jid):
'''Returns the tag of a jid'''
if not self._metacontacts_tags.has_key(account):
return None
for tag in self._metacontacts_tags[account]:
for data in self._metacontacts_tags[account][tag]:
if data['jid'] == jid:
return tag
return None
def add_metacontact(self, brother_account, brother_jid, account, jid):
tag = self.get_metacontacts_tag(brother_account, brother_jid)
if not tag:
tag = self.get_new_metacontacts_tag(brother_jid)
self._metacontacts_tags[brother_account][tag] = [{'jid': brother_jid,
'tag': tag}]
if brother_account != account:
common.gajim.connections[brother_account].store_metacontacts(
self._metacontacts_tags[brother_account])

Yann Leboulanger
committed
# be sure jid has no other tag
old_tag = self.get_metacontacts_tag(account, jid)
while old_tag:
self.remove_metacontact(account, jid)
old_tag = self.get_metacontacts_tag(account, jid)
if not self._metacontacts_tags[account].has_key(tag):
self._metacontacts_tags[account][tag] = [{'jid': jid, 'tag': tag}]
else:

Yann Leboulanger
committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
self._metacontacts_tags[account][tag].append({'jid': jid,
'tag': tag})
common.gajim.connections[account].store_metacontacts(
self._metacontacts_tags[account])
def remove_metacontact(self, account, jid):
found = None
for tag in self._metacontacts_tags[account]:
for data in self._metacontacts_tags[account][tag]:
if data['jid'] == jid:
found = data
break
if found:
self._metacontacts_tags[account][tag].remove(data)
break
common.gajim.connections[account].store_metacontacts(
self._metacontacts_tags[account])
def has_brother(self, account, jid):
for account in self._metacontacts_tags:
tag = self.get_metacontacts_tag(account, jid)
if tag and len(self._metacontacts_tags[account][tag]) > 1:
return True
return False
def get_metacontacts_jids(self, tag):
'''Returns all jid for the given tag in the form {acct: [jid1, jid2],.}'''
answers = {}
for account in self._metacontacts_tags:
if self._metacontacts_tags[account].has_key(tag):
answers[account] = []
for data in self._metacontacts_tags[account][tag]:
answers[account].append(data['jid'])
return answers
def get_metacontacts_family(self, account, jid):
'''return the family of the given jid, including jid in the form:
[{'account': acct, 'jid': jid, 'order': order}, ]
'order' is optional'''

Yann Leboulanger
committed
tag = self.get_metacontacts_tag(account, jid)
if not tag:

Yann Leboulanger
committed
answers = []
for account in self._metacontacts_tags:
if self._metacontacts_tags[account].has_key(tag):
for data in self._metacontacts_tags[account][tag]:
data['account'] = account
answers.append(data)
return answers
def _get_data_score(self, data):
'''compute thescore of a gived data
data is {'jid': jid, 'account': account, 'order': order}
order is optional
score = (max_order - order)*10000 + is_jabber*priority*10 + status'''

Yann Leboulanger
committed
jid = data['jid']
account = data['account']
max_order = 0
order = 0
if data.has_key('order'):
order = data['order']
if order:
family = self.get_metacontacts_family(account, jid)
for data_ in family:
if data_.has_key('order') and data_['order'] > max_order:
max_order = data_['order']

Yann Leboulanger
committed
contact = self.get_contact_with_highest_priority(account, jid)
score = (max_order - order)*10000
if common.gajim.get_transport_name_from_jid(jid) is None:
score += 10
if contact.priority > 0:
score += contact.priority * 10
score += ['not in roster', 'error', 'offline', 'invisible', 'dnd', 'xa',
'away', 'chat', 'online', 'requested', 'message'].index(contact.show)

Yann Leboulanger
committed
return score
def get_metacontacts_big_brother(self, family):
'''which of the family will be the big brother under wich all
others will be ?'''
max_score = 0
max_data = family[0]
for data in family:
score = self._get_data_score(data)
if score > max_score:
max_score = score
max_data = data
return max_data
def is_pm_from_jid(self, account, jid):
'''Returns True if the given jid is a private message jid'''
if jid in self._contacts[account]:
return False
return True
def is_pm_from_contact(self, account, contact):
'''Returns True if the given contact is a private message contact'''
return False
return True
def get_jid_list(self, account):
return self._contacts[account].keys()
def contact_from_gc_contact(self, gc_contact):
'''Create a Contact instance from a GC_Contact instance'''
jid = gc_contact.get_full_jid()
return Contact(jid = jid, resource = '', name = gc_contact.name,
groups = [], show = gc_contact.show, status = gc_contact.status,
sub = 'none')
def create_gc_contact(self, room_jid='', name='', show='', status='',
role='', affiliation='', jid='', resource=''):
return GC_Contact(room_jid, name, show, status, role, affiliation, jid,
resource)
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
def add_gc_contact(self, account, gc_contact):
# No such account before ?
if not self._gc_contacts.has_key(account):
self._contacts[account] = {gc_contact.room_jid : {gc_contact.name: \
gc_contact}}
return
# No such room_jid before ?
if not self._gc_contacts[account].has_key(gc_contact.room_jid):
self._gc_contacts[account][gc_contact.room_jid] = {gc_contact.name: \
gc_contact}
return
self._gc_contacts[account][gc_contact.room_jid][gc_contact.name] = \
gc_contact
def remove_gc_contact(self, account, gc_contact):
if not self._gc_contacts.has_key(account):
return
if not self._gc_contacts[account].has_key(gc_contact.room_jid):
return
if not self._gc_contacts[account][gc_contact.room_jid].has_key(
gc_contact.name):
return
del self._gc_contacts[account][gc_contact.room_jid][gc_contact.name]
# It was the last nick in room ?
if not len(self._gc_contacts[account][gc_contact.room_jid]):
del self._gc_contacts[account][gc_contact.room_jid]
def remove_room(self, account, room_jid):
if not self._gc_contacts.has_key(account):
return
if not self._gc_contacts[account].has_key(room_jid):
return
del self._gc_contacts[account][room_jid]
def get_gc_list(self, account):
if not self._gc_contacts.has_key(account):
return []
return self._gc_contacts[account].keys()
def get_nick_list(self, account, room_jid):
gc_list = self.get_gc_list(account)
if not room_jid in gc_list:
return []
return self._gc_contacts[account][room_jid].keys()
def get_gc_contact(self, account, room_jid, nick):
nick_list = self.get_nick_list(account, room_jid)
if not nick in nick_list:
return None
return self._gc_contacts[account][room_jid][nick]