Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim-plugins.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYann Leboulanger <asterix@lagaule.org>2017-09-27 23:21:20 +0300
committerYann Leboulanger <asterix@lagaule.org>2017-09-27 23:21:20 +0300
commit72db56fda87344dcc9ccbed3cbfc58a5ec6ccd8d (patch)
treef678b3724f7c1d12c70c95f4b2d1706817f3e43d
parent3e7571db91e63a15f269d0cba64948a88314d3c2 (diff)
remove not ported plugins
-rw-r--r--file_sharing/__init__.py1
-rw-r--r--file_sharing/config_dialog.ui43
-rw-r--r--file_sharing/database.py199
-rw-r--r--file_sharing/fileshare_window.py399
-rw-r--r--file_sharing/fshare.py182
-rw-r--r--file_sharing/fshare_protocol.py163
-rw-r--r--file_sharing/manifest.ini9
-rw-r--r--gnome_session_manager/__init__.py1
-rw-r--r--gnome_session_manager/gnome_session_manager.pngbin714 -> 0 bytes
-rw-r--r--gnome_session_manager/manifest.ini8
-rw-r--r--gnome_session_manager/plugin.py90
-rw-r--r--gotr/__init__.py1
-rw-r--r--gotr/config_dialog.ui382
-rw-r--r--gotr/contact_otr_window.ui355
-rw-r--r--gotr/gotr.pngbin3405 -> 0 bytes
-rw-r--r--gotr/manifest.ini8
-rw-r--r--gotr/otrmodule.py665
-rw-r--r--gotr/potr/__init__.py27
-rw-r--r--gotr/potr/compatcrypto/__init__.py21
-rw-r--r--gotr/potr/compatcrypto/common.py108
-rw-r--r--gotr/potr/compatcrypto/pycrypto.py144
-rw-r--r--gotr/potr/context.py568
-rw-r--r--gotr/potr/crypt.py795
-rw-r--r--gotr/potr/proto.py465
-rw-r--r--gotr/potr/utils.py66
-rw-r--r--gotr/ui.py581
-rw-r--r--offline_bookmarks/__init__.py2
-rw-r--r--offline_bookmarks/config_dialog.ui458
-rw-r--r--offline_bookmarks/manifest.ini9
-rw-r--r--offline_bookmarks/offline_bookmarks.pngbin686 -> 0 bytes
-rw-r--r--offline_bookmarks/offline_bookmarks.py371
-rw-r--r--snarl_notifications/__init__.py1
-rw-r--r--snarl_notifications/manifest.ini9
-rw-r--r--snarl_notifications/plugin.py89
-rw-r--r--snarl_notifications/pySnarl.py365
-rw-r--r--snarl_notifications/snarl_notifications.pngbin2324 -> 0 bytes
-rw-r--r--ubuntu_integration/__init__.py1
-rw-r--r--ubuntu_integration/doc/example.jpgbin11097 -> 0 bytes
-rw-r--r--ubuntu_integration/manifest.ini12
-rw-r--r--ubuntu_integration/plugin.py196
-rw-r--r--ubuntu_integration/ubuntu_integration.pngbin817 -> 0 bytes
-rw-r--r--url_shortener/__init__.py1
-rw-r--r--url_shortener/config_dialog.ui112
-rw-r--r--url_shortener/manifest.ini12
-rw-r--r--url_shortener/url_shortener.pngbin373 -> 0 bytes
-rw-r--r--url_shortener/url_shortener.py232
-rw-r--r--whiteboard/__init__.py1
-rw-r--r--whiteboard/brush_tool.pngbin806 -> 0 bytes
-rw-r--r--whiteboard/line_tool.pngbin1054 -> 0 bytes
-rw-r--r--whiteboard/manifest.ini8
-rw-r--r--whiteboard/oval_tool.pngbin989 -> 0 bytes
-rw-r--r--whiteboard/plugin.py486
-rw-r--r--whiteboard/whiteboard.pngbin1550 -> 0 bytes
-rw-r--r--whiteboard/whiteboard_widget.py418
-rw-r--r--whiteboard/whiteboard_widget.ui192
55 files changed, 0 insertions, 8256 deletions
diff --git a/file_sharing/__init__.py b/file_sharing/__init__.py
deleted file mode 100644
index e30b2af..0000000
--- a/file_sharing/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from fshare import FileSharePlugin
diff --git a/file_sharing/config_dialog.ui b/file_sharing/config_dialog.ui
deleted file mode 100644
index 77c4cb3..0000000
--- a/file_sharing/config_dialog.ui
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0"?>
-<interface>
- <requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkWindow" id="window1">
- <child>
- <object class="GtkVBox" id="hbox111">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <child>
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Incoming folder:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="dl_folder">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">&#x25CF;</property>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/file_sharing/database.py b/file_sharing/database.py
deleted file mode 100644
index 9becfc6..0000000
--- a/file_sharing/database.py
+++ /dev/null
@@ -1,199 +0,0 @@
-import sqlite3
-from common import app
-import sys
-import os
-
-class FilesharingDatabase:
- def __init__(self, plugin):
- self.plugin = plugin
- path_l = os.path.split(plugin.config.FILE_PATH)
- path = os.path.join(path_l[0], 'shared_files.db')
- db_exist = os.path.exists(path)
- self.conn = sqlite3.connect(path)
- # Enable foreign keys contraints
- self.conn.cursor().execute("pragma foreign_keys = on")
- if not db_exist:
- self.create_database()
-
- # NOTE: Make sure we are getting and setting the requester without its
- # resource
- def create_database(self):
- c = self.conn.cursor()
- # Create tables
- c.execute("CREATE TABLE permissions" +
- "(fid integer REFERENCES files(fid) ON DELETE CASCADE, " +
- "account text, requester text)")
- c.execute("CREATE TABLE files" +
- "(fid INTEGER PRIMARY KEY AUTOINCREMENT," +
- " file_path text, relative_path text, hash_sha1 text," +
- "size numeric, description text, mod_date text, is_dir boolean)")
- # Save (commit) the changes
- self.conn.commit()
- c.close()
-
- def get_toplevel_files(self, account, requester):
- c = self.conn.cursor()
- data = (account, requester)
- c.execute("SELECT relative_path, hash_sha1, size, description, " +
- "mod_date, is_dir FROM (files JOIN permissions ON" +
- " files.fid=permissions.fid) WHERE account=? AND requester=?" +
- " AND relative_path NOT LIKE '%/%'", data)
- result = c.fetchall()
- c.close()
- return result
-
- def get_files_from_dir(self, account, requester, dir_):
- c = self.conn.cursor()
- data = (account, requester, dir_ + '/%')
- c.execute("SELECT relative_path, hash_sha1, size, description, " +
- "mod_date, is_dir FROM (files JOIN permissions ON" +
- " files.fid=permissions.fid) WHERE account=? AND requester=?" +
- " AND relative_path LIKE ?", data)
- result = c.fetchall()
- c.close()
- fresult = []
- for r in result:
- name = r[0][len(dir_) + 1:]
- if '/' not in name:
- fresult.append(r)
- return fresult
-
- def get_files(self, account, requester):
- """
- >>> file_ = ('file_path', 'relative_path', 'hash', 999, 'description', \
- 'date', False)
- >>> foo = add_file('account@gajim', 'requester@jabber', file_)
- >>> result = get_files('account@gajim', 'requester@jabber')
- >>> len(result)
- 1
- >>> _delete_file(1)
- """
- c = self.conn.cursor()
- data = (account, requester)
- c.execute("SELECT relative_path, hash_sha1, size, description, " +
- "mod_date, is_dir FROM (files JOIN permissions ON" +
- " files.fid=permissions.fid) WHERE account=? AND requester=?", data)
- result = c.fetchall()
- c.close()
- return result
-
- def get_file(self, account, requester, hash_, name):
- c = self.conn.cursor()
- if hash_:
- data = (account, requester, hash_)
- sql = "SELECT relative_path, hash_sha1, size, description, " + \
- "mod_date, file_path FROM (files JOIN permissions ON" + \
- " files.fid=permissions.fid) WHERE account=? AND requester=?" +\
- " AND hash_sha1=?"
- else:
- data = (account, requester, name)
- sql = "SELECT relative_path, hash_sha1, size, description, " + \
- "mod_date, file_path FROM (files JOIN permissions ON" + \
- " files.fid=permissions.fid) WHERE account=? AND requester=?" +\
- " AND relative_path=?"
- c.execute(sql, data)
- result = c.fetchall()
- c.close()
- if result == []:
- return None
- else:
- return result[0]
-
- def get_files_name(self, account, requester):
- result = self.get_files(account, requester)
- flist = []
- for r in result:
- flist.append(r[0])
- return flist
-
- def add_file(self, account, requester, file_):
- """
- >>> file_ = ('file_path', 'relative_path', 'hash', 999, 'description', \
- 'date', False)
- >>> add_file('account@gajim', 'requester@jabber', file_)
- 1
- >>> _delete_file(1)
- """
- self._check_duplicate(account, requester, file_)
- requester = app.get_jid_without_resource(requester)
- c = self.conn.cursor()
- c.execute("INSERT INTO files (file_path, " +
- "relative_path, hash_sha1, size, description, mod_date, " +
- " is_dir) VALUES (?,?,?,?,?,?,?)", file_)
- fid = c.lastrowid
- permission_data = (fid, account, requester)
- c.execute("INSERT INTO permissions VALUES (?,?,?)", permission_data)
- self.conn.commit()
- c.close()
- return fid
-
- def _check_duplicate(self, account, requester, file_):
- c = self.conn.cursor()
- data = (account, requester, file_[1])
- c.execute("SELECT * FROM (files JOIN permissions ON" +
- " files.fid=permissions.fid) WHERE account=? AND requester=?" +
- " AND relative_path=? ", data)
- result = c.fetchall()
- if file_[2] != '':
- data = (account, requester, file_[2])
- c.execute("SELECT * FROM (files JOIN permissions ON" +
- " files.fid=permissions.fid) WHERE account=? AND requester=?" +
- " AND hash_sha1=?)", data)
- result.extend(c.fetchall())
- if len(result) > 0:
- raise Exception('Duplicated entry')
- c.close()
-
- def _delete_file(self, fid):
- c = self.conn.cursor()
- data = (fid, )
- c.execute("DELETE FROM files WHERE fid=?", data)
- self.conn.commit()
- c.close()
-
- def _delete_dir(self, dir_, account, requester):
- c = self.conn.cursor()
- data = (account, requester, dir_, dir_ + '/%')
- sql = "DELETE FROM files WHERE fid IN " + \
- " (SELECT files.fid FROM files, permissions WHERE" + \
- " files.fid=permissions.fid AND account=?"+ \
- " AND requester=? AND (relative_path=? OR relative_path LIKE ?))"
- c.execute(sql, data)
- self.conn.commit()
- c.close()
-
- def delete(self, account, requester, relative_path):
- c = self.conn.cursor()
- data = (account, requester, relative_path)
- c.execute("SELECT files.fid, is_dir FROM (files JOIN permissions ON" +
- " files.fid=permissions.fid) WHERE account=? AND requester=? AND " +
- "relative_path=? ", data)
- result = c.fetchone()
- c.close()
- if result[1] == 0:
- self._delete_file(result[0])
- else:
- self._delete_dir(relative_path, account, requester)
-
- def delete_all(self, account, requester):
- c = self.conn.cursor()
- data = (account, requester)
- sql = "DELETE FROM files WHERE fid IN (SELECT fid FROM permissions" + \
- " WHERE account=? AND requester=?)"
- c.execute(sql, data)
- self.conn.commit()
- c.close()
-
-
-if __name__ == "__main__":
- """
- DELETE DATABASE FILE BEFORE RUNNING TESTS
- """
- import doctest
- path = sys.path[0]
- path = path + '/' + 'shared_files.db'
- conn = sqlite3.connect(path)
- # Enable foreign keys contraints
- conn.cursor().execute("pragma foreign_keys = on")
- FilesharingDatabase.create_database()
- doctest.testmod()
diff --git a/file_sharing/fileshare_window.py b/file_sharing/fileshare_window.py
deleted file mode 100644
index e9e19c1..0000000
--- a/file_sharing/fileshare_window.py
+++ /dev/null
@@ -1,399 +0,0 @@
-import gtk
-import gobject
-from common import gajim
-from common import helpers
-from common.file_props import FilesProp
-import fshare
-import os
-import fshare_protocol
-
-class FileShareWindow(gtk.Window):
-
- def __init__(self, plugin):
- self.plugin = plugin
- gtk.Window.__init__(self)
- self.set_title('File Share')
- self.connect('delete_event', self.delete_event)
- self.set_position(gtk.WIN_POS_CENTER)
- self.set_default_size(400, 400)
- # Children
- self.notebook = gtk.Notebook()
- # Browse page
- self.bt_search = gtk.Button('Search')
- self.bt_search.connect('clicked', self.on_bt_search_clicked)
- self.entry_search = gtk.Entry(max=0)
- self.entry_search.set_size_request(300, -1)
- self.exp_advance = gtk.Expander('Advance')
- self.ts_search = gtk.TreeStore(gobject.TYPE_STRING)
- self.tv_search = gtk.TreeView(self.ts_search)
- self.tv_search.connect('row-expanded', self.row_expanded)
- self.tv_search.connect('button-press-event',
- self.on_treeview_button_press_event)
- self.browse_popup = gtk.Menu()
- mi_download = gtk.MenuItem('Download')
- mi_property = gtk.MenuItem('Property')
- mi_download.show()
- mi_property.show()
- mi_download.connect('activate', self.on_download_clicked)
- self.browse_popup.append(mi_download)
- self.browse_popup.append(mi_property)
- self.browse_sw = gtk.ScrolledWindow()
- self.browse_sw.add(self.tv_search)
- self.tvcolumn_browse = gtk.TreeViewColumn('')
- self.tv_search.append_column(self.tvcolumn_browse)
- self.cell_browse = gtk.CellRendererText()
- self.tvcolumn_browse.pack_start(self.cell_browse, True)
- self.tvcolumn_browse.add_attribute(self.cell_browse, 'text', 0)
- self.lbl_browse = gtk.Label('Browse')
- self.browse_hbox = gtk.HBox()
- self.browse_hbox2 = gtk.HBox()
- self.browse_vbox = gtk.VBox()
- self.browse_hbox.pack_start(self.entry_search, True, True, 10)
- self.browse_hbox.pack_start(self.bt_search, False, False, 10)
- self.browse_vbox.pack_start(self.browse_hbox, False, False, 10)
- self.browse_vbox.pack_start(self.exp_advance, False, False, 10)
- self.browse_vbox.pack_start(self.browse_sw, True, True, 10)
- self.notebook.append_page(self.browse_vbox, self.lbl_browse)
- # file references for tv_search
- self.browse_fref = {}
- # contact references for tv_search
- self.browse_jid = {}
- # Information of the files inserted in the treeview: name, size, etc
- self.brw_file_info = {}
- # dummy row children so that we can get expanders
- self.empty_row_child = {}
- # Manage page
- self.bt_add_file = gtk.Button('Add file')
- self.bt_add_file.connect('clicked', self.add_file)
- self.bt_add_dir = gtk.Button('Add directory')
- self.bt_add_dir.connect('clicked', self.add_directory)
- self.bt_remove = gtk.Button('Remove')
- self.bt_remove.connect('clicked', self.remove_file_clicked)
- self.bt_remove_all = gtk.Button('Remove all')
- self.bt_remove_all.connect('clicked', self.remove_all_clicked)
- self.ts_contacts = gtk.TreeStore(gobject.TYPE_STRING)
- self.cbb_contacts = gtk.ComboBoxEntry(self.ts_contacts)
- self.cbb_contacts.connect('changed', self.__check_combo_edit)
- cbb_entry = self.cbb_contacts.child
- self.cbb_completion = gtk.EntryCompletion()
- cbb_entry.set_completion(self.cbb_completion)
- self.cbb_completion.set_model(self.ts_contacts)
- self.cbb_completion.set_text_column(0)
- self.bt_stophash = gtk.Button('Calculating hash')
- self.lbl_manage = gtk.Label('Manage Shared Files')
- self.ts_files = gtk.TreeStore(gobject.TYPE_STRING)
- self.tv_files = gtk.TreeView(self.ts_files)
- self.treeSelection_files = self.tv_files.get_selection()
- self.treeSelection_files.connect('changed', self.row_selected)
- self.manage_sw = gtk.ScrolledWindow()
- self.manage_sw.add(self.tv_files)
- self.tvcolumn = gtk.TreeViewColumn('')
- self.tv_files.append_column(self.tvcolumn)
- self.cell = gtk.CellRendererText()
- self.tvcolumn.pack_start(self.cell, True)
- self.tvcolumn.add_attribute(self.cell, 'text', 0)
- self.pb_filehash = gtk.ProgressBar()
- self.manage_hbox = gtk.HBox()
- self.manage_hbox2 = gtk.HBox()
- self.manage_vbox = gtk.VBox()
- self.manage_vbox2 = gtk.VBox()
- self.manage_hbox.pack_start(self.bt_stophash , False, False, 10)
- self.manage_hbox.pack_start(self.pb_filehash, True, True, 10)
- self.manage_hbox.set_sensitive(False)
- self.manage_vbox.pack_start(self.cbb_contacts, False, False, 10)
- self.manage_vbox.pack_start(self.manage_hbox, False, False, 10)
- self.manage_hbox2.pack_start(self.manage_sw, True, True, 10)
- self.manage_hbox2.pack_start(self.manage_vbox2, False, False, 10)
- self.manage_vbox2.pack_start(self.bt_add_file, False, False, 10)
- self.manage_vbox2.pack_start(self.bt_add_dir, False, False, 10)
- self.manage_vbox2.pack_start(self.bt_remove, False, False, 10)
- self.manage_vbox2.pack_start(self.bt_remove_all, False, False, 10)
- self.manage_vbox2.set_sensitive(False)
- self.manage_vbox.pack_start(self.manage_hbox2, True, True, 10)
- self.notebook.append_page(self.manage_vbox, self.lbl_manage)
- # Preferences page
- self.lbl_pref = gtk.Label('Preferences')
- self.entry_dir_pref = gtk.Entry(max=0)
- self.entry_dir_pref.set_text(self.plugin.config['incoming_dir'])
- self.bt_sel_dir_pref = gtk.Button('Select dir', gtk.STOCK_OPEN)
- self.bt_sel_dir_pref.connect('clicked', self.on_bt_sel_dir_pref_clicked)
- self.frm_dir_pref = gtk.Frame('Incoming files directory')
- self.frm_dir_pref.set_shadow_type(gtk.SHADOW_IN)
- self.pref_hbox = gtk.HBox()
- self.pref_vbox = gtk.VBox()
- self.pref_hbox.pack_start(self.entry_dir_pref, True, True, 10)
- self.pref_hbox.pack_start(self.bt_sel_dir_pref, False, False, 10)
- self.frm_dir_pref.add(self.pref_hbox)
- self.pref_vbox.pack_start(self.frm_dir_pref, False, False, 10)
- self.notebook.append_page(self.pref_vbox , self.lbl_pref)
- self.add(self.notebook)
- self.show_all()
-
- def set_account(self, account):
- self.account = account
- # connect window to protocol
- pro = fshare.FileSharePlugin.prohandler[self.account]
- pro.set_window(self)
-
- def add_file(self, widget):
- dialog = gtk.FileChooserDialog('Add file to be shared', self,
- gtk.FILE_CHOOSER_ACTION_OPEN,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
- gtk.RESPONSE_OK)
- )
- dialog.set_select_multiple(True)
- response = dialog.run()
- if response == gtk.RESPONSE_OK:
- file_list = dialog.get_filenames()
- self.add_items_tvcontacts(file_list)
- dialog.destroy()
-
- def add_directory(self, widget):
- dialog = gtk.FileChooserDialog('Add directory to be shared', self,
- gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
- gtk.RESPONSE_ACCEPT)
- )
- response = dialog.run()
- if response == gtk.RESPONSE_ACCEPT:
- file_list = []
- file_list.append(dialog.get_filename())
- self.add_items_tvcontacts(file_list)
- dialog.destroy()
-
- def add_file_list(self, flist, treestore, fref = {}, root = None):
- # file references to their in the treeStore
- for f in flist:
- # keeps track of the relative dir path
- tail = ''
- # keeps track of the parent row
- parent = None
- dirpath = f.split('/')
- if len(dirpath) == 1:
- # Top level file, it doesnt have parent, add it right away
- fref[dirpath[0]] = treestore.insert(root, 0, (dirpath[0],))
- else:
- for dir_ in dirpath:
- if tail + dir_ not in fref:
- fref[tail + dir_] = treestore.append(parent, (dir_,))
- parent = fref[tail + dir_]
- tail = tail + dir_ + '/'
- return fref
-
- def __convert_date(self, epoch):
- # Converts date-time from seconds from epoch to iso 8601
- import time, datetime
- ts = time.gmtime(epoch)
- dt = datetime.datetime(ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour,
- ts.tm_min, ts.tm_sec)
- return dt.isoformat()
-
- def add_items_tvcontacts(self, file_list, parentdir = None, parent = None):
- # TODO: execute this method inside of a thread
- for f in file_list:
- # Relative name to be used internally in the shared folders
- relative_name = f.split('/')[-1]
- if parentdir:
- relative_name = parentdir + '/' + relative_name
- short_name = relative_name.split('/')[-1]
- if short_name[0] == '.':
- # Return if it is a hidden file
- return
- if parent:
- row = self.ts_files.append(parent, (short_name,))
- else:
- row = self.ts_files.insert(None, 0, (short_name,))
- # File info
- size = os.path.getsize(f)
- is_dir = os.path.isdir(f)
- mod_date = os.path.getmtime(f)
- mod_date = self.__convert_date(mod_date)
- # TODO: add hash
- file_ = (f, relative_name, '', size, '', mod_date, is_dir)
- requester = self.cbb_contacts.get_active_text()
- try:
- fid = self.plugin.database.add_file(self.account, requester,
- file_)
- except Exception, e:
- if e == 'Duplicated entry':
- print 'Error: ' + e
- continue
- else:
- raise Exception(e)
- if is_dir:
- tmpfl = os.listdir(f)
- fl = []
- for item in tmpfl:
- fl.append(f + '/' + item)
- self.add_items_tvcontacts(fl, relative_name, row)
-
- def add_contact_browse(self, contact):
- fjid = contact.get_full_jid()
- jid = gajim.get_jid_without_resource(fjid)
- contacts = gajim.contacts.get_contacts(self.account, jid)
- for con in contacts:
- if con.show in ('offline', 'error') and not \
- con.supports(fshare_protocol.NS_FILE_SHARING):
- break
- cjid = con.get_full_jid()
- r = self.ts_search.insert(None, 0, (cjid, ))
- self.browse_jid[cjid] = r
- pro = fshare.FileSharePlugin.prohandler[self.account]
- # Request list of files from peer
- stanza = pro.request(cjid)
- if pro.conn.connection:
- pro.conn.connection.send(stanza)
-
- def add_contact_manage(self, contact):
- self.contacts_rows = []
- self.cbb_contacts.grab_focus()
- for c in gajim.contacts.iter_contacts(self.account):
- jid = gajim.get_jid_without_resource(c.get_full_jid())
- r = self.ts_contacts.insert(None, len(self.ts_contacts), (jid, ))
- if c.get_full_jid() == contact.get_full_jid():
- self.cbb_contacts.set_active_iter(r)
- self.contacts_rows.append(r)
- self.manage_vbox2.set_sensitive(True)
- self.bt_remove.set_sensitive(False)
- self.add_file_list(self.plugin.database.get_files_name(self.account,
- gajim.get_jid_without_resource(contact.get_full_jid())),
- self.ts_files)
-
- def delete_event(self, widget, data=None):
- fshare.FileSharePlugin.filesharewindow = {}
- return False
-
- def __check_combo_edit(self, widget, data=None):
- self.ts_files.clear()
- entry = self.cbb_contacts.child
- contact = entry.get_text()
- self.manage_vbox2.set_sensitive(False)
- for i in self.contacts_rows:
- # If the contact in the comboboxentry is include inside of the
- # combobox
- if contact == self.ts_contacts.get_value(i, 0):
- self.add_file_list(self.plugin.database.get_files_name(
- self.account, contact), self.ts_files)
- self.manage_vbox2.set_sensitive(True)
- self.bt_remove.set_sensitive(False)
- break
-
- def remove_file_clicked(self, widget, data=None):
- entry = self.cbb_contacts.child
- contact = entry.get_text()
- sel = self.treeSelection_files.get_selected()
- relative_name = self.ts_files.get_value(sel[1], 0)
- self.ts_files.remove(sel[1])
- self.plugin.database.delete(self.account, contact, relative_name)
- widget.set_sensitive(False)
-
- def remove_all_clicked(self, widget, data=None):
- entry = self.cbb_contacts.child
- contact = entry.get_text()
- self.plugin.database.delete_all(self.account, contact)
- self.ts_files.clear()
-
- def row_selected(self, widget, data=None):
- # When row is selected in tv_files
- sel = self.treeSelection_files.get_selected()
- if not sel[1]:
- return
- depth = self.ts_files.iter_depth(sel[1])
- # Don't remove file and dirs that aren't at the root level
- if depth == 0:
- self.bt_remove.set_sensitive(True)
- else:
- self.bt_remove.set_sensitive(False)
-
- def row_expanded(self, widget, iter_, path, data=None):
- name = None
- for key in self.empty_row_child:
- parent = self.ts_search.iter_parent(self.empty_row_child[key])
- p = self.ts_search.get_path(parent)
- if p == path:
- name = key
- break
- if name:
- # if we found that the expanded row is the parent of the empty row
- # remove it from the treestore and empty_row dictionary. Then ask
- # peer for list of files of that directory
- i = self.empty_row_child[name]
- pro = fshare.FileSharePlugin.prohandler[self.account]
- contact = self.get_contact_from_iter(self.ts_search, i)
- contact = gajim.contacts.get_contact_with_highest_priority(
- self.account, contact)
- stanza = pro.request(contact.get_full_jid(), name, isFile=False)
- if pro.conn.connection:
- pro.conn.connection.send(stanza)
- self.ts_search.remove(i)
- del self.empty_row_child[name]
-
- def get_contact_from_iter(self, treestore, iter_):
- toplevel = treestore.get_iter_root()
- while toplevel:
- if treestore.is_ancestor(toplevel, iter_):
- return gajim.get_jid_without_resource(treestore.get_value(
- toplevel, 0))
- toplevel = treestore.iter_next(toplevel)
-
- def on_treeview_button_press_event(self, treeview, event):
- if event.button == 3:
- x = int(event.x)
- y = int(event.y)
- time = event.time
- pthinfo = treeview.get_path_at_pos(x, y)
- if pthinfo is not None:
- path, col, cellx, celly = pthinfo
- treeview.grab_focus()
- treeview.set_cursor( path, col, 0)
- treestore = treeview.get_model()
- it = treestore.get_iter(path)
- if treestore.iter_depth(it) != 0:
- self.browse_popup.popup(None, None, None, event.button, time)
- return True
-
- def on_bt_search_clicked(self, widget, data=None):
- pass
-
- def on_download_clicked(self, widget, data=None):
- tree, row = self.tv_search.get_selection().get_selected()
- path = tree.get_path(row)
- file_info = self.brw_file_info[path]
- fjid = self.get_contact_from_iter(tree, row)
- # Request the file
- file_path = os.path.join(self.plugin.config['incoming_dir'],
- file_info[0])
- sid = helpers.get_random_string_16()
- new_file_props = FilesProp.getNewFileProp(self.account, sid)
- new_file_props.file_name = file_path
- print file_path
- new_file_props.name = file_info[0]
- new_file_props.desc = file_info[4]
- new_file_props.size = file_info[2]
- new_file_props.date = file_info[1]
- new_file_props.hash_ = None if file_info[3] == '' else file_info[3]
- new_file_props.type_ = 'r'
- tsid = gajim.connections[self.account].start_file_transfer(fjid,
- new_file_props, True)
- new_file_props.transport_sid = tsid
- ft_window = gajim.interface.instances['file_transfers']
- contact = gajim.contacts.get_contact_from_full_jid(self.account, fjid)
- ft_window .add_transfer(self.account, contact, new_file_props)
-
- def on_bt_sel_dir_pref_clicked(self, widget, data=None):
- chooser = gtk.FileChooserDialog(title='Incoming files directory',
- action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
- buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
- gtk.RESPONSE_OK))
- response = chooser.run()
- if response == gtk.RESPONSE_OK:
- file_name = chooser.get_filename()
- self.entry_dir_pref.set_text(file_name)
- self.plugin.config['incoming_dir'] = file_name
- chooser.destroy()
-
-
-if __name__ == "__main__":
- f = FileShareWindow(None)
- f.show()
- gtk.main()
diff --git a/file_sharing/fshare.py b/file_sharing/fshare.py
deleted file mode 100644
index 7b490ae..0000000
--- a/file_sharing/fshare.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# -*- coding: utf-8 -*-
-##
-import database
-import gtk
-import os
-import base64
-import urllib2
-from plugins import GajimPlugin
-from plugins.helpers import log_calls
-import gui_menu_builder
-import gtkgui_helpers
-from common import gajim
-from fileshare_window import FileShareWindow
-import fshare_protocol
-from common import ged
-from common import caps_cache
-from common import xmpp
-from plugins.gui import GajimPluginConfigDialog
-
-
-class FileSharePlugin(GajimPlugin):
-
- filesharewindow = {}
- prohandler = {}
-
- @log_calls('FileSharePlugin')
- def init(self):
- self.activated = False
- self.description = _('This plugin allows you to share folders'
- ' with a peer using jingle file transfer.')
- self.config_dialog = FileSharePluginConfigDialog(self)
- home_path = os.path.expanduser('~/')
- self.config_default_values = {'incoming_dir': (home_path, '')}
- self.database = database.FilesharingDatabase(self)
- # Create one protocol handler per account
- accounts = gajim.contacts.get_accounts()
- for account in gajim.contacts.get_accounts():
- FileSharePlugin.prohandler[account] = \
- fshare_protocol.Protocol(account, self)
- self.events_handlers = {
- 'raw-iq-received': (ged.CORE, self._nec_raw_iq)
- }
-
- def activate(self):
- self.activated = True
- # Add fs feature
- if fshare_protocol.NS_FILE_SHARING not in gajim.gajim_common_features:
- gajim.gajim_common_features.append(fshare_protocol.NS_FILE_SHARING)
- self._compute_caps_hash()
- # Replace the contact menu
- self.__get_contact_menu = gui_menu_builder.get_contact_menu
- gui_menu_builder.get_contact_menu = self.contact_menu
- # Replace get_file_info
- for account in gajim.contacts.get_accounts():
- conn = gajim.connections[account]
- self._get_file_info = conn.get_file_info
- conn.get_file_info = self.get_file_info
-
- def deactivate(self):
- self.activated = False
- # Remove fs feature
- if fshare_protocol.NS_FILE_SHARING not in gajim.gajim_common_features:
- gajim.gajim_common_features.remove(fshare_protocol.NS_FILE_SHARING)
- self._compute_caps_hash()
- # Restore the contact menu
- gui_menu_builder.get_contact_menu = self.__get_contact_menu
- # Restore get_file_info
- for account in gajim.contacts.get_accounts():
- conn = gajim.connections[account]
- conn.get_file_info = self._get_file_info
-
- def _compute_caps_hash(self):
- for a in gajim.connections:
- gajim.caps_hash[a] = caps_cache.compute_caps_hash([
- gajim.gajim_identity], gajim.gajim_common_features + \
- gajim.gajim_optional_features[a])
- # re-send presence with new hash
- connected = gajim.connections[a].connected
- if connected > 1 and gajim.SHOW_LIST[connected] != 'invisible':
- gajim.connections[a].change_status(gajim.SHOW_LIST[connected],
- gajim.connections[a].status)
-
- def _nec_raw_iq(self, obj):
- if obj.stanza.getTag('match',
- namespace=fshare_protocol.NS_FILE_SHARING) and self.activated:
- account = obj.conn.name
- pro = FileSharePlugin.prohandler[account]
- pro.handler(obj.stanza)
- raise xmpp.NodeProcessed
-
- def __get_contact_menu(self, contact, account):
- raise NotImplementedError
-
- def contact_menu(self, contact, account):
- menu = self.__get_contact_menu(contact, account)
- fs = gtk.MenuItem('File sharing')
- submenu = gtk.Menu()
- fs.set_submenu(submenu)
- bf = gtk.MenuItem('Browse files')
- bf.connect('activate', self.browse_menu_clicked, account, contact)
- msf = gtk.MenuItem('Manage shared files')
- msf.connect('activate', self.manage_menu_clicked, account, contact)
- enable_fs = gtk.CheckMenuItem('Enable file sharing')
- enable_fs.set_active(True)
- submenu.attach(bf, 0, 1, 0, 1)
- submenu.attach(msf, 0, 1, 1, 2)
- submenu.attach(enable_fs, 0, 1, 2, 3)
- if gajim.account_is_disconnected(account) or \
- contact.show in ('offline', 'error') or not \
- contact.supports(fshare_protocol.NS_FILE_SHARING):
- bf.set_sensitive(False)
- submenu.show()
- bf.show()
- msf.show()
- enable_fs.show()
- fs.show()
- menu.attach(fs, 0, 1, 3, 4)
- return menu
-
- def _get_file_info(self, peerjid, hash_=None, name=None, account=None):
- raise NotImplementedError
-
- def get_file_info(self, peerjid, hash_=None, name=None, account=None):
- file_info = self._get_file_info(hash_, name)
- if file_info:
- return file_info
- raw_info = self.database.get_file(account, peerjid, hash_, name)
- file_info = {'name': raw_info[0],
- 'file-name' : raw_info[5],
- 'hash' : raw_info[1],
- 'size' : raw_info[2],
- 'date' : raw_info[4],
- 'peerjid' : peerjid
- }
-
- return file_info
-
- def __get_fsw_instance(self, account):
- # Makes sure we only have one instance of the window per account
- if account not in FileSharePlugin.filesharewindow:
- FileSharePlugin.filesharewindow[account] = fsw = FileShareWindow(
- self)
- FileSharePlugin.prohandler[account].set_window(fsw)
- return FileSharePlugin.filesharewindow[account]
-
- def __init_window(self, account, contact):
- fsw = self.__get_fsw_instance(account)
- fsw.set_account(account)
- fsw.contacts_rows = []
- fsw.ts_contacts.clear()
- fsw.ts_search.clear()
- # Add information to widgets
- fsw.add_contact_manage(contact)
- fsw.add_contact_browse(contact)
- return fsw
-
- def manage_menu_clicked(self, widget, account, contact):
- fsw = self.__init_window(account, contact)
- fsw.notebook.set_current_page(1)
-
- def browse_menu_clicked(self, widget, account, contact):
- fsw = self.__init_window(account, contact)
- fsw.notebook.set_current_page(0)
-
-class FileSharePluginConfigDialog(GajimPluginConfigDialog):
- def init(self):
- self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
- 'config_dialog.ui')
- self.xml = gtk.Builder()
- self.xml.set_translation_domain('gajim_plugins')
- self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, ['hbox111'])
- hbox = self.xml.get_object('hbox111')
- self.child.pack_start(hbox)
- self.connect('hide', self.on_hide)
-
- def on_run(self):
- widget = self.xml.get_object('dl_folder')
- widget.set_text(str(self.plugin.config['incoming_dir']))
-
- def on_hide(self, widget):
- widget = self.xml.get_object('dl_folder')
- self.plugin.config['incoming_dir'] = widget.get_text()
diff --git a/file_sharing/fshare_protocol.py b/file_sharing/fshare_protocol.py
deleted file mode 100644
index 3247b46..0000000
--- a/file_sharing/fshare_protocol.py
+++ /dev/null
@@ -1,163 +0,0 @@
-from common import xmpp
-from common import helpers
-from common import gajim
-from common import XMPPDispatcher
-from common.xmpp import Hashes
-# Namespace for file sharing
-NS_FILE_SHARING = 'http://gajim.org/protocol/filesharing'
-
-class Protocol():
-
- def __init__(self, account, plugin):
- self.account = account
- self.plugin = plugin
- self.conn = gajim.connections[self.account]
- # get our jid with resource
- 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 on_request(self, stanza):
- 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
- if stanza.getTag('error'):
- # TODO: better handle this
- return
- jid = gajim.get_jid_without_resource(fjid)
- req = stanza.getTag('match').getTag('request')
- if req.getTag('directory') and not \
- req.getTag('directory').getChildren():
- # We just received a toplevel directory request
- files = self.plugin.database.get_toplevel_files(self.account, jid)
- response = self.offer(stanza.getID(), fjid, files)
- self.conn.connection.send(response)
- elif req.getTag('directory') and req.getTag('directory').getTag('name'):
- dir_ = req.getTag('directory').getTag('name').getData()[1:]
- files = self.plugin.database.get_files_from_dir(self.account, jid, dir_)
- response = self.offer(stanza.getID(), fjid, files)
- self.conn.connection.send(response)
-
- 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 get_files_info(stanza):
- # Crawls the stanza in search for file and dir structure.
- files = []
- dirs = []
- children = stanza.getTag('match').getTag('offer').getChildren()
- for c in children:
- if c.getName() == 'file':
- f = {'name' : \
- c.getTag('name').getData()[1:] if c.getTag('name') else '',
- 'size' : c.getTag('size').getData() if c.getTag('size') else '',
- 'date' : c.getTag('date').getData() if c.getTag('date') else '',
- 'desc' : c.getTag('desc').getData() if c.getTag('desc') else '',
- # TODO: handle different hash algo
- 'hash' : c.getTag('hash').getData() if c.getTag('hash') else '',
- }
- files.append(f)
- else:
- dirname = c.getTag('name')
- if dirname is None:
- return None
- dirs.append(dirname.getData()[1:])
- return (files, dirs)
-
diff --git a/file_sharing/manifest.ini b/file_sharing/manifest.ini
deleted file mode 100644
index c3297fd..0000000
--- a/file_sharing/manifest.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[info]
-name: File Sharing
-short_name: fshare
-#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
-
-max_gajim_version: 0.15.9
diff --git a/gnome_session_manager/__init__.py b/gnome_session_manager/__init__.py
deleted file mode 100644
index 4f4909c..0000000
--- a/gnome_session_manager/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from plugin import GnomeSessionManagerPlugin
diff --git a/gnome_session_manager/gnome_session_manager.png b/gnome_session_manager/gnome_session_manager.png
deleted file mode 100644
index 4196d6a..0000000
--- a/gnome_session_manager/gnome_session_manager.png
+++ /dev/null
Binary files differ
diff --git a/gnome_session_manager/manifest.ini b/gnome_session_manager/manifest.ini
deleted file mode 100644
index 88a204b..0000000
--- a/gnome_session_manager/manifest.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[info]
-name: GNOME SessionManager
-short_name: gnome_session_manager
-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.15.9
diff --git a/gnome_session_manager/plugin.py b/gnome_session_manager/plugin.py
deleted file mode 100644
index ffb9268..0000000
--- a/gnome_session_manager/plugin.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- coding: utf-8 -*-
-
-## Copyright (C) 2010 Philippe Normand <phil@base-art.net>
-##
-## This file is part of Gajim.
-##
-## Gajim 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 3 only.
-##
-## Gajim 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.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-##
-
-import dbus
-from common import app
-from common import ged
-from common import dbus_support
-import gui_interface
-from plugins import GajimPlugin
-from plugins.helpers import log_calls, log
-
-GNOME_STATUS = [u'online', u'invisible', u'dnd', u'idle']
-PRESENCE_INTERFACE = "org.gnome.SessionManager.Presence"
-
-class GnomeSessionManagerPlugin(GajimPlugin):
-
- @log_calls('GnomeSessionManagerPlugin')
- def init(self):
- self.description = _('Set and react on GNOME Session presence settings')
- self.config_dialog = None
- self.events_handlers = {}
-
- @log_calls('GnomeSessionManagerPlugin')
- def activate(self):
- if not dbus_support.supported:
- return
-
- self.bus = dbus_support.session_bus.SessionBus()
- try:
- self.session_presence = self.bus.get_object("org.gnome.SessionManager",
- "/org/gnome/SessionManager/Presence")
- except:
- app.log.debug("GNOME SessionManager D-Bus service not found")
- return
-
- self.active = True
- app.ged.register_event_handler('our-show', ged.POSTGUI,
- self.on_our_status)
- self.bus.add_signal_receiver(self.gnome_presence_changed,
- "StatusChanged", PRESENCE_INTERFACE)
-
- @log_calls('GnomeSessionManagerPlugin')
- def deactivate(self):
- if not dbus_support.supported or not self.active:
- return
-
- self.active = False
- self.bus.remove_signal_receiver(self.gnome_presence_changed, "StatusChanged",
- dbus_interface=PRESENCE_INTERFACE)
- app.ged.remove_event_handler('our-show', ged.POSTGUI, self.on_our_status)
-
-
- def gnome_presence_changed(self, status, *args, **kw):
- if not app.interface.remote_ctrl:
- try:
- import remote_control
- app.interface.remote_ctrl = remote_control.Remote()
- except:
- return
- remote_gajim = app.interface.remote_ctrl.signal_object
- gajim_status = GNOME_STATUS[status]
- accounts = remote_gajim.list_accounts()
- for account in accounts:
- message = remote_gajim.get_status_message(account)
- remote_gajim.change_status(gajim_status, message, account)
-
- def on_our_status(self, network_event):
- try:
- gnome_status = GNOME_STATUS.index(network_event.show)
- except ValueError:
- print "GNOME SessionManager doesn't support %r status" % network_event.show
- else:
- self.session_presence.SetStatus(dbus.UInt32(gnome_status),
- dbus_interface=PRESENCE_INTERFACE)
diff --git a/gotr/__init__.py b/gotr/__init__.py
deleted file mode 100644
index 8fa452d..0000000
--- a/gotr/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from otrmodule import OtrPlugin
diff --git a/gotr/config_dialog.ui b/gotr/config_dialog.ui
deleted file mode 100644
index b9dd8d9..0000000
--- a/gotr/config_dialog.ui
+++ /dev/null
@@ -1,382 +0,0 @@
-<?xml version="1.0"?>
-<interface>
- <requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkListStore" id="account_store">
- <columns>
- <!-- column-name accountname -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkListStore" id="fingerprint_store">
- <columns>
- <!-- column-name screenname -->
- <column type="gchararray"/>
- <!-- column-name status -->
- <column type="gchararray"/>
- <!-- column-name verified -->
- <column type="gboolean"/>
- <!-- column-name fingerprint -->
- <column type="gchararray"/>
- <!-- column-name account -->
- <column type="gchararray"/>
- <!-- column-name tooltip -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkMenu" id="fprclipboard_menu">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="ubuntu_local">True</property>
- <child>
- <object class="GtkMenuItem" id="copyfprclipboard_item">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes" comments="Context menu item">Copy to clipboard</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="clipboard_button_cb"/>
- </object>
- </child>
- </object>
- <object class="GtkNotebook" id="notebook1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkFrame" id="frame1">
- <property name="visible">True</property>
- <property name="border_width">12</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">none</property>
- <child>
- <object class="GtkAlignment" id="alignment1">
- <property name="visible">True</property>
- <property name="left_padding">12</property>
- <child>
- <object class="GtkVBox" id="vbox2">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <property name="spacing">5</property>
- <child>
- <object class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <property name="spacing">5</property>
- <child>
- <object class="GtkLabel" id="fingerprint_label_desc">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="Descriptive label">Fingerprint:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="fingerprint_label">
- <property name="visible">True</property>
- <property name="label">&lt;tt&gt;-------- -------- -------- -------- -------- &lt;/tt&gt;</property>
- <property name="use_markup">True</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="regenerate_button">
- <property name="label" translatable="yes" comments="Generate Fingerprint button">(Re-)generate</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="regenerate_button_clicked_cb"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame" id="frame2">
- <property name="visible">True</property>
- <property name="border_width">12</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">none</property>
- <child>
- <object class="GtkAlignment" id="alignment2">
- <property name="visible">True</property>
- <property name="left_padding">12</property>
- <child>
- <object class="GtkVBox" id="vbox3">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkCheckButton" id="enable_check">
- <property name="label" translatable="yes" comments="checkbox">Enable private (Off-the-Record) messaging</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="flags_toggled_cb" after="yes"/>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="advertise_check">
- <property name="label" translatable="yes" comments="checkbox">Advertise Off-the-Record messaging support</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="flags_toggled_cb" after="yes"/>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="autoinitiate_check">
- <property name="label" translatable="yes" comments="checkbox">Automatically start private messaging</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="flags_toggled_cb" after="yes"/>
- </object>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="require_check">
- <property name="label" translatable="yes" comments="checkbox">Require private messaging</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="flags_toggled_cb" after="yes"/>
- </object>
- <packing>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child type="label">
- <object class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="title above options">&lt;b&gt;Default OTR Settings&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child type="label">
- <object class="GtkHBox" id="hbox2">
- <property name="visible">True</property>
- <property name="spacing">5</property>
- <child>
- <object class="GtkLabel" id="label4">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="label for account selector">&lt;b&gt;Off-the-Record settings for:&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBox" id="account_combobox">
- <property name="visible">True</property>
- <property name="model">account_store</property>
- <signal name="changed" handler="account_combobox_changed_cb"/>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext1"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="tab label">OTR Settings</property>
- </object>
- <packing>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="vbox4">
- <property name="visible">True</property>
- <property name="border_width">12</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
- <child>
- <object class="GtkTreeView" id="fingerprint_view">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="model">fingerprint_store</property>
- <property name="search_column">0</property>
- <property name="tooltip_column">5</property>
- <signal name="button-press-event" handler="fpr_button_pressed_cb" swapped="no"/>
- <child>
- <object class="GtkTreeViewColumn" id="name_column">
- <property name="resizable">True</property>
- <property name="title">Name</property>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext2"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="status_column">
- <property name="resizable">True</property>
- <property name="title">Status</property>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext3"/>
- <attributes>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="validated_column">
- <property name="resizable">True</property>
- <property name="title">Validated</property>
- <child>
- <object class="GtkCellRendererToggle" id="cellrenderertoggle1"/>
- <attributes>
- <attribute name="active">2</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="fingerprint_column">
- <property name="resizable">True</property>
- <property name="title">Fingerprint</property>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext4"/>
- <attributes>
- <attribute name="markup">3</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="account_column">
- <property name="resizable">True</property>
- <property name="title">Account</property>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext5"/>
- <attributes>
- <attribute name="text">4</attribute>
- </attributes>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox3">
- <property name="visible">True</property>
- <property name="spacing">5</property>
- <property name="homogeneous">True</property>
- <child>
- <object class="GtkButton" id="verify_button">
- <property name="label" translatable="yes" comments="button">Verify Fingerprint</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="verify_button_clicked_cb"/>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="forget_button">
- <property name="label" translatable="yes" comments="button">Forget Fingerprint</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="forget_button_clicked_cb"/>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="tab label">Known Fingerprints</property>
- </object>
- <packing>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- </object>
-</interface>
diff --git a/gotr/contact_otr_window.ui b/gotr/contact_otr_window.ui
deleted file mode 100644
index 9e697b8..0000000
--- a/gotr/contact_otr_window.ui
+++ /dev/null
@@ -1,355 +0,0 @@
-<?xml version="1.0"?>
-<interface>
- <requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkNotebook" id="otr_settings_notebook">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkVBox" id="otr_fp_vbox">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <property name="orientation">vertical</property>
- <property name="spacing">5</property>
- <property name="homogeneous">True</property>
- <child>
- <object class="GtkLabel" id="our_fp_label">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label">Your fingerprint:
-&lt;span weight="bold" face="monospace"&gt;01234567 89ABCDEF 01234567 89ABCDEF 01234567&lt;/span&gt;</property>
- <property name="use_markup">True</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="their_fp_label">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label">Purported fingerprint for asdfasdf@xyzxyzxyz.de:
-&lt;span weight="bold" face="monospace"&gt;01234567 89ABCDEF 01234567 89ABCDEF 01234567&lt;/span&gt;</property>
- <property name="use_markup">True</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBox" id="verified_combobox">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="model">verifiedmodel</property>
- <property name="active">0</property>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext1"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="tab label">Authentication</property>
- </object>
- <packing>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame" id="frame2">
- <property name="visible">True</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">none</property>
- <child>
- <object class="GtkVBox" id="otr_settings_vbox">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <property name="orientation">vertical</property>
- <property name="spacing">5</property>
- <property name="homogeneous">True</property>
- <child>
- <object class="GtkCheckButton" id="otr_policy_allow_v2_checkbutton">
- <property name="label" translatable="yes" comments="checkbox">OTR version 2 allowed</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="otr_policy_require_checkbutton">
- <property name="label" translatable="yes" comments="checkbox">Encryption required</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="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="otr_policy_send_tag_checkbutton">
- <property name="label" translatable="yes" comments="checkbox">Show others we understand OTR</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="otr_policy_start_on_tag_checkbutton">
- <property name="label" translatable="yes" comments="checkbox">Automatically initiate encryption if partner understands OTR</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="position">4</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="label">
- <object class="GtkCheckButton" id="otr_default_checkbutton">
- <property name="label" translatable="yes" comments="checkbox">Use the default settings</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="tab label">OTR Settings</property>
- </object>
- <packing>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- </object>
- <object class="GtkListStore" id="verifiedmodel">
- <columns>
- <!-- column-name verified -->
- <column type="gchararray"/>
- </columns>
- <data>
- <row>
- <col id="0" translatable="yes" comments="unverified option (dropdown label)">I have NOT verified that the purported fingerprint is in fact the correct fingerprint for that contact.</col>
- </row>
- <row>
- <col id="0" translatable="yes" comments="verified option (dropdown label)">I have verified that the purported fingerprint is in fact the correct fingerprint for that contact.</col>
- </row>
- </data>
- </object>
- <object class="GtkWindow" id="otr_smp_window">
- <property name="resizable">False</property>
- <child>
- <object class="GtkVBox" id="vbox3">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkVBox" id="vbox4">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <property name="orientation">vertical</property>
- <property name="spacing">5</property>
- <child>
- <object class="GtkLabel" id="desclabel1">
- <property name="visible">True</property>
- <property name="label">label</property>
- <property name="use_markup">True</property>
- <property name="wrap">True</property>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox2">
- <property name="visible">True</property>
- <child>
- <object class="GtkCheckButton" id="qcheckbutton">
- <property name="label" translatable="yes" comments="checkbox for socialist millionaire protocol with question support">Use question: </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="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="qentry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="desclabel2">
- <property name="visible">True</property>
- <property name="label">label</property>
- </object>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="secret_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox3">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <child>
- <object class="GtkProgressBar" id="progressbar">
- <property name="visible">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHButtonBox" id="hbuttonbox2">
- <property name="visible">True</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="smp_cancel_button">
- <property name="label">gtk-cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="smp_ok_button">
- <property name="label">gtk-ok</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- <object class="GtkMenuItem" id="otr_submenu">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="label" translatable="yes" comments="contact's submenu entry">Off-the-Record Encryption</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu" id="otr_submenu_menu">
- <child>
- <object class="GtkMenuItem" id="otr_settings_menuitem">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="menu entry for contact's otr settings">OTR settings / fingerprint</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="_on_otr_settings_menuitem_activate"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="smp_otr_menuitem">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="menu entry for SMP authentication">Authenticate contact</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="_on_smp_otr_menuitem_activate"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="start_otr_menuitem">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="menu entry for starting OTR session">Start / Refresh OTR</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="_on_start_otr_menuitem_activate"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="end_otr_menuitem">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="label" translatable="yes" comments="menu entry for killing an OTR session">End OTR</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="_on_end_otr_menuitem_activate"/>
- </object>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/gotr/gotr.png b/gotr/gotr.png
deleted file mode 100644
index 520915e..0000000
--- a/gotr/gotr.png
+++ /dev/null
Binary files differ
diff --git a/gotr/manifest.ini b/gotr/manifest.ini
deleted file mode 100644
index 07081fa..0000000
--- a/gotr/manifest.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[info]
-name: Off-The-Record Encryption
-short_name: gotr
-version: 1.7.2
-description: Provide OTR encryption
-authors: Kjell Braden <afflux.gajim@pentabarf.de>
-homepage: http://gajim-otr.pentabarf.de
-max_gajim_version: 0.15.9
diff --git a/gotr/otrmodule.py b/gotr/otrmodule.py
deleted file mode 100644
index ea6144d..0000000
--- a/gotr/otrmodule.py
+++ /dev/null
@@ -1,665 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-## otrmodule.py
-##
-## Copyright 2008-2012 Kjell Braden <afflux@pentabarf.de>
-##
-## This file is part of Gajim.
-##
-## Gajim 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 3 only.
-##
-## Gajim 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.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-##
-
-
-'''
-Off-The-Record encryption plugin.
-
-:author: Kjell self.Braden <kb.otr@pentabarf.de>
-:since: 2008
-:copyright: Copyright 2008-2012 Kjell Braden <afflux@pentabarf.de>
-:license: GPL
-'''
-
-MINVERSION = (1,0,0,'beta5')
-MINCRYPTOVERSION = (2,1,0,'final',0)
-IGNORE = True
-PASS = False
-
-DEFAULTFLAGS = {
- 'ALLOW_V1':False,
- 'ALLOW_V2':True,
- 'REQUIRE_ENCRYPTION':False,
- 'SEND_TAG':True,
- 'WHITESPACE_START_AKE':True,
- 'ERROR_START_AKE':True,
- }
-
-MMS = 1024
-PROTOCOL = 'xmpp'
-
-enc_tip = 'A private chat session <i>is established</i> to this contact ' \
- 'with this fingerprint'
-unused_tip = 'A private chat session is established to this contact using ' \
- '<i>another</i> fingerprint'
-ended_tip = 'The private chat session to this contact has <i>ended</i>'
-inactive_tip = 'Communication to this contact is currently ' \
- '<i>unencrypted</i>'
-
-import logging
-import os
-import pickle
-import time
-import sys
-
-import common.xmpp
-from common import gajim
-from common import ged
-from common.connection_handlers_events import MessageOutgoingEvent
-from plugins import GajimPlugin
-from message_control import TYPE_CHAT, MessageControl
-from plugins.helpers import log_calls, log
-from plugins.plugin import GajimPluginException
-
-import ui
-
-sys.path.insert(0, os.path.dirname(ui.__file__))
-
-from HTMLParser import HTMLParser
-from htmlentitydefs import name2codepoint
-
-HAS_CRYPTO = True
-try:
- import Crypto
- if not hasattr(Crypto, 'version_info') \
- or Crypto.version_info < MINCRYPTOVERSION:
- raise ImportError('PyCrypto not found or too old')
-except ImportError:
- HAS_CRYPTO = False
-
-HAS_POTR = True
-try:
- import potr
- import potr.crypt
- import potr.context
- if not hasattr(potr, 'VERSION') or potr.VERSION < MINVERSION:
- raise ImportError('old / unsupported python-otr version')
-
- potrrootlog = logging.getLogger('potr')
- potrrootlog.handlers = []
- potrrootlog.propagate = False
- gajimrootlog = logging.getLogger('gajim')
- for h in gajimrootlog.handlers:
- potrrootlog.addHandler(h)
-
- def get_jid_from_fjid(fjid):
- return gajim.get_room_and_nick_from_fjid(fjid)[0]
-
- class GajimContext(potr.context.Context):
- # self.peer is fjid
- # self.jid does not contain resource
- __slots__ = ['smpWindow', 'jid']
-
- def __init__(self, account, peer):
- super(GajimContext, self).__init__(account, peer)
- self.jid = get_jid_from_fjid(peer)
- self.trustName = self.jid
- self.smpWindow = ui.ContactOtrSmpWindow(self)
-
- def inject(self, msg, appdata=None):
- log.debug('inject(appdata=%s)', appdata)
- msg = unicode(msg)
- account = self.user.accountname
-
- stanza = common.xmpp.Message(to=self.peer, body=msg, typ='chat')
- if appdata is not None:
- session = appdata.get('session', None)
- if session is not None:
- stanza.setThread(session.thread_id)
- gajim.connections[account].connection.send(stanza, now=True)
-
- def setState(self, newstate):
- if self.state == potr.context.STATE_ENCRYPTED:
- # we were encrypted
- if newstate == potr.context.STATE_ENCRYPTED:
- # and are still -> it's just a refresh
- OtrPlugin.gajim_log(
- _('Private conversation with %s refreshed.') % self.peer,
- self.user.accountname, self.peer)
- elif newstate == potr.context.STATE_FINISHED:
- # and aren't anymore -> other side disconnected
- OtrPlugin.gajim_log(_('%s has ended his/her private '
- 'conversation with you. You should do the same.')
- % self.peer, self.user.accountname, self.peer)
- else:
- if newstate == potr.context.STATE_ENCRYPTED:
- # we are now encrypted
- trust = self.getCurrentTrust()
- if trust is None:
- fpr = str(self.getCurrentKey())
- OtrPlugin.gajim_log(_('New fingerprint for %(peer)s: %(fpr)s')
- % {'peer': self.peer, 'fpr': fpr},
- self.user.accountname, self.peer)
- self.setCurrentTrust('')
- trustStr = 'authenticated' if bool(trust) else '*unauthenticated*'
- OtrPlugin.gajim_log(
- _('%(trustStr)s secured OTR conversation with %(peer)s started')
- % {'trustStr': trustStr, 'peer': self.peer},
- self.user.accountname, self.peer)
-
- if self.state != potr.context.STATE_PLAINTEXT and \
- newstate == potr.context.STATE_PLAINTEXT:
- # we are now plaintext
- OtrPlugin.gajim_log(
- _('Private conversation with %s lost.') % self.peer,
- self.user.accountname, self.peer)
-
- super(GajimContext, self).setState(newstate)
- OtrPlugin.update_otr(self.peer, self.user.accountname)
- self.user.plugin.update_context_list()
-
- def getPolicy(self, key):
- ret = self.user.plugin.get_flags(self.user.accountname, self.jid)[key]
- log.debug('getPolicy(key=%s) = %s', key, ret)
- return ret
-
- class GajimOtrAccount(potr.context.Account):
- contextclass = GajimContext
- def __init__(self, plugin, accountname):
- global PROTOCOL, MMS
- self.plugin = plugin
- self.accountname = accountname
- name = gajim.get_jid_from_account(accountname)
- super(GajimOtrAccount, self).__init__(name, PROTOCOL, MMS)
- self.keyFilePath = os.path.join(gajim.gajimpaths.data_root, accountname)
-
- def dropPrivkey(self):
- try:
- os.remove(self.keyFilePath + '.key3')
- except IOError, e:
- if e.errno != 2:
- log.exception('IOError occurred when removing key file for %s',
- self.name)
- self.privkey = None
-
- def loadPrivkey(self):
- try:
- with open(self.keyFilePath + '.key3', 'rb') as keyFile:
- return potr.crypt.PK.parsePrivateKey(keyFile.read())[0]
- except IOError, e:
- if e.errno != 2:
- log.exception('IOError occurred when loading key file for %s',
- self.name)
- return None
-
- def savePrivkey(self):
- try:
- with open(self.keyFilePath + '.key3', 'wb') as keyFile:
- keyFile.write(self.getPrivkey().serializePrivateKey())
- except IOError, e:
- log.exception('IOError occurred when loading key file for %s',
- self.name)
-
- def loadTrusts(self, newCtxCb=None):
- ''' load the fingerprint trustdb '''
- # it has the same format as libotr, therefore the
- # redundant account / proto field
- try:
- with open(self.keyFilePath + '.fpr', 'r') as fprFile:
- for line in fprFile:
- ctx, acc, proto, fpr, trust = line[:-1].split('\t')
-
- if acc != self.name or proto != PROTOCOL:
- continue
-
- jid = get_jid_from_fjid(ctx)
- self.setTrust(jid, fpr, trust)
- except IOError, e:
- if e.errno != 2:
- log.exception('IOError occurred when loading fpr file for %s',
- self.name)
-
- def saveTrusts(self):
- try:
- with open(self.keyFilePath + '.fpr', 'w') as fprFile:
- for uid, trusts in self.trusts.iteritems():
- for fpr, trustVal in trusts.iteritems():
- fprFile.write('\t'.join(
- (uid, self.name, PROTOCOL, fpr, trustVal)))
- fprFile.write('\n')
- except IOError, e:
- log.exception('IOError occurred when loading fpr file for %s',
- self.name)
-except ImportError:
- HAS_POTR = False
-
-def otr_dialog_destroy(widget, *args, **kwargs):
- widget.destroy()
-
-class OtrPlugin(GajimPlugin):
- otr = None
- def init(self):
-
- self.description = _('See http://www.cypherpunks.ca/otr/')
- self.us = {}
-
-
- if not HAS_POTR:
- self.activatable = False
- self.available_text = _('Can\'t find potr. Verify this ' \
- 'plugin\'s integrity.')
- return
-
- if not HAS_CRYPTO:
- self.activatable = False
- self.available_text = _('PyCrypto not installed or too old.')
- return
-
-
- self.config_dialog = ui.OtrPluginConfigDialog(self)
- self.events_handlers = {}
- self.events_handlers['message-received'] = (ged.PRECORE,
- self.handle_incoming_msg)
- self.events_handlers['message-outgoing'] = (ged.OUT_PRECORE,
- self.handle_outgoing_msg)
-
- self.gui_extension_points = {
- 'chat_control' : (self.cc_connect, self.cc_disconnect)
- }
-
- for acc in gajim.contacts.get_accounts():
- self.us[acc] = GajimOtrAccount(self, acc)
- self.us[acc].loadTrusts()
-
- acc = str(acc)
- if acc not in self.config or None not in self.config[acc]:
- self.config[acc] = {None:DEFAULTFLAGS.copy()}
- self.update_context_list()
-
- @log_calls('OtrPlugin')
- def activate(self):
- if not HAS_CRYPTO or not HAS_POTR or not hasattr(potr, 'VERSION') \
- or potr.VERSION < MINVERSION:
- raise GajimPluginException(self.available_text)
-
- def get_otr_status(self, account, contact):
- ctx = self.us[account].getContext(contact.get_full_jid())
-
- finished = ctx.state == potr.context.STATE_FINISHED
- encrypted = finished or ctx.state == potr.context.STATE_ENCRYPTED
- trusted = encrypted and bool(ctx.getCurrentTrust())
- return (encrypted, trusted, finished)
-
- def cc_connect(self, cc):
- def update_otr(print_status=False):
- enc_status, authenticated, finished = \
- self.get_otr_status(cc.account, cc.contact)
- otr_status_text = ''
-
- if finished:
- otr_status_text = u'finished OTR connection'
- elif authenticated:
- otr_status_text = u'authenticated secure OTR connection'
- elif enc_status:
- otr_status_text = u'*unauthenticated* secure OTR connection'
-
- cc._show_lock_image(enc_status, u'OTR', enc_status, True,
- authenticated)
- if print_status and otr_status_text:
- cc.print_conversation_line(u'[OTR] %s' % otr_status_text,
- 'status', '', None)
- cc.update_otr = update_otr
- cc.update_otr(True)
-
- # hijack authentication button with our submenu
- def authbutton_cb(widget):
- if not cc.gpg_is_active and not (cc.session and
- cc.session.enable_encryption):
- ui.get_otr_submenu(self, cc).get_submenu().popup(None,
- None, None, 0, 0)
- else:
- cc._on_authentication_button_clicked(widget)
- self.overwrite_handler(cc, cc.authentication_button, authbutton_cb)
-
- # hijack context menu
- cc.orig_prepare_context_menu = cc.prepare_context_menu
- def inject_menu(hide_buttonbar_items=False):
- menu = cc.orig_prepare_context_menu(hide_buttonbar_items)
- menu.insert(ui.get_otr_submenu(self, cc), 8)
- return menu
- cc.prepare_context_menu = inject_menu
-
- def cc_disconnect(self, cc):
- try:
- self.overwrite_handler(cc, cc.authentication_button,
- cc._on_authentication_button_clicked)
- cc.prepare_context_menu = cc.orig_prepare_context_menu
- del cc.update_otr
- except AttributeError:
- pass
-
- def menu_settings_cb(self, item, control):
- ctx = self.us[control.account].getContext(control.contact.get_full_jid())
- dlg = ui.ContactOtrWindow(self, ctx)
- dlg.run()
- dlg.destroy()
-
- def menu_start_cb(self, item, control):
- gajim.nec.push_outgoing_event(MessageOutgoingEvent(None,
- account=control.account, jid=control.contact.jid,
- message=u'?OTRv?', type_='chat',
- resource=control.contact.resource, is_loggable=False))
-
- def menu_end_cb(self, item, control):
- fjid = control.contact.get_full_jid()
- thread_id = control.session.thread_id if control.session else None
-
- self.us[control.account].getContext(fjid).disconnect(
- appdata={'session':control.session})
-
- def menu_smp_cb(self, item, control):
- ctx = self.us[control.account].getContext(control.contact.get_full_jid())
- ctx.smpWindow.show(False)
-
- @staticmethod
- def overwrite_handler(window, control, handler):
- for id_, v in window.handlers.iteritems():
- if v == control:
- break
- else:
- raise LookupError
-
- del window.handlers[id_]
- control.disconnect(id_)
- id_ = control.connect('clicked', handler)
- window.handlers[id_] = control
-
- def set_flags(self, value, account=None, contact=None):
- if isinstance(account, unicode):
- account = account.encode()
-
- if account not in self.config:
- self.config[account] = {None:DEFAULTFLAGS.copy()}
-
- if account is None and contact is not None:
- # don't set per-contact options without account
- raise Exception("can't set contact flags without account")
-
- config = self.config[account]
- config[contact] = value
-
- self.config[account] = config
-
- def get_flags(self, account=None, contact=None, fallback=True):
- if isinstance(account, unicode):
- account = account.encode()
-
- setting = DEFAULTFLAGS.copy()
- if account in self.config:
- setting.update(self.config[account][None])
- if contact in self.config[account] \
- and self.config[account][contact] is not None:
- setting.update(self.config[account][contact])
- elif not fallback:
- return None
- return setting
-
- def update_context_list(self):
- self.config_dialog.fpr_model.clear()
- for us in self.us.itervalues():
- usedFpr = set()
- for fjid, ctx in us.ctxs.iteritems():
- # get active contexts first
- key = ctx.getCurrentKey()
- if not key:
- continue
- fpr = key.cfingerprint()
- usedFpr.add(fpr)
-
- human_hash = potr.human_hash(fpr)
- trust = bool(us.getTrust(ctx.trustName, fpr))
-
- if ctx.state == potr.context.STATE_ENCRYPTED:
- state = "encrypted"
- tip = enc_tip
- elif ctx.state == potr.context.STATE_FINISHED:
- state = "finished"
- tip = ended_tip
- else:
- state = 'inactive'
- tip = inactive_tip
-
- self.config_dialog.fpr_model.append((fjid, state, trust,
- '<tt>%s</tt>' % human_hash, us.name, tip, fpr))
-
- for uid, trusts in us.trusts.iteritems():
- for fpr, trust in trusts.iteritems():
- if fpr in usedFpr:
- continue
-
- state = 'inactive'
- tip = inactive_tip
-
- human_hash = potr.human_hash(fpr)
-
- self.config_dialog.fpr_model.append((uid, state, bool(trust),
- '<tt>%s</tt>' % human_hash, us.name, tip, fpr))
-
- @classmethod
- def gajim_log(cls, msg, account, fjid, no_print=False,
- is_status_message=True, thread_id=None):
- if not isinstance(fjid, unicode):
- fjid = unicode(fjid)
- if not isinstance(account, unicode):
- account = unicode(account)
-
- resource = gajim.get_resource_from_jid(fjid)
- jid = gajim.get_jid_without_resource(fjid)
- tim = time.localtime()
-
- if is_status_message is True:
- if not no_print:
- ctrl = cls.get_control(fjid, account)
- if ctrl:
- ctrl.print_conversation_line(u'[OTR] %s' % msg, 'status',
- '', None)
- id = gajim.logger.write('chat_msg_recv', fjid,
- message=u'[OTR: %s]' % msg, tim=tim)
- # gajim.logger.write() only marks a message as unread (and so
- # only returns an id) when fjid is a real contact (NOT if it's a
- # GC private chat)
- if id:
- gajim.logger.set_read_messages([id])
- else:
- session = gajim.connections[account].get_or_create_session(fjid,
- thread_id)
- session.received_thread_id |= bool(thread_id)
- session.last_receive = time.time()
-
- if not session.control:
- # look for an existing chat control without a session
- ctrl = cls.get_control(fjid, account)
- if ctrl:
- session.control = ctrl
- session.control.set_session(session)
-
- msg_id = gajim.logger.write('chat_msg_recv', fjid,
- message=u'[OTR: %s]' % msg, tim=tim)
- session.roster_message(jid, msg, tim=tim, msg_id=msg_id,
- msg_type='chat', resource=resource)
-
- @classmethod
- def update_otr(cls, user, acc, print_status=False):
- ctrl = cls.get_control(user, acc)
- if ctrl:
- ctrl.update_otr(print_status)
-
- @staticmethod
- def get_control(fjid, account):
- # first try to get the window with the full jid
- ctrl = gajim.interface.msg_win_mgr.get_control(fjid, account)
- if ctrl:
- # got one, be happy
- return ctrl
-
- # otherwise try without the resource
- ctrl = gajim.interface.msg_win_mgr.get_control(
- gajim.get_jid_without_resource(fjid), account)
- # but only use it when it's not a GC window
- if ctrl and ctrl.TYPE_ID == TYPE_CHAT:
- return ctrl
-
- def handle_incoming_msg(self, event):
- ctx = None
- account = event.conn.name
- accjid = gajim.get_jid_from_account(account)
-
- if event.encrypted is not False or not event.stanza.getTag('body') \
- or not isinstance(event.stanza.getBody(), unicode):
- return PASS
-
- try:
- ctx = self.us[account].getContext(event.fjid)
- msgtxt, tlvs = ctx.receiveMessage(event.msgtxt,
- appdata={'session':event.session})
- except potr.context.NotOTRMessage, e:
- # received message was not OTR - pass it on
- return PASS
- except potr.context.UnencryptedMessage, e:
- # we are encrypted but got some plaintext
- # display it with a warning
- tlvs = []
- msgtxt = _('The following message received from %(jid)s was '
- '*not encrypted*: [%(error)s]') % {'jid': event.fjid,
- 'error': e.args[0]}
- except potr.context.NotEncryptedError, e:
- # we got some encrypted data
- # but we don't have an encrypted session
- self.gajim_log(_('The encrypted message received from %s is '
- 'unreadable, as you are not currently communicating '
- 'privately') % event.fjid, account, event.fjid)
- return IGNORE
- except potr.context.ErrorReceived, e:
- # got a protocol error
- self.gajim_log(_('We received the following OTR error '
- 'message from %(jid)s: [%(error)s]') % {'jid': event.fjid,
- 'error': e.args[0].error},
- account, event.fjid)
- return IGNORE
- except potr.crypt.InvalidParameterError, e:
- # received a packet we cannot process (probably tampered or
- # sent to wrong session)
- self.gajim_log(_('We received an unreadable OTR message '
- 'from %(jid)s. It has probably been tampered with, '
- 'or was sent from an older OTR session.')
- % {'jid':event.fjid}, account, event.fjid)
- return IGNORE
- except RuntimeError, e:
- # generic library bug?
- self.gajim_log(_('The following error occurred when trying to '
- 'decrypt a message from %(jid)s: [%(error)s]') % {
- 'jid': event.fjid, 'error': e},
- account, event.fjid)
- return IGNORE
-
- if ctx is not None:
- ctx.smpWindow.handle_tlv(tlvs)
-
- stripper = HTMLStripper()
- stripper.feed(unicode(msgtxt or ''))
- event.msgtxt = stripper.stripped_data
- event.stanza.setBody(event.msgtxt)
- event.stanza.setXHTML(msgtxt)
-
- 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
-
- if event.session:
- fjid = event.session.get_to()
- else:
- fjid = event.jid
- if event.resource:
- fjid += '/' + event.resource
-
- message = event.xhtml or escape(event.message)
-
- try:
- newmsg = self.us[event.account].getContext(fjid).sendMessage(
- potr.context.FRAGMENT_SEND_ALL_BUT_LAST, message,
- appdata={'session':event.session})
- except potr.context.NotEncryptedError, e:
- if e.args[0] == potr.context.EXC_FINISHED:
- self.gajim_log(_('Your message was not send. Either end '
- 'your private conversation, or restart it'), event.account,
- fjid)
- return IGNORE
- else:
- raise e
-
- if event.xhtml: # if we had html before, replace with new content
- event.xhtml = newmsg
-
- stripper = HTMLStripper()
- stripper.feed(unicode(newmsg or ''))
- event.message = stripper.stripped_data
-
- return PASS
-
-
-class HTMLStripper(HTMLParser):
- def reset(self):
- self.stripped_data = ''
- HTMLParser.reset(self)
-
- def handle_data(self, data):
- self.stripped_data += data
-
- def handle_starttag(self, tag, attrs):
- if tag == 'br':
- self.stripped_data += '\n'
-
- def handle_entityref(self, name):
- c = unichr(name2codepoint[name])
- self.stripped_data += c
- def handle_charref(self, name):
- if name.startswith('x'):
- c = unichr(int(name[1:], 16))
- else:
- c = unichr(int(name))
- self.stripped_data += c
-
- def unknown_decl(self, data):
- if data.startswith('CDATA['):
- self.stripped_data += data[6:]
-
- def feed(self, data):
- data = data.replace('\n', '')
- HTMLParser.feed(self, data)
-
-def escape(s):
- '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
- If the optional flag quote is true, the quotation mark character (")
- is also translated.'''
- s = s.replace("&", "&amp;") # Must be done first!
- s = s.replace("<", "&lt;")
- s = s.replace(">", "&gt;")
- s = s.replace("\n", "<br/>")
- return s
-
-## TODO:
-## - disconnect ctxs on disconnect
diff --git a/gotr/potr/__init__.py b/gotr/potr/__init__.py
deleted file mode 100644
index c6e8d02..0000000
--- a/gotr/potr/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2011-2012 Kjell Braden <afflux@pentabarf.de>
-#
-# This file is part of the python-potr library.
-#
-# python-potr is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# any later version.
-#
-# python-potr 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-# some python3 compatibilty
-from __future__ import unicode_literals
-
-from potr import context
-from potr import proto
-from potr.utils import human_hash
-
-''' version is: (major, minor, patch, sub) with sub being one of 'alpha',
-'beta', 'final' '''
-VERSION = (1, 0, 0, 'beta7')
diff --git a/gotr/potr/compatcrypto/__init__.py b/gotr/potr/compatcrypto/__init__.py
deleted file mode 100644
index e0f82d0..0000000
--- a/gotr/potr/compatcrypto/__init__.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2012 Kjell Braden <afflux@pentabarf.de>
-#
-# This file is part of the python-potr library.
-#
-# python-potr is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# any later version.
-#
-# python-potr 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-
-from potr.compatcrypto.common import *
-
-from potr.compatcrypto.pycrypto import *
diff --git a/gotr/potr/compatcrypto/common.py b/gotr/potr/compatcrypto/common.py
deleted file mode 100644
index 61f2bea..0000000
--- a/gotr/potr/compatcrypto/common.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# Copyright 2012 Kjell Braden <afflux@pentabarf.de>
-#
-# This file is part of the python-potr library.
-#
-# python-potr is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# any later version.
-#
-# python-potr 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-# some python3 compatibilty
-from __future__ import unicode_literals
-
-import logging
-import struct
-
-from potr.utils import human_hash, bytes_to_long, unpack, pack_mpi
-
-DEFAULT_KEYTYPE = 0x0000
-pkTypes = {}
-def registerkeytype(cls):
- if cls.keyType is None:
- raise TypeError('registered key class needs a type value')
- pkTypes[cls.keyType] = cls
- return cls
-
-def generateDefaultKey():
- return pkTypes[DEFAULT_KEYTYPE].generate()
-
-class PK(object):
- keyType = None
-
- @classmethod
- def generate(cls):
- raise NotImplementedError
-
- @classmethod
- def parsePayload(cls, data, private=False):
- raise NotImplementedError
-
- def sign(self, data):
- raise NotImplementedError
- def verify(self, data):
- raise NotImplementedError
- def fingerprint(self):
- raise NotImplementedError
-
- def serializePublicKey(self):
- return struct.pack(b'!H', self.keyType) \
- + self.getSerializedPublicPayload()
-
- def getSerializedPublicPayload(self):
- buf = b''
- for x in self.getPublicPayload():
- buf += pack_mpi(x)
- return buf
-
- def getPublicPayload(self):
- raise NotImplementedError
-
- def serializePrivateKey(self):
- return struct.pack(b'!H', self.keyType) \
- + self.getSerializedPrivatePayload()
-
- def getSerializedPrivatePayload(self):
- buf = b''
- for x in self.getPrivatePayload():
- buf += pack_mpi(x)
- return buf
-
- def getPrivatePayload(self):
- raise NotImplementedError
-
- def cfingerprint(self):
- return '{0:040x}'.format(bytes_to_long(self.fingerprint()))
-
- @classmethod
- def parsePrivateKey(cls, data):
- implCls, data = cls.getImplementation(data)
- logging.debug('Got privkey of type %r', implCls)
- return implCls.parsePayload(data, private=True)
-
- @classmethod
- def parsePublicKey(cls, data):
- implCls, data = cls.getImplementation(data)
- logging.debug('Got pubkey of type %r', implCls)
- return implCls.parsePayload(data)
-
- def __str__(self):
- return human_hash(self.cfingerprint())
- def __repr__(self):
- return '<{cls}(fpr=\'{fpr}\')>'.format(
- cls=self.__class__.__name__, fpr=str(self))
-
- @staticmethod
- def getImplementation(data):
- typeid, data = unpack(b'!H', data)
- cls = pkTypes.get(typeid, None)
- if cls is None:
- raise NotImplementedError('unknown typeid %r' % typeid)
- return cls, data
diff --git a/gotr/potr/compatcrypto/pycrypto.py b/gotr/potr/compatcrypto/pycrypto.py
deleted file mode 100644
index 2800431..0000000
--- a/gotr/potr/compatcrypto/pycrypto.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# Copyright 2012 Kjell Braden <afflux@pentabarf.de>
-#
-# This file is part of the python-potr library.
-#
-# python-potr is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# any later version.
-#
-# python-potr 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-from Crypto import Cipher
-from Crypto.Hash import SHA256 as _SHA256
-from Crypto.Hash import SHA as _SHA1
-from Crypto.Hash import HMAC as _HMAC
-from Crypto.PublicKey import DSA
-from Crypto.Random import random
-from numbers import Number
-
-from potr.compatcrypto import common
-from potr.utils import read_mpi, bytes_to_long, long_to_bytes
-
-def SHA256(data):
- return _SHA256.new(data).digest()
-
-def SHA1(data):
- return _SHA1.new(data).digest()
-
-def HMAC(key, data, mod):
- return _HMAC.new(key, msg=data, digestmod=mod).digest()
-
-def SHA1HMAC(key, data):
- return HMAC(key, data, _SHA1)
-
-def SHA256HMAC(key, data):
- return HMAC(key, data, _SHA256)
-
-def SHA256HMAC160(key, data):
- return SHA256HMAC(key, data)[:20]
-
-def AESCTR(key, counter=0):
- if isinstance(counter, Number):
- counter = Counter(counter)
- if not isinstance(counter, Counter):
- raise TypeError
- return Cipher.AES.new(key, Cipher.AES.MODE_CTR, counter=counter)
-
-class Counter(object):
- def __init__(self, prefix):
- self.prefix = prefix
- self.val = 0
-
- def inc(self):
- self.prefix += 1
- self.val = 0
-
- def __setattr__(self, attr, val):
- if attr == 'prefix':
- self.val = 0
- super(Counter, self).__setattr__(attr, val)
-
- def __repr__(self):
- return '<Counter(p={p!r},v={v!r})>'.format(p=self.prefix, v=self.val)
-
- def byteprefix(self):
- return long_to_bytes(self.prefix, 8)
-
- def __call__(self):
- bytesuffix = long_to_bytes(self.val, 8)
- self.val += 1
- return self.byteprefix() + bytesuffix
-
-@common.registerkeytype
-class DSAKey(common.PK):
- keyType = 0x0000
-
- def __init__(self, key=None, private=False):
- self.priv = self.pub = None
-
- if not isinstance(key, tuple):
- raise TypeError('4/5-tuple required for key')
-
- if len(key) == 5 and private:
- self.priv = DSA.construct(key)
- self.pub = self.priv.publickey()
- elif len(key) == 4 and not private:
- self.pub = DSA.construct(key)
- else:
- raise TypeError('wrong number of arguments for ' \
- 'private={0!r}: got {1} '
- .format(private, len(key)))
-
- def getPublicPayload(self):
- return (self.pub.p, self.pub.q, self.pub.g, self.pub.y)
-
- def getPrivatePayload(self):
- return (self.priv.p, self.priv.q, self.priv.g, self.priv.y, self.priv.x)
-
- def fingerprint(self):
- return SHA1(self.getSerializedPublicPayload())
-
- def sign(self, data):
- # 2 <= K <= q
- K = random.randrange(2, self.priv.q)
- r, s = self.priv.sign(data, K)
- return long_to_bytes(r, 20) + long_to_bytes(s, 20)
-
- def verify(self, data, sig):
- r, s = bytes_to_long(sig[:20]), bytes_to_long(sig[20:])
- return self.pub.verify(data, (r, s))
-
- def __hash__(self):
- return bytes_to_long(self.fingerprint())
-
- def __eq__(self, other):
- if not isinstance(other, type(self)):
- return False
- return self.fingerprint() == other.fingerprint()
-
- def __ne__(self, other):
- return not (self == other)
-
- @classmethod
- def generate(cls):
- privkey = DSA.generate(1024)
- return cls((privkey.key.y, privkey.key.g, privkey.key.p, privkey.key.q,
- privkey.key.x), private=True)
-
- @classmethod
- def parsePayload(cls, data, private=False):
- p, data = read_mpi(data)
- q, data = read_mpi(data)
- g, data = read_mpi(data)
- y, data = read_mpi(data)
- if private:
- x, data = read_mpi(data)
- return cls((y, g, p, q, x), private=True), data
- return cls((y, g, p, q), private=False), data
diff --git a/gotr/potr/context.py b/gotr/potr/context.py
deleted file mode 100644
index 3ec3d74..0000000
--- a/gotr/potr/context.py
+++ /dev/null
@@ -1,568 +0,0 @@
-# Copyright 2011-2012 Kjell Braden <afflux@pentabarf.de>
-#
-# This file is part of the python-potr library.
-#
-# python-potr is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# any later version.
-#
-# python-potr 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-# some python3 compatibilty
-from __future__ import unicode_literals
-
-try:
- type(basestring)
-except NameError:
- # all strings are unicode in python3k
- basestring = str
- unicode = str
-
-# callable is not available in python 3.0 and 3.1
-try:
- type(callable)
-except NameError:
- from collections import Callable
- def callable(x):
- return isinstance(x, Callable)
-
-
-import base64
-import logging
-import struct
-
-logger = logging.getLogger(__name__)
-
-from potr import crypt
-from potr import proto
-from potr import compatcrypto
-
-from time import time
-
-EXC_UNREADABLE_MESSAGE = 1
-EXC_FINISHED = 2
-
-HEARTBEAT_INTERVAL = 60
-STATE_PLAINTEXT = 0
-STATE_ENCRYPTED = 1
-STATE_FINISHED = 2
-FRAGMENT_SEND_ALL = 0
-FRAGMENT_SEND_ALL_BUT_FIRST = 1
-FRAGMENT_SEND_ALL_BUT_LAST = 2
-
-OFFER_NOTSENT = 0
-OFFER_SENT = 1
-OFFER_REJECTED = 2
-OFFER_ACCEPTED = 3
-
-class Context(object):
- def __init__(self, account, peername):
- self.user = account
- self.peer = peername
- self.policy = {}
- self.crypto = crypt.CryptEngine(self)
- self.tagOffer = OFFER_NOTSENT
- self.mayRetransmit = 0
- self.lastSend = 0
- self.lastMessage = None
- self.state = STATE_PLAINTEXT
- self.trustName = self.peer
-
- self.fragmentInfo = None
- self.fragment = None
- self.discardFragment()
-
- def getPolicy(self, key):
- raise NotImplementedError
-
- def inject(self, msg, appdata=None):
- raise NotImplementedError
-
- def policyOtrEnabled(self):
- return self.getPolicy('ALLOW_V2') or self.getPolicy('ALLOW_V1')
-
- def discardFragment(self):
- self.fragmentInfo = (0, 0)
- self.fragment = []
-
- def fragmentAccumulate(self, message):
- '''Accumulate a fragmented message. Returns None if the fragment is
- to be ignored, returns a string if the message is ready for further
- processing'''
-
- params = message.split(b',')
- if len(params) < 5 or not params[1].isdigit() or not params[2].isdigit():
- logger.warning('invalid formed fragmented message: %r', params)
- self.discardFragment()
- return message
-
-
- K, N = self.fragmentInfo
- try:
- k = int(params[1])
- n = int(params[2])
- except ValueError:
- logger.warning('invalid formed fragmented message: %r', params)
- self.discardFragment()
- return message
-
- fragData = params[3]
-
- logger.debug(params)
-
- if n >= k == 1:
- # first fragment
- self.discardFragment()
- self.fragmentInfo = (k, n)
- self.fragment.append(fragData)
- elif N == n >= k > 1 and k == K+1:
- # accumulate
- self.fragmentInfo = (k, n)
- self.fragment.append(fragData)
- else:
- # bad, discard
- self.discardFragment()
- logger.warning('invalid fragmented message: %r', params)
- return message
-
- if n == k > 0:
- assembled = b''.join(self.fragment)
- self.discardFragment()
- return assembled
-
- return None
-
- def removeFingerprint(self, fingerprint):
- self.user.removeFingerprint(self.trustName, fingerprint)
-
- def setTrust(self, fingerprint, trustLevel):
- ''' sets the trust level for the given fingerprint.
- trust is usually:
- - the empty string for known but untrusted keys
- - 'verified' for manually verified keys
- - 'smp' for smp-style verified keys '''
- self.user.setTrust(self.trustName, fingerprint, trustLevel)
-
- def getTrust(self, fingerprint, default=None):
- return self.user.getTrust(self.trustName, fingerprint, default)
-
- def setCurrentTrust(self, trustLevel):
- self.setTrust(self.crypto.theirPubkey.cfingerprint(), trustLevel)
-
- def getCurrentKey(self):
- return self.crypto.theirPubkey
-
- def getCurrentTrust(self):
- ''' returns a 2-tuple: first element is the current fingerprint,
- second is:
- - None if the key is unknown yet
- - a non-empty string if the key is trusted
- - an empty string if the key is untrusted '''
- if self.crypto.theirPubkey is None:
- return None
- return self.getTrust(self.crypto.theirPubkey.cfingerprint(), None)
-
- def receiveMessage(self, messageData, appdata=None):
- IGN = None, []
-
- if not self.policyOtrEnabled():
- raise NotOTRMessage(messageData)
-
- message = self.parse(messageData)
-
- if message is None:
- # nothing to see. move along.
- return IGN
-
- logger.debug(repr(message))
-
- if self.getPolicy('SEND_TAG'):
- if isinstance(message, basestring):
- # received a plaintext message without tag
- # we should not tag anymore
- self.tagOffer = OFFER_REJECTED
- else:
- # got something OTR-ish, cool!
- self.tagOffer = OFFER_ACCEPTED
-
- if isinstance(message, proto.Query):
- self.handleQuery(message, appdata=appdata)
-
- if isinstance(message, proto.TaggedPlaintext):
- # it's actually a plaintext message
- if self.state != STATE_PLAINTEXT or \
- self.getPolicy('REQUIRE_ENCRYPTION'):
- # but we don't want plaintexts
- raise UnencryptedMessage(message.msg)
-
- raise NotOTRMessage(message.msg)
-
- return IGN
-
- if isinstance(message, proto.AKEMessage):
- self.crypto.handleAKE(message, appdata=appdata)
- return IGN
-
- if isinstance(message, proto.DataMessage):
- ignore = message.flags & proto.MSGFLAGS_IGNORE_UNREADABLE
-
- if self.state != STATE_ENCRYPTED:
- self.sendInternal(proto.Error(
- 'You sent encrypted to {user}, who wasn\'t expecting it.'
- .format(user=self.user.name).encode('utf-8')), appdata=appdata)
- if ignore:
- return IGN
- raise NotEncryptedError(EXC_UNREADABLE_MESSAGE)
-
- try:
- plaintext, tlvs = self.crypto.handleDataMessage(message)
- self.processTLVs(tlvs, appdata=appdata)
- if plaintext and self.lastSend < time() - HEARTBEAT_INTERVAL:
- self.sendInternal(b'', appdata=appdata)
- return plaintext or None, tlvs
- except crypt.InvalidParameterError:
- if ignore:
- return IGN
- logger.exception('decryption failed')
- raise
- if isinstance(message, basestring):
- if self.state != STATE_PLAINTEXT or \
- self.getPolicy('REQUIRE_ENCRYPTION'):
- raise UnencryptedMessage(message)
-
- if isinstance(message, proto.Error):
- raise ErrorReceived(message)
-
- raise NotOTRMessage(messageData)
-
- def sendInternal(self, msg, tlvs=[], appdata=None):
- self.sendMessage(FRAGMENT_SEND_ALL, msg, tlvs=tlvs, appdata=appdata,
- flags=proto.MSGFLAGS_IGNORE_UNREADABLE)
-
- def sendMessage(self, sendPolicy, msg, flags=0, tlvs=[], appdata=None):
- if self.policyOtrEnabled():
- self.lastSend = time()
-
- if isinstance(msg, proto.OTRMessage):
- # we want to send a protocol message (probably internal)
- # so we don't need further protocol encryption
- # also we can't add TLVs to arbitrary protocol messages
- if tlvs:
- raise TypeError('can\'t add tlvs to protocol message')
- else:
- # we got plaintext to send. encrypt it
- msg = self.processOutgoingMessage(msg, flags, tlvs)
-
- if isinstance(msg, proto.OTRMessage) \
- and not isinstance(msg, proto.Query):
- # if it's a query message, it must not get fragmented
- return self.sendFragmented(bytes(msg), policy=sendPolicy, appdata=appdata)
- else:
- msg = bytes(msg)
- return msg
-
- def processOutgoingMessage(self, msg, flags, tlvs=[]):
- isQuery = self.parseExplicitQuery(msg) is not None
- if isQuery:
- return self.user.getDefaultQueryMessage(self.getPolicy)
-
- if self.state == STATE_PLAINTEXT:
- if self.getPolicy('REQUIRE_ENCRYPTION'):
- if not isQuery:
- self.lastMessage = msg
- self.lastSend = time()
- self.mayRetransmit = 2
- # TODO notify
- msg = self.user.getDefaultQueryMessage(self.getPolicy)
- return msg
- if self.getPolicy('SEND_TAG') and self.tagOffer != OFFER_REJECTED:
- self.tagOffer = OFFER_SENT
- versions = set()
- if self.getPolicy('ALLOW_V1'):
- versions.add(1)
- if self.getPolicy('ALLOW_V2'):
- versions.add(2)
- return proto.TaggedPlaintext(msg, versions)
- return msg
- if self.state == STATE_ENCRYPTED:
- msg = self.crypto.createDataMessage(msg, flags, tlvs)
- self.lastSend = time()
- return msg
- if self.state == STATE_FINISHED:
- raise NotEncryptedError(EXC_FINISHED)
-
- def disconnect(self, appdata=None):
- if self.state != STATE_FINISHED:
- self.sendInternal(b'', tlvs=[proto.DisconnectTLV()], appdata=appdata)
- self.setState(STATE_PLAINTEXT)
- self.crypto.finished()
- else:
- self.setState(STATE_PLAINTEXT)
-
- def setState(self, newstate):
- self.state = newstate
-
- def _wentEncrypted(self):
- self.setState(STATE_ENCRYPTED)
-
- def sendFragmented(self, msg, policy=FRAGMENT_SEND_ALL, appdata=None):
- mms = self.maxMessageSize(appdata)
- msgLen = len(msg)
- if mms != 0 and msgLen > mms:
- fms = mms - 19
- fragments = [ msg[i:i+fms] for i in range(0, msgLen, fms) ]
-
- fc = len(fragments)
-
- if fc > 65535:
- raise OverflowError('too many fragments')
-
- for fi in range(len(fragments)):
- ctr = unicode(fi+1) + ',' + unicode(fc) + ','
- fragments[fi] = b'?OTR,' + ctr.encode('ascii') \
- + fragments[fi] + b','
-
- if policy == FRAGMENT_SEND_ALL:
- for f in fragments:
- self.inject(f, appdata=appdata)
- return None
- elif policy == FRAGMENT_SEND_ALL_BUT_FIRST:
- for f in fragments[1:]:
- self.inject(f, appdata=appdata)
- return fragments[0]
- elif policy == FRAGMENT_SEND_ALL_BUT_LAST:
- for f in fragments[:-1]:
- self.inject(f, appdata=appdata)
- return fragments[-1]
-
- else:
- if policy == FRAGMENT_SEND_ALL:
- self.inject(msg, appdata=appdata)
- return None
- else:
- return msg
-
- def processTLVs(self, tlvs, appdata=None):
- for tlv in tlvs:
- if isinstance(tlv, proto.DisconnectTLV):
- logger.info('got disconnect tlv, forcing finished state')
- self.setState(STATE_FINISHED)
- self.crypto.finished()
- # TODO cleanup
- continue
- if isinstance(tlv, proto.SMPTLV):
- self.crypto.smpHandle(tlv, appdata=appdata)
- continue
- logger.info('got unhandled tlv: {0!r}'.format(tlv))
-
- def smpAbort(self, appdata=None):
- if self.state != STATE_ENCRYPTED:
- raise NotEncryptedError
- self.crypto.smpAbort(appdata=appdata)
-
- def smpIsValid(self):
- return self.crypto.smp and self.crypto.smp.prog != crypt.SMPPROG_CHEATED
-
- def smpIsSuccess(self):
- return self.crypto.smp.prog == crypt.SMPPROG_SUCCEEDED \
- if self.crypto.smp else None
-
- def smpGotSecret(self, secret, question=None, appdata=None):
- if self.state != STATE_ENCRYPTED:
- raise NotEncryptedError
- self.crypto.smpSecret(secret, question=question, appdata=appdata)
-
- def smpInit(self, secret, question=None, appdata=None):
- if self.state != STATE_ENCRYPTED:
- raise NotEncryptedError
- self.crypto.smp = None
- self.crypto.smpSecret(secret, question=question, appdata=appdata)
-
- def handleQuery(self, message, appdata=None):
- if 2 in message.versions and self.getPolicy('ALLOW_V2'):
- self.authStartV2(appdata=appdata)
- elif 1 in message.versions and self.getPolicy('ALLOW_V1'):
- self.authStartV1(appdata=appdata)
-
- def authStartV1(self, appdata=None):
- raise NotImplementedError()
-
- def authStartV2(self, appdata=None):
- self.crypto.startAKE(appdata=appdata)
-
- def parseExplicitQuery(self, message):
- otrTagPos = message.find(proto.OTRTAG)
-
- if otrTagPos == -1:
- return None
-
- indexBase = otrTagPos + len(proto.OTRTAG)
-
- if len(message) <= indexBase:
- return None
-
- compare = message[indexBase]
-
- hasq = compare == b'?'[0]
- hasv = compare == b'v'[0]
-
- if not hasq and not hasv:
- return None
-
- hasv |= len(message) > indexBase+1 and message[indexBase+1] == b'v'[0]
- if hasv:
- end = message.find(b'?', indexBase+1)
- else:
- end = indexBase+1
- return message[indexBase:end]
-
- def parse(self, message, nofragment=False):
- otrTagPos = message.find(proto.OTRTAG)
- if otrTagPos == -1:
- if proto.MESSAGE_TAG_BASE in message:
- return proto.TaggedPlaintext.parse(message)
- else:
- return message
-
- indexBase = otrTagPos + len(proto.OTRTAG)
-
- if len(message) <= indexBase:
- return message
-
- compare = message[indexBase]
-
- if nofragment is False and compare == b','[0]:
- message = self.fragmentAccumulate(message[indexBase:])
- if message is None:
- return None
- else:
- return self.parse(message, nofragment=True)
- else:
- self.discardFragment()
-
- queryPayload = self.parseExplicitQuery(message)
- if queryPayload is not None:
- return proto.Query.parse(queryPayload)
-
- if compare == b':'[0] and len(message) > indexBase + 4:
- try:
- infoTag = base64.b64decode(message[indexBase+1:indexBase+5])
- classInfo = struct.unpack(b'!HB', infoTag)
-
- cls = proto.messageClasses.get(classInfo, None)
- if cls is None:
- return message
-
- logger.debug('{user} got msg {typ!r}' \
- .format(user=self.user.name, typ=cls))
- return cls.parsePayload(message[indexBase+5:])
- except (TypeError, struct.error):
- logger.exception('could not parse OTR message %s', message)
- return message
-
- if message[indexBase:indexBase+7] == b' Error:':
- return proto.Error(message[indexBase+7:])
-
- return message
-
- def maxMessageSize(self, appdata=None):
- """Return the max message size for this context."""
- return self.user.maxMessageSize
-
- def getExtraKey(self, extraKeyAppId=None, extraKeyAppData=None, appdata=None):
- """ retrieves the generated extra symmetric key.
-
- if extraKeyAppId is set, notifies the chat partner about intended
- usage (additional application specific information can be supplied in
- extraKeyAppData).
-
- returns the 256 bit symmetric key """
-
- if self.state != STATE_ENCRYPTED:
- raise NotEncryptedError
- if extraKeyAppId is not None:
- tlvs = [proto.ExtraKeyTLV(extraKeyAppId, extraKeyAppData)]
- self.sendInternal(b'', tlvs=tlvs, appdata=appdata)
- return self.crypto.extraKey
-
-class Account(object):
- contextclass = Context
- def __init__(self, name, protocol, maxMessageSize, privkey=None):
- self.name = name
- self.privkey = privkey
- self.policy = {}
- self.protocol = protocol
- self.ctxs = {}
- self.trusts = {}
- self.maxMessageSize = maxMessageSize
- self.defaultQuery = '?OTRv{versions}?\n{accountname} has requested ' \
- 'an Off-the-Record private conversation. However, you ' \
- 'do not have a plugin to support that.\nSee '\
- 'http://otr.cypherpunks.ca/ for more information.'
-
- def __repr__(self):
- return '<{cls}(name={name!r})>'.format(cls=self.__class__.__name__,
- name=self.name)
-
- def getPrivkey(self, autogen=True):
- if self.privkey is None:
- self.privkey = self.loadPrivkey()
- if self.privkey is None:
- if autogen is True:
- self.privkey = compatcrypto.generateDefaultKey()
- self.savePrivkey()
- else:
- raise LookupError
- return self.privkey
-
- def loadPrivkey(self):
- raise NotImplementedError
-
- def savePrivkey(self):
- raise NotImplementedError
-
- def saveTrusts(self):
- raise NotImplementedError
-
- def getContext(self, uid, newCtxCb=None):
- if uid not in self.ctxs:
- self.ctxs[uid] = self.contextclass(self, uid)
- if callable(newCtxCb):
- newCtxCb(self.ctxs[uid])
- return self.ctxs[uid]
-
- def getDefaultQueryMessage(self, policy):
- v = '2' if policy('ALLOW_V2') else ''
- msg = self.defaultQuery.format(accountname=self.name, versions=v)
- return msg.encode('ascii')
-
- def setTrust(self, key, fingerprint, trustLevel):
- if key not in self.trusts:
- self.trusts[key] = {}
- self.trusts[key][fingerprint] = trustLevel
- self.saveTrusts()
-
- def getTrust(self, key, fingerprint, default=None):
- if key not in self.trusts:
- return default
- return self.trusts[key].get(fingerprint, default)
-
- def removeFingerprint(self, key, fingerprint):
- if key in self.trusts and fingerprint in self.trusts[key]:
- del self.trusts[key][fingerprint]
-
-class NotEncryptedError(RuntimeError):
- pass
-class UnencryptedMessage(RuntimeError):
- pass
-class ErrorReceived(RuntimeError):
- pass
-class NotOTRMessage(RuntimeError):
- pass
diff --git a/gotr/potr/crypt.py b/gotr/potr/crypt.py
deleted file mode 100644
index 3a4bb4b..0000000
--- a/gotr/potr/crypt.py
+++ /dev/null
@@ -1,795 +0,0 @@
-# Copyright 2011-2012 Kjell Braden <afflux@pentabarf.de>
-#
-# This file is part of the python-potr library.
-#
-# python-potr is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# any later version.
-#
-# python-potr 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-# some python3 compatibilty
-from __future__ import unicode_literals
-
-import logging
-import struct
-
-
-from potr.compatcrypto import SHA256, SHA1, SHA1HMAC, SHA256HMAC, \
- SHA256HMAC160, Counter, AESCTR, PK, random
-from potr.utils import bytes_to_long, long_to_bytes, pack_mpi, read_mpi
-from potr import proto
-
-logger = logging.getLogger(__name__)
-
-STATE_NONE = 0
-STATE_AWAITING_DHKEY = 1
-STATE_AWAITING_REVEALSIG = 2
-STATE_AWAITING_SIG = 4
-STATE_V1_SETUP = 5
-
-
-DH_MODULUS = 2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919
-DH_MODULUS_2 = DH_MODULUS-2
-DH_GENERATOR = 2
-DH_BITS = 1536
-DH_MAX = 2**DH_BITS
-SM_ORDER = (DH_MODULUS - 1) // 2
-
-def check_group(n):
- return 2 <= n <= DH_MODULUS_2
-def check_exp(n):
- return 1 <= n < SM_ORDER
-
-class DH(object):
- @classmethod
- def set_params(cls, prime, gen):
- cls.prime = prime
- cls.gen = gen
-
- def __init__(self):
- self.priv = random.randrange(2, 2**320)
- self.pub = pow(self.gen, self.priv, self.prime)
-
-DH.set_params(DH_MODULUS, DH_GENERATOR)
-
-class DHSession(object):
- def __init__(self, sendenc, sendmac, rcvenc, rcvmac):
- self.sendenc = sendenc
- self.sendmac = sendmac
- self.rcvenc = rcvenc
- self.rcvmac = rcvmac
- self.sendctr = Counter(0)
- self.rcvctr = Counter(0)
- self.sendmacused = False
- self.rcvmacused = False
-
- def __repr__(self):
- return '<{cls}(send={s!r},rcv={r!r})>' \
- .format(cls=self.__class__.__name__,
- s=self.sendmac, r=self.rcvmac)
-
- @classmethod
- def create(cls, dh, y):
- s = pow(y, dh.priv, DH_MODULUS)
- sb = pack_mpi(s)
-
- if dh.pub > y:
- sendbyte = b'\1'
- rcvbyte = b'\2'
- else:
- sendbyte = b'\2'
- rcvbyte = b'\1'
-
- sendenc = SHA1(sendbyte + sb)[:16]
- sendmac = SHA1(sendenc)
- rcvenc = SHA1(rcvbyte + sb)[:16]
- rcvmac = SHA1(rcvenc)
- return cls(sendenc, sendmac, rcvenc, rcvmac)
-
-class CryptEngine(object):
- def __init__(self, ctx):
- self.ctx = ctx
- self.ake = None
-
- self.sessionId = None
- self.sessionIdHalf = False
- self.theirKeyid = 0
- self.theirY = None
- self.theirOldY = None
-
- self.ourOldDHKey = None
- self.ourDHKey = None
- self.ourKeyid = 0
-
- self.sessionkeys = {0:{0:None, 1:None}, 1:{0:None, 1:None}}
- self.theirPubkey = None
- self.savedMacKeys = []
-
- self.smp = None
- self.extraKey = None
-
- def revealMacs(self, ours=True):
- if ours:
- dhs = self.sessionkeys[1].values()
- else:
- dhs = ( v[1] for v in self.sessionkeys.values() )
- for v in dhs:
- if v is not None:
- if v.rcvmacused:
- self.savedMacKeys.append(v.rcvmac)
- if v.sendmacused:
- self.savedMacKeys.append(v.sendmac)
-
- def rotateDHKeys(self):
- self.revealMacs(ours=True)
- self.ourOldDHKey = self.ourDHKey
- self.sessionkeys[1] = self.sessionkeys[0].copy()
- self.ourDHKey = DH()
- self.ourKeyid += 1
-
- self.sessionkeys[0][0] = None if self.theirY is None else \
- DHSession.create(self.ourDHKey, self.theirY)
- self.sessionkeys[0][1] = None if self.theirOldY is None else \
- DHSession.create(self.ourDHKey, self.theirOldY)
-
- logger.debug('{0}: Refreshing ourkey to {1} {2}'.format(
- self.ctx.user.name, self.ourKeyid, self.sessionkeys))
-
- def rotateYKeys(self, new_y):
- self.theirOldY = self.theirY
- self.revealMacs(ours=False)
- self.sessionkeys[0][1] = self.sessionkeys[0][0]
- self.sessionkeys[1][1] = self.sessionkeys[1][0]
- self.theirY = new_y
- self.theirKeyid += 1
-
- self.sessionkeys[0][0] = DHSession.create(self.ourDHKey, self.theirY)
- self.sessionkeys[1][0] = DHSession.create(self.ourOldDHKey, self.theirY)
-
- logger.debug('{0}: Refreshing theirkey to {1} {2}'.format(
- self.ctx.user.name, self.theirKeyid, self.sessionkeys))
-
- def handleDataMessage(self, msg):
- if self.saneKeyIds(msg) is False:
- raise InvalidParameterError
-
- sesskey = self.sessionkeys[self.ourKeyid - msg.rkeyid] \
- [self.theirKeyid - msg.skeyid]
-
- logger.debug('sesskeys: {0!r}, our={1}, r={2}, their={3}, s={4}' \
- .format(self.sessionkeys, self.ourKeyid, msg.rkeyid,
- self.theirKeyid, msg.skeyid))
-
- if msg.mac != SHA1HMAC(sesskey.rcvmac, msg.getMacedData()):
- logger.error('HMACs don\'t match')
- raise InvalidParameterError
- sesskey.rcvmacused = True
-
- newCtrPrefix = bytes_to_long(msg.ctr)
- if newCtrPrefix <= sesskey.rcvctr.prefix:
- logger.error('CTR must increase (old %r, new %r)',
- sesskey.rcvctr.prefix, newCtrPrefix)
- raise InvalidParameterError
-
- sesskey.rcvctr.prefix = newCtrPrefix
-
- logger.debug('handle: enc={0!r} mac={1!r} ctr={2!r}' \
- .format(sesskey.rcvenc, sesskey.rcvmac, sesskey.rcvctr))
-
- plaintextData = AESCTR(sesskey.rcvenc, sesskey.rcvctr) \
- .decrypt(msg.encmsg)
-
- if b'\0' in plaintextData:
- plaintext, tlvData = plaintextData.split(b'\0', 1)
- tlvs = proto.TLV.parse(tlvData)
- else:
- plaintext = plaintextData
- tlvs = []
-
- if msg.rkeyid == self.ourKeyid:
- self.rotateDHKeys()
- if msg.skeyid == self.theirKeyid:
- self.rotateYKeys(bytes_to_long(msg.dhy))
-
- return plaintext, tlvs
-
- def smpSecret(self, secret, question=None, appdata=None):
- if self.smp is None:
- logger.debug('Creating SMPHandler')
- self.smp = SMPHandler(self)
-
- self.smp.gotSecret(secret, question=question, appdata=appdata)
-
- def smpHandle(self, tlv, appdata=None):
- if self.smp is None:
- logger.debug('Creating SMPHandler')
- self.smp = SMPHandler(self)
- self.smp.handle(tlv, appdata=appdata)
-
- def smpAbort(self, appdata=None):
- if self.smp is None:
- logger.debug('Creating SMPHandler')
- self.smp = SMPHandler(self)
- self.smp.abort(appdata=appdata)
-
- def createDataMessage(self, message, flags=0, tlvs=None):
- # check MSGSTATE
- if self.theirKeyid == 0:
- raise InvalidParameterError
-
- if tlvs is None:
- tlvs = []
-
- sess = self.sessionkeys[1][0]
- sess.sendctr.inc()
-
- logger.debug('create: enc={0!r} mac={1!r} ctr={2!r}' \
- .format(sess.sendenc, sess.sendmac, sess.sendctr))
-
- # plaintext + TLVS
- plainBuf = message + b'\0' + b''.join([ bytes(t) for t in tlvs])
- encmsg = AESCTR(sess.sendenc, sess.sendctr).encrypt(plainBuf)
-
- msg = proto.DataMessage(flags, self.ourKeyid-1, self.theirKeyid,
- long_to_bytes(self.ourDHKey.pub), sess.sendctr.byteprefix(),
- encmsg, b'', b''.join(self.savedMacKeys))
- msg.mac = SHA1HMAC(sess.sendmac, msg.getMacedData())
- return msg
-
- def saneKeyIds(self, msg):
- anyzero = self.theirKeyid == 0 or msg.skeyid == 0 or msg.rkeyid == 0
- if anyzero or (msg.skeyid != self.theirKeyid and \
- msg.skeyid != self.theirKeyid - 1) or \
- (msg.rkeyid != self.ourKeyid and msg.rkeyid != self.ourKeyid - 1):
- return False
- if self.theirOldY is None and msg.skeyid == self.theirKeyid - 1:
- return False
- return True
-
- def startAKE(self, appdata=None):
- self.ake = AuthKeyExchange(self.ctx.user.getPrivkey(), self.goEncrypted)
- outMsg = self.ake.startAKE()
- self.ctx.sendInternal(outMsg, appdata=appdata)
-
- def handleAKE(self, inMsg, appdata=None):
- outMsg = None
-
- if not self.ctx.getPolicy('ALLOW_V2'):
- return
-
- if isinstance(inMsg, proto.DHCommit):
- if self.ake is None or self.ake.state != STATE_AWAITING_REVEALSIG:
- self.ake = AuthKeyExchange(self.ctx.user.getPrivkey(),
- self.goEncrypted)
- outMsg = self.ake.handleDHCommit(inMsg)
-
- elif isinstance(inMsg, proto.DHKey):
- if self.ake is None:
- return # ignore
- outMsg = self.ake.handleDHKey(inMsg)
-
- elif isinstance(inMsg, proto.RevealSig):
- if self.ake is None:
- return # ignore
- outMsg = self.ake.handleRevealSig(inMsg)
-
- elif isinstance(inMsg, proto.Signature):
- if self.ake is None:
- return # ignore
- self.ake.handleSignature(inMsg)
-
- if outMsg is not None:
- self.ctx.sendInternal(outMsg, appdata=appdata)
-
- def goEncrypted(self, ake):
- if ake.dh.pub == ake.gy:
- logger.warning('We are receiving our own messages')
- raise InvalidParameterError
-
- # TODO handle new fingerprint
- self.theirPubkey = ake.theirPubkey
-
- self.sessionId = ake.sessionId
- self.sessionIdHalf = ake.sessionIdHalf
- self.theirKeyid = ake.theirKeyid
- self.ourKeyid = ake.ourKeyid
- self.theirY = ake.gy
- self.theirOldY = None
- self.extraKey = ake.extraKey
-
- if self.ourKeyid != ake.ourKeyid + 1 or self.ourOldDHKey != ake.dh.pub:
- self.ourDHKey = ake.dh
- self.sessionkeys[0][0] = DHSession.create(self.ourDHKey, self.theirY)
- self.rotateDHKeys()
-
- # we don't need the AKE anymore, free the reference
- self.ake = None
-
- self.ctx._wentEncrypted()
- logger.info('went encrypted with {0}'.format(self.theirPubkey))
-
- def finished(self):
- self.smp = None
-
-class AuthKeyExchange(object):
- def __init__(self, privkey, onSuccess):
- self.privkey = privkey
- self.state = STATE_NONE
- self.r = None
- self.encgx = None
- self.hashgx = None
- self.ourKeyid = 1
- self.theirPubkey = None
- self.theirKeyid = 1
- self.enc_c = None
- self.enc_cp = None
- self.mac_m1 = None
- self.mac_m1p = None
- self.mac_m2 = None
- self.mac_m2p = None
- self.sessionId = None
- self.sessionIdHalf = False
- self.dh = DH()
- self.onSuccess = onSuccess
- self.gy = None
- self.extraKey = None
- self.lastmsg = None
-
- def startAKE(self):
- self.r = long_to_bytes(random.getrandbits(128))
-
- gxmpi = pack_mpi(self.dh.pub)
-
- self.hashgx = SHA256(gxmpi)
- self.encgx = AESCTR(self.r).encrypt(gxmpi)
-
- self.state = STATE_AWAITING_DHKEY
-
- return proto.DHCommit(self.encgx, self.hashgx)
-
- def handleDHCommit(self, msg):
- self.encgx = msg.encgx
- self.hashgx = msg.hashgx
-
- self.state = STATE_AWAITING_REVEALSIG
- return proto.DHKey(long_to_bytes(self.dh.pub))
-
- def handleDHKey(self, msg):
- if self.state == STATE_AWAITING_DHKEY:
- self.gy = bytes_to_long(msg.gy)
-
- # check 2 <= g**y <= p-2
- if not check_group(self.gy):
- logger.error('Invalid g**y received: %r', self.gy)
- return
-
- self.createAuthKeys()
-
- aesxb = self.calculatePubkeyAuth(self.enc_c, self.mac_m1)
-
- self.state = STATE_AWAITING_SIG
-
- self.lastmsg = proto.RevealSig(self.r, aesxb, b'')
- self.lastmsg.mac = SHA256HMAC160(self.mac_m2,
- self.lastmsg.getMacedData())
- return self.lastmsg
-
- elif self.state == STATE_AWAITING_SIG:
- logger.info('received DHKey while not awaiting DHKEY')
- if msg.gy == self.gy:
- logger.info('resending revealsig')
- return self.lastmsg
- else:
- logger.info('bad state for DHKey')
-
- def handleRevealSig(self, msg):
- if self.state != STATE_AWAITING_REVEALSIG:
- logger.error('bad state for RevealSig')
- raise InvalidParameterError
-
- self.r = msg.rkey
- gxmpi = AESCTR(self.r).decrypt(self.encgx)
- if SHA256(gxmpi) != self.hashgx:
- logger.error('Hashes don\'t match')
- logger.info('r=%r, hashgx=%r, computed hash=%r, gxmpi=%r',
- self.r, self.hashgx, SHA256(gxmpi), gxmpi)
- raise InvalidParameterError
-
- self.gy = read_mpi(gxmpi)[0]
- self.createAuthKeys()
-
- if msg.mac != SHA256HMAC160(self.mac_m2, msg.getMacedData()):
- logger.error('HMACs don\'t match')
- logger.info('mac=%r, mac_m2=%r, data=%r', msg.mac, self.mac_m2,
- msg.getMacedData())
- raise InvalidParameterError
-
- self.checkPubkeyAuth(self.enc_c, self.mac_m1, msg.encsig)
-
- aesxb = self.calculatePubkeyAuth(self.enc_cp, self.mac_m1p)
- self.sessionIdHalf = True
-
- self.onSuccess(self)
-
- self.ourKeyid = 0
- self.state = STATE_NONE
-
- cmpmac = struct.pack(b'!I', len(aesxb)) + aesxb
-
- return proto.Signature(aesxb, SHA256HMAC160(self.mac_m2p, cmpmac))
-
- def handleSignature(self, msg):
- if self.state != STATE_AWAITING_SIG:
- logger.error('bad state (%d) for Signature', self.state)
- raise InvalidParameterError
-
- if msg.mac != SHA256HMAC160(self.mac_m2p, msg.getMacedData()):
- logger.error('HMACs don\'t match')
- raise InvalidParameterError
-
- self.checkPubkeyAuth(self.enc_cp, self.mac_m1p, msg.encsig)
-
- self.sessionIdHalf = False
-
- self.onSuccess(self)
-
- self.ourKeyid = 0
- self.state = STATE_NONE
-
- def createAuthKeys(self):
- s = pow(self.gy, self.dh.priv, DH_MODULUS)
- sbyte = pack_mpi(s)
- self.sessionId = SHA256(b'\x00' + sbyte)[:8]
- enc = SHA256(b'\x01' + sbyte)
- self.enc_c = enc[:16]
- self.enc_cp = enc[16:]
- self.mac_m1 = SHA256(b'\x02' + sbyte)
- self.mac_m2 = SHA256(b'\x03' + sbyte)
- self.mac_m1p = SHA256(b'\x04' + sbyte)
- self.mac_m2p = SHA256(b'\x05' + sbyte)
- self.extraKey = SHA256(b'\xff' + sbyte)
-
- def calculatePubkeyAuth(self, key, mackey):
- pubkey = self.privkey.serializePublicKey()
- buf = pack_mpi(self.dh.pub)
- buf += pack_mpi(self.gy)
- buf += pubkey
- buf += struct.pack(b'!I', self.ourKeyid)
- MB = self.privkey.sign(SHA256HMAC(mackey, buf))
-
- buf = pubkey
- buf += struct.pack(b'!I', self.ourKeyid)
- buf += MB
- return AESCTR(key).encrypt(buf)
-
- def checkPubkeyAuth(self, key, mackey, encsig):
- auth = AESCTR(key).decrypt(encsig)
- self.theirPubkey, auth = PK.parsePublicKey(auth)
-
- receivedKeyid, auth = proto.unpack(b'!I', auth)
- if receivedKeyid == 0:
- raise InvalidParameterError
-
- authbuf = pack_mpi(self.gy)
- authbuf += pack_mpi(self.dh.pub)
- authbuf += self.theirPubkey.serializePublicKey()
- authbuf += struct.pack(b'!I', receivedKeyid)
-
- if self.theirPubkey.verify(SHA256HMAC(mackey, authbuf), auth) is False:
- raise InvalidParameterError
- self.theirKeyid = receivedKeyid
-
-SMPPROG_OK = 0
-SMPPROG_CHEATED = -2
-SMPPROG_FAILED = -1
-SMPPROG_SUCCEEDED = 1
-
-class SMPHandler:
- def __init__(self, crypto):
- self.crypto = crypto
- self.state = 1
- self.g1 = DH_GENERATOR
- self.g2 = None
- self.g3 = None
- self.g3o = None
- self.x2 = None
- self.x3 = None
- self.prog = SMPPROG_OK
- self.pab = None
- self.qab = None
- self.questionReceived = False
- self.secret = None
- self.p = None
- self.q = None
-
- def abort(self, appdata=None):
- self.state = 1
- self.sendTLV(proto.SMPABORTTLV(), appdata=appdata)
-
- def sendTLV(self, tlv, appdata=None):
- self.crypto.ctx.sendInternal(b'', tlvs=[tlv], appdata=appdata)
-
- def handle(self, tlv, appdata=None):
- logger.debug('handling TLV {0.__class__.__name__}'.format(tlv))
- self.prog = SMPPROG_CHEATED
- if isinstance(tlv, proto.SMPABORTTLV):
- self.state = 1
- return
- is1qTlv = isinstance(tlv, proto.SMP1QTLV)
- if isinstance(tlv, proto.SMP1TLV) or is1qTlv:
- if self.state != 1:
- self.abort(appdata=appdata)
- return
-
- msg = tlv.mpis
-
- if not check_group(msg[0]) or not check_group(msg[3]) \
- or not check_exp(msg[2]) or not check_exp(msg[5]) \
- or not check_known_log(msg[1], msg[2], self.g1, msg[0], 1) \
- or not check_known_log(msg[4], msg[5], self.g1, msg[3], 2):
- logger.error('invalid SMP1TLV received')
- self.abort(appdata=appdata)
- return
-
- self.questionReceived = is1qTlv
-
- self.g3o = msg[3]
-
- self.x2 = random.randrange(2, DH_MAX)
- self.x3 = random.randrange(2, DH_MAX)
-
- self.g2 = pow(msg[0], self.x2, DH_MODULUS)
- self.g3 = pow(msg[3], self.x3, DH_MODULUS)
-
- self.prog = SMPPROG_OK
- self.state = 0
- return
- if isinstance(tlv, proto.SMP2TLV):
- if self.state != 2:
- self.abort(appdata=appdata)
- return
-
- msg = tlv.mpis
- mp = msg[6]
- mq = msg[7]
-
- if not check_group(msg[0]) or not check_group(msg[3]) \
- or not check_group(msg[6]) or not check_group(msg[7]) \
- or not check_exp(msg[2]) or not check_exp(msg[5]) \
- or not check_exp(msg[9]) or not check_exp(msg[10]) \
- or not check_known_log(msg[1], msg[2], self.g1, msg[0], 3) \
- or not check_known_log(msg[4], msg[5], self.g1, msg[3], 4):
- logger.error('invalid SMP2TLV received')
- self.abort(appdata=appdata)
- return
-
- self.g3o = msg[3]
- self.g2 = pow(msg[0], self.x2, DH_MODULUS)
- self.g3 = pow(msg[3], self.x3, DH_MODULUS)
-
- if not self.check_equal_coords(msg[6:11], 5):
- logger.error('invalid SMP2TLV received')
- self.abort(appdata=appdata)
- return
-
- r = random.randrange(2, DH_MAX)
- self.p = pow(self.g3, r, DH_MODULUS)
- msg = [self.p]
- qa1 = pow(self.g1, r, DH_MODULUS)
- qa2 = pow(self.g2, self.secret, DH_MODULUS)
- self.q = qa1*qa2 % DH_MODULUS
- msg.append(self.q)
- msg += self.proof_equal_coords(r, 6)
-
- inv = invMod(mp)
- self.pab = self.p * inv % DH_MODULUS
- inv = invMod(mq)
- self.qab = self.q * inv % DH_MODULUS
-
- msg.append(pow(self.qab, self.x3, DH_MODULUS))
- msg += self.proof_equal_logs(7)
-
- self.state = 4
- self.prog = SMPPROG_OK
- self.sendTLV(proto.SMP3TLV(msg), appdata=appdata)
- return
- if isinstance(tlv, proto.SMP3TLV):
- if self.state != 3:
- self.abort(appdata=appdata)
- return
-
- msg = tlv.mpis
-
- if not check_group(msg[0]) or not check_group(msg[1]) \
- or not check_group(msg[5]) or not check_exp(msg[3]) \
- or not check_exp(msg[4]) or not check_exp(msg[7]) \
- or not self.check_equal_coords(msg[:5], 6):
- logger.error('invalid SMP3TLV received')
- self.abort(appdata=appdata)
- return
-
- inv = invMod(self.p)
- self.pab = msg[0] * inv % DH_MODULUS
- inv = invMod(self.q)
- self.qab = msg[1] * inv % DH_MODULUS
-
- if not self.check_equal_logs(msg[5:8], 7):
- logger.error('invalid SMP3TLV received')
- self.abort(appdata=appdata)
- return
-
- md = msg[5]
- msg = [pow(self.qab, self.x3, DH_MODULUS)]
- msg += self.proof_equal_logs(8)
-
- rab = pow(md, self.x3, DH_MODULUS)
- self.prog = SMPPROG_SUCCEEDED if self.pab == rab else SMPPROG_FAILED
-
- if self.prog != SMPPROG_SUCCEEDED:
- logger.error('secrets don\'t match')
- self.abort(appdata=appdata)
- self.crypto.ctx.setCurrentTrust('')
- return
-
- logger.info('secrets matched')
- if not self.questionReceived:
- self.crypto.ctx.setCurrentTrust('smp')
- self.state = 1
- self.sendTLV(proto.SMP4TLV(msg), appdata=appdata)
- return
- if isinstance(tlv, proto.SMP4TLV):
- if self.state != 4:
- self.abort(appdata=appdata)
- return
-
- msg = tlv.mpis
-
- if not check_group(msg[0]) or not check_exp(msg[2]) \
- or not self.check_equal_logs(msg[:3], 8):
- logger.error('invalid SMP4TLV received')
- self.abort(appdata=appdata)
- return
-
- rab = pow(msg[0], self.x3, DH_MODULUS)
-
- self.prog = SMPPROG_SUCCEEDED if self.pab == rab else SMPPROG_FAILED
-
- if self.prog != SMPPROG_SUCCEEDED:
- logger.error('secrets don\'t match')
- self.abort(appdata=appdata)
- self.crypto.ctx.setCurrentTrust('')
- return
-
- logger.info('secrets matched')
- self.crypto.ctx.setCurrentTrust('smp')
- self.state = 1
- return
-
- def gotSecret(self, secret, question=None, appdata=None):
- ourFP = self.crypto.ctx.user.getPrivkey().fingerprint()
- if self.state == 1:
- # first secret -> SMP1TLV
- combSecret = SHA256(b'\1' + ourFP +
- self.crypto.theirPubkey.fingerprint() +
- self.crypto.sessionId + secret)
-
- self.secret = bytes_to_long(combSecret)
-
- self.x2 = random.randrange(2, DH_MAX)
- self.x3 = random.randrange(2, DH_MAX)
-
- msg = [pow(self.g1, self.x2, DH_MODULUS)]
- msg += proof_known_log(self.g1, self.x2, 1)
- msg.append(pow(self.g1, self.x3, DH_MODULUS))
- msg += proof_known_log(self.g1, self.x3, 2)
-
- self.prog = SMPPROG_OK
- self.state = 2
- if question is None:
- self.sendTLV(proto.SMP1TLV(msg), appdata=appdata)
- else:
- self.sendTLV(proto.SMP1QTLV(question, msg), appdata=appdata)
- if self.state == 0:
- # response secret -> SMP2TLV
- combSecret = SHA256(b'\1' + self.crypto.theirPubkey.fingerprint() +
- ourFP + self.crypto.sessionId + secret)
-
- self.secret = bytes_to_long(combSecret)
-
- msg = [pow(self.g1, self.x2, DH_MODULUS)]
- msg += proof_known_log(self.g1, self.x2, 3)
- msg.append(pow(self.g1, self.x3, DH_MODULUS))
- msg += proof_known_log(self.g1, self.x3, 4)
-
- r = random.randrange(2, DH_MAX)
-
- self.p = pow(self.g3, r, DH_MODULUS)
- msg.append(self.p)
-
- qb1 = pow(self.g1, r, DH_MODULUS)
- qb2 = pow(self.g2, self.secret, DH_MODULUS)
- self.q = qb1 * qb2 % DH_MODULUS
- msg.append(self.q)
-
- msg += self.proof_equal_coords(r, 5)
-
- self.state = 3
- self.sendTLV(proto.SMP2TLV(msg), appdata=appdata)
-
- def proof_equal_coords(self, r, v):
- r1 = random.randrange(2, DH_MAX)
- r2 = random.randrange(2, DH_MAX)
- temp2 = pow(self.g1, r1, DH_MODULUS) \
- * pow(self.g2, r2, DH_MODULUS) % DH_MODULUS
- temp1 = pow(self.g3, r1, DH_MODULUS)
-
- cb = SHA256(struct.pack(b'B', v) + pack_mpi(temp1) + pack_mpi(temp2))
- c = bytes_to_long(cb)
-
- temp1 = r * c % SM_ORDER
- d1 = (r1-temp1) % SM_ORDER
-
- temp1 = self.secret * c % SM_ORDER
- d2 = (r2 - temp1) % SM_ORDER
- return c, d1, d2
-
- def check_equal_coords(self, coords, v):
- (p, q, c, d1, d2) = coords
- temp1 = pow(self.g3, d1, DH_MODULUS) * pow(p, c, DH_MODULUS) \
- % DH_MODULUS
-
- temp2 = pow(self.g1, d1, DH_MODULUS) \
- * pow(self.g2, d2, DH_MODULUS) \
- * pow(q, c, DH_MODULUS) % DH_MODULUS
-
- cprime = SHA256(struct.pack(b'B', v) + pack_mpi(temp1) + pack_mpi(temp2))
-
- return long_to_bytes(c, 32) == cprime
-
- def proof_equal_logs(self, v):
- r = random.randrange(2, DH_MAX)
- temp1 = pow(self.g1, r, DH_MODULUS)
- temp2 = pow(self.qab, r, DH_MODULUS)
-
- cb = SHA256(struct.pack(b'B', v) + pack_mpi(temp1) + pack_mpi(temp2))
- c = bytes_to_long(cb)
- temp1 = self.x3 * c % SM_ORDER
- d = (r - temp1) % SM_ORDER
- return c, d
-
- def check_equal_logs(self, logs, v):
- (r, c, d) = logs
- temp1 = pow(self.g1, d, DH_MODULUS) \
- * pow(self.g3o, c, DH_MODULUS) % DH_MODULUS
-
- temp2 = pow(self.qab, d, DH_MODULUS) \
- * pow(r, c, DH_MODULUS) % DH_MODULUS
-
- cprime = SHA256(struct.pack(b'B', v) + pack_mpi(temp1) + pack_mpi(temp2))
- return long_to_bytes(c, 32) == cprime
-
-def proof_known_log(g, x, v):
- r = random.randrange(2, DH_MAX)
- c = bytes_to_long(SHA256(struct.pack(b'B', v) + pack_mpi(pow(g, r, DH_MODULUS))))
- temp = x * c % SM_ORDER
- return c, (r-temp) % SM_ORDER
-
-def check_known_log(c, d, g, x, v):
- gd = pow(g, d, DH_MODULUS)
- xc = pow(x, c, DH_MODULUS)
- gdxc = gd * xc % DH_MODULUS
- return SHA256(struct.pack(b'B', v) + pack_mpi(gdxc)) == long_to_bytes(c, 32)
-
-def invMod(n):
- return pow(n, DH_MODULUS_2, DH_MODULUS)
-
-class InvalidParameterError(RuntimeError):
- pass
diff --git a/gotr/potr/proto.py b/gotr/potr/proto.py
deleted file mode 100644
index 91904a8..0000000
--- a/gotr/potr/proto.py
+++ /dev/null
@@ -1,465 +0,0 @@
-# Copyright 2011-2012 Kjell Braden <afflux@pentabarf.de>
-#
-# This file is part of the python-potr library.
-#
-# python-potr is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# any later version.
-#
-# python-potr 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-# some python3 compatibilty
-from __future__ import unicode_literals
-
-import base64
-import struct
-from potr.utils import pack_mpi, read_mpi, pack_data, read_data, unpack
-
-OTRTAG = b'?OTR'
-MESSAGE_TAG_BASE = b' \t \t\t\t\t \t \t \t '
-MESSAGE_TAGS = {
- 1:b' \t \t \t ',
- 2:b' \t\t \t ',
- 3:b' \t\t \t\t',
- }
-
-MSGTYPE_NOTOTR = 0
-MSGTYPE_TAGGEDPLAINTEXT = 1
-MSGTYPE_QUERY = 2
-MSGTYPE_DH_COMMIT = 3
-MSGTYPE_DH_KEY = 4
-MSGTYPE_REVEALSIG = 5
-MSGTYPE_SIGNATURE = 6
-MSGTYPE_V1_KEYEXCH = 7
-MSGTYPE_DATA = 8
-MSGTYPE_ERROR = 9
-MSGTYPE_UNKNOWN = -1
-
-MSGFLAGS_IGNORE_UNREADABLE = 1
-
-tlvClasses = {}
-messageClasses = {}
-
-hasByteStr = bytes == str
-def bytesAndStrings(cls):
- if hasByteStr:
- cls.__str__ = lambda self: self.__bytes__()
- else:
- cls.__str__ = lambda self: str(self.__bytes__(), encoding='ascii')
- return cls
-
-def registermessage(cls):
- if not hasattr(cls, 'parsePayload'):
- raise TypeError('registered message types need parsePayload()')
- messageClasses[cls.version, cls.msgtype] = cls
- return cls
-
-def registertlv(cls):
- if not hasattr(cls, 'parsePayload'):
- raise TypeError('registered tlv types need parsePayload()')
- if cls.typ is None:
- raise TypeError('registered tlv type needs type ID')
- tlvClasses[cls.typ] = cls
- return cls
-
-
-def getslots(cls, base):
- ''' helper to collect all the message slots from ancestors '''
- clss = [cls]
-
- for cls in clss:
- if cls == base:
- continue
-
- clss.extend(cls.__bases__)
-
- for slot in cls.__slots__:
- yield slot
-
-@bytesAndStrings
-class OTRMessage(object):
- __slots__ = ['payload']
- version = 0x0002
- msgtype = 0
-
- def __eq__(self, other):
- if not isinstance(other, self.__class__):
- return False
-
- for slot in getslots(self.__class__, OTRMessage):
- if getattr(self, slot) != getattr(other, slot):
- return False
- return True
-
- def __neq__(self, other):
- return not self.__eq__(other)
-
-class Error(OTRMessage):
- __slots__ = ['error']
- def __init__(self, error):
- super(Error, self).__init__()
- self.error = error
-
- def __repr__(self):
- return '<proto.Error(%r)>' % self.error
-
- def __bytes__(self):
- return b'?OTR Error:' + self.error
-
-class Query(OTRMessage):
- __slots__ = ['versions']
- def __init__(self, versions=set()):
- super(Query, self).__init__()
- self.versions = versions
-
- @classmethod
- def parse(cls, data):
- if not isinstance(data, bytes):
- raise TypeError('can only parse bytes')
- udata = data.decode('ascii', errors='replace')
-
- versions = set()
- if len(udata) > 0 and udata[0] == '?':
- udata = udata[1:]
- versions.add(1)
-
- if len(udata) > 0 and udata[0] == 'v':
- versions.update(( int(c) for c in udata if c.isdigit() ))
- return cls(versions)
-
- def __repr__(self):
- return '<proto.Query(versions=%r)>' % (self.versions)
-
- def __bytes__(self):
- d = b'?OTR'
- if 1 in self.versions:
- d += b'?'
- d += b'v'
-
- # in python3 there is only int->unicode conversion
- # so I convert to unicode and encode it to a byte string
- versions = [ '%d' % v for v in self.versions if v != 1 ]
- d += ''.join(versions).encode('ascii')
-
- d += b'?'
- return d
-
-class TaggedPlaintext(Query):
- __slots__ = ['msg']
- def __init__(self, msg, versions):
- super(TaggedPlaintext, self).__init__(versions)
- self.msg = msg
-
- def __bytes__(self):
- data = self.msg + MESSAGE_TAG_BASE
- for v in self.versions:
- data += MESSAGE_TAGS[v]
- return data
-
- def __repr__(self):
- return '<proto.TaggedPlaintext(versions={versions!r},msg={msg!r})>' \
- .format(versions=self.versions, msg=self.msg)
-
- @classmethod
- def parse(cls, data):
- tagPos = data.find(MESSAGE_TAG_BASE)
- if tagPos < 0:
- raise TypeError(
- 'this is not a tagged plaintext ({0!r:.20})'.format(data))
-
- tags = [ data[i:i+8] for i in range(tagPos, len(data), 8) ]
- versions = set([ version for version, tag in MESSAGE_TAGS.items() if tag
- in tags ])
-
- return TaggedPlaintext(data[:tagPos], versions)
-
-class GenericOTRMessage(OTRMessage):
- __slots__ = ['data']
- fields = []
-
- def __init__(self, *args):
- super(GenericOTRMessage, self).__init__()
- if len(args) != len(self.fields):
- raise TypeError('%s needs %d arguments, got %d' %
- (self.__class__.__name__, len(self.fields), len(args)))
-
- super(GenericOTRMessage, self).__setattr__('data',
- dict(zip((f[0] for f in self.fields), args)))
-
- def __getattr__(self, attr):
- if attr in self.data:
- return self.data[attr]
- raise AttributeError(
- "'{t!r}' object has no attribute '{attr!r}'".format(attr=attr,
- t=self.__class__.__name__))
-
- def __setattr__(self, attr, val):
- if attr in self.__slots__:
- super(GenericOTRMessage, self).__setattr__(attr, val)
- else:
- self.__getattr__(attr) # existence check
- self.data[attr] = val
-
- def __bytes__(self):
- data = struct.pack(b'!HB', self.version, self.msgtype) \
- + self.getPayload()
- return b'?OTR:' + base64.b64encode(data) + b'.'
-
- def __repr__(self):
- name = self.__class__.__name__
- data = ''
- for k, _ in self.fields:
- data += '%s=%r,' % (k, self.data[k])
- return '<proto.%s(%s)>' % (name, data)
-
- @classmethod
- def parsePayload(cls, data):
- data = base64.b64decode(data)
- args = []
- for _, ftype in cls.fields:
- if ftype == 'data':
- value, data = read_data(data)
- elif isinstance(ftype, bytes):
- value, data = unpack(ftype, data)
- elif isinstance(ftype, int):
- value, data = data[:ftype], data[ftype:]
- args.append(value)
- return cls(*args)
-
- def getPayload(self, *ffilter):
- payload = b''
- for k, ftype in self.fields:
- if k in ffilter:
- continue
-
- if ftype == 'data':
- payload += pack_data(self.data[k])
- elif isinstance(ftype, bytes):
- payload += struct.pack(ftype, self.data[k])
- else:
- payload += self.data[k]
- return payload
-
-class AKEMessage(GenericOTRMessage):
- __slots__ = []
-
-@registermessage
-class DHCommit(AKEMessage):
- __slots__ = []
- msgtype = 0x02
- fields = [('encgx', 'data'), ('hashgx', 'data'), ]
-
-@registermessage
-class DHKey(AKEMessage):
- __slots__ = []
- msgtype = 0x0a
- fields = [('gy', 'data'), ]
-
-@registermessage
-class RevealSig(AKEMessage):
- __slots__ = []
- msgtype = 0x11
- fields = [('rkey', 'data'), ('encsig', 'data'), ('mac', 20),]
-
- def getMacedData(self):
- p = self.encsig
- return struct.pack(b'!I', len(p)) + p
-
-@registermessage
-class Signature(AKEMessage):
- __slots__ = []
- msgtype = 0x12
- fields = [('encsig', 'data'), ('mac', 20)]
-
- def getMacedData(self):
- p = self.encsig
- return struct.pack(b'!I', len(p)) + p
-
-@registermessage
-class DataMessage(GenericOTRMessage):
- __slots__ = []
- msgtype = 0x03
- fields = [('flags', b'!B'), ('skeyid', b'!I'), ('rkeyid', b'!I'),
- ('dhy', 'data'), ('ctr', 8), ('encmsg', 'data'), ('mac', 20),
- ('oldmacs', 'data'), ]
-
- def getMacedData(self):
- return struct.pack(b'!HB', self.version, self.msgtype) + \
- self.getPayload('mac', 'oldmacs')
-
-@bytesAndStrings
-class TLV(object):
- __slots__ = []
- typ = None
-
- def getPayload(self):
- raise NotImplementedError
-
- def __repr__(self):
- val = self.getPayload()
- return '<{cls}(typ={t},len={l},val={v!r})>'.format(t=self.typ,
- l=len(val), v=val, cls=self.__class__.__name__)
-
- def __bytes__(self):
- val = self.getPayload()
- return struct.pack(b'!HH', self.typ, len(val)) + val
-
- @classmethod
- def parse(cls, data):
- if not data:
- return []
- typ, length, data = unpack(b'!HH', data)
- return [tlvClasses[typ].parsePayload(data[:length])] \
- + cls.parse(data[length:])
-
- def __eq__(self, other):
- if not isinstance(other, self.__class__):
- return False
-
- for slot in getslots(self.__class__, TLV):
- if getattr(self, slot) != getattr(other, slot):
- return False
- return True
-
- def __neq__(self, other):
- return not self.__eq__(other)
-
-@registertlv
-class PaddingTLV(TLV):
- typ = 0
-
- __slots__ = ['padding']
-
- def __init__(self, padding):
- super(PaddingTLV, self).__init__()
- self.padding = padding
-
- def getPayload(self):
- return self.padding
-
- @classmethod
- def parsePayload(cls, data):
- return cls(data)
-
-@registertlv
-class DisconnectTLV(TLV):
- typ = 1
- def __init__(self):
- super(DisconnectTLV, self).__init__()
-
- def getPayload(self):
- return b''
-
- @classmethod
- def parsePayload(cls, data):
- if len(data) > 0:
- raise TypeError('DisconnectTLV must not contain data. got {0!r}'
- .format(data))
- return cls()
-
-class SMPTLV(TLV):
- __slots__ = ['mpis']
- dlen = None
-
- def __init__(self, mpis=None):
- super(SMPTLV, self).__init__()
- if mpis is None:
- mpis = []
- if self.dlen is None:
- raise TypeError('no amount of mpis specified in dlen')
- if len(mpis) != self.dlen:
- raise TypeError('expected {0} mpis, got {1}'
- .format(self.dlen, len(mpis)))
- self.mpis = mpis
-
- def getPayload(self):
- d = struct.pack(b'!I', len(self.mpis))
- for n in self.mpis:
- d += pack_mpi(n)
- return d
-
- @classmethod
- def parsePayload(cls, data):
- mpis = []
- if cls.dlen > 0:
- count, data = unpack(b'!I', data)
- for _ in range(count):
- n, data = read_mpi(data)
- mpis.append(n)
- if len(data) > 0:
- raise TypeError('too much data for {0} mpis'.format(cls.dlen))
- return cls(mpis)
-
-@registertlv
-class SMP1TLV(SMPTLV):
- typ = 2
- dlen = 6
-
-@registertlv
-class SMP1QTLV(SMPTLV):
- typ = 7
- dlen = 6
- __slots__ = ['msg']
-
- def __init__(self, msg, mpis):
- self.msg = msg
- super(SMP1QTLV, self).__init__(mpis)
-
- def getPayload(self):
- return self.msg + b'\0' + super(SMP1QTLV, self).getPayload()
-
- @classmethod
- def parsePayload(cls, data):
- msg, data = data.split(b'\0', 1)
- mpis = SMP1TLV.parsePayload(data).mpis
- return cls(msg, mpis)
-
-@registertlv
-class SMP2TLV(SMPTLV):
- typ = 3
- dlen = 11
-
-@registertlv
-class SMP3TLV(SMPTLV):
- typ = 4
- dlen = 8
-
-@registertlv
-class SMP4TLV(SMPTLV):
- typ = 5
- dlen = 3
-
-@registertlv
-class SMPABORTTLV(SMPTLV):
- typ = 6
- dlen = 0
-
- def getPayload(self):
- return b''
-
-@registertlv
-class ExtraKeyTLV(TLV):
- typ = 8
-
- __slots__ = ['appid', 'appdata']
-
- def __init__(self, appid, appdata):
- super(ExtraKeyTLV, self).__init__()
- self.appid = appid
- self.appdata = appdata
- if appdata is None:
- self.appdata = b''
-
- def getPayload(self):
- return self.appid + self.appdata
-
- @classmethod
- def parsePayload(cls, data):
- return cls(data[:4], data[4:])
diff --git a/gotr/potr/utils.py b/gotr/potr/utils.py
deleted file mode 100644
index e41ca46..0000000
--- a/gotr/potr/utils.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2012 Kjell Braden <afflux@pentabarf.de>
-#
-# This file is part of the python-potr library.
-#
-# python-potr is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# any later version.
-#
-# python-potr 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-# some python3 compatibilty
-from __future__ import unicode_literals
-
-
-import struct
-
-def pack_mpi(n):
- return pack_data(long_to_bytes(n))
-def read_mpi(data):
- n, data = read_data(data)
- return bytes_to_long(n), data
-def pack_data(data):
- return struct.pack(b'!I', len(data)) + data
-def read_data(data):
- datalen, data = unpack(b'!I', data)
- return data[:datalen], data[datalen:]
-def unpack(fmt, buf):
- s = struct.Struct(fmt)
- return s.unpack(buf[:s.size]) + (buf[s.size:],)
-
-
-def bytes_to_long(b):
- l = len(b)
- s = 0
- for i in range(l):
- s += byte_to_long(b[i:i+1]) << 8*(l-i-1)
- return s
-
-def long_to_bytes(l, n=0):
- b = b''
- while l != 0 or n > 0:
- b = long_to_byte(l & 0xff) + b
- l >>= 8
- n -= 1
- return b
-
-def byte_to_long(b):
- return struct.unpack(b'B', b)[0]
-def long_to_byte(l):
- return struct.pack(b'B', l)
-
-def human_hash(fp):
- fp = fp.upper()
- fplen = len(fp)
- wordsize = fplen//5
- buf = ''
- for w in range(0, fplen, wordsize):
- buf += '{0} '.format(fp[w:w+wordsize])
- return buf.rstrip()
diff --git a/gotr/ui.py b/gotr/ui.py
deleted file mode 100644
index 31ce978..0000000
--- a/gotr/ui.py
+++ /dev/null
@@ -1,581 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-## ui.py
-##
-## Copyright 2008-2012 Kjell Braden <afflux@pentabarf.de>
-##
-## This file is part of Gajim.
-##
-## Gajim 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 3 only.
-##
-## Gajim 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.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-##
-import gobject
-import gtk
-from common import gajim
-from plugins.gui import GajimPluginConfigDialog
-
-import otrmodule
-try:
- import potr
- import potr.proto
-except ImportError:
- pass
-
-
-class OtrPluginConfigDialog(GajimPluginConfigDialog):
- def init(self):
- self.GTK_BUILDER_FILE_PATH = \
- self.plugin.local_file_path('config_dialog.ui')
- self.B = gtk.Builder()
- self.B.set_translation_domain('gajim_plugins')
- self.B.add_from_file(self.GTK_BUILDER_FILE_PATH)
-
- self.fpr_model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
- gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING,
- gobject.TYPE_STRING, gobject.TYPE_STRING)
-
- self.otr_account_store = self.B.get_object('account_store')
-
- for account in sorted(gajim.contacts.get_accounts()):
- self.otr_account_store.append(row=(account,))
-
- self.fpr_view = self.B.get_object('fingerprint_view')
- self.fpr_view.set_model(self.fpr_model)
- self.fpr_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
-
- if len(self.otr_account_store) > 0:
- self.B.get_object('account_combobox').set_active(0)
-
- self.child.pack_start(self.B.get_object('notebook1'))
-
- self.flags = dict()
- flagList = (
- ('ALLOW_V2', 'enable_check'),
- ('SEND_TAG', 'advertise_check'),
- ('WHITESPACE_START_AKE', 'autoinitiate_check'),
- ('REQUIRE_ENCRYPTION', 'require_check')
- )
- for flagName, checkBoxName in flagList:
- self.flags[flagName] = self.B.get_object(checkBoxName)
-
- self.B.connect_signals(self)
-
- def on_run(self):
- self.plugin.update_context_list()
- self.account_combobox_changed_cb(self.B.get_object('account_combobox'))
-
- def fpr_button_pressed_cb(self, tw, event):
- if event.button == 3:
- pthinfo = tw.get_path_at_pos(int(event.x), int(event.y))
-
- if pthinfo is None:
- # only show the popup when we right clicked on list content
- # ie. don't show it when we click at empty rows
- return False
-
- # if the row under the mouse is already selected, we keep the
- # selection, otherwise we only select the new item
- keep_selection = tw.get_selection().path_is_selected(pthinfo[0])
-
- pop = self.B.get_object('fprclipboard_menu')
- pop.popup(None, None, None, event.button, event.time)
-
- # keep_selection=True -> no further processing of click event
- # keep_selection=False-> further processing -> GTK usually selects
- # the item below the cursor
- return keep_selection
-
- def clipboard_button_cb(self, menuitem):
- mod, paths = self.fpr_view.get_selection().get_selected_rows()
-
- fprs = []
- for path in paths:
- it = mod.get_iter(path)
- jid, fpr = mod.get(it, 0, 6)
- fprs.append('%s: %s' % (jid, potr.human_hash(fpr)))
- gtk.Clipboard().set_text('\n'.join(fprs))
- gtk.Clipboard(selection='PRIMARY').set_text('\n'.join(fprs))
-
- def flags_toggled_cb(self, button):
- if button == self.B.get_object('enable_check'):
- new_status = button.get_active()
- self.B.get_object('advertise_check').set_sensitive(new_status)
- self.B.get_object('autoinitiate_check').set_sensitive(new_status)
- self.B.get_object('require_check').set_sensitive(new_status)
-
- if new_status is False:
- self.B.get_object('advertise_check').set_active(False)
- self.B.get_object('autoinitiate_check').set_active(False)
- self.B.get_object('require_check').set_active(False)
-
- box = self.B.get_object('account_combobox')
- active = box.get_active()
- if active > -1:
- account = self.otr_account_store[active][0]
-
- flagValues = {}
- for key, box in self.flags.iteritems():
- flagValues[key] = box.get_active()
- self.plugin.set_flags(flagValues, account)
-
- def account_combobox_changed_cb(self, box, *args):
- fpr_label = self.B.get_object('fingerprint_label')
- regen_button = self.B.get_object('regenerate_button')
-
- active = box.get_active()
- fpr = '-------- -------- -------- -------- --------'
- try:
- if active > -1:
- regen_button.set_sensitive(True)
- account = self.otr_account_store[active][0]
-
- otr_flags = self.plugin.get_flags(account)
- for key, box in self.flags.iteritems():
- box.set_active(otr_flags[key])
-
- fpr = str(self.plugin.us[account].getPrivkey(autogen=False))
- regen_button.set_label('Regenerate')
- else:
- regen_button.set_sensitive(False)
- except LookupError, e:
- # Account not found, no private key available - display the
- # empty one
- regen_button.set_label('Generate')
- finally:
- self.B.get_object('fingerprint_label').set_markup('<tt>%s</tt>'%fpr)
-
- def forget_button_clicked_cb(self, button, *args):
- accounts = {}
- for acc in gajim.connections.iterkeys():
- accounts[gajim.get_jid_from_account(acc)] = acc
-
-
- mod, paths = self.fpr_view.get_selection().get_selected_rows()
-
- for path in paths:
- it = mod.get_iter(path)
- user, human_fpr, a, fpr = mod.get(it, 0, 3, 4, 6)
-
- dlg = gtk.Dialog('Confirm removal of fingerprint', self,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_YES, gtk.RESPONSE_YES,
- gtk.STOCK_NO, gtk.RESPONSE_NO)
- )
- l = gtk.Label()
- l.set_markup('Are you sure you want remove the following '
- 'fingerprint for the contact %s on the account %s?'
- '\n\n%s' % (user, a, human_fpr))
- l.set_line_wrap(True)
- dlg.vbox.pack_start(l)
- dlg.show_all()
-
- if dlg.run() == gtk.RESPONSE_YES:
- ctx = self.plugin.us[accounts[a]].getContext(user)
- ctx.removeFingerprint(fpr)
- dlg.destroy()
- self.plugin.us[accounts[a]].saveTrusts()
-
- self.plugin.update_context_list()
-
- def verify_button_clicked_cb(self, button, *args):
- accounts = {}
- for acc in gajim.connections.iterkeys():
- accounts[gajim.get_jid_from_account(acc)] = acc
-
- mod, paths = self.fpr_view.get_selection().get_selected_rows()
-
- # open the window for the first selected row
- for path in paths[0:1]:
- it = mod.get_iter(path)
- fjid, fpr, a = mod.get(it, 0, 6, 4)
-
- ctx = self.plugin.us[accounts[a]].getContext(fjid)
-
- dlg = ContactOtrWindow(self.plugin, ctx, fpr=fpr, parent=self)
- dlg.run()
- dlg.destroy()
- break
-
- def regenerate_button_clicked_cb(self, button, *args):
- box = self.B.get_object('account_combobox')
- active = box.get_active()
- if active > -1:
- account = self.otr_account_store[active][0]
- button.set_sensitive(False)
- try:
- self.plugin.us[account].getPrivkey(autogen=False)
- self.plugin.us[account].dropPrivkey()
- except LookupError:
- pass
- self.plugin.us[account].getPrivkey(autogen=True)
- self.account_combobox_changed_cb(box, *args)
- button.set_sensitive(True)
-
-
-import gtkgui_helpers
-from common import gajim
-
-our_fp_text = _('Your fingerprint:\n' \
- '<span weight="bold" face="monospace">%s</span>')
-their_fp_text = _('Purported fingerprint for %(jid)s:\n' \
- '<span weight="bold" face="monospace">%(fp)s</span>')
-
-another_q = _('You may want to authenticate your buddy as well by asking'\
- 'your own question.')
-smp_query = _('<b>%s is trying to authenticate you using a secret only known '\
- 'to him/her and you.</b>')
-smp_q_query = _('<b>%s has chosen a question for you to answer to '\
- 'authenticate yourself:</b>')
-enter_secret = _('Please enter your secret below.')
-
-smp_init = _('<b>You are trying to authenticate %s using a secret only known ' \
- 'to him/her and yourself.</b>')
-choose_q = _('You can choose a question as a hint for your buddy below.')
-
-class ContactOtrSmpWindow:
- def gw(self, n):
- return self.xml.get_object(n)
-
- def __init__(self, ctx):
- self.question = None
- self.smp_running = False
- self.ctx = ctx
- self.account = ctx.user.accountname
-
- self.plugin = ctx.user.plugin
-
- self.GTK_BUILDER_FILE_PATH = \
- self.plugin.local_file_path('contact_otr_window.ui')
- self.xml = gtk.Builder()
- self.xml.set_translation_domain('gajim_plugins')
- self.xml.add_from_file(self.GTK_BUILDER_FILE_PATH)
-
- self.window = self.gw('otr_smp_window')
- self.window.set_title(_('OTR settings for %s') % ctx.peer)
-
- # the lambda thing is an anonymous helper that just discards the
- # parameters and calls hide_on_delete on clicking the window's
- # close button
- self.window.connect('delete-event', lambda d,o:
- self.window.hide_on_delete())
-
- self.gw('smp_cancel_button').connect('clicked', self._on_destroy)
- self.gw('smp_ok_button').connect('clicked', self._apply)
- self.gw('qcheckbutton').connect('toggled', self._toggle)
-
- self.gw('qcheckbutton').set_no_show_all(False)
- self.gw('qentry').set_no_show_all(False)
- self.gw('desclabel2').set_no_show_all(False)
-
- def _toggle(self, w, *args):
- self.gw('qentry').set_sensitive(w.get_active())
-
- def show(self, response):
- self.smp_running = False
- self.finished = False
-
- self.gw('smp_cancel_button').set_sensitive(True)
- self.gw('smp_ok_button').set_sensitive(True)
- self.gw('progressbar').set_fraction(0)
- self.gw('secret_entry').set_text('')
-
- self.response = response
- self.window.show_all()
- if response:
- self.gw('qcheckbutton').set_sensitive(False)
- if self.question is None:
- self.gw('qcheckbutton').set_active(False)
- self.gw('qcheckbutton').hide()
- self.gw('qentry').hide()
- self.gw('desclabel2').hide()
- self.gw('qcheckbutton').set_sensitive(False)
- self.gw('desclabel1').set_markup((smp_query % self.ctx.peer)
- + ' ' + enter_secret)
- else:
- self.gw('qcheckbutton').set_active(True)
- self.gw('qcheckbutton').show()
- self.gw('qentry').show()
- self.gw('qentry').set_sensitive(True)
- self.gw('qentry').set_editable(False)
- self.gw('desclabel2').show()
- self.gw('qentry').set_text(self.question)
-
- self.gw('desclabel1').set_markup(smp_q_query % self.ctx.peer)
- self.gw('desclabel2').set_markup(enter_secret)
- else:
- self.gw('qcheckbutton').show()
- self.gw('qcheckbutton').set_active(True)
- self.gw('qcheckbutton').set_mode(True)
- self.gw('qcheckbutton').set_sensitive(True)
- self.gw('qentry').set_sensitive(True)
- self.gw('qentry').show()
- self.gw('qentry').set_text("")
-
- self.gw('qentry').set_editable(True)
- self.gw('qentry').set_sensitive(True)
-
- self.gw('desclabel2').show()
- self.gw('desclabel1').set_markup((smp_init % self.ctx.peer) + ' '
- + choose_q)
- self.gw('desclabel2').set_markup(enter_secret)
-
- def _abort(self, text=None, appdata=None):
- self.smp_running = False
-
- self.ctx.smpAbort(appdata=appdata)
- if text:
- self.plugin.gajim_log(text, self.account, self.ctx.peer)
-
- def _finish(self, text):
- self.smp_running = False
- self.finished = True
-
- self.gw('qcheckbutton').set_active(False)
- self.gw('qcheckbutton').hide()
- self.gw('qentry').hide()
- self.gw('desclabel2').hide()
-
- self.gw('qcheckbutton').set_sensitive(False)
- self.gw('smp_cancel_button').set_sensitive(False)
- self.gw('smp_ok_button').set_sensitive(True)
- self.gw('progressbar').set_fraction(1)
- self.plugin.gajim_log(text, self.account, self.ctx.peer)
- self.gw('desclabel1').set_markup(text)
-
- self.plugin.update_otr(self.ctx.peer, self.account, True)
- self.ctx.user.saveTrusts()
- self.plugin.update_context_list()
-
- def get_tlv(self, tlvs, check):
- for tlv in tlvs:
- if isinstance(tlv, check):
- return tlv
- return None
-
- def handle_tlv(self, tlvs):
- if tlvs:
- is1qtlv = self.get_tlv(tlvs, potr.proto.SMP1QTLV)
- # check for TLV_SMP_ABORT or state = CHEATED
- if self.smp_running and not self.ctx.smpIsValid():
- self._finish(_('SMP verifying aborted'))
-
- # check for TLV_SMP1
- elif self.get_tlv(tlvs, potr.proto.SMP1TLV):
- self.smp_running = True
- self.question = None
- self.show(True)
- self.gw('progressbar').set_fraction(0.3)
-
- # check for TLV_SMP1Q
- elif is1qtlv:
- self.smp_running = True
- self.question = is1qtlv.msg
- self.show(True)
- self.gw('progressbar').set_fraction(0.3)
-
- # check for TLV_SMP2
- elif self.get_tlv(tlvs, potr.proto.SMP2TLV):
- self.gw('progressbar').set_fraction(0.6)
-
- # check for TLV_SMP3
- elif self.get_tlv(tlvs, potr.proto.SMP3TLV):
- if self.ctx.smpIsSuccess():
- text = _('SMP verifying succeeded')
- if self.question is not None:
- text += ' '+another_q
- self._finish(text)
- else:
- self._finish(_('SMP verifying failed'))
-
- # check for TLV_SMP4
- elif self.get_tlv(tlvs, potr.proto.SMP4TLV):
- if self.ctx.smpIsSuccess():
- text = _('SMP verifying succeeded')
- if self.question is not None:
- text += ' '+another_q
- self._finish(text)
- else:
- self._finish(_('SMP verifying failed'))
-
- def _on_destroy(self, widget):
- if self.smp_running:
- self._abort(_('user aborted SMP authentication'))
- self.window.hide_all()
-
- def _apply(self, widget, appdata=None):
- if self.finished:
- self.window.hide_all()
- return
- secret = self.gw('secret_entry').get_text()
- if self.response:
- self.ctx.smpGotSecret(secret, appdata=appdata)
- else:
- if self.gw('qcheckbutton').get_active():
- qtext = self.gw('qentry').get_text()
- self.ctx.smpInit(secret, question=qtext, appdata=appdata)
- else:
- self.ctx.smpInit(secret, appdata=appdata)
- self.gw('progressbar').set_fraction(0.3)
- self.smp_running = True
- widget.set_sensitive(False)
-
-class ContactOtrWindow(gtk.Dialog):
- def gw(self, n):
- return self.xml.get_object(n)
-
- def __init__(self, plugin, ctx, fpr=None, parent=None):
- fjid = ctx.peer
- gtk.Dialog.__init__(self, title=_('OTR settings for %s') % fjid,
- parent=parent,
- flags=gtk.DIALOG_DESTROY_WITH_PARENT,
- buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
- gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
-
- self.ctx = ctx
- self.fjid = fjid
- self.jid = gajim.get_room_and_nick_from_fjid(self.fjid)[0]
- self.account = ctx.user.accountname
- self.fpr = fpr
- self.plugin = plugin
-
- if self.fpr is None:
- key = self.ctx.getCurrentKey()
- if key is not None:
- self.fpr = key.cfingerprint()
-
- self.GTK_BUILDER_FILE_PATH = \
- self.plugin.local_file_path('contact_otr_window.ui')
- self.xml = gtk.Builder()
- self.xml.set_translation_domain('gajim_plugins')
- self.xml.add_from_file(self.GTK_BUILDER_FILE_PATH)
- self.notebook = self.gw('otr_settings_notebook')
- self.child.pack_start(self.notebook)
-
- self.connect('response', self.on_response)
- self.gw('otr_default_checkbutton').connect('toggled',
- self._otr_default_checkbutton_toggled)
-
- # always set the label containing our fingerprint
- self.gw('our_fp_label').set_markup(our_fp_text % ctx.user.getPrivkey())
-
- if self.fpr is None:
- # make the fingerprint widgets insensitive
- # when not encrypted
- for widget in self.gw('otr_fp_vbox').get_children():
- widget.set_sensitive(False)
- # show that the fingerprint is unknown
- self.gw('their_fp_label').set_markup(their_fp_text % {
- 'jid': self.fjid, 'fp': _('unknown')})
- self.gw('verified_combobox').set_active(-1)
- else:
- # make the fingerprint widgets sensitive when encrypted
- for widget in self.gw('otr_fp_vbox').get_children():
- widget.set_sensitive(True)
- # show their fingerprint
- fp = potr.human_hash(self.fpr)
- self.gw('their_fp_label').set_markup(their_fp_text % {
- 'jid': self.fjid, 'fp': fp})
- # set the trust combobox
- if ctx.getCurrentTrust():
- self.gw('verified_combobox').set_active(1)
- else:
- self.gw('verified_combobox').set_active(0)
-
- otr_flags = self.plugin.get_flags(self.account, self.jid, fallback=False)
-
- if otr_flags is not None:
- self.gw('otr_default_checkbutton').set_active(0)
- for w in self.gw('otr_settings_vbox').get_children():
- w.set_sensitive(True)
- else:
- # per-user settings not available,
- # using default settings
- otr_flags = self.plugin.get_flags(self.account)
- self.gw('otr_default_checkbutton').set_active(1)
- for w in self.gw('otr_settings_vbox').get_children():
- w.set_sensitive(False)
-
- self.gw('otr_policy_allow_v2_checkbutton').set_active(
- otr_flags['ALLOW_V2'])
- self.gw('otr_policy_require_checkbutton').set_active(
- otr_flags['REQUIRE_ENCRYPTION'])
- self.gw('otr_policy_send_tag_checkbutton').set_active(
- otr_flags['SEND_TAG'])
- self.gw('otr_policy_start_on_tag_checkbutton').set_active(
- otr_flags['WHITESPACE_START_AKE'])
-
- self.child.show_all()
-
- def on_response(self, dlg, response, *args):
- if response != gtk.RESPONSE_ACCEPT:
- return
-
-
- # -1 when nothing is selected
- # (ie. the connection is not encrypted)
- trust_state = self.gw('verified_combobox').get_active()
- if trust_state == 1 and not self.ctx.getTrust(self.fpr):
- self.ctx.setTrust(self.fpr, 'verified')
- self.ctx.user.saveTrusts()
- self.plugin.update_context_list()
- elif trust_state == 0:
- self.ctx.setTrust(self.fpr, '')
- self.ctx.user.saveTrusts()
- self.plugin.update_context_list()
-
- self.plugin.update_otr(self.ctx.peer, self.ctx.user.accountname, True)
-
- if self.gw('otr_default_checkbutton').get_active():
- # default is enabled, so remove any user-specific
- # settings if available
- self.plugin.set_flags(None, self.account, self.jid)
- else:
- # build the flags using the checkboxes
- flags = {}
- flags['ALLOW_V2'] = \
- self.gw('otr_policy_allow_v2_checkbutton').get_active()
- flags['REQUIRE_ENCRYPTION'] = \
- self.gw('otr_policy_require_checkbutton').get_active()
- flags['SEND_TAG'] = \
- self.gw('otr_policy_send_tag_checkbutton').get_active()
- flags['WHITESPACE_START_AKE'] = \
- self.gw('otr_policy_start_on_tag_checkbutton').get_active()
-
- self.plugin.set_flags(flags, self.account, self.jid)
-
- def _otr_default_checkbutton_toggled(self, widget):
- for w in self.gw('otr_settings_vbox').get_children():
- w.set_sensitive(not widget.get_active())
-
-def get_otr_submenu(plugin, control):
- GTK_BUILDER_FILE_PATH = \
- plugin.local_file_path('contact_otr_window.ui')
- xml = gtk.Builder()
- xml.set_translation_domain('gajim_plugins')
- xml.add_from_file(GTK_BUILDER_FILE_PATH)
-
- otr_submenu = xml.get_object('otr_submenu')
- otr_settings_menuitem, smp_otr_menuitem, start_otr_menuitem, \
- end_otr_menuitem = otr_submenu.get_submenu().get_children()
-
- otr_submenu.set_sensitive(True)
- otr_settings_menuitem.connect('activate', plugin.menu_settings_cb, control)
- start_otr_menuitem.connect('activate', plugin.menu_start_cb, control)
- end_otr_menuitem.connect('activate', plugin.menu_end_cb, control)
- smp_otr_menuitem.connect('activate', plugin.menu_smp_cb, control)
-
- enc, _, fin = plugin.get_otr_status(control.account, control.contact)
- # can end only when not in PLAINTEXT
- end_otr_menuitem.set_sensitive(enc)
- # can SMP only when ENCRYPTED
- smp_otr_menuitem.set_sensitive(enc and not fin)
- return otr_submenu
diff --git a/offline_bookmarks/__init__.py b/offline_bookmarks/__init__.py
deleted file mode 100644
index ef43880..0000000
--- a/offline_bookmarks/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-
-from offline_bookmarks import OfflineBookmarksPlugin
diff --git a/offline_bookmarks/config_dialog.ui b/offline_bookmarks/config_dialog.ui
deleted file mode 100644
index 0091b86..0000000
--- a/offline_bookmarks/config_dialog.ui
+++ /dev/null
@@ -1,458 +0,0 @@
-<?xml version="1.0"?>
-<interface>
- <requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkWindow" id="manage_bookmarks_window">
- <property name="border_width">12</property>
- <property name="title" translatable="yes">Manage Bookmarks</property>
- <property name="default_width">550</property>
- <property name="default_height">300</property>
- <property name="type_hint">dialog</property>
- <child>
- <object class="GtkVBox" id="vbox86">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkHBox" id="hbox2965">
- <property name="visible">True</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkVBox" id="vbox94">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow37">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="bookmarks_treeview">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="headers_visible">False</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHButtonBox" id="hbuttonbox25">
- <property name="visible">True</property>
- <property name="spacing">6</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="add_bookmark_button">
- <property name="label">gtk-add</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="on_add_bookmark_button_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="remove_bookmark_button">
- <property name="label">gtk-remove</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="on_remove_bookmark_button_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkTable" id="table33">
- <property name="n_rows">11</property>
- <property name="n_columns">2</property>
- <property name="column_spacing">12</property>
- <property name="row_spacing">6</property>
- <child>
- <object class="GtkLabel" id="label318">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">_Password:</property>
- <property name="use_underline">True</property>
- </object>
- <packing>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="pass_entry">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="server_entry">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label317">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">_Server:</property>
- <property name="use_underline">True</property>
- </object>
- <packing>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label316">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Roo_m:</property>
- <property name="use_underline">True</property>
- </object>
- <packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="room_entry">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">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>
- <object class="GtkEntry" id="nick_entry">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label315">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">_Nickname:</property>
- <property name="use_underline">True</property>
- </object>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label325">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">_Title:</property>
- <property name="use_underline">True</property>
- </object>
- <packing>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="title_entry">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label326">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Pr_int status:</property>
- <property name="use_underline">True</property>
- </object>
- <packing>
- <property name="top_attach">6</property>
- <property name="bottom_attach">7</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkComboBox" id="print_status_combobox">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <signal name="changed" handler="on_print_status_combobox_changed"/>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext1"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">6</property>
- <property name="bottom_attach">7</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <child>
- <object class="GtkCheckButton" id="autojoin_checkbutton">
- <property name="label" translatable="yes">A_uto join</property>
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">If checked, Gajim will join this group chat on startup</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_autojoin_checkbutton_toggled"/>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="minimize_checkbutton">
- <property name="label" translatable="yes">Minimi_ze on Auto Join</property>
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_minimize_checkbutton_toggled"/>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="right_attach">2</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="yalign">1</property>
- <property name="ypad">4</property>
- <property name="label" translatable="yes">Import bookmarks:</property>
- </object>
- <packing>
- <property name="right_attach">2</property>
- <property name="top_attach">7</property>
- <property name="bottom_attach">8</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Import from:</property>
- </object>
- <packing>
- <property name="top_attach">9</property>
- <property name="bottom_attach">10</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="import_button">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="focus_on_click">False</property>
- <signal name="clicked" handler="on_import_button_clicked"/>
- <child>
- <object class="GtkHBox" id="hbox2">
- <property name="visible">True</property>
- <child>
- <object class="GtkImage" id="image1">
- <property name="visible">True</property>
- <property name="xalign">1</property>
- <property name="stock">gtk-add</property>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label4">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Import</property>
- </object>
- <packing>
- <property name="pack_type">end</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">10</property>
- <property name="bottom_attach">11</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBox" id="import_from">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="focus_on_click">False</property>
- <signal name="changed" handler="on_import_from_changed"/>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext2"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">9</property>
- <property name="bottom_attach">10</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Import to:</property>
- </object>
- <packing>
- <property name="top_attach">8</property>
- <property name="bottom_attach">9</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <object class="GtkComboBox" id="import_to">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="focus_on_click">False</property>
- <signal name="changed" handler="on_import_to_changed"/>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext3"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">8</property>
- <property name="bottom_attach">9</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/offline_bookmarks/manifest.ini b/offline_bookmarks/manifest.ini
deleted file mode 100644
index f365aac..0000000
--- a/offline_bookmarks/manifest.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[info]
-name: Offline Bookmarks
-short_name: offline_bookmarks
-version: 0.2.1
-description: Saving bookmarks inside the plugin configuration file. Allows the use of locally stored bookmarks if the server does not support the storage of bookmarks (eg talk.google.com).
- Support to import bookmarks from one account to another.
-authors = Denis Fomin <fominde@gmail.com>
-homepage = http://trac-plugins.gajim.org/wiki/OfflineBookmarksPlugin
-max_gajim_version: 0.15.9
diff --git a/offline_bookmarks/offline_bookmarks.png b/offline_bookmarks/offline_bookmarks.png
deleted file mode 100644
index 6cf6443..0000000
--- a/offline_bookmarks/offline_bookmarks.png
+++ /dev/null
Binary files differ
diff --git a/offline_bookmarks/offline_bookmarks.py b/offline_bookmarks/offline_bookmarks.py
deleted file mode 100644
index 285490f..0000000
--- a/offline_bookmarks/offline_bookmarks.py
+++ /dev/null
@@ -1,371 +0,0 @@
-# -*- coding: utf-8 -*-
-##
-
-import gtk
-
-import gtkgui_helpers
-from plugins.gui import GajimPluginConfigDialog
-from plugins import GajimPlugin
-from plugins.helpers import log_calls
-from common import ged
-from common import app
-from common.i18n import Q_
-from config import ManageBookmarksWindow
-
-
-class OfflineBookmarksPlugin(GajimPlugin):
-
- @log_calls('OfflineBookmarksPlugin')
- def init(self):
- self.description = _('Saving bookmarks inside the plugin configuration '
- 'file. Allows the use of locally stored bookmarks if the server '
- 'does not support the storage of bookmarks (eg talk.google.com).\n'
- 'Support to import bookmarks from one account to another.')
-
- self.events_handlers = {
- 'bookmarks-received': (ged.POSTGUI, self.bookmarks_received),
- 'signed-in': (ged.POSTGUI, self.handle_event_signed_in),}
-
- self.gui_extension_points = {
- 'groupchat_control': (self.connect_with_gc_control,
- self.disconnect_from_gc_control),}
- self.controls = []
- self.config_dialog = OfflineBookmarksPluginConfigDialog(self)
-
- @log_calls('OfflineBookmarksPlugin')
- def activate(self):
- pass
-
- @log_calls('OfflineBookmarksPlugin')
- def deactivate(self):
- pass
-
- def save_bookmarks(self, account, bookmarks):
- jid = app.get_jid_from_account(account)
- if jid not in self.config:
- self.config[jid] = {}
- self.config[jid] = bookmarks
-
- def bookmarks_received(self, obj):
- self.save_bookmarks(obj.conn.name, obj.bookmarks)
-
- def handle_event_signed_in(self, obj):
- account = obj.conn.name
- connection = app.connections[account]
- jid = app.get_jid_from_account(obj.conn.name)
- bm_jids = [b['jid'] for b in connection.bookmarks]
- if jid in self.config:
- for bm in self.config[jid]:
- if bm['jid'] not in bm_jids:
- connection.bookmarks.append(bm)
- invisible_show = app.SHOW_LIST.index('invisible')
- # do not autojoin if we are invisible
- if connection.connected == invisible_show:
- return
- # do not autojoin if bookmarks supported
- bookmarks_supported = self.is_bookmark_supported(
- app.connections[account])
- if not bookmarks_supported:
- app.interface.auto_join_bookmarks(connection.name)
-
- def connect_with_gc_control(self, gc_control):
- control = Base(self, gc_control)
- self.controls.append(control)
-
- def disconnect_from_gc_control(self, gc_control):
- for control in self.controls:
- control.disconnect_from_gc_control()
- self.controls = []
-
- def is_bookmark_supported(self, account):
- if account.is_zeroconf:
- return False
- return (account.private_storage_supported or (
- account.pubsub_supported and account.pubsub_publish_options_supported))
-
-
-class Base(object):
- def __init__(self, plugin, gc_control):
- self.plugin = plugin
- self.gc_control = gc_control
- self.create_buttons()
-
- def create_buttons(self):
- # create button
- actions_hbox = self.gc_control.xml.get_object('actions_hbox')
- self.button = gtk.Button(label=None, stock=None, use_underline=True)
- self.button.set_property('relief', gtk.RELIEF_NONE)
- self.button.set_property('can-focus', False)
- img = gtk.Image()
- if gtkgui_helpers.gtk_icon_theme.has_icon('bookmark-new'):
- img.set_from_icon_name('bookmark-new', gtk.ICON_SIZE_MENU)
- else:
- img.set_from_stock('gtk-add', gtk.ICON_SIZE_MENU)
- self.button.set_image(img)
- self.button.set_tooltip_text(_('Bookmark this room(local)'))
- send_button = self.gc_control.xml.get_object('send_button')
- send_button_pos = actions_hbox.child_get_property(send_button,
- 'position')
- actions_hbox.add_with_properties(self.button, 'position',
- send_button_pos - 1, 'expand', False)
- self.button.set_no_show_all(True)
- id_ = self.button.connect('clicked', self.add_bookmark_button_clicked)
- self.gc_control.handlers[id_] = self.button
- for bm in app.connections[self.gc_control.account].bookmarks:
- if bm['jid'] == self.gc_control.contact.jid:
- self.button.hide()
- break
- else:
- account = self.gc_control.account
- bookmarks_supported = self.plugin.is_bookmark_supported(
- app.connections[account])
- self.button.set_sensitive(not bookmarks_supported)
- self.button.set_visible(not bookmarks_supported)
-
- def add_bookmark_button_clicked(self, widget):
- """
- Bookmark the room, without autojoin and not minimized
- """
- from dialogs import ErrorDialog, InformationDialog
- password = app.gc_passwords.get(self.gc_control.room_jid, '')
- account = self.gc_control.account
-
- bm = {'name': self.gc_control.name,
- 'jid': self.gc_control.room_jid,
- 'autojoin': 0,
- 'minimize': 0,
- 'password': password,
- 'nick': self.gc_control.nick}
-
- place_found = False
- index = 0
- # check for duplicate entry and respect alpha order
- for bookmark in app.connections[account].bookmarks:
- if bookmark['jid'] == bm['jid']:
- ErrorDialog(
- _('Bookmark already set'),
- _('Group Chat "%s" is already in your bookmarks.') % \
- bm['jid'])
- return
- if bookmark['name'] > bm['name']:
- place_found = True
- break
- index += 1
- if place_found:
- app.connections[account].bookmarks.insert(index, bm)
- else:
- app.connections[account].bookmarks.append(bm)
- self.plugin.save_bookmarks(account, app.connections[account].bookmarks)
- app.interface.roster.set_actions_menu_needs_rebuild()
- InformationDialog(
- _('Bookmark has been added successfully'),
- _('You can manage your bookmarks via Actions menu in your roster.'))
-
- def disconnect_from_gc_control(self):
- actions_hbox = self.gc_control.xml.get_object('actions_hbox')
- actions_hbox.remove(self.button)
-
-
-class OfflineBookmarksPluginConfigDialog(GajimPluginConfigDialog,
- ManageBookmarksWindow):
- def init(self):
- self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
- 'config_dialog.ui')
- self.xml = gtk.Builder()
- self.xml.set_translation_domain('gajim_plugins')
- self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
- ['vbox86'])
- vbox = self.xml.get_object('vbox86')
- self.child.pack_start(vbox)
- self.import_from_combo = self.xml.get_object('import_from')
- self.import_to_combo = self.xml.get_object('import_to')
-
- def on_run(self):
- self.fill_treeview()
-
- #Prepare comboboxes
- self.print_status_combobox = self.xml.get_object('print_status_combobox')
- model = gtk.ListStore(str, str)
- self.option_list = {'': _('Default'), 'all': Q_('?print_status:All'),
- 'in_and_out': _('Enter and leave only'),
- 'none': Q_('?print_status:None')}
- opts = sorted(self.option_list.keys())
- for opt in opts:
- model.append([self.option_list[opt], opt])
- self.print_status_combobox.set_model(model)
- self.print_status_combobox.set_active(1)
- #Prepare import_from combobox
- model = gtk.ListStore(str)
- for account in self.accounts:
- model.append([account,])
- for account_jid in self.plugin.config:
- if account_jid not in self.plugin.config_default_values and \
- account_jid not in self.jids:
- model.append([account_jid,])
- self.import_from_combo.set_model(model)
- #Prepare import_to combobox
- model = gtk.ListStore(str)
- for account in self.accounts:
- model.append([account,])
- self.import_to_combo.set_model(model)
-
- self.selection = self.view.get_selection()
- self.selection.connect('changed', self.bookmark_selected)
-
- #Prepare input fields
- self.title_entry = self.xml.get_object('title_entry')
- self.title_entry.connect('changed', self.on_title_entry_changed)
- self.nick_entry = self.xml.get_object('nick_entry')
- self.nick_entry.connect('changed', self.on_nick_entry_changed)
- self.server_entry = self.xml.get_object('server_entry')
- self.server_entry.connect('changed', self.on_server_entry_changed)
- self.room_entry = self.xml.get_object('room_entry')
- self.room_entry.connect('changed', self.on_room_entry_changed)
- self.pass_entry = self.xml.get_object('pass_entry')
- self.pass_entry.connect('changed', self.on_pass_entry_changed)
- self.autojoin_checkbutton = self.xml.get_object('autojoin_checkbutton')
- self.minimize_checkbutton = self.xml.get_object('minimize_checkbutton')
-
- self.xml.connect_signals(self)
- self.connect('hide', self.on_hide)
-
-
- self.show_all()
- self.view.set_cursor((0,))
-
- def fill_treeview(self):
- # Account-JID, RoomName, Room-JID, Autojoin, Minimize, Passowrd, Nick,
- # Show_Status
- self.treestore = gtk.TreeStore(str, str, str, bool, bool, str, str, str)
- self.treestore.set_sort_column_id(1, gtk.SORT_ASCENDING)
- self.accounts = []
- self.jids = []
-
- # Store bookmarks in treeview.
- for account in app.connections:
- if app.connections[account].connected <= 1:
- continue
- if app.connections[account].is_zeroconf:
- continue
-
- self.accounts.append(account)
- self.jids.append(app.get_jid_from_account(account))
- iter_ = self.treestore.append(None, [None, account, None, None,
- None, None, None, None])
-
- for bookmark in app.connections[account].bookmarks:
- if bookmark['name'] == '':
- # No name was given for this bookmark.
- # Use the first part of JID instead...
- name = bookmark['jid'].split("@")[0]
- bookmark['name'] = name
- from common import helpers
- # make '1', '0', 'true', 'false' (or other) to True/False
- autojoin = helpers.from_xs_boolean_to_python_boolean(
- bookmark['autojoin'])
-
- minimize = helpers.from_xs_boolean_to_python_boolean(
- bookmark['minimize'])
-
- print_status = bookmark.get('print_status', '')
- if print_status not in ('', 'all', 'in_and_out', 'none'):
- print_status = ''
- self.treestore.append(iter_, [
- account,
- bookmark['name'],
- bookmark['jid'],
- autojoin,
- minimize,
- bookmark['password'],
- bookmark['nick'],
- print_status ])
-
- self.view = self.xml.get_object('bookmarks_treeview')
- self.view.set_model(self.treestore)
- self.view.expand_all()
-
- renderer = gtk.CellRendererText()
- column = gtk.TreeViewColumn('Bookmarks', renderer, text=1)
- if self.view.get_column(0):
- self.view.remove_column(self.view.get_column(0))
- self.view.append_column(column)
-
- def on_hide(self, widget):
- """
- Parse the treestore data into our new bookmarks array, then send the new
- bookmarks to the server.
- """
- (model, iter_) = self.selection.get_selected()
- if iter_ and model.iter_parent(iter_):
- #bookmark selected, check it
- if not self.check_valid_bookmark():
- return
-
- for account in self.treestore:
- account_unicode = account[1].decode('utf-8')
- app.connections[account_unicode].bookmarks = []
-
- for bm in account.iterchildren():
- # Convert True/False/None to '1' or '0'
- autojoin = unicode(int(bm[3]))
- minimize = unicode(int(bm[4]))
- name = bm[1]
- if name:
- name = name.decode('utf-8')
- jid = bm[2]
- if jid:
- jid = jid.decode('utf-8')
- pw = bm[5]
- if pw:
- pw = pw.decode('utf-8')
- nick = bm[6]
- if nick:
- nick = nick.decode('utf-8')
-
- # create the bookmark-dict
- bmdict = { 'name': name, 'jid': jid, 'autojoin': autojoin,
- 'minimize': minimize, 'password': pw, 'nick': nick,
- 'print_status': bm[7]}
-
- app.connections[account_unicode].bookmarks.append(bmdict)
-
- bookmarks_supported = self.plugin.is_bookmark_supported(
- app.connections[account_unicode])
- if bookmarks_supported:
- app.connections[account_unicode].store_bookmarks()
- self.plugin.save_bookmarks(account_unicode,
- app.connections[account_unicode].bookmarks)
- app.interface.roster.set_actions_menu_needs_rebuild()
-
- def on_import_to_changed(self, treeview):
- self.on_import_from_changed(self.import_from_combo)
-
- def on_import_from_changed(self, widget):
- if widget.get_active() == -1 or self.import_to_combo.get_active() == -1:
- self.xml.get_object('import_button').set_sensitive(False)
- else:
- if widget.get_active_text() != self.import_to_combo.get_active_text():
- self.xml.get_object('import_button').set_sensitive(True)
- else:
- self.xml.get_object('import_button').set_sensitive(False)
-
- def on_import_button_clicked(self, widget):
- from_ = self.import_from_combo.get_active_text()
- to_connection = app.connections[self.import_to_combo.get_active_text()]
- to_bookmarks = to_connection.bookmarks
-
- if from_ in self.accounts:
- from_bookmarks = app.connections[from_].bookmarks
- else:
- from_bookmarks = self.plugin.config[from_]
- for bm in from_bookmarks:
- for bookmark in to_bookmarks:
- if bookmark['jid'] == bm['jid']:
- break
- else:
- to_bookmarks.append(bm)
-
- self.fill_treeview()
- self.view.set_cursor((0,))
- self.import_from_combo.set_active(-1)
- self.import_to_combo.set_active(-1)
diff --git a/snarl_notifications/__init__.py b/snarl_notifications/__init__.py
deleted file mode 100644
index b504dfd..0000000
--- a/snarl_notifications/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from plugin import SnarlNotificationsPlugin
diff --git a/snarl_notifications/manifest.ini b/snarl_notifications/manifest.ini
deleted file mode 100644
index 52d6f98..0000000
--- a/snarl_notifications/manifest.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[info]
-name: Snarl Notifications
-short_name: snarl_notifications
-version: 0.1.2
-description: Shows events notification using Snarl (http://www.fullphat.net/) under Windows. Snarl needs to be installed in system.
- PySnarl bindings are used (http://sourceforge.net/projects/pysnarl/).
-authors = Yann Leboulanger <asterix@lagaule.org>
-homepage = http://trac-plugins.gajim.org/wiki/SnarlNotificationsPlugin
-max_gajim_version: 0.15.9
diff --git a/snarl_notifications/plugin.py b/snarl_notifications/plugin.py
deleted file mode 100644
index 659fdf3..0000000
--- a/snarl_notifications/plugin.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# -*- coding: utf-8 -*-
-##
-## This file is part of Gajim.
-##
-## Gajim 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 3 only.
-##
-## Gajim 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.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-##
-'''
-Events notifications using Snarl
-
-Fancy events notifications under Windows using Snarl infrastructure.
-
-:note: plugin is at proof-of-concept state.
-
-:author: Yann Leboulanger <asterix@lagaule.org>
-:since: 08 April 2012
-:copyright: Copyright (2012) Yann Leboulanger <asterix@lagaule.org>
-:license: GPL
-'''
-
-import pySnarl
-
-from common import gajim
-from plugins import GajimPlugin
-from plugins.helpers import log_calls, log
-from common import ged
-import os.path
-
-class SnarlActionHandler(pySnarl.EventHandler):
- def OnNotificationInvoked(self, uid):
- account, jid, msg_type = uid.split()
- gajim.interface.handle_event(account, jid, msg_type)
-
-class SnarlNotificationsPlugin(GajimPlugin):
-
- @log_calls('SnarlNotificationsPlugin')
- def init(self):
- self.description = _('Shows events notification using Snarl '
- '(http://www.fullphat.net/) under Windows. '
- 'Snarl needs to be installed in system.\n'
- 'PySnarl bindings are used (http://code.google.com/p/pysnarl/).')
- self.config_dialog = None
- self.h = SnarlActionHandler
- self.snarl_win = pySnarl.SnarlApp(
- "pySnarl/Gajim", # app signature
- "Gajim", # app title
- os.path.abspath("..\data\pixmaps\gajim.ico"), # icon
- "", # config Tool
- "Gajim will use Snarl to display notifications", # hint
- False, # IsDaemon
- self.h, # event handler
- [] # classes
- )
-
- self.events_handlers = {'notification' : (ged.PRECORE, self.notif)}
-
- @log_calls('SnarlNotificationsPlugin')
- def notif(self, obj):
- if obj.do_popup:
- uid = obj.conn.name + " " + obj.jid + " " + obj.popup_msg_type
- self.snarl_win.notify(
- [], # actions
- "", # callbackScript
- "", # callbackScriptType
- "", # class
- "", # defaultCallback
- 5, # duration
- os.path.abspath(obj.popup_image),#r"C:\Documents and Settings\Administrateur\Mes documents\gajim\data\pixmaps\gajim.ico", # icon
- "", # mergeUID
- 0, # priority
- "", # replaceUID
- obj.popup_text,
- obj.popup_title,
- uid, # UID
- "", # sound
- -1, # percent
- 0, # log
- 64, # sensitivity
- )
- obj.do_popup = False
diff --git a/snarl_notifications/pySnarl.py b/snarl_notifications/pySnarl.py
deleted file mode 100644
index 5488cbe..0000000
--- a/snarl_notifications/pySnarl.py
+++ /dev/null
@@ -1,365 +0,0 @@
-# -*- coding: utf-8 -*-
-# ActiveX/COM Snarl python library
-# For proper functioning requires Snarl 2.5.1 or later !
-# Version 0.0.2
-
-# Changelog (in reverse chronological order):
-# -------------------------------------------
-# 0.0.2 by Pako 2012-01-30 14:12 UTC+1
-# - base64 icon format is now supported
-# 0.0.1 by Pako 2012-01-06 15:30 UTC+1
-# - initial version
-#-------------------------------------------------------
-
-from win32com.client import constants, gencache, Dispatch, DispatchWithEvents
-#===============================================================================
-
-def IsRunning():
- from win32gui import FindWindow
- return FindWindow("w>Snarl", "Snarl")
-#===============================================================================
-
-class EventHandler:
-
- def OnActivated(self):
- self.fn("DaemonActivated")
-
- def OnNotificationActionSelected(self, uid, command):
- self.fn("ActionSelected.%s" % command, payload = uid)
-
- def OnNotificationClosed(self, uid):
- self.fn("Closed", payload = uid)
-
- def OnNotificationExpired(self, uid):
- self.fn("Expired", payload = uid)
-
- def OnNotificationInvoked(self, uid):
- self.fn("Invoked", payload = uid)
-
- def OnQuit(self):
- self.fn("AppQuit")
-
- def OnShowAbout(self):
- self.fn("ShowAbout")
-
- def OnShowConfig(self):
- self.fn("ShowConfig")
-
- def OnSnarlLaunched(self):
- self.fn("Launched")
-
- def OnSnarlQuit(self):
- self.fn("Quit")
-
- def OnSnarlStarted(self):
- self.fn("Started")
-
- def OnSnarlStopped(self):
- self.fn("Stopped")
-
- def OnUserAway(self):
- self.fn("UserAway")
-
- def OnUserReturned(self):
- self.fn("UserReturned")
-#===============================================================================
-
-class SnarlApp(object):
- """Creates an SnarlApp object"""
-
- def __init__(
- self,
- signature,
- title,
- icon = "",
- configTool = "",
- hint = "",
- isDaemon = False,
- eventHandler = None,
- classes = []
- ):
- self.app = gencache.EnsureDispatch("libsnarl25.SnarlApp")
- for d in constants.__dicts__: # we must find the right dictionary ...
- if 'ERROR_NOTIFICATION_NOT_FOUND' in d: # this is it !
- break
- codes = d.items()
- codes.sort(reverse = True)
- self.statCodes = dict([[v,k] for k,v in codes[6:]])
- self.statCodes[codes[2][1]] = codes[2][0] # added SUCCESS
- self.classes = NotifClasses(classes)
- if eventHandler:
- self.SetEventHandler(eventHandler)
- self.SetTo(
- configTool,
- hint,
- icon,
- isDaemon,
- signature,
- title
- )
-
-
- def SetTo(
- self,
- configTool,
- hint,
- icon,
- isDaemon,
- signature,
- title
- ):
- self.app.Classes = self.classes.Classes()
- self.app.ConfigTool = configTool
- self.app.Hint = hint
- self.app.Icon = icon
- self.app.IsDaemon = isDaemon
- self.app.Signature = signature
- self.app.Title = title
-
-
- def GetStatusCode(self, code):
- return self.statCodes[code]
-
-
- def SetEventHandler(self, handler):
- self.events = DispatchWithEvents(self.app, handler)
-
-
- def register(self):
- return self.GetStatusCode(self.app.Register())
-
-
- def unregister(self):
- return self.GetStatusCode(self.app.Unregister())
-
-
- def tidyUp(self):
- self.app.TidyUp()
-
-
- def addClass(
- self,
- classId,
- name,
- enabled = True,
- title = "",
- text = "",
- icon = "",
- callback = "",
- duration = -1,
- sound = "",
- ):
- cntOld = self.classes.count()
- cntNew = self.classes.add(classId, name, enabled, title, text, icon, callback, duration, sound)
- return int(not cntNew == cntOld + 1)
-
-
- def remClass(self, id):
- cntOld = self.classes.count()
- cntNew = self.classes.remove(id)
- return int(not cntNew == cntOld - 1)
-
-
- def clearClasses(self):
- return self.classes.makeEmpty()
-
-
- def classesCount(self):
- return self.classes.count()
-
-
- def notify(
- self,
- actions,
- callbackScript,
- callbackScriptType,
- cls,
- defaultCallback,
- duration,
- icon,
- mergeUID,
- priority,
- replaceUID,
- text,
- title,
- uid,
- sound = None,
- percent = None,
- log = None,
- sensitivity = None
- ):
- note = Notification(
- actions,
- callbackScript,
- callbackScriptType,
- cls,
- defaultCallback,
- duration,
- "",
- mergeUID,
- priority,
- replaceUID,
- text,
- title,
- uid,
- )
- if icon:
- if icon[1] == ":":
- note.Add("icon", icon, True)
- else:
- note.Add("icon-base64", icon.replace("=","%"), True)
- if sound:
- note.Add("sound", sound, True)
- if percent is not None and percent > -1:
- note.Add("value-percent", str(percent), True)
- if log is not None:
- note.Add("log", str(log), True)
- if sensitivity is not None:
- note.Add("sensitivity", str(sensitivity), True)
- return self.GetStatusCode(self.app.Show(note.Note())[0])
-
-
- def hideNotification(self, uid):
- return self.GetStatusCode(self.app.Hide(uid))
-
-
- def isVisible(self, uid):
- return self.GetStatusCode(self.app.IsVisible(uid))
-
-
- def getEtcPath(self):
- return self.app.GetEtcPath()
-
-
- def makePath(self, pth):
- return self.app.GetEtcPath(pth)
-
-
- def isInstalled(self):
- return self.app.IsSnarlInstalled()
-
-
- def isRunning(self):
- return self.app.IsSnarlRunning()
-
-
- def version(self):
- ver = self.app.SnarlVersion()
- return ver if ver > 0 else self.GetStatusCode(-ver)
-
-
- def isConnected(self):
- return self.app.IsConnected
-
-
- def getLibVersion(self):
- return self.app.LibVersion
-
-
- def getLibRevision(self):
- return self.app.LibRevision
-
-
- def Destroy(self):
- self.app.TidyUp()
- if self.events:
- del self.events
- del self.app
-#===============================================================================
-
-class NotifClasses(object):
- def __init__(self, classes = []):
- self.clss = Dispatch("libsnarl25.Classes")
- for cls in classes:
- self.add(*cls)
-
- def count(self):
- return self.clss.Count()
-
- def add(self, *cls):
- self.clss.Add(*cls)
- return self.clss.Count()
-
- def remove(self, cls):
- self.clss.Remove(cls)
- return self.clss.Count()
-
- def makeEmpty(self):
- self.clss.MakeEmpty()
- return self.clss.Count()
-
- def Classes(self):
- return self.clss
-#===============================================================================
-
-class Notification(object):
- def __init__(
- self,
- actions,
- callbackScript,
- callbackScriptType,
- cls,
- defaultCallback,
- duration,
- icon,
- mergeUID,
- priority,
- replaceUID,
- text,
- title,
- uid,
- ):
- nt = Dispatch("libsnarl25.Notification")
- nt.Actions = NotifActions(actions).Actions()
- nt.CallbackScript = callbackScript
- nt.CallbackScriptType = callbackScriptType
- nt.Class = cls
- nt.DefaultCallback = defaultCallback
- nt.Duration = duration
- if icon:
- nt.Icon = icon
- nt.MergeUID = mergeUID
- nt.Priority = priority
- nt.ReplaceUID = replaceUID
- nt.Text = text
- nt.Title = title
- nt.UID = uid
- self.nt = nt
-
- def Add(self, name, value, update):
- self.nt.Add(name, value, True)
-
- def Note(self):
- return self.nt
-#===============================================================================
-
-class NotifActions(object):
- def __init__(self, actions=[]):
- self.actns = Dispatch("libsnarl25.Actions")
- self.actions = actions
- for action in actions:
- self.actns.Add(*action)
-
- def add(self, actn):
- self.actns.Add(*actn)
- self.actions.append(actn)
- return self.actns.Count()
-
- def remove(self, actn):
- if actn in self.actions:
- ix = self.actions.index(actn)
- self.actns.Remove(ix + 1)
- self.actions.pop(ix)
- return self.actns.Count()
-
- def makeEmpty(self):
- self.actns.MakeEmpty()
- self.actions = []
- return self.actns.Count()
-
- def Actions(self):
- return self.actns
-#===============================================================================
-
-
-
diff --git a/snarl_notifications/snarl_notifications.png b/snarl_notifications/snarl_notifications.png
deleted file mode 100644
index 9535573..0000000
--- a/snarl_notifications/snarl_notifications.png
+++ /dev/null
Binary files differ
diff --git a/ubuntu_integration/__init__.py b/ubuntu_integration/__init__.py
deleted file mode 100644
index db3c5d3..0000000
--- a/ubuntu_integration/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from plugin import UbuntuIntegrationPlugin
diff --git a/ubuntu_integration/doc/example.jpg b/ubuntu_integration/doc/example.jpg
deleted file mode 100644
index 97fc76d..0000000
--- a/ubuntu_integration/doc/example.jpg
+++ /dev/null
Binary files differ
diff --git a/ubuntu_integration/manifest.ini b/ubuntu_integration/manifest.ini
deleted file mode 100644
index d60d83b..0000000
--- a/ubuntu_integration/manifest.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[info]
-name: Ubuntu Ayatana Integration
-short_name: ubuntu_integration
-version: 0.1.4
-description: This plugin integrates Gajim with the Ubuntu Messaging Menu.
-
- You must have python-indicate and python-xdg (and Gajim obviously) installed to enable this plugin.
-
- Many thanks to the guys from gajim@conference.gajim.org for answering my questions :)
-authors: Michael Kainer <kaini@linuxlovers.at>
-homepage: http://trac-plugins.gajim.org/wiki/UbuntuIntegrationPlugin
-max_gajim_version: 0.15.9
diff --git a/ubuntu_integration/plugin.py b/ubuntu_integration/plugin.py
deleted file mode 100644
index 185e8d9..0000000
--- a/ubuntu_integration/plugin.py
+++ /dev/null
@@ -1,196 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Ubuntu Ayatana Integration plugin.
-
-TODO:
-* handle gc-invitation, subscription_request: it looks like they don't fire
-* nice error if plugin can't load
-* me menu
-* permanent integration into the messaging menu after quitting gajim
-* show/hide gajim on root menu entry
-* switch workspace on click on events
-* corrent group chat handling
-* hide gajim if the plugin is disabled
-
-:author: Michael Kainer <kaini@linuxlovers.at>
-:since: 21st October 2010
-:copyright: Copyright (2010) Michael Kainer <kaini1123@gmail.com>
-:license: GPLv3
-"""
-# Python
-import time
-# Gajim
-from plugins import GajimPlugin
-from plugins.plugin import GajimPluginException
-from plugins.helpers import log_calls
-from common import gajim
-import gtkgui_helpers
-try:
- from xdg.BaseDirectory import load_data_paths
- import indicate
-except ImportError:
- pass
-
-
-class UbuntuIntegrationPlugin(GajimPlugin):
- """
- Class for Messaging Menu and Me Menu.
- """
-
- @log_calls("UbuntuIntegrationPlugin")
- def init(self):
- """
- Does nothing.
- """
- self.description = _('This plugin integrates Gajim '
- 'with the Ubuntu Messaging Menu.\n\n'
- 'You must have python-indicate and python-xdg (and Gajim obviously)'
- ' installed to enable this plugin.\n\n'
- 'Many thanks to the guys from gajim@conference.gajim.org for '
- 'answering my questions :)')
- self.config_dialog = None
- self.test_activatable()
-
- def test_activatable(self):
- self.available_text = ''
- try:
- from xdg.BaseDirectory import load_data_paths
- except ImportError:
- self.activatable = False
- self.available_text += _('python-xdg is missing! '
- 'Install python-xdg.\n')
- try:
- import indicate
- except ImportError:
- self.activatable = False
- self.available_text += _('python-indicate is missing! '
- 'Install python-indicate.')
-
- @log_calls("UbuntuIntegrationPlugin")
- def activate(self):
- """
- Displays gajim in the Messaging Menu.
- """
- # {(account, jid): (indicator, [event, ...]), ...}
- self.events = {}
-
- version = gajim.version.split('-')[0]
- if version == '0.15' and self.available_text:
- raise GajimPluginException(self.available_text)
-
- self.server = indicate.indicate_server_ref_default()
- self.server.set_type("message.im")
- dfile = ""
- for file in load_data_paths("applications/gajim.desktop"):
- dfile = file
- break
- if not dfile:
- raise GajimPluginException("Can't locate gajim.desktop!")
- self.server.set_desktop_file(dfile)
- self.server.show()
-
- gajim.events.event_added_subscribe(self.on_event_added)
- gajim.events.event_removed_subscribe(self.on_event_removed)
-
- @log_calls("UbuntuIntegrationPlugin")
- def deactivate(self):
- """
- Cleaning up.
- """
- gajim.events.event_added_unsubscribe(self.on_event_added)
- gajim.events.event_removed_unsubscribe(self.on_event_removed)
-
- if hasattr(self, 'server'):
- self.server.hide()
- del self.server
-
- if hasattr(self, 'events'):
- for (_, event) in self.events:
- event[0].hide()
- del self.events
-
- def on_indicator_activate(self, indicator, _):
- """
- Forwards the action to gajims event handler.
- """
- key = indicator.key
- event = self.events[key][1][0]
- gajim.interface.handle_event(event.account, event.jid, event.type_)
-
- def on_event_added(self, event):
- """
- Adds "Nickname Time" to the Messaging menu.
- """
- print "----", event.type_
-
- # Basic variables
- account = event.account
- jid = event.jid
- when = time.time()
- contact = ""
- key = (account, jid)
-
- # Check if the event is valid and modify the variables
- 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:
- print "ignored"
- return
-
- print account, jid, when, contact
-
- # Add a new indicator if necessary
- if key not in self.events:
- indicator = indicate.Indicator()
- indicator.set_property("name", contact)
- indicator.set_property_time("time", when)
- indicator.set_property_bool("draw-attention", True)
- if gajim.config.get("show_avatars_in_roster"):
- pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(jid)
- if pixbuf not in (None, "ask"):
- indicator.set_property_icon("icon", pixbuf)
- indicator.connect("user-display", self.on_indicator_activate)
- indicator.show()
- indicator.key = key
- self.events[key] = (indicator, [])
-
- # Prepare the event and save it
- event.time = when
- self.events[key][1].append(event)
-
- def on_event_removed(self, events):
- """
- Goes through the events and removes them from the array and
- the indicator if there are no longer any events pending.
- """
- for event in events:
- print "====", event.type_
-
- key = (event.account, event.jid)
-
- if key not in self.events and \
- event in self.events[key][1]:
- self.events[key][1].remove(event)
-
- if len(self.events[key][1]) == 0: # remove indicator
- self.events[key][0].hide()
- del self.events[key]
- else: # set the indicator time to the text event
- self.events[key][0].set_property_time("time",
- self.events[key][1][0].time)
- else:
- print "ignored"
diff --git a/ubuntu_integration/ubuntu_integration.png b/ubuntu_integration/ubuntu_integration.png
deleted file mode 100644
index 959e99d..0000000
--- a/ubuntu_integration/ubuntu_integration.png
+++ /dev/null
Binary files differ
diff --git a/url_shortener/__init__.py b/url_shortener/__init__.py
deleted file mode 100644
index 3592535..0000000
--- a/url_shortener/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from url_shortener import UrlShortenerPlugin
diff --git a/url_shortener/config_dialog.ui b/url_shortener/config_dialog.ui
deleted file mode 100644
index d1424ec..0000000
--- a/url_shortener/config_dialog.ui
+++ /dev/null
@@ -1,112 +0,0 @@
-<?xml version="1.0"?>
-<interface>
- <requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkWindow" id="window1">
- <child>
- <object class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkFrame" id="frame1">
- <property name="visible">True</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">none</property>
- <child>
- <object class="GtkTable" id="table1">
- <property name="visible">True</property>
- <property name="n_rows">2</property>
- <property name="n_columns">2</property>
- <child>
- <object class="GtkSpinButton" id="max_chars">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">&#x25CF;</property>
- <property name="width_chars">6</property>
- <property name="snap_to_ticks">True</property>
- <property name="numeric">True</property>
- <signal name="value_changed" handler="avatar_size_value_changed"/>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="y_options"/>
- </packing>
- </child>
- <child>
- <object class="GtkSpinButton" id="in_max_chars">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">&#x25CF;</property>
- <property name="width_chars">6</property>
- <property name="snap_to_ticks">True</property>
- <property name="numeric">True</property>
- <signal name="value_changed" handler="on_in_max_chars_value_changed"/>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_EXPAND</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="incoming message">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="xpad">13</property>
- <property name="label" translatable="yes">incoming message</property>
- <property name="track_visited_links">False</property>
- </object>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_EXPAND</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="avatar_size_lebel">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="xpad">12</property>
- <property name="label" translatable="yes">outgoing message</property>
- <property name="track_visited_links">False</property>
- </object>
- <packing>
- <property name="y_options">GTK_EXPAND</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="label">
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="label" translatable="yes">&lt;b&gt;The maximum length not be shortened links(chars):&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="padding">6</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="shorten_outgoing">
- <property name="label" translatable="yes">shorten links in outgoing messages</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="shorten_outgoing_toggled"/>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/url_shortener/manifest.ini b/url_shortener/manifest.ini
deleted file mode 100644
index b23ec79..0000000
--- a/url_shortener/manifest.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[info]
-name: Url Shortener
-short_name: url_shortener
-version: 0.3.5
-description: Plugin that allows users to shorten a long URL in messages.
- For example, you can turn this link:
- https://trac.gajim.org/timeline
- Into this link:
- http://bit.ly/THy6ZK
-authors: Denis Fomin <fominde@gmail.com>
-homepage: http://trac-plugins.gajim.org/wiki/UrlShortenerPlugin
-max_gajim_version: 0.15.9
diff --git a/url_shortener/url_shortener.png b/url_shortener/url_shortener.png
deleted file mode 100644
index fa1faa5..0000000
--- a/url_shortener/url_shortener.png
+++ /dev/null
Binary files differ
diff --git a/url_shortener/url_shortener.py b/url_shortener/url_shortener.py
deleted file mode 100644
index bd47efe..0000000
--- a/url_shortener/url_shortener.py
+++ /dev/null
@@ -1,232 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import gtk
-import json
-import urllib
-import urllib2
-from common import app
-from common import ged
-from plugins import GajimPlugin
-from plugins.helpers import log_calls
-from plugins.gui import GajimPluginConfigDialog
-
-APIKEY = 'R_fcba926fc7978bd19acbca73ec82b2be'
-USER = 'dicson'
-
-class UrlShortenerPlugin(GajimPlugin):
- @log_calls('UrlShortenerPlugin')
- def init(self):
- self.description = _('Plugin that allows users to shorten a long URL '
- 'in received messages.\n'
- 'For example, you can turn this link:\n'
- 'https://trac.gajim.org/timeline\n'
- 'Into this link:\n'
- 'http://bit.ly/THy6ZK')
- self.config_dialog = UrlShortenerPluginConfigDialog(self)
- self.gui_extension_points = {
- 'chat_control_base': (self.connect_with_chat_control,
- self.disconnect_from_chat_control),
- 'print_special_text': (self.print_special_text,
- self.print_special_text1),}
- self.config_default_values = {
- 'MAX_CHARS': (50, ('MAX_CHARS(30-...)')),
- 'IN_MAX_CHARS': (50, ('MAX_CHARS(30-...)')),
- 'SHORTEN_OUTGOING': (False, ''),}
- self.events_handlers = {'message-outgoing': (ged.OUT_PRECORE,
- self.handle_outgoing_msg),
- 'gc-message-outgoing': (ged.OUT_PRECORE,
- self.handle_outgoing_msg)}
- self.chat_control = None
- self.controls = []
-
- def handle_outgoing_msg(self, event):
- if not self.active:
- return
- if not event.message:
- return
- if not self.config['SHORTEN_OUTGOING']:
- return
- if hasattr(event, 'shortened'):
- return
-
- iterator = app.interface.basic_pattern_re.finditer(event.message)
- for match in iterator:
- start, end = match.span()
- link = event.message[start:end]
- if len(link) < self.config['MAX_CHARS']:
- continue
- short_link = None
- try:
- params = urllib.urlencode({'longUrl': link,
- 'login': USER,
- 'apiKey': APIKEY,
- 'format': 'json'})
- req = urllib2.Request('http://api.bit.ly/v3/shorten?%s' % params)
- response = urllib2.urlopen(req)
- j = json.load(response)
- if j['status_code'] == 200:
- short_link = j['data']['url']
- except urllib2.HTTPError, e:
- pass
- if short_link:
- event.message = event.message.replace(link, short_link)
- event.callback_args[1] = event.message
- event.shortened = True
-
- @log_calls('UrlShortenerPlugin')
- def connect_with_chat_control(self, chat_control):
- self.chat_control = chat_control
- control = Base(self, self.chat_control)
- self.controls.append(control)
-
- @log_calls('UrlShortenerPlugin')
- def disconnect_from_chat_control(self, chat_control):
- for control in self.controls:
- control.disconnect_from_chat_control()
- self.controls = []
-
- def print_special_text(self, tv, special_text, other_tags, graphics=True,
- additional_data={}):
- for control in self.controls:
- if control.chat_control.conv_textview != tv:
- continue
- control.print_special_text(special_text, other_tags, graphics=True)
-
- def print_special_text1(self, chat_control, special_text, other_tags=None,
- graphics=True, additional_data={}):
- for control in self.controls:
- if control.chat_control == chat_control:
- control.disconnect_from_chat_control()
- self.controls.remove(control)
-
-
-class Base(object):
- def __init__(self, plugin, chat_control):
- self.plugin = plugin
- self.chat_control = chat_control
- self.textview = self.chat_control.conv_textview
-
- self.id_ = self.textview.tv.connect('motion_notify_event',
- self.on_textview_motion_notify_event)
- self.chat_control.handlers[self.id_] = self.textview.tv
-
- def print_special_text(self, special_text, other_tags, graphics=True):
- if not self.plugin.active:
- return
- is_xhtml_link = None
- text_is_valid_uri = False
- buffer_ = self.textview.tv.get_buffer()
-
- # Detect XHTML-IM link
- ttt = buffer_.get_tag_table()
- tags_ = [(ttt.lookup(t) if isinstance(t, str) else t) for t in other_tags]
- for t in tags_:
- is_xhtml_link = getattr(t, 'href', None)
- if is_xhtml_link:
- break
- # Check if we accept this as an uri
- schemes = app.config.get('uri_schemes').split()
- for scheme in schemes:
- if special_text.startswith(scheme):
- text_is_valid_uri = True
- if special_text.startswith('www.') or special_text.startswith('ftp.') \
- or text_is_valid_uri and not is_xhtml_link:
- if len(special_text) < self.plugin.config['IN_MAX_CHARS']:
- return
- end_iter = buffer_.get_end_iter()
- mark = buffer_.create_mark(None, end_iter, True)
- app.thread_interface(self.insert_hyperlink, [mark, special_text,
- ttt])
- self.textview.plugin_modified = True
-
- def insert_hyperlink(self, mark, special_text, ttt):
- try:
- params = urllib.urlencode({'longUrl': special_text,
- 'login': USER,
- 'apiKey': APIKEY,
- 'format': 'json'})
- req = urllib2.Request('http://api.bit.ly/v3/shorten?%s' % params)
- response = urllib2.urlopen(req)
- j = json.load(response)
- if j['status_code'] == 200:
- special_text = j['data']['url']
- except urllib2.HTTPError, e:
- pass
-
- buffer_ = mark.get_buffer()
- end_iter = buffer_.get_iter_at_mark(mark)
- buffer_.insert_with_tags(end_iter, special_text, ttt.lookup('url'))
-
- def on_textview_motion_notify_event(self, widget, event):
- pointer_x, pointer_y = self.textview.tv.window.get_pointer()[0:2]
- x, y = self.textview.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,
- pointer_x, pointer_y)
- tags = self.textview.tv.get_iter_at_location(x, y).get_tags()
- tag_table = self.textview.tv.get_buffer().get_tag_table()
- buffer_ = self.textview.tv.get_buffer()
- for tag in tags:
- if tag != tag_table.lookup('url'):
- continue
- it = self.textview.tv.get_iter_at_location(x, y)
- st = it.copy()
- st.backward_to_tag_toggle(tag_table.lookup('url'))
- it.forward_to_tag_toggle(tag_table.lookup('url'))
- text = buffer_.get_text(st, it, include_hidden_chars=True)
- if text.startswith('http://bit.ly/'):
- try:
- params = urllib.urlencode({'shortUrl': text,
- 'login': USER,
- 'apiKey': APIKEY,
- 'format': 'json'})
- req = urllib2.Request('http://api.bit.ly/v3/expand?%s' \
- % params)
- response = urllib2.urlopen(req)
- j = json.load(response)
- if j['status_code'] != 200:
- raise Exception('%s'%j['status_txt'])
- txt = j['data']['expand'][0]['long_url']
- self.textview.tv.set_tooltip_text(txt)
- self.textview.on_textview_motion_notify_event(widget, event)
- return
- except Exception, e:
- break
-
- self.textview.tv.set_tooltip_text('')
- self.textview.on_textview_motion_notify_event(widget, event)
-
- def disconnect_from_chat_control(self):
- pass
-
-
-class UrlShortenerPluginConfigDialog(GajimPluginConfigDialog):
- def init(self):
- self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
- 'config_dialog.ui')
- self.xml = gtk.Builder()
- self.xml.set_translation_domain('gajim_plugins')
- self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, ['vbox1'])
- self.max_chars_spinbutton = self.xml.get_object('max_chars')
- self.max_chars_spinbutton.get_adjustment().set_all(30, 30, 99999, 1,
- 10, 0)
- self.in_max_chars_spinbutton = self.xml.get_object('in_max_chars')
- self.in_max_chars_spinbutton.get_adjustment().set_all(30, 30, 99999, 1,
- 10, 0)
- self.shorten_outgoing = self.xml.get_object('shorten_outgoing')
- hbox = self.xml.get_object('vbox1')
- self.child.pack_start(hbox)
-
- self.xml.connect_signals(self)
-
- def on_run(self):
- self.max_chars_spinbutton.set_value(self.plugin.config['MAX_CHARS'])
- self.in_max_chars_spinbutton.set_value(self.plugin.config['IN_MAX_CHARS'])
- self.shorten_outgoing.set_active(self.plugin.config['SHORTEN_OUTGOING'])
-
- def avatar_size_value_changed(self, spinbutton):
- self.plugin.config['MAX_CHARS'] = spinbutton.get_value()
-
- def on_in_max_chars_value_changed(self, spinbutton):
- self.plugin.config['IN_MAX_CHARS'] = spinbutton.get_value()
-
- def shorten_outgoing_toggled(self, checkbutton):
- self.plugin.config['SHORTEN_OUTGOING'] = checkbutton.get_active()
diff --git a/whiteboard/__init__.py b/whiteboard/__init__.py
deleted file mode 100644
index 802d00c..0000000
--- a/whiteboard/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from plugin import WhiteboardPlugin
diff --git a/whiteboard/brush_tool.png b/whiteboard/brush_tool.png
deleted file mode 100644
index 266c321..0000000
--- a/whiteboard/brush_tool.png
+++ /dev/null
Binary files differ
diff --git a/whiteboard/line_tool.png b/whiteboard/line_tool.png
deleted file mode 100644
index 151f584..0000000
--- a/whiteboard/line_tool.png
+++ /dev/null
Binary files differ
diff --git a/whiteboard/manifest.ini b/whiteboard/manifest.ini
deleted file mode 100644
index 8a56c0f..0000000
--- a/whiteboard/manifest.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[info]
-name: Whiteboard
-short_name: whiteboard
-version: 0.2.2
-description: Shows a whiteboard in chat. python-pygoocanvas is required.
-authors = Yann Leboulanger <asterix@lagaule.org>
-homepage = http://trac-plugins.gajim.org/wiki/WhiteboardPlugin
-max_gajim_version: 0.15.9
diff --git a/whiteboard/oval_tool.png b/whiteboard/oval_tool.png
deleted file mode 100644
index efd6f0c..0000000
--- a/whiteboard/oval_tool.png
+++ /dev/null
Binary files differ
diff --git a/whiteboard/plugin.py b/whiteboard/plugin.py
deleted file mode 100644
index d9e8225..0000000
--- a/whiteboard/plugin.py
+++ /dev/null
@@ -1,486 +0,0 @@
-## plugins/whiteboard/plugin.py
-##
-## Copyright (C) 2009 Jeff Ling <jeff.ummu AT gmail.com>
-## Copyright (C) 2010 Yann Leboulanger <asterix AT lagaule.org>
-##
-## This file is part of Gajim.
-##
-## Gajim 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 3 only.
-##
-## Gajim 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.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-##
-
-'''
-Whiteboard plugin.
-
-:author: Yann Leboulanger <asterix@lagaule.org>
-:since: 1st November 2010
-:copyright: Copyright (2010) Yann Leboulanger <asterix@lagaule.org>
-:license: GPL
-'''
-
-
-from common import helpers
-from common import gajim
-from plugins import GajimPlugin
-from plugins.plugin import GajimPluginException
-from plugins.helpers import log_calls, log
-import common.xmpp
-import gtk
-import chat_control
-from common import ged
-from common.jingle_session import JingleSession
-from common.jingle_content import JingleContent
-from common.jingle_transport import JingleTransport, TransportType
-import dialogs
-from whiteboard_widget import Whiteboard, HAS_GOOCANVAS
-from common import xmpp
-from common import caps_cache
-
-NS_JINGLE_XHTML = 'urn:xmpp:tmp:jingle:apps:xhtml'
-NS_JINGLE_SXE = 'urn:xmpp:tmp:jingle:transports:sxe'
-NS_SXE = 'urn:xmpp:sxe:0'
-
-class WhiteboardPlugin(GajimPlugin):
- @log_calls('WhiteboardPlugin')
- def init(self):
- self.description = _('Shows a whiteboard in chat.'
- ' python-pygoocanvas is required.')
- self.config_dialog = None
- self.events_handlers = {
- 'jingle-request-received': (ged.GUI1, self._nec_jingle_received),
- 'jingle-connected-received': (ged.GUI1, self._nec_jingle_connected),
- 'jingle-disconnected-received': (ged.GUI1,
- self._nec_jingle_disconnected),
- 'raw-message-received': (ged.GUI1, self._nec_raw_message),
- }
- self.gui_extension_points = {
- 'chat_control_base' : (self.connect_with_chat_control,
- self.disconnect_from_chat_control),
- 'chat_control_base_update_toolbar': (self.update_button_state,
- None),
- }
- self.controls = []
- self.sid = None
-
- @log_calls('WhiteboardPlugin')
- def _compute_caps_hash(self):
- for a in gajim.connections:
- gajim.caps_hash[a] = caps_cache.compute_caps_hash([
- gajim.gajim_identity], gajim.gajim_common_features + \
- gajim.gajim_optional_features[a])
- # re-send presence with new hash
- connected = gajim.connections[a].connected
- if connected > 1 and gajim.SHOW_LIST[connected] != 'invisible':
- gajim.connections[a].change_status(gajim.SHOW_LIST[connected],
- gajim.connections[a].status)
-
- @log_calls('WhiteboardPlugin')
- def activate(self):
- if not HAS_GOOCANVAS:
- raise GajimPluginException('python-pygoocanvas is missing!')
- if NS_JINGLE_SXE not in gajim.gajim_common_features:
- gajim.gajim_common_features.append(NS_JINGLE_SXE)
- if NS_SXE not in gajim.gajim_common_features:
- gajim.gajim_common_features.append(NS_SXE)
- self._compute_caps_hash()
-
- @log_calls('WhiteboardPlugin')
- def deactivate(self):
- if NS_JINGLE_SXE in gajim.gajim_common_features:
- gajim.gajim_common_features.remove(NS_JINGLE_SXE)
- if NS_SXE in gajim.gajim_common_features:
- gajim.gajim_common_features.remove(NS_SXE)
- self._compute_caps_hash()
-
- @log_calls('WhiteboardPlugin')
- def connect_with_chat_control(self, control):
- if isinstance(control, chat_control.ChatControl):
- base = Base(self, control)
- self.controls.append(base)
-
- @log_calls('WhiteboardPlugin')
- def disconnect_from_chat_control(self, chat_control):
- for base in self.controls:
- base.disconnect_from_chat_control()
- self.controls = []
-
- @log_calls('WhiteboardPlugin')
- def update_button_state(self, control):
- for base in self.controls:
- if base.chat_control == control:
- if control.contact.supports(NS_JINGLE_SXE) and \
- control.contact.supports(NS_SXE):
- base.button.set_sensitive(True)
- tooltip_text = _('Show whiteboard')
- else:
- base.button.set_sensitive(False)
- tooltip_text = _('Client on the other side '
- 'does not support the whiteboard')
- base.button.set_tooltip_text(tooltip_text)
-
- @log_calls('WhiteboardPlugin')
- def show_request_dialog(self, account, fjid, jid, sid, content_types):
- def on_ok():
- session = gajim.connections[account].get_jingle_session(fjid, sid)
- self.sid = session.sid
- if not session.accepted:
- session.approve_session()
- for content in content_types:
- session.approve_content(content)
- for _jid in (fjid, jid):
- ctrl = gajim.interface.msg_win_mgr.get_control(_jid, account)
- if ctrl:
- break
- if not ctrl:
- # create it
- gajim.interface.new_chat_from_jid(account, jid)
- ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
- session = session.contents[('initiator', 'xhtml')]
- ctrl.draw_whiteboard(session)
-
- def on_cancel():
- session = gajim.connections[account].get_jingle_session(fjid, sid)
- session.decline_session()
-
- contact = gajim.contacts.get_first_contact_from_jid(account, jid)
- if contact:
- name = contact.get_shown_name()
- else:
- name = jid
- pritext = _('Incoming Whiteboard')
- sectext = _('%(name)s (%(jid)s) wants to start a whiteboard with '
- 'you. Do you want to accept?') % {'name': name, 'jid': jid}
- dialog = dialogs.NonModalConfirmationDialog(pritext, sectext=sectext,
- on_response_ok=on_ok, on_response_cancel=on_cancel)
- dialog.popup()
-
- @log_calls('WhiteboardPlugin')
- def _nec_jingle_received(self, obj):
- if not HAS_GOOCANVAS:
- return
- content_types = set(c[0] for c in obj.contents)
- if 'xhtml' not in content_types:
- return
- self.show_request_dialog(obj.conn.name, obj.fjid, obj.jid, obj.sid,
- content_types)
-
- @log_calls('WhiteboardPlugin')
- def _nec_jingle_connected(self, obj):
- if not HAS_GOOCANVAS:
- return
- account = obj.conn.name
- ctrl = (gajim.interface.msg_win_mgr.get_control(obj.fjid, account)
- or gajim.interface.msg_win_mgr.get_control(obj.jid, account))
- if not ctrl:
- return
- session = gajim.connections[obj.conn.name].get_jingle_session(obj.fjid,
- obj.sid)
-
- if ('initiator', 'xhtml') not in session.contents:
- return
-
- session = session.contents[('initiator', 'xhtml')]
- ctrl.draw_whiteboard(session)
-
- @log_calls('WhiteboardPlugin')
- def _nec_jingle_disconnected(self, obj):
- for base in self.controls:
- if base.sid == obj.sid:
- base.stop_whiteboard(reason = obj.reason)
-
- @log_calls('WhiteboardPlugin')
- def _nec_raw_message(self, obj):
- if not HAS_GOOCANVAS:
- return
- if obj.stanza.getTag('sxe', namespace=NS_SXE):
- account = obj.conn.name
-
- try:
- fjid = helpers.get_full_jid_from_iq(obj.stanza)
- except helpers.InvalidFormat:
- obj.conn.dispatch('ERROR', (_('Invalid Jabber ID'),
- _('A message from a non-valid JID arrived, it has been '
- 'ignored.')))
-
- jid = gajim.get_jid_without_resource(fjid)
- ctrl = (gajim.interface.msg_win_mgr.get_control(fjid, account)
- or gajim.interface.msg_win_mgr.get_control(jid, account))
- if not ctrl:
- return
- sxe = obj.stanza.getTag('sxe')
- if not sxe:
- return
- sid = sxe.getAttr('session')
- if (jid, sid) not in obj.conn._sessions:
- pass
-# newjingle = JingleSession(con=self, weinitiate=False, jid=jid, sid=sid)
-# self.addJingle(newjingle)
-
- # we already have such session in dispatcher...
- session = obj.conn.get_jingle_session(fjid, sid)
- cn = session.contents[('initiator', 'xhtml')]
- error = obj.stanza.getTag('error')
- if error:
- action = 'iq-error'
- else:
- action = 'edit'
-
- cn.on_stanza(obj.stanza, sxe, error, action)
-# def __editCB(self, stanza, content, error, action):
- #new_tags = sxe.getTags('new')
- #remove_tags = sxe.getTags('remove')
-
- #if new_tags is not None:
- ## Process new elements
- #for tag in new_tags:
- #if tag.getAttr('type') == 'element':
- #ctrl.whiteboard.recieve_element(tag)
- #elif tag.getAttr('type') == 'attr':
- #ctrl.whiteboard.recieve_attr(tag)
- #ctrl.whiteboard.apply_new()
-
- #if remove_tags is not None:
- ## Delete rids
- #for tag in remove_tags:
- #target = tag.getAttr('target')
- #ctrl.whiteboard.image.del_rid(target)
-
- # Stop propagating this event, it's handled
- return True
-
-
-class Base(object):
- def __init__(self, plugin, chat_control):
- self.plugin = plugin
- self.chat_control = chat_control
- self.chat_control.draw_whiteboard = self.draw_whiteboard
- self.contact = self.chat_control.contact
- self.account = self.chat_control.account
- self.jid = self.contact.get_full_jid()
- self.create_buttons()
- self.whiteboard = None
- self.sid = None
-
- def create_buttons(self):
- # create whiteboard button
- actions_hbox = self.chat_control.xml.get_object('actions_hbox')
- self.button = gtk.ToggleButton(label=None, use_underline=True)
- self.button.set_property('relief', gtk.RELIEF_NONE)
- self.button.set_property('can-focus', False)
- img = gtk.Image()
- img_path = self.plugin.local_file_path('whiteboard.png')
- pixbuf = gtk.gdk.pixbuf_new_from_file(img_path)
- iconset = gtk.IconSet(pixbuf=pixbuf)
- factory = gtk.IconFactory()
- factory.add('whiteboard', iconset)
- img_path = self.plugin.local_file_path('brush_tool.png')
- pixbuf = gtk.gdk.pixbuf_new_from_file(img_path)
- iconset = gtk.IconSet(pixbuf=pixbuf)
- factory.add('brush_tool', iconset)
- img_path = self.plugin.local_file_path('line_tool.png')
- pixbuf = gtk.gdk.pixbuf_new_from_file(img_path)
- iconset = gtk.IconSet(pixbuf=pixbuf)
- factory.add('line_tool', iconset)
- img_path = self.plugin.local_file_path('oval_tool.png')
- pixbuf = gtk.gdk.pixbuf_new_from_file(img_path)
- iconset = gtk.IconSet(pixbuf=pixbuf)
- factory.add('oval_tool', iconset)
- factory.add_default()
- img.set_from_stock('whiteboard', gtk.ICON_SIZE_MENU)
- self.button.set_image(img)
- send_button = self.chat_control.xml.get_object('send_button')
- send_button_pos = actions_hbox.child_get_property(send_button,
- 'position')
- actions_hbox.add_with_properties(self.button, 'position',
- send_button_pos - 1, 'expand', False)
- id_ = self.button.connect('toggled', self.on_whiteboard_button_toggled)
- self.chat_control.handlers[id_] = self.button
- self.button.show()
-
- def draw_whiteboard(self, content):
- hbox = self.chat_control.xml.get_object('chat_control_hbox')
- if len(hbox.get_children()) == 1:
- self.whiteboard = Whiteboard(self.account, self.contact, content,
- self.plugin)
- # set minimum size
- self.whiteboard.hbox.set_size_request(300, 0)
- hbox.pack_start(self.whiteboard.hbox, expand=False, fill=False)
- self.whiteboard.hbox.show_all()
- self.button.set_active(True)
- content.control = self
- self.sid = content.session.sid
-
- def on_whiteboard_button_toggled(self, widget):
- """
- Popup whiteboard
- """
- if widget.get_active():
- if not self.whiteboard:
- self.start_whiteboard()
- else:
- self.stop_whiteboard()
-
- def start_whiteboard(self):
- conn = gajim.connections[self.chat_control.account]
- jingle = JingleSession(conn, weinitiate=True, jid=self.jid)
- self.sid = jingle.sid
- conn._sessions[jingle.sid] = jingle
- content = JingleWhiteboard(jingle)
- content.control = self
- jingle.add_content('xhtml', content)
- jingle.start_session()
-
- def stop_whiteboard(self, reason=None):
- conn = gajim.connections[self.chat_control.account]
- self.sid = None
- session = conn.get_jingle_session(self.jid, media='xhtml')
- if session:
- session.end_session()
- self.button.set_active(False)
- if reason:
- txt = _('Whiteboard stopped: %(reason)s') % {'reason': reason}
- self.chat_control.print_conversation(txt, 'info')
- if not self.whiteboard:
- return
- hbox = self.chat_control.xml.get_object('chat_control_hbox')
- if self.whiteboard.hbox in hbox.get_children():
- if hasattr(self.whiteboard, 'hbox'):
- hbox.remove(self.whiteboard.hbox)
- self.whiteboard = None
-
- def disconnect_from_chat_control(self):
- actions_hbox = self.chat_control.xml.get_object('actions_hbox')
- actions_hbox.remove(self.button)
-
-class JingleWhiteboard(JingleContent):
- ''' Jingle Whiteboard sessions consist of xhtml content'''
- def __init__(self, session, transport=None):
- if not transport:
- transport = JingleTransportSXE()
- JingleContent.__init__(self, session, transport)
- self.media = 'xhtml'
- self.negotiated = True # there is nothing to negotiate
- self.last_rid = 0
- self.callbacks['session-accept'] += [self._sessionAcceptCB]
- self.callbacks['session-terminate'] += [self._stop]
- self.callbacks['session-terminate-sent'] += [self._stop]
- self.callbacks['edit'] = [self._EditCB]
-
- def _EditCB(self, stanza, content, error, action):
- new_tags = content.getTags('new')
- remove_tags = content.getTags('remove')
-
- if new_tags is not None:
- # Process new elements
- for tag in new_tags:
- if tag.getAttr('type') == 'element':
- self.control.whiteboard.recieve_element(tag)
- elif tag.getAttr('type') == 'attr':
- self.control.whiteboard.recieve_attr(tag)
- self.control.whiteboard.apply_new()
-
- if remove_tags is not None:
- # Delete rids
- for tag in remove_tags:
- target = tag.getAttr('target')
- self.control.whiteboard.image.del_rid(target)
-
- def _sessionAcceptCB(self, stanza, content, error, action):
- log.debug('session accepted')
- self.session.connection.dispatch('WHITEBOARD_ACCEPTED',
- (self.session.peerjid, self.session.sid))
-
- def generate_rids(self, x):
- # generates x number of rids and returns in list
- rids = []
- for x in range(x):
- rids.append(str(self.last_rid))
- self.last_rid += 1
- return rids
-
- def send_whiteboard_node(self, items, rids):
- # takes int rid and dict items and sends it as a node
- # sends new item
- jid = self.session.peerjid
- sid = self.session.sid
- message = xmpp.Message(to=jid)
- sxe = message.addChild(name='sxe', attrs={'session': sid},
- namespace=NS_SXE)
-
- for x in rids:
- if items[x]['type'] == 'element':
- parent = x
- attrs = {'rid': x,
- 'name': items[x]['data'][0].getName(),
- 'type': items[x]['type']}
- sxe.addChild(name='new', attrs=attrs)
- if items[x]['type'] == 'attr':
- attr_name = items[x]['data']
- chdata = items[parent]['data'][0].getAttr(attr_name)
- attrs = {'rid': x,
- 'name': attr_name,
- 'type': items[x]['type'],
- 'chdata': chdata,
- 'parent': parent}
- sxe.addChild(name='new', attrs=attrs)
- self.session.connection.connection.send(message)
-
- def delete_whiteboard_node(self, rids):
- message = xmpp.Message(to=self.session.peerjid)
- sxe = message.addChild(name='sxe', attrs={'session': self.session.sid},
- namespace=NS_SXE)
-
- for x in rids:
- sxe.addChild(name='remove', attrs = {'target': x})
- self.session.connection.connection.send(message)
-
- def send_items(self, items, rids):
- # recieves dict items and a list of rids of items to send
- # TODO: is there a less clumsy way that doesn't involve passing
- # whole list
- self.send_whiteboard_node(items, rids)
-
- def del_item(self, rids):
- self.delete_whiteboard_node(rids)
-
- def encode(self, xml):
- # encodes it sendable string
- return 'data:text/xml,' + urllib.quote(xml)
-
- def _fill_content(self, content):
- content.addChild(NS_JINGLE_XHTML + ' description')
-
- def _stop(self, *things):
- pass
-
- def __del__(self):
- pass
-
-def get_content(desc):
- return JingleWhiteboard
-
-common.jingle_content.contents[NS_JINGLE_XHTML] = get_content
-
-class JingleTransportSXE(JingleTransport):
- def __init__(self, node=None):
- if gajim.config.get('version') == '0.15':
- JingleTransport.__init__(self, TransportType.streaming)
- else:
- JingleTransport.__init__(self, TransportType.SOCKS5)
-
- def make_transport(self, candidates=None):
- transport = JingleTransport.make_transport(self, candidates)
- transport.setNamespace(NS_JINGLE_SXE)
- transport.setTagData('host', 'TODO')
- return transport
-
-common.jingle_transport.transports[NS_JINGLE_SXE] = JingleTransportSXE
diff --git a/whiteboard/whiteboard.png b/whiteboard/whiteboard.png
deleted file mode 100644
index 13318e3..0000000
--- a/whiteboard/whiteboard.png
+++ /dev/null
Binary files differ
diff --git a/whiteboard/whiteboard_widget.py b/whiteboard/whiteboard_widget.py
deleted file mode 100644
index 2a9562c..0000000
--- a/whiteboard/whiteboard_widget.py
+++ /dev/null
@@ -1,418 +0,0 @@
-## plugins/whiteboard/whiteboard_widget.py
-##
-## Copyright (C) 2009 Jeff Ling <jeff.ummu AT gmail.com>
-## Copyright (C) 2010 Yann Leboulanger <asterix AT lagaule.org>
-##
-## This file is part of Gajim.
-##
-## Gajim 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 3 only.
-##
-## Gajim 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.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-##
-
-import gtk
-import gtkgui_helpers
-try:
- import goocanvas
- HAS_GOOCANVAS = True
-except:
- HAS_GOOCANVAS = False
-from common.xmpp import Node
-from common import gajim
-from dialogs import FileChooserDialog
-
-'''
-A whiteboard widget made for Gajim.
-- Ummu
-'''
-
-class Whiteboard(object):
- def __init__(self, account, contact, session, plugin):
- self.plugin = plugin
- file_path = plugin.local_file_path('whiteboard_widget.ui')
- xml = gtk.Builder()
- xml.set_translation_domain('gajim_plugins')
- xml.add_from_file(file_path)
- self.hbox = xml.get_object('whiteboard_hbox')
- self.canevas = goocanvas.Canvas()
- self.hbox.pack_start(self.canevas)
- self.hbox.reorder_child(self.canevas, 0)
- self.canevas.set_flags(gtk.CAN_FOCUS)
- self.fg_color_select_button = xml.get_object('fg_color_button')
- self.root = self.canevas.get_root_item()
- self.tool_buttons = []
- for tool in ('brush', 'oval', 'line', 'delete'):
- self.tool_buttons.append(xml.get_object(tool + '_button'))
- xml.get_object('brush_button').set_active(True)
-
- # Events
- self.canevas.connect('button-press-event', self.button_press_event)
- self.canevas.connect('button-release-event', self.button_release_event)
- self.canevas.connect('motion-notify-event', self.motion_notify_event)
- self.canevas.connect('item-created', self.item_created)
-
- # Config
- self.line_width = 2
- xml.get_object('size_scale').set_value(2)
- self.color = str(self.fg_color_select_button.get_color())
-
- # SVG Storage
- self.image = SVGObject(self.root, session)
-
- xml.connect_signals(self)
-
- # Temporary Variables for items
- self.item_temp = None
- self.item_temp_coords = (0, 0)
- self.item_data = None
-
- # Will be {ID: {type:'element', data:[node, goocanvas]}, ID2: {}} instance
- self.recieving = {}
-
- def on_tool_button_toggled(self, widget):
- for btn in self.tool_buttons:
- if btn == widget:
- continue
- btn.set_active(False)
-
- def on_brush_button_toggled(self, widget):
- if widget.get_active():
- self.image.draw_tool = 'brush'
- self.on_tool_button_toggled(widget)
-
- def on_oval_button_toggled(self, widget):
- if widget.get_active():
- self.image.draw_tool = 'oval'
- self.on_tool_button_toggled(widget)
-
- def on_line_button_toggled(self, widget):
- if widget.get_active():
- self.image.draw_tool = 'line'
- self.on_tool_button_toggled(widget)
-
- def on_delete_button_toggled(self, widget):
- if widget.get_active():
- self.image.draw_tool = 'delete'
- self.on_tool_button_toggled(widget)
-
- def on_clear_button_clicked(self, widget):
- self.image.clear_canvas()
-
- def on_export_button_clicked(self, widget):
- SvgChooserDialog(self.image.export_svg)
-
- def on_fg_color_button_color_set(self, widget):
- self.color = str(self.fg_color_select_button.get_color())
-
- def item_created(self, canvas, item, model):
- print 'item created'
- item.connect('button-press-event', self.item_button_press_events)
-
- def item_button_press_events(self, item, target_item, event):
- if self.image.draw_tool == 'delete':
- self.image.del_item(item)
-
- def on_size_scale_format_value(self, widget):
- self.line_width = int(widget.get_value())
-
- def button_press_event(self, widget, event):
- x = event.x
- y = event.y
- state = event.state
- self.item_temp_coords = (x, y)
-
- if self.image.draw_tool == 'brush':
- self.item_temp = goocanvas.Ellipse(parent=self.root,
- center_x=x,
- center_y=y,
- radius_x=1,
- radius_y=1,
- stroke_color=self.color,
- fill_color=self.color,
- line_width=self.line_width)
- self.item_data = 'M %s,%s L ' % (x, y)
-
- elif self.image.draw_tool == 'oval':
- self.item_data = True
-
- if self.image.draw_tool == 'line':
- self.item_data = 'M %s,%s L' % (x, y)
-
- def motion_notify_event(self, widget, event):
- x = event.x
- y = event.y
- state = event.state
- if self.item_temp is not None:
- self.item_temp.remove()
-
- if self.item_data is not None:
- if self.image.draw_tool == 'brush':
- self.item_data = self.item_data + '%s,%s ' % (x, y)
- self.item_temp = goocanvas.Path(parent=self.root,
- data=self.item_data, line_width=self.line_width,
- stroke_color=self.color)
- elif self.image.draw_tool == 'oval':
- self.item_temp = goocanvas.Ellipse(parent=self.root,
- center_x=self.item_temp_coords[0] + (x - self.item_temp_coords[0]) / 2,
- center_y=self.item_temp_coords[1] + (y - self.item_temp_coords[1]) / 2,
- radius_x=abs(x - self.item_temp_coords[0]) / 2,
- radius_y=abs(y - self.item_temp_coords[1]) / 2,
- stroke_color=self.color,
- line_width=self.line_width)
- elif self.image.draw_tool == 'line':
- self.item_data = 'M %s,%s L' % self.item_temp_coords
- self.item_data = self.item_data + ' %s,%s' % (x, y)
- self.item_temp = goocanvas.Path(parent=self.root,
- data=self.item_data, line_width=self.line_width,
- stroke_color=self.color)
-
- def button_release_event(self, widget, event):
- x = event.x
- y = event.y
- state = event.state
-
- if self.image.draw_tool == 'brush':
- self.item_data = self.item_data + '%s,%s' % (x, y)
- if x == self.item_temp_coords[0] and y == self.item_temp_coords[1]:
- goocanvas.Ellipse(parent=self.root,
- center_x=x,
- center_y=y,
- radius_x=1,
- radius_y=1,
- stroke_color=self.color,
- fill_color=self.color,
- line_width=self.line_width)
- self.image.add_path(self.item_data, self.line_width, self.color)
-
- if self.image.draw_tool == 'oval':
- cx = self.item_temp_coords[0] + (x - self.item_temp_coords[0]) / 2
- cy = self.item_temp_coords[1] + (y - self.item_temp_coords[1]) / 2
- rx = abs(x - self.item_temp_coords[0]) / 2
- ry = abs(y - self.item_temp_coords[1]) / 2
- self.image.add_ellipse(cx, cy, rx, ry, self.line_width, self.color)
-
- if self.image.draw_tool == 'line':
- self.item_data = 'M %s,%s L' % self.item_temp_coords
- self.item_data = self.item_data + ' %s,%s' % (x, y)
- if x == self.item_temp_coords[0] and y == self.item_temp_coords[1]:
- goocanvas.Ellipse(parent=self.root,
- center_x=x,
- center_y=y,
- radius_x=1,
- radius_y=1,
- stroke_color='black',
- fill_color='black',
- line_width=self.line_width)
- self.image.add_path(self.item_data, self.line_width, self.color)
-
- if self.image.draw_tool == 'delete':
- pass
-
- self.item_data = None
- if self.item_temp is not None:
- self.item_temp.remove()
- self.item_temp = None
-
- def recieve_element(self, element):
- node = self.image.g.addChild(name=element.getAttr('name'))
- self.image.g.addChild(node=node)
- self.recieving[element.getAttr('rid')] = {'type':'element',
- 'data':[node],
- 'children':[]}
-
- def recieve_attr(self, element):
- node = self.recieving[element.getAttr('parent')]['data'][0]
- node.setAttr(element.getAttr('name'), element.getAttr('chdata'))
-
- self.recieving[element.getAttr('rid')] = {'type':'attr',
- 'data':element.getAttr('name'),
- 'parent':node}
- self.recieving[element.getAttr('parent')]['children'].append(element.getAttr('rid'))
-
- def apply_new(self):
- for x in self.recieving.keys():
- if self.recieving[x]['type'] == 'element':
- self.image.add_recieved(x, self.recieving)
-
- self.recieving = {}
-
-class SvgChooserDialog(FileChooserDialog):
- def __init__(self, on_response_ok=None, on_response_cancel=None):
- '''
- Choose in which SVG file to store the image
- '''
- def on_ok(widget, callback):
- '''
- check if file exists and call callback
- '''
- path_to_file = self.get_filename()
- path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
- (path_to_file,))[0]
- widget.destroy()
- callback(path_to_file)
-
- FileChooserDialog.__init__(self,
- title_text=_('Save Image as...'),
- action=gtk.FILE_CHOOSER_ACTION_SAVE,
- buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE,
- gtk.RESPONSE_OK),
- current_folder='',
- default_response=gtk.RESPONSE_OK,
- on_response_ok=(on_ok, on_response_ok),
- on_response_cancel=on_response_cancel)
-
- filter_ = gtk.FileFilter()
- filter_.set_name(_('All files'))
- filter_.add_pattern('*')
- self.add_filter(filter_)
-
- filter_ = gtk.FileFilter()
- filter_.set_name(_('SVG Files'))
- filter_.add_pattern('*.svg')
- self.add_filter(filter_)
- self.set_filter(filter_)
-
-
-class SVGObject():
- ''' A class to store the svg document and make changes to it.'''
-
- def __init__(self, root, session, height=300, width=300):
- # Will be {ID: {type:'element', data:[node, goocanvas]}, ID2: {}} instance
- self.items = {}
- self.root = root
- self.draw_tool = 'brush'
-
- # sxe session
- self.session = session
-
- # initialize svg document
- self.svg = Node(node='<svg/>')
- self.svg.setAttr('version', '1.1')
- self.svg.setAttr('height', str(height))
- self.svg.setAttr('width', str(width))
- self.svg.setAttr('xmlns', 'http://www.w3.org/2000/svg')
- # TODO: make this settable
- self.g = self.svg.addChild(name='g')
- self.g.setAttr('fill', 'none')
- self.g.setAttr('stroke-linecap', 'round')
-
- def add_path(self, data, line_width, color):
- ''' adds the path to the items listing, both minidom node and goocanvas
- object in a tuple '''
-
- goocanvas_obj = goocanvas.Path(parent=self.root, data=data,
- line_width=line_width, stroke_color=color)
- goocanvas_obj.connect('button-press-event', self.item_button_press_events)
-
- node = self.g.addChild(name='path')
- node.setAttr('d', data)
- node.setAttr('stroke-width', str(line_width))
- node.setAttr('stroke', color)
- self.g.addChild(node=node)
-
- rids = self.session.generate_rids(4)
- self.items[rids[0]] = {'type':'element', 'data':[node, goocanvas_obj], 'children':rids[1:]}
- self.items[rids[1]] = {'type':'attr', 'data':'d', 'parent':node}
- self.items[rids[2]] = {'type':'attr', 'data':'stroke-width', 'parent':node}
- self.items[rids[3]] = {'type':'attr', 'data':'stroke', 'parent':node}
-
- self.session.send_items(self.items, rids)
-
- def add_recieved(self, parent_rid, new_items):
- ''' adds the path to the items listing, both minidom node and goocanvas
- object in a tuple '''
- node = new_items[parent_rid]['data'][0]
-
- self.items[parent_rid] = new_items[parent_rid]
- for x in new_items[parent_rid]['children']:
- self.items[x] = new_items[x]
-
- if node.getName() == 'path':
- goocanvas_obj = goocanvas.Path(parent=self.root,
- data=node.getAttr('d'),
- line_width=int(node.getAttr('stroke-width')),
- stroke_color=node.getAttr('stroke'))
-
- if node.getName() == 'ellipse':
- goocanvas_obj = goocanvas.Ellipse(parent=self.root,
- center_x=float(node.getAttr('cx')),
- center_y=float(node.getAttr('cy')),
- radius_x=float(node.getAttr('rx')),
- radius_y=float(node.getAttr('ry')),
- stroke_color=node.getAttr('stroke'),
- line_width=float(node.getAttr('stroke-width')))
-
- self.items[parent_rid]['data'].append(goocanvas_obj)
- goocanvas_obj.connect('button-press-event', self.item_button_press_events)
-
- def add_ellipse(self, cx, cy, rx, ry, line_width, stroke_color):
- ''' adds the ellipse to the items listing, both minidom node and goocanvas
- object in a tuple '''
-
- goocanvas_obj = goocanvas.Ellipse(parent=self.root,
- center_x=cx,
- center_y=cy,
- radius_x=rx,
- radius_y=ry,
- stroke_color=stroke_color,
- line_width=line_width)
- goocanvas_obj.connect('button-press-event', self.item_button_press_events)
-
- node = self.g.addChild(name='ellipse')
- node.setAttr('cx', str(cx))
- node.setAttr('cy', str(cy))
- node.setAttr('rx', str(rx))
- node.setAttr('ry', str(ry))
- node.setAttr('stroke-width', str(line_width))
- node.setAttr('stroke', stroke_color)
- self.g.addChild(node=node)
-
- rids = self.session.generate_rids(7)
- self.items[rids[0]] = {'type':'element', 'data':[node, goocanvas_obj], 'children':rids[1:]}
- self.items[rids[1]] = {'type':'attr', 'data':'cx', 'parent':node}
- self.items[rids[2]] = {'type':'attr', 'data':'cy', 'parent':node}
- self.items[rids[3]] = {'type':'attr', 'data':'rx', 'parent':node}
- self.items[rids[4]] = {'type':'attr', 'data':'ry', 'parent':node}
- self.items[rids[5]] = {'type':'attr', 'data':'stroke-width', 'parent':node}
- self.items[rids[6]] = {'type':'attr', 'data':'stroke', 'parent':node}
-
- self.session.send_items(self.items, rids)
-
- def del_item(self, item):
- rids = []
- for x in self.items.keys():
- if self.items[x]['type'] == 'element':
- if self.items[x]['data'][1] == item:
- for y in self.items[x]['children']:
- rids.append(y)
- self.del_rid(y)
- rids.append(x)
- self.del_rid(x)
- break
- self.session.del_item(rids)
-
- def clear_canvas(self):
- for x in self.items.keys():
- if self.items[x]['type'] == 'element':
- self.del_rid(x)
-
- def del_rid(self, rid):
- if self.items[rid]['type'] == 'element':
- self.items[rid]['data'][1].remove()
- del self.items[rid]
-
- def export_svg(self, filename):
- f = open(filename, 'w')
- f.writelines(str(self.svg))
- f.close()
-
- def item_button_press_events(self, item, target_item, event):
- self.del_item(item)
diff --git a/whiteboard/whiteboard_widget.ui b/whiteboard/whiteboard_widget.ui
deleted file mode 100644
index fddd2cb..0000000
--- a/whiteboard/whiteboard_widget.ui
+++ /dev/null
@@ -1,192 +0,0 @@
-<?xml version="1.0"?>
-<interface>
- <requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy project-wide -->
- <object class="GtkHBox" id="whiteboard_hbox">
- <property name="visible">True</property>
- <property name="border_width">3</property>
- <property name="spacing">6</property>
- <child>
- <placeholder/>
- </child>
- <child>
- <object class="GtkVBox" id="vbuttonbox1">
- <property name="visible">True</property>
- <property name="border_width">6</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkToggleButton" id="brush_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Brush Tool: Draw freehand lines</property>
- <signal name="toggled" handler="on_brush_button_toggled"/>
- <child>
- <object class="GtkImage" id="image5">
- <property name="visible">True</property>
- <property name="stock">brush_tool</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="oval_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Oval Tool: Draw circles and ellipses</property>
- <signal name="toggled" handler="on_oval_button_toggled"/>
- <child>
- <object class="GtkImage" id="image6">
- <property name="visible">True</property>
- <property name="stock">oval_tool</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="line_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Line Tool: Draw straight lines</property>
- <signal name="toggled" handler="on_line_button_toggled"/>
- <child>
- <object class="GtkImage" id="image7">
- <property name="visible">True</property>
- <property name="stock">line_tool</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="delete_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Delete Tool: Remove individual figures</property>
- <signal name="toggled" handler="on_delete_button_toggled"/>
- <child>
- <object class="GtkImage" id="image2">
- <property name="visible">True</property>
- <property name="stock">gtk-delete</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="clear_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Clear Canvas: Cleanup canvas</property>
- <signal name="clicked" handler="on_clear_button_clicked"/>
- <child>
- <object class="GtkImage" id="image3">
- <property name="visible">True</property>
- <property name="stock">gtk-clear</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="export_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Export Image: Save image to svg file</property>
- <signal name="clicked" handler="on_export_button_clicked"/>
- <child>
- <object class="GtkImage" id="image4">
- <property name="visible">True</property>
- <property name="stock">gtk-save-as</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkVScale" id="size_scale">
- <property name="height_request">68</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="tooltip_text" translatable="yes">Line width</property>
- <property name="orientation">vertical</property>
- <property name="adjustment">adjustment1</property>
- <property name="inverted">True</property>
- <property name="digits">0</property>
- <property name="value_pos">bottom</property>
- <signal name="value_changed" handler="on_size_scale_format_value"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkColorButton" id="fg_color_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Foreground color</property>
- <property name="color">#000000000000</property>
- <signal name="color_set" handler="on_fg_color_button_color_set"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">7</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <object class="GtkImage" id="image1">
- <property name="visible">True</property>
- <property name="stock">gtk-delete</property>
- </object>
- <object class="GtkAdjustment" id="adjustment1">
- <property name="value">2</property>
- <property name="lower">1</property>
- <property name="upper">110</property>
- <property name="step_increment">1</property>
- <property name="page_increment">10</property>
- <property name="page_size">10</property>
- </object>
-</interface>