...
 
Commits (117)
[info]
name: Anti Spam
short_name: anti_spam
version: 0.3
version: 0.3.1
description: Block some incoming messages.
authors = Yann Leboulanger <asterix@lagaule.org>
Denis Fomin <fominde@gmail.com>
homepage = http://trac-plugins.gajim.org/wiki/AntiSpamPlugin
max_gajim_version: 0.16.9
from plugin import AppindicatorIntegrationPlugin
[info]
name: Appindicator integration
short_name: appindicator_integration
version: 0.1.5
description: This plugin integrates Gajim with the appindicator. You must have python-appindicator (and Gajim obviously) installed to enable this plugin.
Rewriten from Ubuntu Ayatana Integration plugin
homepage: http://trac-plugins.gajim.org/wiki/AppindicatorSupportPlugin
authors: Denis Borenko <borenko@rambler.ru>
max_gajim_version: 0.16.9
# -*- coding: utf-8 -*-
"""
Appindicator integration plugin.
Rewriten from Ubuntu Ayatana Integration plugin
2013 Denis Borenko <borenko@rambler.ru>
:license: GPLv3
"""
# Python
import os
import time
import gobject
#GTK
import gtkgui_helpers
import gtk
try:
import appindicator
except:
appindicator = None
# Gajim
from common import gajim, ged
from plugins import GajimPlugin
from plugins.plugin import GajimPluginException
from plugins.helpers import log_calls
class AppindicatorIntegrationPlugin(GajimPlugin):
@log_calls("AppindicatorIntegrationPlugin")
def init(self):
self.description = _('This plugin integrates Gajim with the appindicator.\n'
'You must have python-appindicator (and Gajim obviously) '
'installed to enable this plugin.\n')
self.config_dialog = None
self.test_activatable()
self.events_handlers = {'our-show': (ged.GUI2, self.set_indicator_icon)}
def test_activatable(self):
self.available_text = ''
if not appindicator:
self.activatable = False
self.available_text += _('\n\n Error: python-appindicator is missing! \n'
'Please install it.\n')
@log_calls("AppindicatorIntegrationPlugin")
def activate(self):
self.events = {}
self.attention_icon = "tray-message"
self.online_icon = "tray-online"
self.offline_icon = "tray-offline"
self.connected = 0
self.connect_menu_item = gtk.MenuItem('Connect')
self.connect_menu_item.connect("activate", self.connect)
self.show_gajim_menu_item = gtk.MenuItem('Show/hide roster')
self.show_gajim_menu_item.connect("activate", self.roster_raise)
self.show_gajim_menu_item.show()
self.event_separator = gtk.SeparatorMenuItem()
self.menuEventInsertIndex = 3
itemExitSeparator = gtk.SeparatorMenuItem()
itemExitSeparator.show()
itemExit = gtk.MenuItem('Exit')
itemExit.connect("activate", self.on_exit_menuitem_activate)
itemExit.show()
self.menu = gtk.Menu()
self.menu.append(self.connect_menu_item)
self.menu.append(self.show_gajim_menu_item)
self.menu.append(self.event_separator)
self.menu.append(itemExitSeparator)
self.menu.append(itemExit)
self.menu.show()
self.indicator = appindicator.Indicator("Gajim", self.offline_icon,
appindicator.CATEGORY_APPLICATION_STATUS)
self.indicator.set_attention_icon(self.attention_icon)
self.indicator.set_status(appindicator.STATUS_ACTIVE)
self.indicator.set_menu(self.menu)
self.set_indicator_icon()
gajim.events.event_added_subscribe(self.on_event_added)
gajim.events.event_removed_subscribe(self.on_event_removed)
def connect(self, widget, data=None):
for account in gajim.connections:
if gajim.config.get_per('accounts', account,
'sync_with_global_status'):
gajim.connections[account].change_status('online','online')
def set_indicator_icon(self, obj=''):
is_connected = 0
for account in gajim.connections:
if not gajim.config.get_per('accounts', account,
'sync_with_global_status'):
continue
if gajim.account_is_connected(account):
is_connected = 1
break
if self.connected != is_connected:
self.connected = is_connected
if self.connected == 1:
self.indicator.set_icon(self.online_icon)
self.connect_menu_item.hide()
else:
self.indicator.set_icon(self.offline_icon)
self.connect_menu_item.show()
@log_calls("AppindicatorPlugin")
def deactivate(self):
gajim.events.event_added_unsubscribe(self.on_event_added)
gajim.events.event_removed_unsubscribe(self.on_event_removed)
if hasattr(self, 'indicator'):
self.indicator.set_status(appindicator.STATUS_PASSIVE)
del self.indicator
def roster_raise(self, widget, data=None):
win = gajim.interface.roster.window
if win.is_active():
gobject.idle_add(win.hide)
else:
win.present()
# preserve the 'steal focus preservation'
#if self._is_first():
# win.window.focus(gtk.get_current_event_time())
#else:
win.window.focus(long(time.time()))
def on_exit_menuitem_activate(self, widget, data=None):
gajim.interface.roster.on_quit_request()
def event_raise(self, widget, event):
gajim.interface.handle_event(event.account, event.jid, event.type_)
win = gajim.interface.roster.window
if not win.is_active():
win.present()
def on_event_added(self, event):
account = event.account
jid = event.jid
when = time.time()
contact = ""
key = (account, jid)
if event.type_ == "chat" or \
event.type_ == "printed_chat" or \
event.type_ == "normal" or \
event.type_ == "printed_normal" or \
event.type_ == "file-request" or \
event.type_ == "jingle-incoming":
contact = gajim.contacts.get_contact_from_full_jid(account, jid)
if contact:
contact = contact.get_shown_name()
else:
contact = jid
elif event.type_ == "pm" or event.type_ == "printed_pm":
contact = gajim.get_nick_from_jid(gajim.get_room_from_fjid(jid)) + \
"/" + gajim.get_room_and_nick_from_fjid(jid)[1]
elif event.type_ == "printed_marked_gc_msg":
contact = gajim.get_nick_from_jid(gajim.get_room_from_fjid(jid))
else:
return
#print account, jid, when, contact
event.time = when
if key not in self.events:
icon = None
if gajim.config.get("show_avatars_in_roster"):
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(jid)
if pixbuf not in (None, "ask"):
icon = gtk.Image()
icon.set_from_pixbuf(pixbuf)
else:
file_path = gtkgui_helpers.get_path_to_generic_or_avatar(jid)
if os.path.isfile(file_path):
fd = fopen(file_path, 'rb')
data = fd.read()
icon = gtk.Image()
icon.set_from_pixbuf(gtkgui_helpers.get_pixbuf_from_data(data))
item = gtk.ImageMenuItem(contact + " (1)")
if icon:
item.set_image(icon)
item.set_always_show_image(True)
item.connect("activate", self.event_raise, event)
item.show()
self.menu.insert(item, self.menuEventInsertIndex)
self.event_separator.show()
self.events[key] = {}
self.events[key]['item'] = item
self.events[key]['contact'] = contact
self.events[key]['events'] = [event]
else:
self.events[key]['events'].append(event)
item = self.events[key]['item']
item.set_label(self.events[key]['contact'] +
" (" + str(len(self.events[key]['events'])) + ")")
self.indicator.set_status(appindicator.STATUS_ATTENTION)
def on_event_removed(self, events):
for event in events:
key = (event.account, event.jid)
if key in self.events and event in self.events[key]['events']:
self.events[key]['events'].remove(event)
if len(self.events[key]['events']) == 0: # remove indicator
self.menu.remove(self.events[key]['item'])
del self.events[key]
else:
self.events[key]['item'].connect("activate",
self.event_raise, self.events[key]['events'][-1])
if len(self.events) == 0:
self.event_separator.hide()
self.indicator.set_status(appindicator.STATUS_ACTIVE)
\ No newline at end of file
[info]
name: Banner Tweaks
short_name: banner_tweaks
version: 0.1.1
version: 0.1.2
description: Allows user to tweak chat window banner appearance (eg. make it compact).
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://trac-plugins.gajim.org/wiki/BannerTweaksPlugin
max_gajim_version: 0.16.9
from plugin import BirthDayPlugin
[info]
name: Birthday reminder
short_name: birthday_reminder
version: 0.0.3
description: Birthday reminder plugin. Reminder before 5,3,1 and birthday days.
authors: Evgeniy Popov <evgeniypopov@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/BirthdayReminderPlugin
import os
import glob
import datetime
from xml.dom.minidom import *
import gobject
from plugins import GajimPlugin
from plugins.helpers import log_calls
from notify import popup
from common import configpaths
from common import gajim
from common import ged
class BirthDayPlugin(GajimPlugin):
@log_calls('BirthDayPlugin')
def init(self):
self.config_dialog = None
self.description = ('Birthday reminder plugin')
self.events_handlers = {
'roster-received': (ged.GUI2, self.roster_received)}
configpath = configpaths.ConfigPaths()
cache_path = configpath.cache_root
self.vcard_path = os.path.join(cache_path, 'vcards') + os.sep
self.timeout_id = 0
self.showed_accounts = []
def check_birthdays(self, account=None):
def show_popup(account, jid):
contact_instances = gajim.contacts.get_contacts(account, jid)
contact = gajim.contacts.get_highest_prio_contact_from_contacts(
contact_instances)
if contact:
nick = gobject.markup_escape_text(contact.get_shown_name())
try:
image = os.path.dirname(__file__) + os.sep + \
'birthday_reminder_large.png'
except:
image = None
popup('Send message', contact.jid, account, msg_type='', \
path_to_image=image, title=title, text=text + ' ' + nick)
accounts = gajim.contacts.get_accounts()
vcards = []
date_dict = {}
for jid in glob.glob(self.vcard_path + '*@*'):
if os.path.isfile(jid):
vcards.append(jid)
for xmldoc in vcards:
try:
xml = parse(xmldoc)
except:
pass
else:
name = xml.getElementsByTagName('BDAY')
for node in name:
try:
data = node.childNodes[0].nodeValue
date_dict[xmldoc[len(self.vcard_path):][:-1]] = data
except:
pass
today = datetime.date.today()
for key, value in date_dict.iteritems():
try:
convert_date = datetime.datetime.strptime(value, "%Y-%m-%d")
user_bday = datetime.date(today.year, convert_date.month,
convert_date.day)
except:
continue
if user_bday < today:
user_bday = user_bday.replace(year=today.year+1)
time_to_bday = abs(user_bday - today)
title = "BirthDay Reminder"
text = None
if time_to_bday.days > 5:
continue
if time_to_bday.days == 5:
text = "5 days before BDay"
elif time_to_bday.days == 3:
text = "3 days before BDay"
elif time_to_bday.days == 1:
text = "Tomorrow BDay"
elif time_to_bday.days == 0:
text = "Today BDay"
if not text:
continue
if account:
show_popup(account,key)
else:
for acct in accounts:
show_popup(account, key)
return True
@log_calls('BirthDayPlugin')
def activate(self):
#self.check_birthdays()
self.timeout_id = gobject.timeout_add_seconds(24*3600,
self.check_birthdays)
@log_calls('BirthDayPlugin')
def deactivate(self):
if self.timeout_id > 0:
gobject.source_remove(self.timeout_id)
@log_calls('BirthDayPlugin')
def roster_received(self, obj):
if obj.conn.name not in self.showed_accounts:
self.check_birthdays(obj.conn.name)
self.showed_accounts.append(obj.conn.name)
[info]
name: Chatstate in roster
short_name: chatstate
version: 0.5.1
version: 0.5.2
description: Chat State Notifications in roster.
Font color of the contact varies depending on the chat state.
The plugin does not work if you use custom font color for contacts in roster.
authors = Denis Fomin <fominde@gmail.com>
homepage = http://trac-plugins.gajim.org/wiki/ChatstatePlugin
max_gajim_version: 0.16.9
......@@ -122,12 +122,21 @@ class Base(object):
if nick.startswith('* '):
nick = nick.lstrip('* ').split(' ')[0]
nick = nick.lstrip(gajim.config.get('before_nickname'))
nick = nick.lstrip(u'\u200E').rstrip(u'\u200E')
nick = nick.lstrip(u'\u200F').rstrip(u'\u200F')
nicks = gajim.contacts.get_nick_list(self.chat_control.account,
self.chat_control.room_jid)
if nick not in nicks:
return
nick = nick + gajim.config.get('gc_refer_to_nick_char') + ' '
return
message_buffer = self.chat_control.msg_textview.get_buffer()
if message_buffer.get_char_count() < 1:
nick = nick + gajim.config.get('gc_refer_to_nick_char')
else:
start, end = message_buffer.get_bounds()
if message_buffer.get_text(start, end, True)[-1] != ' ':
nick = ' ' + nick
nick += ' '
message_buffer.insert_at_cursor(nick)
self.chat_control.msg_textview.grab_focus()
......
[info]
name: Clickable Nicknames
short_name: clickable_nicknames
version: 0.2.1
version: 0.3.2
description: Clickable nicknames in the conversation textview.
authors: Denis Fomin <fominde@gmail.com>
authors: Andrey Musikhin <melomansegfault@gmail.com>
Denis Fomin <fominde@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/ClickableNicknamesPlugin
max_gajim_version: 0.16.9
......@@ -50,7 +50,7 @@ clients = {
'http://online.yandex.ru/caps': ['yaonline.png', 'Yaonline'],
'http://psi-im.org/caps': ['psi.png', 'Psi'],
'http://jimm.net.ru/caps': ['jimm-aspro.png', 'Jimm'],
'http://stranger.kiev.ua/caps': ['fin.png', 'Fin-bot'],
'http://jabga.ru': ['fin.png', 'Fin jabber'],
'http://bluendo.com/protocol/caps': ['lampiro.png', 'Lampiro'],
'nimbuzz:caps': ['nimbuzz.png', 'Nimbuzz'],
'http://nimbuzz.com/caps': ['nimbuzz.png', 'Nimbuzz'],
......@@ -121,7 +121,6 @@ clients = {
'http://sim-im.org/caps': ['sim.png', 'Sim'],
'http://www.igniterealtime.org/project/spark/caps': ['spark.png', 'Spark'],
'http://emess.eqx.su/caps': ['emess.png', 'Emess'],
'http://fin.jabber.ru/caps': ['bot.png', 'Fin-bot'],
'https://www.jappix.com/': ['jappix.png', 'Jappix'],
'http://japyt.googlecode.com': ['japyt.png', 'Japyt'],
'http://www.xfire.com/': ['xfire.png', 'Xfire'],
......@@ -145,6 +144,9 @@ clients = {
'http://opensource.palm.com/packages.html': ['palm.png', 'Palm'],
'http://spectrum.im/': ['spectrum.png', 'Spectrum'],
'http://tigase.org/messenger': ['tigase.png', 'Tigase'],
'httр://sleekxmpp.com/ver/1.1.11': ['poezio.png', 'Poezio'],
'http://jitsi.org' :['jitsi.png', 'Jitsi'],
'http://miranda-ng.org/caps' :['miranda_ng.png', 'Miranda NG'],
}
libpurple_clients ={
'adium': 'http://www.adium.im/',
......@@ -226,6 +228,8 @@ class ClientsIconsPlugin(GajimPlugin):
# rewrite avatar
if vcard_table.get_property('n-columns') == 4:
if tooltip.avatar_image not in vcard_table.get_children():
return
avatar_widget_idx = vcard_table.get_children().index(
tooltip.avatar_image)
vcard_table.remove(vcard_table.get_children()[avatar_widget_idx])
......@@ -292,6 +296,8 @@ class ClientsIconsPlugin(GajimPlugin):
# rewrite avatar
if vcard_table.get_property('n-columns') == 4:
if tooltip.avatar_image not in vcard_table.get_children():
return
avatar_widget_idx = vcard_table.get_children().index(
tooltip.avatar_image)
vcard_table.remove(vcard_table.get_children()[avatar_widget_idx])
......@@ -303,12 +309,15 @@ class ClientsIconsPlugin(GajimPlugin):
if not caps:
return gtk.image_new_from_pixbuf(self.default_pixbuf), _('Unknown')
if 'pidgin.im' in caps:
if 'pidgin.im/' in caps:
caps = 'libpurple'
for client in libpurple_clients:
if client in contact.resource.lower():
caps = libpurple_clients[client]
if 'sleekxmpp.com'in caps:
caps = 'httр://sleekxmpp.com/ver/1.1.11'
caps_from_jid = self.check_jid(contact.jid)
if caps_from_jid:
caps = caps_from_jid
......@@ -362,12 +371,13 @@ class ClientsIconsPlugin(GajimPlugin):
roster.model)
if not child_iters:
return
if roster.model[child_iters[0]][self.renderer_num] is None:
caps = contact.client_caps._node
if not caps:
caps = self.check_jid(jid)
self.set_icon(roster.model, child_iters[0], self.renderer_num,
caps)
for iter_ in child_iters:
if roster.model[iter_][self.renderer_num] is None:
caps = contact.client_caps._node
if not caps:
caps = self.check_jid(jid)
self.set_icon(roster.model, iter_, self.renderer_num,
caps)
@log_calls('ClientsIconsPlugin')
def connect_with_groupchat_control(self, chat_control):
......@@ -501,8 +511,9 @@ class ClientsIconsPlugin(GajimPlugin):
# zeroconf
return
iter_ = roster._get_contact_iter(iq_obj.jid, iq_obj.conn.name, contact,
roster.model)[0]
iters = roster._get_contact_iter(iq_obj.jid, iq_obj.conn.name, contact,
roster.model)
iter_ = iters[0]
if contact.show == 'error':
self.set_icon(roster.model, iter_, self.renderer_num, None)
......@@ -520,17 +531,20 @@ class ClientsIconsPlugin(GajimPlugin):
if tag:
caps = tag[0].getAttr('node')
if caps:
if 'pidgin.im' in caps:
if 'pidgin.im/' in caps:
caps = 'libpurple'
for client in libpurple_clients:
if client in contact.resource.lower():
caps = libpurple_clients[client]
if 'sleekxmpp.com'in caps:
caps = 'httр://sleekxmpp.com/ver/1.1.11'
caps_from_jid = self.check_jid(iq_obj.jid)
if caps_from_jid:
caps = caps_from_jid
self.set_icon(roster.model, iter_, self.renderer_num, caps)
for iter_ in iters:
self.set_icon(roster.model, iter_, self.renderer_num, caps)
def gc_presence_received(self, iq_obj):
if not self.config['show_in_groupchats']:
......@@ -543,8 +557,10 @@ class ClientsIconsPlugin(GajimPlugin):
tag = iq_obj.stanza.getTags('c')
if tag:
caps = tag[0].getAttr('node')
if 'pidgin.im' in caps:
if 'pidgin.im/' in caps:
caps = 'libpurple'
if 'sleekxmpp.com' in caps:
caps = 'httр://sleekxmpp.com/ver/1.1.11'
iter_ = iq_obj.gc_control.get_contact_iter(iq_obj.nick.decode('utf-8'))
model = iq_obj.gc_control.model
if model[iter_][self.muc_renderer_num] is not None:
......
......@@ -6,6 +6,7 @@
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="border_width">9</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkCheckButton" id="show_in_roster">
......
[info]
name: Clients icons
short_name: clients_icons
version: 3.1
version: 4.6
description: Shows the client icons in the roster
and in groupchats.
For icons in tooltip support, you need to install Gajim r14117 or above.
authors = Denis Fomin <fominde@gmail.com>
authors: Denis Fomin <fominde@gmail.com>
Artem Klyop <art.klyop@gmail.com>
homepage = http://trac-plugins.gajim.org/wiki/ClientsIconsPlugin
max_gajim_version: 0.16.9
from .emoticons_pack import EmoticonsPackPlugin
This diff is collapsed.
[MacThemes2]
icon: Smile.png
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Mac Themes 2 Smilies <br/>
<img src="preview.image"/>
</body>
authors: David Lanham
homepage: https://trac.gajim.org/wiki/MacThemes2
[Citters Emoticons from Psi]
icon: smile.png
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Citters Emoticons from Psi<br/>
<img src="preview.image"/>
</body>
converter: churchyard (JID: churchyard@njs.netlab.cz)
homepage: https://trac.gajim.org/wiki/cittersemoticons
[EmotiPonies]
icon: pinkiesmile.png
description: <body xmlns='http://www.w3.org/1999/xhtml'>
EmotiPonies <br/>
<img src="preview.image"/>
</body>
authors: midori-no-ink (Copyright (c) Hasbro, Inc.)
homepage: https://trac.gajim.org/wiki/EmotiPonies
[Google Talk Emoticons]
icon: equal_smile.gif
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Google Talk Emoticons<br/>
<img src="preview.image"/>
</body>
converter: Chris Cook
homepage: https://trac.gajim.org/wiki/gtalkemoticons
[ICQ6 Emoticons]
icon: Smiley1.gif
description: <body xmlns='http://www.w3.org/1999/xhtml'>
ICQ6 Emoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/ICQ6-emotions
[Kolobok Animated Emoticons]
icon: ab.gif
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Kolobok Animated Emoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/kolobok-animated-emoticons
[Pidgin Emoticons]
icon: smile.png
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Pidgin Emoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/PidginEmoticons
[Psynova qip's theme]
icon: ab.gif
authors: zOrg
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Psynova qip's theme<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/psynovaemoticons
[Yahoo Emoticons]
icon: equal_smile.gif
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Yahoo Emoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/YahooEmoticons
[Nekomoticons]
icon: s01.jpg
authors: somik (http://somik.deviantart.com/)
converter: Dotterian (http://dotterian.ru/)
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Nekomoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/Nekomoticons
[Trollicons]
icon: Happy-EverythingWentBetterThanExpected.png
authors: Sagar Pandya
Chris Li
Jonathan E. Chen
converter: Matthieu L.
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Trollicons (Rage Icons for Gajim)<br/>
<img src="preview.image"/>
</body>
homepage: https://github.com/sagargp/trollicons/blob/master/readme.md#downloads
This diff is collapsed.
[info]
name: Emoticons pack
short_name: emoticons_pack
version: 0.0.4
description: Install, update and view detailed legend of emoticons
authors: Denis Fomin <fominde@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/
from common import xmpp
from common import helpers
from common import gajim
from common import XMPPDispatcher
from common.xmpp import Hashes
import nbxmpp
from nbxmpp import Hashes
try:
from common import helpers
from common import gajim
except ImportError:
print "Import Error: Ignore if we are testing"
# Namespace for file sharing
NS_FILE_SHARING = 'http://gajim.org/protocol/filesharing'
NS_FILE_SHARING = 'urn:xmpp:fis'
class Protocol():
'''
Creates and extracts information from stanzas
'''
def __init__(self, ourjid):
# set our jid with resource
self.ourjid = ourjid
def request(self, contact, stanzaID, path=None):
iq = nbxmpp.Iq(typ='get', to=contact, frm=self.ourjid)
iq.setID(stanzaID)
query = iq.setQuery()
query.setNamespace(NS_FILE_SHARING)
if path:
query.setAttr('node', path)
return iq
def buildFileNode(self, file_info):
node = nbxmpp.Node(tag='file')
node.setNamespace(nbxmpp.NS_JINGLE_FILE_TRANSFER)
if not 'name' in file_info:
raise Exception("Child name is required.")
node.addChild(name='name').setData(file_info['name'])
if 'date' in file_info:
node.addChild(name='date').setData(file_info['date'])
if 'desc' in file_info:
node.addChild(name='desc').setData(file_info['desc'])
if 'size' in file_info:
node.addChild(name='size').setData(file_info['size'])
if 'hash' in file_info:
h = Hashes()
h.addHash(file_info['hash'], 'sha-1')
node.addChild(node=h)
return node
def offer(self, id_, contact, node, items):
iq = nbxmpp.Iq(typ='result', to=contact, frm=self.ourjid,
attrs={'id': id_})
query = iq.setQuery()
query.setNamespace(NS_FILE_SHARING)
if node:
query.setAttr('node', node)
for item in items:
if item['type'] == 'file':
fn = self.buildFileNode(item)
query.addChild(node=fn)
elif item['type'] == 'directory':
query.addChild(name='directory', attrs={'name': item['name']})
else:
raise Exception("Unexpected Type")
return iq
class ProtocolDispatcher():
'''
Sends and receives stanzas
'''
def __init__(self, account, plugin):
self.account = account
......@@ -16,37 +80,33 @@ class Protocol():
self.ourjid = gajim.get_jid_from_account(self.account)
self.fsw = None
def set_window(self, window):
self.fsw = window
def request(self, contact, name=None, isFile=False):
iq = xmpp.Iq(typ='get', to=contact, frm=self.ourjid)
match = iq.addChild(name='match', namespace=NS_FILE_SHARING)
request = match.addChild(name='request')
if not isFile and name is None:
request.addChild(name='directory')
elif not isFile and name is not None:
dir_ = request.addChild(name='directory')
dir_.addChild(name='name').addData('/' + name)
elif isFile:
pass
return iq
def __buildReply(self, typ, stanza):
iq = xmpp.Iq(typ, to=stanza.getFrom(), frm=stanza.getTo(),
attrs={'id': stanza.getID()})
iq.addChild(name='match', namespace=NS_FILE_SHARING)
return iq
def set_window(self, fsw):
self.fsw = fsw
def on_request(self, stanza):
def handler(self, stanza, fjid):
# handles incoming match stanza
# TODO: Stanza checking
if stanza.getType() == 'get':
self.on_request(stanza, fjid)
elif stanza.getType() == 'result':
return self.on_offer(stanza, fjid)
else:
# TODO: reply with malformed stanza error
pass
def on_request(self, stanza, fjid):
'''
try:
fjid = helpers.get_full_jid_from_iq(stanza)
except helpers.InvalidFormat:
# A message from a non-valid JID arrived, it has been ignored.
return
return -1
'''
if stanza.getTag('error'):
# TODO: better handle this
return
return -1
jid = gajim.get_jid_without_resource(fjid)
req = stanza.getTag('match').getTag('request')
if req.getTag('directory') and not \
......@@ -60,82 +120,24 @@ class Protocol():
files = self.plugin.database.get_files_from_dir(self.account, jid, dir_)
response = self.offer(stanza.getID(), fjid, files)
self.conn.connection.send(response)
return 0
def on_offer(self, stanza):
# We just got a stanza offering files
fjid = helpers.get_full_jid_from_iq(stanza)
info = get_files_info(stanza)
if fjid not in self.fsw.browse_jid or not info:
# We weren't expecting anything from this contact, do nothing
# Or we didn't receive any offering files
return
flist = []
for f in info[0]:
flist.append(f['name'])
flist.extend(info[1])
self.fsw.browse_fref = self.fsw.add_file_list(flist, self.fsw.ts_search,
self.fsw.browse_fref,
self.fsw.browse_jid[fjid]
)
for f in info[0]:
iter_ = self.fsw.browse_fref[f['name']]
path = self.fsw.ts_search.get_path(iter_)
self.fsw.brw_file_info[path] = (f['name'], f['date'], f['size'],
f['hash'], f['desc'])
# TODO: add tooltip
'''
for f in info[0]:
r = self.fsw.browse_fref[f['name']]
path = self.fsw.ts_search.get_path(r)
# AM HERE WORKING ON THE TOOLTIP
tooltip.set_text('noooo')
self.fsw.tv_search.set_tooltip_row(tooltip, path)
'''
for dir_ in info[1]:
if dir_ not in self.fsw.empty_row_child:
parent = self.fsw.browse_fref[dir_]
row = self.fsw.ts_search.append(parent, ('',))
self.fsw.empty_row_child[dir_] = row
def handler(self, stanza):
# handles incoming match stanza
if stanza.getTag('match').getTag('offer'):
self.on_offer(stanza)
elif stanza.getTag('match').getTag('request'):
self.on_request(stanza)
else:
# TODO: reply with malformed stanza error
pass
def offer(self, id_, contact, items):
iq = xmpp.Iq(typ='result', to=contact, frm=self.ourjid,
attrs={'id': id_})
match = iq.addChild(name='match', namespace=NS_FILE_SHARING)
offer = match.addChild(name='offer')
if len(items) == 0:
offer.addChild(name='directory')
else:
for i in items:
# if it is a directory
if i[5] == True:
item = offer.addChild(name='directory')
name = item.addChild('name')
name.setData('/' + i[0])
else:
item = offer.addChild(name='file')
item.addChild('name').setData('/' + i[0])
if i[1] != '':
h = Hashes()
h.addHash(i[1], 'sha-1')
item.addChild(node=h)
item.addChild('size').setData(i[2])
item.addChild('desc').setData(i[3])
item.addChild('date').setData(i[4])
return iq
def set_window(self, fsw):
self.fsw = fsw
def on_offer(self, stanza, fjid):
offered = []
query = stanza.getQuery()
for child in query.getChildren():
if child.getName() == 'directory':
offered.append({'name' : child.getAttr('name'),
'type' : 'directory'})
elif child.getName() == 'file':
attrs = {'type' : 'file'}
grandchildren = child.getChildren()
for grandchild in grandchildren:
attrs[grandchild.getName()] = grandchild.getData()
offered.append(attrs)
else:
print 'File sharing. Cant handle unknown type: ' + str(child)
return offered
def get_files_info(stanza):
......
[info]
name: File Sharing
short_name: fshare
#version: 0.1
#version: 0.1.1
description: This plugin allows you to share folders with your peers using jingle file transfer.
authors: Jefry Lagrange <jefry.reyes@gmail.com>
homepage: www.google.com
homepage: www.gajim.org
max_gajim_version: 0.16.9
#/usr/bin/python
import unittest
from mock import Mock
import sys, os
sys.path.append(os.path.abspath(sys.path[0]) + '/../')
import fshare_protocol
import nbxmpp
class TestProtocol(unittest.TestCase):
def setUp(self):
self.protocol = fshare_protocol.Protocol('test@gajim.org/test')
def test_request(self):
iq = self.protocol.request('peer@gajim.org/test', '1234', 'documents/test2.txt')
self.assertEqual(iq.getType(), 'get')
self.assertNotEqual(iq.getID(), None)
self.assertEqual(iq.getQuery().getName(), 'query')
self.assertEqual(iq.getQuery().getNamespace(), fshare_protocol.NS_FILE_SHARING)
self.assertEqual(iq.getQuery().getAttr('node'), 'documents/test2.txt')
def test_buildFileNode(self):
file_info = {'name' : 'test2.text',
'desc' : 'test',
'hash' : '00000',
'size' : '00000',
'type' : 'file'
}
node = self.protocol.buildFileNode(file_info)
self.assertEqual(node.getName(), 'file')
self.assertEqual(node.getNamespace(), nbxmpp.NS_JINGLE_FILE_TRANSFER)
self.assertEqual(len(node.getChildren()), 4)
def test_offer(self):
items = [ {'name' : 'test2.text',
'desc' : 'test',
'hash' : '00000',
'size' : '00000',
'type' : 'file'
},
{
'name' : 'secret docs',
'type' : 'directory'
}
]
iq = self.protocol.offer('1234', 'peer@gajim.org/test',
'documents', items)
self.assertEqual(iq.getType(), 'result')
self.assertNotEqual(iq.getID(), None)
self.assertEqual(iq.getQuery().getName(), 'query')
self.assertEqual(iq.getQuery().getNamespace(), fshare_protocol.NS_FILE_SHARING)
self.assertEqual(iq.getQuery().getAttr('node'), 'documents')
node = iq.getQuery()
self.assertEqual(len(node.getChildren()), 2)
# Mock modules
fshare_protocol.gajim = Mock()
fshare_protocol.helpers = Mock()
class TestProtocolDispatcher(unittest.TestCase):
def setUp(self):
account = 'test@gajim.org/test'
testc = {account : Mock()}
fshare_protocol.gajim.connections = testc
self.dispatcher = fshare_protocol.ProtocolDispatcher(
account, Mock())
def test_handler(self):
protocol = fshare_protocol.Protocol('test@gajim.org/test')
iq = protocol.request('peer@gajim.org/test', '1234',
'documents/test2.txt')
#offer = self.dispatcher.on_offer
request = self.dispatcher.on_request
#self.dispatcher.on_offer = Mock()
self.dispatcher.on_request = Mock()
self.dispatcher.handler(iq, 'peer@gajim.org/test')
assert(self.dispatcher.on_request.called)
self.dispatcher.on_request = request
def test_offer(self):
from fshare_protocol import OfferHandled
items = [ {'name' : 'test2.text',
'type' : 'file'
},
{
'name' : 'secret docs',
'type' : 'directory'
}
]
protocol = fshare_protocol.Protocol('test@gajim.org/test')
iq = protocol.offer('1234', 'peer@gajim.org/test',
'documents', items)
offered_files = self.dispatcher.on_offer(iq, 'peer@gajim.org/test')
self.assertEqual(len(offered_files), 2)
def test_request(self):
pass
if __name__ == '__main__':
unittest.main()
......@@ -7,7 +7,7 @@
<object class="GtkTable" id="config_table">
<property name="visible">True</property>
<property name="border_width">6</property>
<property name="n_rows">2</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="column_spacing">7</property>
<property name="row_spacing">5</property>
......@@ -45,7 +45,7 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
<property name="y_options"/>
</packing>
</child>
<child>
......@@ -59,9 +59,28 @@
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="flash_cb">
<property name="label" translatable="yes">Do not flash the LED, only switch it on.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"/>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
......
......@@ -16,8 +16,10 @@ class FlashingKeyboard(GajimPlugin):
self.description = _('Flashing keyboard led when there are unread messages.')
self.config_dialog = FlashingKeyboardPluginConfigDialog(self)
self.config_default_values = {
'command1': ("xset led named 'Scroll Lock'", ''),
'command2': ("xset -led named 'Scroll Lock'", '')}
'command1': ("xset led named 'Scroll Lock'", ''),
'command2': ("xset -led named 'Scroll Lock'", ''),
'flash': (True, ''),
}
self.is_active = None
self.timeout = 500
......@@ -35,15 +37,22 @@ class FlashingKeyboard(GajimPlugin):
if gajim.events.get_nb_systray_events():
if self.id_0:
return
self.id_0 = gobject.timeout_add(self.timeout, self.led_on)
if self.config['flash']:
self.id_0 = gobject.timeout_add(self.timeout, self.led_on)
else:
self.led_on()
self.id_0 = True
else:
if self.id_0:
gobject.source_remove(self.id_0)
if self.config['flash']:
gobject.source_remove(self.id_0)
self.id_0 = None
self.led_off()
def led_on(self):
subprocess.Popen('%s' % self.config['command1'], shell=True).wait()
gobject.timeout_add(self.timeout_off, self.led_off)
if self.config['flash']:
gobject.timeout_add(self.timeout_off, self.led_off)
return True
def led_off(self):
......@@ -54,7 +63,11 @@ class FlashingKeyboard(GajimPlugin):
gajim.events.event_added_subscribe(self.on_event_added)
gajim.events.event_removed_subscribe(self.on_event_removed)
if gajim.events.get_nb_systray_events():
self.id_0 = gobject.timeout_add(self.timeout, self.led_on)
if self.config['flash']:
self.id_0 = gobject.timeout_add(self.timeout, self.led_on)
else:
self.led_on()
self.id_0 = True
@log_calls('FlashingKeyboard')
def deactivate(self):
......@@ -62,6 +75,7 @@ class FlashingKeyboard(GajimPlugin):
gajim.events.event_removed_unsubscribe(self.on_event_removed)
if self.id_0:
gobject.source_remove(self.id_0)
self.led_off()
class FlashingKeyboardPluginConfigDialog(GajimPluginConfigDialog):
......@@ -81,14 +95,18 @@ class FlashingKeyboardPluginConfigDialog(GajimPluginConfigDialog):
self.isactive = self.plugin.active
if self.plugin.active:
gajim.plugin_manager.deactivate_plugin(self.plugin)
for name in self.plugin.config_default_values:
for name in ('command1', 'command2'):
widget = self.xml.get_object(name)
widget.set_text(self.plugin.config[name])
widget = self.xml.get_object('flash_cb')
widget.set_active(not self.plugin.config['flash'])
def on_close_button_clicked(self, widget):
widget = self.xml.get_object('command1')
self.plugin.config['command1'] = widget.get_text()
widget = self.xml.get_object('command2')
self.plugin.config['command2'] = widget.get_text()
widget = self.xml.get_object('flash_cb')
self.plugin.config['flash'] = not widget.get_active()
if self.isactive:
gajim.plugin_manager.activate_plugin(self.plugin)
[info]
name: Flashing Keyboard
short_name: flashing_keyboard
version: 0.1.3
version: 0.1.5
description: Flashing keyboard led when there are unread messages.
authors: Denis Fomin <fominde@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/FlashingKeyboardPlugin
max_gajim_version: 0.16.9
[info]
name: GNOME SessionManager
short_name: gnome_session_manager
version: 0.1.2
version: 0.1.3
description: Set and react on GNOME Session presence settings
authors: Philippe Normand <phil@base-art.net>
homepage: http://base-art.net
max_gajim_version: 0.16.9
[info]
name: Google Translation
short_name: google_translation
version: 0.3.1
version: 0.3.2
description: Translates (currently only incoming) messages using Google Translate.
authors = Mateusz Biliński <mateusz@bilinski.it>
mrDoctorWho <mrdoctorwho@gmail.com>
homepage = http://trac-plugins.gajim.org/wiki/GoogleTranslationPlugin
max_gajim_version: 0.16.9
[info]
name: Off-The-Record Encryption
short_name: gotr
version: 1.7.1
version: 1.7.7
description: Provide OTR encryption
authors: Kjell Braden <afflux.gajim@pentabarf.de>
homepage: http://gajim-otr.pentabarf.de
max_gajim_version: 0.16.9
......@@ -59,6 +59,7 @@ import os
import pickle
import time
import sys
from pprint import pformat
import common.xmpp
from common import gajim
......@@ -85,6 +86,12 @@ try:
except ImportError:
HAS_CRYPTO = False
nb_xmpp = False
import common.xmpp
if not dir(common.xmpp):
import nbxmpp
nb_xmpp = True
HAS_POTR = True
try:
import potr
......@@ -119,7 +126,10 @@ try:
msg = unicode(msg)
account = self.user.accountname
stanza = common.xmpp.Message(to=self.peer, body=msg, typ='chat')
if not nb_xmpp:
stanza = common.xmpp.Message(to=self.peer, body=msg, typ='chat')
else:
stanza = nbxmpp.Message(to=self.peer, body=msg, typ='chat')
if appdata is not None:
session = appdata.get('session', None)
if session is not None:
......@@ -528,7 +538,7 @@ class OtrPlugin(GajimPlugin):
try:
ctx = self.us[account].getContext(event.fjid)
msgtxt, tlvs = ctx.receiveMessage(event.msgtxt,
msgtxt, tlvs = ctx.receiveMessage(event.msgtxt.encode('utf8'),
appdata={'session':event.session})
except potr.context.NotOTRMessage, e:
# received message was not OTR - pass it on
......@@ -574,52 +584,62 @@ class OtrPlugin(GajimPlugin):
ctx.smpWindow.handle_tlv(tlvs)
stripper = HTMLStripper()
stripper.feed(unicode(msgtxt or ''))
stripper.feed((msgtxt or '').decode('utf8'))
event.msgtxt = stripper.stripped_data
event.stanza.setBody(event.msgtxt)
event.stanza.setXHTML(msgtxt)
event.stanza.setXHTML((msgtxt or '').decode('utf8'))
return PASS
def handle_outgoing_msg(self, event):
if hasattr(event, 'otrmessage'):
return PASS
xep_200 = bool(event.session) and event.session.enable_encryption
if xep_200 or not event.message:
return PASS
try:
if hasattr(event, 'otrmessage'):
return PASS
if event.session:
fjid = event.session.get_to()
else:
fjid = event.jid