new way to handle incominf messages, new notification event.
[gajim.git] / src / common / gajim.py
blob872b083bc80b712efd0c6b99a1e33a3647d42ab5
1 # -*- coding:utf-8 -*-
2 ## src/common/gajim.py
3 ##
4 ## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
5 ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
6 ## Travis Shirk <travis AT pobox.com>
7 ## Nikos Kouremenos <kourem AT gmail.com>
8 ## Copyright (C) 2006 Junglecow J <junglecow AT gmail.com>
9 ## Stefan Bethge <stefan AT lanpartei.de>
10 ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
11 ## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
12 ## Stephan Erb <steve-e AT h3c.de>
13 ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
15 ## This file is part of Gajim.
17 ## Gajim is free software; you can redistribute it and/or modify
18 ## it under the terms of the GNU General Public License as published
19 ## by the Free Software Foundation; version 3 only.
21 ## Gajim is distributed in the hope that it will be useful,
22 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
23 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 ## GNU General Public License for more details.
26 ## You should have received a copy of the GNU General Public License
27 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
30 import sys
31 import logging
32 import locale
34 import config
35 import xmpp
36 import defs
37 import common.ged
38 import notify
40 interface = None # The actual interface (the gtk one for the moment)
41 thread_interface = None # Interface to run a thread and then a callback
42 config = config.Config()
43 version = config.get('version')
44 connections = {} # 'account name': 'account (connection.Connection) instance'
45 ipython_window = None
47 ged = common.ged.GlobalEventsDispatcher() # Global Events Dispatcher
48 nec = None # Network Events Controller
49 plugin_manager = None # Plugins Manager
51 log = logging.getLogger('gajim')
53 import logger
54 logger = logger.Logger() # init the logger
56 import configpaths
57 gajimpaths = configpaths.gajimpaths
59 VCARD_PATH = gajimpaths['VCARD']
60 AVATAR_PATH = gajimpaths['AVATAR']
61 MY_EMOTS_PATH = gajimpaths['MY_EMOTS']
62 MY_ICONSETS_PATH = gajimpaths['MY_ICONSETS']
63 MY_MOOD_ICONSETS_PATH = gajimpaths['MY_MOOD_ICONSETS']
64 MY_ACTIVITY_ICONSETS_PATH = gajimpaths['MY_ACTIVITY_ICONSETS']
65 MY_CACERTS = gajimpaths['MY_CACERTS']
66 TMP = gajimpaths['TMP']
67 DATA_DIR = gajimpaths['DATA']
68 ICONS_DIR = gajimpaths['ICONS']
69 HOME_DIR = gajimpaths['HOME']
70 PLUGINS_DIRS = [gajimpaths['PLUGINS_BASE'],
71 gajimpaths['PLUGINS_USER']]
72 PLUGINS_CONFIG_DIR = gajimpaths['PLUGINS_CONFIG_DIR']
74 try:
75 LANG = locale.getdefaultlocale()[0] # en_US, fr_FR, el_GR etc..
76 except (ValueError, locale.Error):
77 # unknown locale, use en is better than fail
78 LANG = None
79 if LANG is None:
80 LANG = 'en'
81 else:
82 LANG = LANG[:2] # en, fr, el etc..
84 os_info = None # used to cache os information
86 from contacts import LegacyContactsAPI
87 from events import Events
89 gmail_domains = ['gmail.com', 'googlemail.com']
91 transport_type = {} # list the type of transport
93 last_message_time = {} # list of time of the latest incomming message
94 # {acct1: {jid1: time1, jid2: time2}, }
95 encrypted_chats = {} # list of encrypted chats {acct1: [jid1, jid2], ..}
97 contacts = LegacyContactsAPI()
98 gc_connected = {} # tell if we are connected to the room or not {acct: {room_jid: True}}
99 gc_passwords = {} # list of the pass required to enter a room {room_jid: password}
100 automatic_rooms = {} # list of rooms that must be automaticaly configured and for which we have a list of invities {account: {room_jid: {'invities': []}}}
101 new_room_nick = None # if it's != None, use this nick instead of asking for a new nickname when there is a conflict.
103 groups = {} # list of groups
104 newly_added = {} # list of contacts that has just signed in
105 to_be_removed = {} # list of contacts that has just signed out
107 events = Events()
109 notification = notify.Notification()
111 nicks = {} # list of our nick names in each account
112 # should we block 'contact signed in' notifications for this account?
113 # this is only for the first 30 seconds after we change our show
114 # to something else than offline
115 # can also contain account/transport_jid to block notifications for contacts
116 # from this transport
117 block_signed_in_notifications = {}
118 con_types = {} # type of each connection (ssl, tls, tcp, ...)
120 sleeper_state = {} # whether we pass auto away / xa or not
121 #'off': don't use sleeper for this account
122 #'online': online and use sleeper
123 #'autoaway': autoaway and use sleeper
124 #'autoxa': autoxa and use sleeper
125 status_before_autoaway = {}
127 # jid of transport contacts for which we need to ask avatar when transport will
128 # be online
129 transport_avatar = {} # {transport_jid: [jid_list]}
131 # Is Gnome configured to activate on single click ?
132 single_click = False
133 SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
134 'invisible', 'error']
136 # zeroconf account name
137 ZEROCONF_ACC_NAME = 'Local'
139 HAVE_ZEROCONF = True
140 try:
141 __import__('avahi')
142 except ImportError:
143 try:
144 __import__('pybonjour')
145 except Exception: # Linux raises ImportError, Windows raises WindowsError
146 HAVE_ZEROCONF = False
148 HAVE_PYCRYPTO = True
149 try:
150 __import__('Crypto')
151 except ImportError:
152 HAVE_PYCRYPTO = False
154 HAVE_GPG = True
155 try:
156 __import__('gnupg', globals(), locals(), [], -1)
157 except ImportError:
158 HAVE_GPG = False
159 else:
160 from os import system
161 if system('gpg -h >/dev/null 2>&1'):
162 HAVE_GPG = False
164 # Depends on use_latex option. Will be correctly set after we config options are
165 # read.
166 HAVE_LATEX = False
168 HAVE_FARSIGHT = True
169 try:
170 __import__('farsight')
171 __import__('gst')
172 except ImportError:
173 HAVE_FARSIGHT = False
174 gajim_identity = {'type': 'pc', 'category': 'client', 'name': 'Gajim'}
175 gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI, xmpp.NS_FILE,
176 xmpp.NS_MUC, xmpp.NS_MUC_USER, xmpp.NS_MUC_ADMIN, xmpp.NS_MUC_OWNER,
177 xmpp.NS_MUC_CONFIG, xmpp.NS_COMMANDS, xmpp.NS_DISCO_INFO, 'ipv6',
178 'jabber:iq:gateway', xmpp.NS_LAST, xmpp.NS_PRIVACY, xmpp.NS_PRIVATE,
179 xmpp.NS_REGISTER, xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED, 'msglog',
180 'sslc2s', 'stringprep', xmpp.NS_PING, xmpp.NS_TIME_REVISED, xmpp.NS_SSN,
181 xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK, xmpp.NS_ROSTERX, xmpp.NS_SECLABEL]
183 # Optional features gajim supports per account
184 gajim_optional_features = {}
186 # Capabilities hash per account
187 caps_hash = {}
189 import caps_cache
190 caps_cache.initialize(logger)
192 def get_nick_from_jid(jid):
193 pos = jid.find('@')
194 return jid[:pos]
196 def get_server_from_jid(jid):
197 pos = jid.find('@') + 1 # after @
198 return jid[pos:]
200 def get_name_and_server_from_jid(jid):
201 name = get_nick_from_jid(jid)
202 server = get_server_from_jid(jid)
203 return name, server
205 def get_room_and_nick_from_fjid(jid):
206 # fake jid is the jid for a contact in a room
207 # gaim@conference.jabber.no/nick/nick-continued
208 # return ('gaim@conference.jabber.no', 'nick/nick-continued')
209 l = jid.split('/', 1)
210 if len(l) == 1: # No nick
211 l.append('')
212 return l
214 def get_real_jid_from_fjid(account, fjid):
216 Return real jid or returns None, if we don't know the real jid
218 room_jid, nick = get_room_and_nick_from_fjid(fjid)
219 if not nick: # It's not a fake_jid, it is a real jid
220 return fjid # we return the real jid
221 real_jid = fjid
222 if interface.msg_win_mgr.get_gc_control(room_jid, account):
223 # It's a pm, so if we have real jid it's in contact.jid
224 gc_contact = contacts.get_gc_contact(account, room_jid, nick)
225 if not gc_contact:
226 return
227 # gc_contact.jid is None when it's not a real jid (we don't know real jid)
228 real_jid = gc_contact.jid
229 return real_jid
231 def get_room_from_fjid(jid):
232 return get_room_and_nick_from_fjid(jid)[0]
234 def get_contact_name_from_jid(account, jid):
235 c = contacts.get_first_contact_from_jid(account, jid)
236 return c.name
238 def get_jid_without_resource(jid):
239 return jid.split('/')[0]
241 def construct_fjid(room_jid, nick):
243 Nick is in UTF-8 (taken from treeview); room_jid is in unicode
245 # fake jid is the jid for a contact in a room
246 # gaim@conference.jabber.org/nick
247 if isinstance(nick, str):
248 nick = unicode(nick, 'utf-8')
249 return room_jid + '/' + nick
251 def get_resource_from_jid(jid):
252 jids = jid.split('/', 1)
253 if len(jids) > 1:
254 return jids[1] # abc@doremi.org/res/res-continued
255 else:
256 return ''
258 def get_number_of_accounts():
260 Return the number of ALL accounts
262 return len(connections.keys())
264 def get_number_of_connected_accounts(accounts_list = None):
266 Returns the number of CONNECTED accounts. Uou can optionally pass an
267 accounts_list and if you do those will be checked, else all will be checked
269 connected_accounts = 0
270 if accounts_list is None:
271 accounts = connections.keys()
272 else:
273 accounts = accounts_list
274 for account in accounts:
275 if account_is_connected(account):
276 connected_accounts = connected_accounts + 1
277 return connected_accounts
279 def account_is_connected(account):
280 if account not in connections:
281 return False
282 if connections[account].connected > 1: # 0 is offline, 1 is connecting
283 return True
284 else:
285 return False
287 def account_is_disconnected(account):
288 return not account_is_connected(account)
290 def zeroconf_is_connected():
291 return account_is_connected(ZEROCONF_ACC_NAME) and \
292 config.get_per('accounts', ZEROCONF_ACC_NAME, 'is_zeroconf')
294 def get_number_of_securely_connected_accounts():
296 Return the number of the accounts that are SSL/TLS connected
298 num_of_secured = 0
299 for account in connections.keys():
300 if account_is_securely_connected(account):
301 num_of_secured += 1
302 return num_of_secured
304 def account_is_securely_connected(account):
305 if account_is_connected(account) and \
306 account in con_types and con_types[account] in ('tls', 'ssl'):
307 return True
308 else:
309 return False
311 def get_transport_name_from_jid(jid, use_config_setting = True):
313 Returns 'aim', 'gg', 'irc' etc
315 If JID is not from transport returns None.
317 #FIXME: jid can be None! one TB I saw had this problem:
318 # in the code block # it is a groupchat presence in handle_event_notify
319 # jid was None. Yann why?
320 if not jid or (use_config_setting and not config.get('use_transports_iconsets')):
321 return
323 host = get_server_from_jid(jid)
324 if host in transport_type:
325 return transport_type[host]
327 # host is now f.e. icq.foo.org or just icq (sometimes on hacky transports)
328 host_splitted = host.split('.')
329 if len(host_splitted) != 0:
330 # now we support both 'icq.' and 'icq' but not icqsucks.org
331 host = host_splitted[0]
333 if host in ('aim', 'irc', 'icq', 'msn', 'sms', 'tlen', 'weather', 'yahoo',
334 'mrim', 'facebook'):
335 return host
336 elif host == 'gg':
337 return 'gadu-gadu'
338 elif host == 'jit':
339 return 'icq'
340 elif host == 'facebook':
341 return 'facebook'
342 else:
343 return None
345 def jid_is_transport(jid):
346 # if not '@' or '@' starts the jid then it is transport
347 if jid.find('@') <= 0:
348 return True
349 return False
351 def get_jid_from_account(account_name):
353 Return the jid we use in the given account
355 name = config.get_per('accounts', account_name, 'name')
356 hostname = config.get_per('accounts', account_name, 'hostname')
357 jid = name + '@' + hostname
358 return jid
360 def get_our_jids():
362 Returns a list of the jids we use in our accounts
364 our_jids = list()
365 for account in contacts.get_accounts():
366 our_jids.append(get_jid_from_account(account))
367 return our_jids
369 def get_hostname_from_account(account_name, use_srv = False):
371 Returns hostname (if custom hostname is used, that is returned)
373 if use_srv and connections[account_name].connected_hostname:
374 return connections[account_name].connected_hostname
375 if config.get_per('accounts', account_name, 'use_custom_host'):
376 return config.get_per('accounts', account_name, 'custom_host')
377 return config.get_per('accounts', account_name, 'hostname')
379 def get_notification_image_prefix(jid):
381 Returns the prefix for the notification images
383 transport_name = get_transport_name_from_jid(jid)
384 if transport_name in ('aim', 'icq', 'msn', 'yahoo', 'facebook'):
385 prefix = transport_name
386 else:
387 prefix = 'jabber'
388 return prefix
390 def get_name_from_jid(account, jid):
392 Return from JID's shown name and if no contact returns jids
394 contact = contacts.get_first_contact_from_jid(account, jid)
395 if contact:
396 actor = contact.get_shown_name()
397 else:
398 actor = jid
399 return actor
401 def get_priority(account, show):
403 Return the priority an account must have
405 if not show:
406 show = 'online'
408 if show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible') and \
409 config.get_per('accounts', account, 'adjust_priority_with_status'):
410 return config.get_per('accounts', account, 'autopriority_' + show)
411 return config.get_per('accounts', account, 'priority')