Commit 1dbb2a89 authored by sb's avatar sb

merge zeroconf branch to trunk

parents ff8ba1f5 6ea11dc7
......@@ -2,6 +2,7 @@
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="accounts_window">
<property name="border_width">12</property>
<property name="title" translatable="yes">Accounts</property>
......@@ -64,7 +65,7 @@
<property name="visible">True</property>
<property name="tooltip" translatable="yes">If you have 2 or more accounts and it is checked, Gajim will list all contacts as if you had one account</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">_Merge accounts</property>
<property name="label" translatable="yes">Mer_ge accounts</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
......@@ -80,6 +81,38 @@
</packing>
</child>
<child>
<widget class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="enable_zeroconf_checkbutton">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">If checked, all local contacts that use a Bonjour compatible chat client (like iChat, Trillian or Gaim) will be shown in roster. You don't need to be connected to a jabber server for it to work.
This is only available if python-avahi is installed and avahi-daemon is running.</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">_Enable link-local messaging</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox15">
<property name="visible">True</property>
......@@ -267,4 +300,5 @@
</widget>
</child>
</widget>
</glade-interface>
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkMenu" id="zeroconf_contact_context_menu">
<child>
<widget class="GtkImageMenuItem" id="start_chat_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Start _Chat</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1534">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="rename_menuitem">
<property name="label" translatable="yes">_Rename</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1535">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="edit_groups_menuitem">
<property name="label" translatable="yes">Edit _Groups</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_send_file_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_file_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send _File</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1536">
<property name="visible">True</property>
<property name="stock">gtk-file</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="assign_openpgp_key_menuitem">
<property name="label" translatable="yes">Assign Open_PGP Key</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_assign_openpgp_key_menuitem_activate" last_modification_time="Thu, 30 Jun 2005 22:57:59 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1537">
<property name="visible">True</property>
<property name="stock">gtk-dialog-authentication</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_special_notification_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Add Special _Notification</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1538">
<property name="visible">True</property>
<property name="stock">gtk-info</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_information_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="information_menuitem">
<property name="label">gtk-info</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1539">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkMenu" id="zeroconf_context_menu">
<child>
<widget class="GtkImageMenuItem" id="status_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1258">
<property name="visible">True</property>
<property name="stock">gtk-network</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="zeroconf_properties_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Modify Account...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1259">
<property name="visible">True</property>
<property name="stock">gtk-preferences</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>
This diff is collapsed.
This diff is collapsed.
......@@ -6,7 +6,8 @@
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005 Travis Shirk <travis@pobox.com>
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
##
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
##
## 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.
......@@ -82,6 +83,7 @@ class Config:
'saveposition': [ opt_bool, True ],
'mergeaccounts': [ opt_bool, False, '', True ],
'sort_by_show': [ opt_bool, True, '', True ],
'enable_zeroconf': [opt_bool, False, _('Enable link-local/zeroconf messaging')],
'use_speller': [ opt_bool, False, ],
'speller_language': [ opt_str, '', _('Language used by speller')],
'print_time': [ opt_str, 'always', _('\'always\' - print time for every message.\n\'sometimes\' - print time every print_ichat_every_foo_minutes minute.\n\'never\' - never print time.')],
......@@ -259,6 +261,11 @@ class Config:
'msgwin-y-position': [opt_int, -1], # Default is to let the wm decide
'msgwin-width': [opt_int, 480],
'msgwin-height': [opt_int, 440],
'is_zeroconf': [opt_bool, False],
'zeroconf_first_name': [ opt_str, '', '', True ],
'zeroconf_last_name': [ opt_str, '', '', True ],
'zeroconf_jabber_id': [ opt_str, '', '', True ],
'zeroconf_email': [ opt_str, '', '', True ],
}, {}),
'statusmsg': ({
'message': [ opt_str, '' ],
......
......@@ -46,6 +46,7 @@ class Connection(ConnectionHandlers):
self.connection = None # xmpppy ClientCommon instance
# this property is used to prevent double connections
self.last_connection = None # last ClientCommon instance
self.is_zeroconf = False
self.gpg = None
self.status = ''
self.priority = gajim.get_priority(name, 'offline')
......
......@@ -120,6 +120,12 @@ status_before_autoaway = {}
SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible']
# zeroconf account name
ZEROCONF_ACC_NAME = 'Local'
priority_dict = {}
for status in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
priority_dict[status] = config.get('autopriority' + status)
def get_nick_from_jid(jid):
pos = jid.find('@')
return jid[:pos]
......
......@@ -74,7 +74,6 @@ class Dispatcher(PlugIn):
self.RegisterProtocol('presence', Presence)
self.RegisterProtocol('message', Message)
self.RegisterDefaultHandler(self.returnStanzaHandler)
# Register Gajim's event handler as soon as dispatcher begins
self.RegisterEventHandler(self._owner._caller._event_dispatcher)
self.on_responses = {}
......@@ -84,7 +83,10 @@ class Dispatcher(PlugIn):
self._owner.lastErrNode = None
self._owner.lastErr = None
self._owner.lastErrCode = None
self.StreamInit()
if hasattr(self._owner, 'StreamInit'):
self._owner.StreamInit()
else:
self.StreamInit()
def plugout(self):
''' Prepares instance to be destructed. '''
......@@ -134,7 +136,7 @@ class Dispatcher(PlugIn):
return 0
except ExpatError:
sys.exc_clear()
self.DEBUG('Invalid XML received from server. Forcing disconnect.')
self.DEBUG('Invalid XML received from server. Forcing disconnect.', 'error')
self._owner.Connection.pollend()
return 0
if len(self._pendingExceptions) > 0:
......
......@@ -183,7 +183,7 @@ class Session:
if self.sendbuffer:
try:
# LOCK_QUEUE
sent=self._send(self.sendbuffer) # âÌÏËÉÒÕÀÝÁÑ ÛÔÕÞËÁ!
sent=self._send(self.sendbuffer) # blocking socket
except:
# UNLOCK_QUEUE
self.set_socket_state(SOCKET_DEAD)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
## common/zeroconf/roster_zeroconf.py
##
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
##
## 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.
##
from common.zeroconf import zeroconf
class Roster:
def __init__(self, zeroconf):
self._data = None
self.zeroconf = zeroconf # our zeroconf instance
def update_roster(self):
for val in self.zeroconf.contacts.values():
self.setItem(val[zeroconf.C_NAME])
def getRoster(self):
#print 'roster_zeroconf.py: getRoster'
if self._data is None:
self._data = {}
self.update_roster()
return self
def getDiffs(self):
''' update the roster with new data and return dict with
jid -> new status pairs to do notifications and stuff '''
diffs = {}
old_data = self._data.copy()
self.update_roster()
for key in old_data.keys():
if self._data.has_key(key):
if old_data[key] != self._data[key]:
diffs[key] = self._data[key]['status']
#print 'roster_zeroconf.py: diffs:' + str(diffs)
return diffs
def setItem(self, jid, name = '', groups = ''):
#print 'roster_zeroconf.py: setItem %s' % jid
(service_jid, domain, interface, protocol, host, address, port, bare_jid, txt) \
= self.zeroconf.get_contact(jid)
self._data[jid]={}
self._data[jid]['ask'] = 'no' #?
self._data[jid]['subscription'] = 'both'
self._data[jid]['groups'] = []
self._data[jid]['resources'] = {}
self._data[jid]['address'] = address
self._data[jid]['host'] = host
self._data[jid]['port'] = port
txt_dict = self.zeroconf.txt_array_to_dict(txt)
if txt_dict.has_key('status'):
status = txt_dict['status']
else:
status = ''
nm = ''
if txt_dict.has_key('1st'):
nm = txt_dict['1st']
if txt_dict.has_key('last'):
if nm != '':
nm += ' '
nm += txt_dict['last']
if nm:
self._data[jid]['name'] = nm
else:
self._data[jid]['name'] = jid
if status == 'avail':
status = 'online'
self._data[jid]['txt_dict'] = txt_dict
if not self._data[jid]['txt_dict'].has_key('msg'):
self._data[jid]['txt_dict']['msg'] = ''
self._data[jid]['status'] = status
self._data[jid]['show'] = status
def delItem(self, jid):
#print 'roster_zeroconf.py: delItem %s' % jid
if self._data.has_key(jid):
del self._data[jid]
def getItem(self, jid):
#print 'roster_zeroconf.py: getItem: %s' % jid
if self._data.has_key(jid):
return self._data[jid]
def __getitem__(self,jid):
#print 'roster_zeroconf.py: __getitem__'
return self._data[jid]
def getItems(self):
#print 'roster_zeroconf.py: getItems'
# Return list of all [bare] JIDs that the roster currently tracks.
return self._data.keys()
def keys(self):
#print 'roster_zeroconf.py: keys'
return self._data.keys()
def getRaw(self):
#print 'roster_zeroconf.py: getRaw'
return self._data
def getResources(self, jid):
#print 'roster_zeroconf.py: getResources(%s)' % jid
return {}
def getGroups(self, jid):
return self._data[jid]['groups']
def getName(self, jid):
if self._data.has_key(jid):
return self._data[jid]['name']
def getStatus(self, jid):
if self._data.has_key(jid):
return self._data[jid]['status']
def getMessage(self, jid):
if self._data.has_key(jid):
return self._data[jid]['txt_dict']['msg']
def getShow(self, jid):
#print 'roster_zeroconf.py: getShow'
return getStatus(jid)
def getPriority(jid):
return 5
def getSubscription(self,jid):
#print 'roster_zeroconf.py: getSubscription'
return 'both'
def Subscribe(self,jid):
pass
def Unsubscribe(self,jid):
pass
def Authorize(self,jid):
pass
def Unauthorize(self,jid):
pass
This diff is collapsed.
This diff is collapsed.
......@@ -31,6 +31,7 @@ import message_control
from chat_control import ChatControlBase
from common import exceptions
from common.zeroconf import connection_zeroconf
if os.name == 'posix': # dl module is Unix Only
try: # rename the process name to gajim
......@@ -618,7 +619,9 @@ class Interface:
jids = full_jid_with_resource.split('/', 1)
jid = jids[0]
gc_control = self.msg_win_mgr.get_control(jid, account)
if gc_control and gc_control.type_id == message_control.TYPE_GC:
if gc_control and gc_control.type_id != message_control.TYPE_GC:
gc_control = None
if gc_control:
if len(jids) > 1: # it's a pm
nick = jids[1]
if not self.msg_win_mgr.get_control(full_jid_with_resource,
......@@ -1406,6 +1409,20 @@ class Interface:
if win.startswith('privacy_list_'):
self.instances[account][win].check_active_default(data)
def handle_event_zc_name_conflict(self, account, data):
dlg = dialogs.InputDialog(_('Username Conflict'),
_('Please type a new username for your local account'),
is_modal = True)
dlg.input_entry.set_text(data)
response = dlg.get_response()
if response == gtk.RESPONSE_OK:
new_name = dlg.input_entry.get_text()
gajim.config.set_per('accounts', account, 'name', new_name)
status = gajim.connections[account].status
gajim.connections[account].username = new_name
gajim.connections[account].change_status(status, '')
def read_sleepy(self):
'''Check idle status and change that status if needed'''
if not self.sleeper.poll():
......@@ -1711,6 +1728,7 @@ class Interface:
'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
'PRIVACY_LISTS_ACTIVE_DEFAULT': \
self.handle_event_privacy_lists_active_default,
'ZC_NAME_CONFLICT': self.handle_event_zc_name_conflict,
}
gajim.handlers = self.handlers
......@@ -1867,9 +1885,13 @@ class Interface:
self.handle_event_file_progress)
gajim.proxy65_manager = proxy65_manager.Proxy65Manager(gajim.idlequeue)
self.register_handlers()
if gajim.config.get('enable_zeroconf'):
gajim.connections[gajim.ZEROCONF_ACC_NAME] = common.zeroconf.connection_zeroconf.ConnectionZeroconf(gajim.ZEROCONF_ACC_NAME)
for account in gajim.config.get_per('accounts'):
gajim.connections[account] = common.connection.Connection(account)
if not gajim.config.get_per('accounts', account, 'is_zeroconf'):
gajim.connections[account] = common.connection.Connection(account)
# gtk hooks
# gtk hooks
gtk.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
gtk.about_dialog_set_url_hook(self.on_launch_browser_mailer, 'url')
......
This diff is collapsed.
......@@ -307,7 +307,14 @@ class GCTooltip(BaseTooltip):
properties.append((show, None))
if contact.jid.strip() != '':
properties.append((_('Jabber ID: '), contact.jid))
jid_markup = '<span weight="bold">' + contact.jid + '</span>'
else:
jid_markup = '<span weight="bold">' + \
gtkgui_helpers.escape_for_pango_markup(contact.get_shown_name()) \
+ '</span>'
properties.append((jid_markup, None))
properties.append((_('Role: '), helpers.get_uf_role(contact.role)))
properties.append((_('Affiliation: '), contact.affiliation.capitalize()))
if hasattr(contact, 'resource') and contact.resource.strip() != '':
properties.append((_('Resource: '),
gtkgui_helpers.escape_for_pango_markup(contact.resource) ))
......@@ -408,10 +415,25 @@ class RosterTooltip(NotificationAreaTooltip):
vcard_table.set_homogeneous(False)
vcard_current_row = 1
properties = []
name_markup = '<b>%s</b>' % gtkgui_helpers.escape_for_pango_markup(
prim_contact.get_shown_name())
properties.append((name_markup, None))
jid_markup = '<span weight="bold">' + prim_contact.jid + '</span>'
properties.append((jid_markup, None))
properties.append((_('Name: '), gtkgui_helpers.escape_for_pango_markup(
prim_contact.get_shown_name())))
if prim_contact.sub:
properties.append(( _('Subscription: '),
gtkgui_helpers.escape_for_pango_markup(helpers.get_uf_sub(prim_contact.sub))))
if prim_contact.keyID:
keyID = None
if len(prim_contact.keyID) == 8:
keyID = prim_contact.keyID
elif len(prim_contact.keyID) == 16:
keyID = prim_contact.keyID[8:]
if keyID:
properties.append((_('OpenPGP: '),
gtkgui_helpers.escape_for_pango_markup(keyID)))
num_resources = 0
# put contacts in dict, where key is priority
contacts_dict = {}
......@@ -422,6 +444,11 @@ class RosterTooltip(NotificationAreaTooltip):
contacts_dict[contact.priority].append(contact)
else:
contacts_dict[contact.priority] = [contact]
if num_resources == 1 and contact.resource:
properties.append((_('Resource: '),
gtkgui_helpers.escape_for_pango_markup(contact.resource) + ' (' + \
unicode(contact.priority) + ')'))
if num_resources > 1:
properties.append((_('Status: '), ' '))
transport = gajim.get_transport_name_from_jid(
......
......@@ -2,6 +2,7 @@
##
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
......@@ -336,3 +337,150 @@ class VcardWindow:
def on_close_button_clicked(self, widget):
self.window.destroy()
class ZeroconfVcardWindow:
def __init__(self, contact, account, is_fake = False):
# the contact variable is the jid if vcard is true
self.xml = gtkgui_helpers.get_glade('zeroconf_information_window.glade')
self.window = self.xml.get_widget('zeroconf_information_window')
self.contact = contact
self.account = account
self.is_fake = is_fake
# self.avatar_mime_type = None
# self.avatar_encoded = None
self.fill_contact_page()
self.fill_personal_page()
self.xml.signal_autoconnect(self)
self.window.show_all()
def on_zeroconf_information_window_destroy(self, widget):
del gajim.interface.instances[self.account]['infos'][self.contact.jid]
def on_zeroconf_information_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
self.window.destroy()
def on_log_history_checkbutton_toggled(self, widget):
#log conversation history?
oldlog = True
no_log_for = gajim.config.get_per('accounts', self.account,
'no_log_for').split()
if self.contact.jid in no_log_for:
oldlog = False
log = widget.get_active()
if not log and not self.contact.jid in no_log_for:
no_log_for.append(self.contact.jid)
if log and self.contact.jid in no_log_for:
no_log_for.remove(self.contact.jid)
if oldlog != log:
gajim.config.set_per('accounts', self.account, 'no_log_for',
' '.join(no_log_for))
def on_PHOTO_eventbox_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3: # right click
menu = gtk.Menu()
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
menuitem.connect('activate',
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
self.contact.jid, self.account, self.contact.name + '.jpeg')
menu.append(menuitem)
menu.connect('selection-done', lambda w:w.destroy())
# show the menu
menu.show_all()
menu.popup(None, None, None, event.button, event.time)
def set_value(self, entry_name, value):
try:
if value and entry_name == 'URL_label':
if gtk.pygtk_version >= (2, 10, 0) and gtk.gtk_version >= (2, 10, 0):