Newer
Older
exec python -OOt "$0" ${1+"$@"}
## - Yann Le Boulanger <asterix@lagaule.org>
## - Vincent Hanquez <tab@snarc.org>
##
## 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 os
if not os.name == 'nt': # py2exe only in windows
pygtk.require('2.0') # py2exe fails on this
try:
import gtk
except RuntimeError, msg:
if str(msg) == 'could not open display':
print 'Gajim needs Xserver to run. Exiting...'
import signal
import getopt
import time
import gtkgui_helpers
from common import i18n
i18n.init()
_ = i18n._
import common.sleepy
import check_for_new_version
from common import gajim
from common import connection
from common import helpers
from common import optparser
profile = ''
try:
opts, args = getopt.getopt(sys.argv[1:], 'hvp:', ['help', 'verbose',
except getopt.error, msg:
print msg
print 'for help use --help'
sys.exit(2)
for o, a in opts:
elif o in ('-v', '--verbose'):
gajim.verbose = True
elif o in ('-p', '--profile'): # gajim --profile name
profile = a

nkour
committed
config_filename = os.path.expanduser('~/.gajim/config')
if os.name == 'nt':
try:
# Documents and Settings\[User Name]\Application Data\Gajim\logs
config_filename = os.environ['appdata'] + '/Gajim/config'
except KeyError:

nkour
committed
if profile:

nkour
committed
parser = optparser.OptionsParser(config_filename)
try:
import winsound # windows-only built-in module for playing wav
except ImportError:
pass
'''Information concerning each contact'''
def __init__(self, jid='', name='', groups=[], show='', status='', sub='',\
ask='', resource='', priority=5, keyID='', role='', affiliation='',\
chatstate=None):
self.jid = jid
self.name = name
self.groups = groups
self.show = show
self.status = status
self.sub = sub
self.ask = ask
self.resource = resource
self.priority = priority
self.keyID = keyID
self.role = role
self.affiliation = affiliation
# please read jep-85 http://www.jabber.org/jeps/jep-0085.html
# we keep track of jep85 support by 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 (the current chatstate)
import roster_window
import systray
import dialogs
import config
GTKGUI_GLADE = 'gtkgui.glade'
class Interface:

Yann Leboulanger
committed
def handle_event_roster(self, account, data):
self.roster.fill_contacts_and_groups_dicts(data, account)
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('Roster', (account, data))

Yann Leboulanger
committed
def handle_event_warning(self, unused, data):
#('WARNING', account, (title_text, section_text))

nkour
committed
dialogs.WarningDialog(data[0], data[1]).get_response()

Yann Leboulanger
committed
def handle_event_error(self, unused, data):
#('ERROR', account, (title_text, section_text))

nkour
committed
dialogs.ErrorDialog(data[0], data[1]).get_response()
def handle_event_information(self, unused, data):
#('INFORMATION', account, (title_text, section_text))
dialogs.InformationDialog(data[0], data[1])
def handle_event_http_auth(self, account, data):
#('HTTP_AUTH', account, (method, url, iq_obj))
dialog = dialogs.ConfirmationDialog(_('HTTP (%s) Authorization for %s') \
% (data[0], data[1]), _('Do you accept this request?'))
if dialog.get_response() == gtk.RESPONSE_OK:
answer = 'yes'
else:
answer = 'no'
gajim.connections[account].build_http_auth_answer(data[2], answer)
def handle_event_error_answer(self, account, array):
id, jid_from, errmsg, errcode = array
if unicode(errcode) in ['403', '406'] and id:
ft = self.windows['file_transfers']
sid = id
if len(id) > 3 and id[2] == '_':
sid = id[3:]
if ft.files_props['s'].has_key(sid):
file_props = ft.files_props['s'][sid]
file_props['error'] = -4
self.handle_event_file_request_error(account,
(jid_from, file_props))
conn = gajim.connections[account]
conn.disconnect_transfer(file_props)
return
elif unicode(errcode) == '404':
sid = id
if len(id) > 3 and id[2] == '_':
sid = id[3:]
if conn.files_props.has_key(sid):
file_props = conn.files_props[sid]
self.handle_event_file_send_error(account,
(jid_from, file_props))
conn.disconnect_transfer(file_props)
return
#('ERROR_ANSWER', account, (id, jid_from. errmsg, errcode))
if jid_from in self.windows[account]['gc']:
self.windows[account]['gc'][jid_from].print_conversation(
'Error %s: %s' % (array[2], array[1]), jid_from)
def handle_event_con_type(self, account, con_type):
# ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp'

Yann Leboulanger
committed
gajim.con_types[account] = con_type

Yann Leboulanger
committed
gajim.allow_notifications[account] = True
def handle_event_status(self, account, status): # OUR status
gobject.timeout_add(30000, self.allow_notif, account)

Yann Leboulanger
committed
gajim.allow_notifications[account] = False
# we are disconnected from all gc
for room_jid in gajim.gc_connected[account]:
if self.windows[account]['gc'].has_key(room_jid):
self.windows[account]['gc'][room_jid].got_disconnected(room_jid)
self.roster.on_status_changed(account, status)
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('AccountPresence', (status, account))
def handle_event_notify(self, account, array):
#('NOTIFY', account, (jid, status, message, resource, priority, keyID,
# role, affiliation, real_jid, reason, actor, statusCode, new_nick))
# if we're here it means contact changed show
statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible']
jid = array[0].split('/')[0]
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
resource = array[3]
if not resource:
resource = ''
priority = array[4]
if jid.find('@') <= 0:
ji = jid.replace('@', '')
else:
ji = jid
#Update user

Yann Leboulanger
committed
if gajim.contacts[account].has_key(ji):
luser = gajim.contacts[account][ji]
user1 = None
resources = []
for u in luser:
resources.append(u.resource)
if u.resource == resource:
user1 = u
break
if user1.show in statuss:
old_show = statuss.index(user1.show)

Yann Leboulanger
committed
return

Yann Leboulanger
committed
user1 = gajim.contacts[account][ji][0]
if user1.show in statuss:
old_show = statuss.index(user1.show)
if (resources != [''] and (len(luser) != 1 or

Yann Leboulanger
committed
luser[0].show != 'offline')) and jid.find('@') > 0:
user1 = Contact(jid = user1.jid, name = user1.name,
groups = user1.groups, show = user1.show,
status = user1.status, sub = user1.sub, ask = user1.ask,
resource = user1.resource, priority = user1.priority,
keyID = user1.keyID)
luser.append(user1)
user1.resource = resource
if user1.jid.find('@') > 0 and len(luser) == 1: # It's not an agent

Yann Leboulanger
committed
if not user1.jid in gajim.newly_added[account]:
gajim.newly_added[account].append(user1.jid)
if user1.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].remove(user1.jid)
gobject.timeout_add(5000, self.roster.remove_newly_added, \
user1.jid, account)
if old_show > 1 and new_show == 0 and gajim.connections[account].\
connected > 1:

Yann Leboulanger
committed
if not user1.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].append(user1.jid)
if user1.jid in gajim.newly_added[account]:
gajim.newly_added[account].remove(user1.jid)
self.roster.draw_contact(user1.jid, account)

Yann Leboulanger
committed
if not gajim.awaiting_messages[account].has_key(jid):
gobject.timeout_add(5000, self.roster.really_remove_contact, \
user1.show = array[1]
user1.status = array[2]
user1.priority = priority
if jid.find('@') <= 0:

Yann Leboulanger
committed
if gajim.contacts[account].has_key(ji):
self.roster.draw_contact(ji, account)

Yann Leboulanger
committed
elif gajim.contacts[account].has_key(ji):
self.roster.chg_contact_status(user1, array[1], array[2], account)
if old_show < 2 and new_show > 1:
if gajim.config.get_per('soundevents', 'contact_connected',
'enabled'):
helpers.play_sound('contact_connected')
if not self.windows[account]['chats'].has_key(jid) and \

Yann Leboulanger
committed
not gajim.awaiting_messages[account].has_key(jid) and \
gajim.config.get('notify_on_signin') and \

Yann Leboulanger
committed
gajim.allow_notifications[account]:
show_notification = False
# check OUR status and if we allow notifications for that status
if gajim.config.get('autopopupaway'): # always notify
show_notification = True
elif gajim.connections[account].connected in (2, 3): # we're online or chat
show_notification = True
if show_notification:
instance = dialogs.PopupNotificationWindow(self,
_('Contact Signed In'), jid, account)
self.roster.popup_notification_windows.append(instance)
if self.remote and self.remote.is_enabled():
(account, array))
# when contact signs out we reset his chatstate

Yann Leboulanger
committed
elif old_show > 1 and new_show < 2:
if gajim.config.get_per('soundevents', 'contact_disconnected',
'enabled'):
helpers.play_sound('contact_disconnected')
if not self.windows[account]['chats'].has_key(jid) and \

Yann Leboulanger
committed
not gajim.awaiting_messages[account].has_key(jid) and \
gajim.config.get('notify_on_signout'):
show_notification = False
# check OUR status and if we allow notifications for that status
if gajim.config.get('autopopupaway'): # always notify
show_notification = True
elif gajim.connections[account].connected in (2, 3): # we're online or chat
show_notification = True
if show_notification:
instance = dialogs.PopupNotificationWindow(self,
_('Contact Signed Out'), jid, account)
self.roster.popup_notification_windows.append(instance)
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('ContactAbsence', (account, array))
elif self.windows[account]['gc'].has_key(ji): # ji is then room_jid
#it is a groupchat presence
#FIXME: upgrade the chat instances (for pm)
#FIXME: real_jid can be None
self.windows[account]['gc'][ji].chg_contact_status(ji, resource,
array[1], array[2], array[6], array[7], array[8], array[9],
array[10], array[11], array[12], account)
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('GCPresence', (account, array))
def handle_event_msg(self, account, array):
#('MSG', account, (jid, msg, time, encrypted, msg_type, subject, chatstate))
jid = gajim.get_jid_without_resource(array[0])
msg_type = array[4]
if jid.find('@') <= 0:
jid = jid.replace('@', '')

Yann Leboulanger
committed
if self.windows[account]['gc'].has_key(jid): # it's a Private Message
nick = array[0].split('/', 1)[1]
fjid = jid + '/' + nick

Yann Leboulanger
committed
if self.windows[account]['chats'].has_key(fjid):
chat_win = self.windows[account]['chats'][fjid]
chat_win.print_conversation(array[1], fjid, tim = array[2])
return

Yann Leboulanger
committed
qs = gajim.awaiting_messages[account]

Yann Leboulanger
committed
if not qs.has_key(fjid):
qs[fjid] = []
qs[fjid].append((array[1], array[2], array[3]))
self.roster.nb_unread += 1
show = gajim.gc_contacts[account][jid][nick].show
c = Contact(jid = fjid, name = nick, groups = ['none'], show = show,

Yann Leboulanger
committed
ask = 'none')
self.roster.new_chat(c, account)

Yann Leboulanger
committed
return
if gajim.config.get('ignore_unknown_contacts') and \

Yann Leboulanger
committed
not gajim.contacts[account].has_key(jid):
return
if self.windows[account]['chats'].has_key(jid):
chat_win = self.windows[account]['chats'][jid]
contact = gajim.get_first_contact_instance_from_jid(account, jid)
if chatstate is not None: # he sent us reply, so he supports jep85
if contact.chatstate == 'ask': # we were jep85 disco?
contact.chatstate = 'active' # no more
chat_win.handle_incoming_chatstate(account, jid, chatstate)
else:
# got no valid jep85 answer, peer does not support it
first = False
if not self.windows[account]['chats'].has_key(jid) and \

Yann Leboulanger
committed
not gajim.awaiting_messages[account].has_key(jid):
first = True

nkour
committed
if gajim.config.get('notify_on_new_message'):
show_notification = False
# check OUR status and if we allow notifications for that status
if gajim.config.get('autopopupaway'): # always show notification
show_notification = True
elif gajim.connections[account].connected in (2, 3): # we're online or chat
show_notification = True
if show_notification:
if msg_type == 'normal': # single message
instance = dialogs.PopupNotificationWindow(self,
_('New Single Message'), jid, account, msg_type)
else: # chat message
instance = dialogs.PopupNotificationWindow(self,
_('New Message'), jid, account, msg_type)
self.roster.popup_notification_windows.append(instance)
# array : (contact, msg, time, encrypted, msg_type, subject)
self.roster.on_message(jid, array[1], array[2], account, array[3],
array[4], array[5])
if gajim.config.get_per('soundevents', 'first_message_received',

Yann Leboulanger
committed
'enabled') and first:
helpers.play_sound('first_message_received')
if gajim.config.get_per('soundevents', 'next_message_received',

Yann Leboulanger
committed
'enabled') and not first:
helpers.play_sound('next_message_received')
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('NewMessage', (account, array))
def handle_event_msgerror(self, account, array):

Yann Leboulanger
committed
#('MSGERROR', account, (jid, error_code, error_msg, msg, time))
jid = jids[0]
gcs = self.windows[account]['gc']
if jid in gcs:
nick = jids[1]
if not self.windows[account]['chats'].has_key(fjid):
gc = gcs[jid]
tv = gc.list_treeview[jid]
model = tv.get_model()

Yann Leboulanger
committed
i = gc.get_contact_iter(jid, nick)
if i:
show = model[i][3]
else:
show = 'offline'
c = Contact(jid = fjid, name = nick, groups = ['none'],
self.roster.new_chat(c, account)
self.windows[account]['chats'][fjid].print_conversation(
'Error %s: %s' % (array[1], array[2]), fjid, 'status')
return
gcs[jid].print_conversation('Error %s: %s' % \

Yann Leboulanger
committed
(array[1], array[2]), jid)
if gcs[jid].get_active_jid() == jid:
gcs[jid].set_subject(jid,
gcs[jid].subjects[jid])
return
if jid.find('@') <= 0:
jid = jid.replace('@', '')

Yann Leboulanger
committed
self.roster.on_message(jid, _('error while sending') + \
' \"%s\" ( %s )' % (array[3], array[2]), array[4], account)
def handle_event_msgsent(self, account, array):
msg = array[1]
# do not play sound when standalone chatstate message (eg no msg)
if msg and gajim.config.get_per('soundevents', 'message_sent', 'enabled'):
helpers.play_sound('message_sent')
def handle_event_subscribe(self, account, array):
#('SUBSCRIBE', account, (jid, text))
dialogs.SubscriptionRequestWindow(self, array[0], array[1], account)
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('Subscribe', (account, array))
def handle_event_subscribed(self, account, array):
#('SUBSCRIBED', account, (jid, resource))

Yann Leboulanger
committed
if gajim.contacts[account].has_key(jid):
c = gajim.get_first_contact_instance_from_jid(account, jid)
c.resource = array[1]
self.roster.remove_contact(c, account)
if _('not in the roster') in c.groups:
c.groups.remove(_('not in the roster'))
if len(c.groups) == 0:
c.groups = [_('General')]
self.roster.add_contact_to_roster(c.jid, account)
gajim.connections[account].update_contact(c.jid, c.name, c.groups)
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
contact1 = Contact(jid = jid, name = jid.split('@')[0],
groups = [_('General')], show = 'online', status = 'online',
ask = 'to', resource = array[1], keyID = keyID)
self.roster.add_contact_to_roster(jid, account)

nkour
committed
dialogs.InformationDialog(_('Authorization accepted'),
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('Subscribed', (account, array))
def handle_event_unsubscribed(self, account, jid):
dialogs.InformationDialog(_('Contact "%s" removed subscription from you') % jid,
_('You will always see him as offline.'))
if self.remote and self.remote.is_enabled():

Yann Leboulanger
committed
self.remote.raise_signal('Unsubscribed', (account, jid))
def handle_event_agent_info(self, account, array):
#('AGENT_INFO', account, (agent, identities, features, items))
if self.windows[account].has_key('disco'):
self.windows[account]['disco'].agent_info(array[0], array[1], \
array[2], array[3])
def handle_event_register_agent_info(self, account, array):
#('AGENT_INFO', account, (agent, infos))
if array[1].has_key('instructions'):

nkour
committed
config.ServiceRegistrationWindow(array[0], array[1], self, account)

nkour
committed
dialogs.ErrorDialog(_('Contact with "%s" cannot be established'\
% array[0]), _('Check your connection or try again later.')).get_response()

Yann Leboulanger
committed
def handle_event_agent_info_items(self, account, array):
#('AGENT_INFO_ITEMS', account, (agent, node, items))
if self.windows[account].has_key('disco'):
self.windows[account]['disco'].agent_info_items(array[0], array[1],
array[2])

Yann Leboulanger
committed
def handle_event_agent_info_info(self, account, array):
#('AGENT_INFO_INFO', account, (agent, node, identities, features))
if self.windows[account].has_key('disco'):
self.windows[account]['disco'].agent_info_info(array[0], array[1], \

Yann Leboulanger
committed
def handle_event_acc_ok(self, account, array):

Yann Leboulanger
committed
#('ACC_OK', account, (name, config))
name = array[0]

nkour
committed
dialogs.InformationDialog(_('Account registration successful'),
_('The account "%s" has been registered with the Jabber server.') % name)

Yann Leboulanger
committed
gajim.config.add_per('accounts', name)
for opt in array[1]:
gajim.config.set_per('accounts', name, opt, array[1][opt])

Yann Leboulanger
committed
self.windows['account_modification'].account_is_ok(array[0])
self.windows[name] = {'infos': {}, 'chats': {}, 'gc': {}, 'gc_config': {}}
self.windows[name]['xml_console'] = dialogs.XMLConsoleWindow(self, name)

Yann Leboulanger
committed
gajim.awaiting_messages[name] = {}
# disconnect from server - our status in roster is offline
gajim.connections[name].connected = 1
gajim.gc_contacts[name] = {}
gajim.gc_connected[name] = {}

Yann Leboulanger
committed
gajim.nicks[name] = array[1]['name']
gajim.allow_notifications[name] = False
gajim.groups[name] = {}
gajim.contacts[name] = {}
gajim.newly_added[name] = []
gajim.to_be_removed[name] = []
gajim.encrypted_chats[name] = []

Yann Leboulanger
committed
gajim.last_message_time[name] = {}

Yann Leboulanger
committed
gajim.status_before_autoaway[name] = ''
gajim.events_for_ui[name] = []
gajim.connections[name].change_status('offline', None, True)
gajim.connections[name].connected = 0
if self.windows.has_key('accounts'):
self.windows['accounts'].init_accounts()
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('NewAccount', (account, array))
def handle_event_quit(self, p1, p2):
def handle_event_myvcard(self, account, array):
if array.has_key('NICKNAME'):
nick = array['NICKNAME']

Yann Leboulanger
committed
gajim.nicks[account] = nick
if self.windows[account]['infos'].has_key(array['jid']):
win = self.windows[account]['infos'][array['jid']]
win.set_values(array)
if self.windows[account]['infos'].has_key(array['jid']):

Yann Leboulanger
committed
win = self.windows[account]['infos'][array['jid']]
elif self.windows[account]['infos'].has_key(array['jid'] + '/' + \
array['resource']):
win = self.windows[account]['infos'][array['jid'] + '/' + \
array['resource']]
if win:
win.set_values(array)
#show avatar in chat
win = None
win = self.windows[account]['chats'][array['jid']]
elif self.windows[account]['chats'].has_key(array['jid'] + '/' + \
array['resource']):
win = self.windows[account]['chats'][array['jid'] + '/' + \
array['resource']]
def handle_event_os_info(self, account, array):
if self.windows[account]['infos'].has_key(array[0]):
win = self.windows[account]['infos'][array[0]]
elif self.windows[account]['infos'].has_key(array[0] + '/' + array[1]):
win = self.windows[account]['infos'][array[0] + '/' + array[1]]
if win:
win.set_os_info(array[1], array[2], array[3])
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('OsInfo', (account, array))
def handle_event_gc_msg(self, account, array):
#('GC_MSG', account, (jid, msg, time))
if not self.windows[account]['gc'].has_key(jid):
return
if len(jids) == 1:
#message from server
self.windows[account]['gc'][jid].print_conversation(array[1], jid, \
tim = array[2])
self.windows[account]['gc'][jid].print_conversation(array[1], jid, \
jids[1], array[2])
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('GCMessage', (account, array))
def handle_event_gc_subject(self, account, array):
#('GC_SUBJECT', account, (jid, subject))
jid = jids[0]
if not self.windows[account]['gc'].has_key(jid):
return
self.windows[account]['gc'][jid].set_subject(jid, array[1])
if len(jids) > 1:
self.windows[account]['gc'][jid].print_conversation(\
'%s has set the subject to %s' % (jids[1], array[1]), jid)
def handle_event_gc_config(self, account, array):
#('GC_CONFIG', account, (jid, config)) config is a dict
jid = array[0].split('/')[0]
if not self.windows[account]['gc_config'].has_key(jid):
self.windows[account]['gc_config'][jid] = \

nkour
committed
config.GroupchatConfigWindow(self, account, jid, array[1])
def handle_event_bad_passphrase(self, account, array):
use_gpg_agent = gajim.config.get('use_gpg_agent')
if use_gpg_agent:
return
keyID = gajim.config.get_per('accounts', account, 'keyid')
self.roster.forget_gpg_passphrase(keyID)
dialogs.WarningDialog(_('Your passphrase is incorrect'),
_('You are currently connected without your OpenPGP key.')).get_response()

Yann Leboulanger
committed
def handle_event_roster_info(self, account, array):
#('ROSTER_INFO', account, (jid, name, sub, ask, groups))
jid = array[0]

Yann Leboulanger
committed
if not gajim.contacts[account].has_key(jid):

Yann Leboulanger
committed
return

Yann Leboulanger
committed
users = gajim.contacts[account][jid]

Yann Leboulanger
committed
if not (array[2] or array[3]):
self.roster.remove_contact(users[0], account)

Yann Leboulanger
committed
del gajim.contacts[account][jid]

Yann Leboulanger
committed
#TODO if it was the only one in its group, remove the group
return
for user in users:
if name:
user.name = name

Yann Leboulanger
committed
user.sub = array[2]
user.ask = array[3]
if array[4]:
user.groups = array[4]
self.roster.draw_contact(jid, account)
if self.remote and self.remote.is_enabled():
self.remote.raise_signal('RosterInfo', (account, array))

Yann Leboulanger
committed
def handle_event_bookmarks(self, account, bms):
# ('BOOKMARKS', account, [{name,jid,autojoin,password,nick}, {}])
# We received a bookmark item from the server (JEP48)
# Auto join GC windows if neccessary
if bm['autojoin'] in ('1', 'true'):
self.roster.join_gc_room(account, bm['jid'], bm['nick'],
bm['password'])
self.roster.make_menu()
def handle_event_file_send_error(self, account, array):
jid = array[0]
file_props = array[1]
ft = self.windows['file_transfers']
ft.set_status(file_props['type'], file_props['sid'], 'stop')
if gajim.config.get('notify_on_new_message'):
# check if we should be notified
instance = dialogs.PopupNotificationWindow(self,
_('File Transfer Error'), jid, account, 'file-send-error', file_props)
self.roster.popup_notification_windows.append(instance)
elif (gajim.connections[account].connected in (2, 3)
and gajim.config.get('autopopup')) or \
gajim.config.get('autopopupaway'):
self.windows['file_transfers'].show_send_error(file_props)
def handle_event_file_request_error(self, account, array):
jid = array[0]
file_props = array[1]
errno = file_props['error']
ft = self.windows['file_transfers']
ft.set_status(file_props['type'], file_props['sid'], 'stop')
if gajim.config.get('notify_on_new_message'):
# check if we should be notified
msg_type = 'file-error'
else:
msg_type = 'file-request-error'
_('File Transfer Error'), jid, account, msg_type, file_props)
self.roster.popup_notification_windows.append(instance)
elif (gajim.connections[account].connected in (2, 3)
and gajim.config.get('autopopup')) or \
gajim.config.get('autopopupaway'):
self.windows['file_transfers'].show_stopped(jid, file_props)
else:
self.windows['file_transfers'].show_request_error(file_props)
def handle_event_file_request(self, account, array):
jid = array[0]
if not gajim.contacts[account].has_key(jid):
return
file_props = array[1]
# FIXME: in 0.9 we'll have a queue for that
# if gajim.config.get('notify_on_new_message'):
# # check if we should be notified
# instance = dialogs.PopupNotificationWindow(self,
# _('File Transfer Request'), jid, account, 'file', file_props)
# self.roster.popup_notification_windows.append(instance)
# elif (gajim.connections[account].connected in (2, 3)
# and gajim.config.get('autopopup')) or \
# gajim.config.get('autopopupaway'):
# contact = gajim.contacts[account][jid][0]
# self.windows['file_transfers'].show_file_request(
# account, contact, file_props)
self.windows['file_transfers'].show_file_request(
account, contact, file_props)
def handle_event_file_progress(self, account, file_props):
self.windows['file_transfers'].set_progress(file_props['type'],
file_props['sid'], file_props['received-len'])
def handle_event_file_rcv_completed(self, account, file_props):
ft = self.windows['file_transfers']
if file_props['error'] == 0:
ft.set_progress(file_props['type'], file_props['sid'],
file_props['received-len'])
else:
ft.set_status(file_props['type'], file_props['sid'], 'stop')
if file_props.has_key('stalled') and file_props['stalled'] or \
file_props.has_key('paused') and file_props['paused']:
jid = unicode(file_props['sender'])
if (gajim.connections[account].connected in (2, 3)
and gajim.config.get('autopopup')) or \
gajim.config.get('autopopupaway'):
ft.show_stopped(jid, file_props)
return
if file_props['error'] == 0:
msg_type = 'file-completed'
event_type = _('File Transfer Completed')
elif file_props['error'] == -1:
msg_type = 'file-stopped'
event_type = _('File Transfer Stopped')
instance = dialogs.PopupNotificationWindow(self, event_type,
jid, account, msg_type, file_props)
self.roster.popup_notification_windows.append(instance)
def handle_event_stanza_arrived(self, account, stanza):
if not self.windows.has_key(account):
return
if self.windows[account].has_key('xml_console'):
self.windows[account]['xml_console'].print_stanza(stanza, 'incoming')
def handle_event_stanza_sent(self, account, stanza):
if not self.windows.has_key(account):
return
if self.windows[account].has_key('xml_console'):
self.windows[account]['xml_console'].print_stanza(stanza, 'outgoing')
'''Check idle status and change that status if needed'''
if not self.sleeper.poll():
return True # renew timeout (loop for ever)

Yann Leboulanger
committed
state = self.sleeper.getState()
for account in gajim.connections:
if not gajim.sleeper_state.has_key(account) or \
not gajim.sleeper_state[account]:

Yann Leboulanger
committed
continue
if state == common.sleepy.STATE_AWAKE and \

Yann Leboulanger
committed
self.roster.send_status(account, 'online',

Yann Leboulanger
committed
elif state == common.sleepy.STATE_AWAY and \
gajim.config.get('autoaway'):

Yann Leboulanger
committed
#we save out online status
gajim.status_before_autoaway[account] = \
gajim.connections[account].status
#we go away (no auto status) [we pass True to auto param]
self.roster.send_status(account, 'away',

Yann Leboulanger
committed
elif state == common.sleepy.STATE_XAWAY and (\
gajim.sleeper_state[account] == 'autoaway' or \
gajim.sleeper_state[account] == 'online') and \
gajim.config.get('autoxa'):
self.roster.send_status(account, 'xa',
return True # renew timeout (loop for ever)
def autoconnect(self):
'''auto connect at startup'''
for a in gajim.connections:
if gajim.config.get_per('accounts', a, 'autoconnect'):
message = self.roster.get_status_message('online')
if message == -1:
return
for a in gajim.connections:
if gajim.config.get_per('accounts', a, 'autoconnect'):
self.roster.send_status(a, 'online', message)
return False
def show_systray(self):
self.systray.show_icon()
def hide_systray(self):
self.systray.hide_icon()
def image_is_ok(self, image):
if not os.path.exists(image):
return False
img = gtk.Image()
try:
img.set_from_file(image)
except:
return False
t = img.get_storage_type()
if t != gtk.IMAGE_PIXBUF and t != gtk.IMAGE_ANIMATION:
def make_regexps(self):
# regexp meta characters are: . ^ $ * + ? { } [ ] \ | ( )
# one escapes the metachars with \
# \S matches anything but ' ' '\t' '\n' '\r' '\f' and '\v'
# \s matches any whitespace character
# \w any alphanumeric character
# \W any non-alphanumeric character
# \b means word boundary. This is a zero-width assertion that
# matches only at the beginning or end of a word.
# ^ matches at the beginning of lines
# * means 0 or more times
# + means 1 or more times
# | means or
# [^*] anything but '*' (inside [] you don't have to escape metachars)
# [^\s*] anything but whitespaces and '*'
# (?<!\S) is a one char lookbehind assertion and asks for any leading whitespace
# and mathces beginning of lines so we have correct formatting detection
# even if the the text is just '*foo*'

nkour
committed
# (?!\S) is the same thing but it's a lookahead assertion
# \S*[^\s\W] --> in the matching string don't match ? or ) etc.. if at the end
# so http://be) will match http://be and http://be)be) will match http://be)be
links = r'\bhttp://\S*[^\s\W]|' r'\bhttps://\S*[^\s\W]|' r'\bnews://\S*[^\s\W]|' r'\bftp://\S*[^\s\W]|' r'\bed2k://\S*[^\s\W]|' r'\bwww\.\S*[^\s\W]|' r'\bftp\.\S*[^\s\W]|'
#2nd one: at_least_one_char@at_least_one_char.at_least_one_char
#detects eg. *b* *bold* *bold bold* test *bold*
#doesn't detect (it's a feature :P) * bold* *bold * * bold * test*bold*

nkour
committed
formatting = r'(?<!\S)\*[^\s*]([^*]*[^\s*])?\*(?!\S)|' r'(?<!\S)/[^\s/]([^/]*[^\s/])?/(?!\S)|' r'(?<!\S)_[^\s_]([^_]*[^\s_])?_(?!\S)'
basic_pattern = links + mail + formatting
self.basic_pattern_re = sre.compile(basic_pattern, sre.IGNORECASE)
emoticons_pattern = ''
for emoticon in self.emoticons: # travel thru emoticons list
emoticon_escaped = sre.escape(emoticon) # espace regexp metachars
emoticons_pattern += emoticon_escaped + '|'# | means or in regexp
emot_and_basic_pattern = emoticons_pattern + basic_pattern
self.emot_and_basic_re = sre.compile(emot_and_basic_pattern,
sre.IGNORECASE)
# at least one character in 3 parts (before @, after @, after .)
self.sth_at_sth_dot_sth_re = sre.compile(r'\S+@\S+\.\S*[^\s)?]')
def on_launch_browser_mailer(self, widget, url, kind):
def init_regexp(self):
#initialize emoticons dictionary
self.emoticons = dict()

Yann Leboulanger
committed
emots = gajim.config.get_per('emoticons')
for emot in emots:
emot_file = gajim.config.get_per('emoticons', emot, 'path')
if not self.image_is_ok(emot_file):
continue
self.emoticons[emot] = emot_file
# update regular expressions
def register_handlers(self, con):

Yann Leboulanger
committed
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
self.handlers = {
'ROSTER': self.handle_event_roster,
'WARNING': self.handle_event_warning,
'ERROR': self.handle_event_error,
'INFORMATION': self.handle_event_information,
'ERROR_ANSWER': self.handle_event_error_answer,
'STATUS': self.handle_event_status,
'NOTIFY': self.handle_event_notify,
'MSG': self.handle_event_msg,
'MSGERROR': self.handle_event_msgerror,
'MSGSENT': self.handle_event_msgsent,
'SUBSCRIBED': self.handle_event_subscribed,
'UNSUBSCRIBED': self.handle_event_unsubscribed,
'SUBSCRIBE': self.handle_event_subscribe,
'AGENT_INFO': self.handle_event_agent_info,
'REGISTER_AGENT_INFO': self.handle_event_register_agent_info,
'AGENT_INFO_ITEMS': self.handle_event_agent_info_items,
'AGENT_INFO_INFO': self.handle_event_agent_info_info,
'QUIT': self.handle_event_quit,
'ACC_OK': self.handle_event_acc_ok,
'MYVCARD': self.handle_event_myvcard,
'VCARD': self.handle_event_vcard,
'OS_INFO': self.handle_event_os_info,
'GC_MSG': self.handle_event_gc_msg,
'GC_SUBJECT': self.handle_event_gc_subject,
'GC_CONFIG': self.handle_event_gc_config,
'BAD_PASSPHRASE': self.handle_event_bad_passphrase,
'ROSTER_INFO': self.handle_event_roster_info,
'BOOKMARKS': self.handle_event_bookmarks,
'CON_TYPE': self.handle_event_con_type,
'FILE_REQUEST': self.handle_event_file_request,
'FILE_REQUEST_ERROR': self.handle_event_file_request_error,
'FILE_SEND_ERROR': self.handle_event_file_send_error,
'STANZA_ARRIVED': self.handle_event_stanza_arrived,
'STANZA_SENT': self.handle_event_stanza_sent,
'HTTP_AUTH': self.handle_event_http_auth,
}
def exec_event(self, account):
ev = gajim.events_for_ui[account].pop(0)
self.handlers[ev[0]](account, ev[1])
def process_connections(self):
try:

Yann Leboulanger
committed
# We copy the list of connections because one can disappear while we
# process()
accounts = []
for account in gajim.connections:

Yann Leboulanger
committed
accounts.append(account)
for account in accounts:
if gajim.connections[account].connected:
gajim.connections[account].process(0.01)
if gajim.socks5queue.connected:
gajim.socks5queue.process(0.01)
for account in gajim.events_for_ui: #when we create a new account we don't have gajim.connection

Yann Leboulanger
committed
while len(gajim.events_for_ui[account]):
gajim.mutex_events_for_ui.lock(self.exec_event, account)
gajim.mutex_events_for_ui.unlock()