Commit 72db56fd authored by Yann Leboulanger's avatar Yann Leboulanger

remove not ported plugins

parent 3e7571db
from fshare import FileSharePlugin
<?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>
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()
This diff is collapsed.
# -*- 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()
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)
[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