diff --git a/frontends/src/jp/base.py b/frontends/src/jp/base.py
new file mode 100644
index 00000000..8cde080b
--- /dev/null
+++ b/frontends/src/jp/base.py
@@ -0,0 +1,263 @@
+#! /usr/bin/python
+# -*- coding: utf-8 -*-
+
+# jp: a SAT command line tool
+# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+from __future__ import with_statement
+from sat.core.i18n import _
+
+#consts
+name = u"jp"
+about = name+u""" v%s (c) Jérôme Poisson (aka Goffi) 2009, 2010, 2011, 2012, 2013, 2014
+
+---
+"""+name+u""" Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (aka Goffi)
+This program comes with ABSOLUTELY NO WARRANTY;
+This is free software, and you are welcome to redistribute it
+under certain conditions.
+---
+
+This software is a command line tool for jabber
+Get the latest version at http://www.goffi.org
+"""
+
+global pbar_available
+pbar_available = True #checked before using ProgressBar
+
+### logging ###
+import logging
+from logging import debug, info, error, warning
+logging.basicConfig(level=logging.DEBUG,
+ format='%(message)s')
+###
+
+import sys
+import os
+from os.path import abspath, basename, dirname
+from argparse import ArgumentParser
+from sat.tools.jid import JID
+import gobject
+from sat_frontends.bridge.DBus import DBusBridgeFrontend
+from sat.core.exceptions import BridgeExceptionNoService, BridgeInitError
+from sat.tools.utils import clean_ustr
+import tarfile
+import tempfile
+import shutil
+try:
+ from progressbar import ProgressBar, Percentage, Bar, ETA, FileTransferSpeed
+except ImportError, e:
+ info (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar'))
+ info (_('Progress bar deactivated\n--\n'))
+ pbar_available=False
+
+
+#version = unicode(self.bridge.getVersion())
+version = "undefined"
+parser = ArgumentParser()
+parser.add_argument('--version', action='version', version=about % version)
+subparser = parser.add_subparsers(dest='subparser_name')
+# File managment
+
+
+
+class JP(object):
+ """
+ This class can be use to establish a connection with the
+ bridge. Moreover, it should manage a main loop.
+
+ To use it, you mainly have to redefine the method run to perform
+ specify what kind of operation you want to perform.
+
+ """
+ def __init__(self, start_mainloop = False):
+ try:
+ self.bridge=DBusBridgeFrontend()
+ except BridgeExceptionNoService:
+ print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
+ sys.exit(1)
+ except BridgeInitError:
+ print(_(u"Can't init bridge"))
+ sys.exit(1)
+
+ self._start_loop = start_mainloop
+
+ def run(self):
+ raise NotImplementedError
+
+ def _run(self):
+ """Call run and lauch a loop if needed"""
+ print "You are connected!"
+ self.run()
+ if self._start_loop:
+ print "Exiting loop..."
+ self.loop.quit()
+
+ def _loop_start(self):
+ self.loop = gobject.MainLoop()
+ try:
+ self.loop.run()
+ except KeyboardInterrupt:
+ info(_("User interruption: good bye"))
+
+ def start_mainloop(self):
+ self._start_loop = True
+
+ def go(self):
+ self.run()
+ if self._start_loop:
+ self._loop_start()
+
+
+class JPWithProfile(JP):
+ """Manage a bridge (inherit from :class:`JP`), but it also adds
+ profile managment, ie, connection to the profile.
+
+ Moreover, some useful methods are predefined such as
+ :py:meth:`check_jids`. The connection to XMPP is automatically
+ managed.
+ """
+
+ def __init__(self, profile_name, start_mainloop = False):
+ JP.__init__(self, start_mainloop)
+ self.profile_name = profile_name
+
+ def check_jids(self, jids):
+ """Check jids validity, transform roster name to corresponding jids
+
+ :param profile: A profile name
+ :param jids: A list of jids
+ :rtype: A list of jids
+ """
+ names2jid = {}
+ nodes2jid = {}
+
+ for contact in self.bridge.getContacts(self.profile):
+ _jid, attr, groups = contact
+ if attr.has_key("name"):
+ names2jid[attr["name"].lower()] = _jid
+ nodes2jid[JID(_jid).node.lower()] = _jid
+
+ def expandJid(jid):
+ _jid = jid.lower()
+ if _jid in names2jid:
+ expanded = names2jid[_jid]
+ elif _jid in nodes2jid:
+ expanded = nodes2jid[_jid]
+ else:
+ expanded = jid
+ return unicode(expanded)
+
+ def check(jid):
+ if not jid.is_valid:
+ error (_("%s is not a valid JID !"), jid)
+ exit(1)
+
+ dest_jids=[]
+ try:
+ for i in range(len(jids)):
+ dest_jids.append(expandJid(jids[i]))
+ check(dest_jids[i])
+ except AttributeError:
+ pass
+
+ return dest_jids
+
+ def check_jabber_connection(self):
+ """Check that jabber status is allright"""
+ def cantConnect(arg):
+ print arg
+ error(_(u"Can't connect profile"))
+ exit(1)
+
+ self.profile = self.bridge.getProfileName(self.profile_name)
+ if not self.profile:
+ error(_("The profile asked doesn't exist"))
+ exit(1)
+
+ if self.bridge.isConnected(self.profile):
+ print "Already connected"
+ else:
+ self._start_loop = True
+ self.bridge.asyncConnect(self.profile, self._run, cantConnect)
+ return
+ self.run()
+
+
+ def _getFullJid(self, param_jid):
+ """Return the full jid if possible (add last resource when find a bare jid"""
+ _jid = JID(param_jid)
+ if not _jid.resource:
+ #if the resource is not given, we try to add the last known resource
+ last_resource = self.bridge.getLastResource(param_jid, self.profile_name)
+ if last_resource:
+ return "%s/%s" % (_jid.bare, last_resource)
+ return param_jid
+
+ def go(self):
+ self.check_jabber_connection()
+ if self._start_loop:
+ self._loop_start()
+
+
+
+class JPAsk(JPWithProfile):
+ def confirm_type(self):
+ """Must return a string containing the confirm type. For instance,
+ FILE_TRANSFER or PIPE_TRANSFER, etc.
+
+ :rtype: str
+ """
+ raise NotImplemented
+
+ def dest_jids(self):
+ return None
+
+ def _askConfirmation(self, confirm_id, confirm_type, data, profile):
+ if profile != self.profile:
+ debug("Ask confirmation ignored: not our profile")
+ return
+ if confirm_type == self.confirm_type():
+ self._confirm_id = confirm_id
+ if self.dest_jids() and not JID(data['from']).bare in [JID(_jid).bare for _jid in self.dest_jids()]:
+ return #file is not sent by a filtered jid
+ else:
+ self.ask(data)
+
+ def ask(self):
+ """
+ The return value is used to answer to the bridge.
+ :rtype: (bool, dict)
+ """
+ raise NotImplementedError
+
+ def answer(self, accepted, answer_data):
+ """
+ :param accepted: boolean
+ :param aswer_data: dict of answer datas
+ """
+ self.bridge.confirmationAnswer(self._confirm_id, False, answer_data, self.profile)
+
+ def run(self):
+ """Auto reply to confirmations requests"""
+ #we register incoming confirmation
+ self.bridge.register("askConfirmation", self._askConfirmation)
+
+ #and we ask those we have missed
+ for confirm_id, confirm_type, data in self.bridge.getWaitingConf(self.profile):
+ self._askConfirmation(confirm_id, confirm_type, data, self.profile)
+
+
diff --git a/frontends/src/jp/common.py b/frontends/src/jp/common.py
new file mode 100644
index 00000000..cbc3fd95
--- /dev/null
+++ b/frontends/src/jp/common.py
@@ -0,0 +1,9 @@
+
+
+class MissingPlugin(Exception):
+ pass
+
+def require(plugin_name):
+ suported_plugins = ["profile"]
+ if plugin_name not in Supported_plugins:
+ raise MissingPlugin
diff --git a/frontends/src/jp/file.py b/frontends/src/jp/file.py
new file mode 100644
index 00000000..720da027
--- /dev/null
+++ b/frontends/src/jp/file.py
@@ -0,0 +1,119 @@
+from logging import debug, info, error, warning
+
+import base
+import sys
+import os
+import os.path
+import tarfile
+#import tempfile
+from os.path import abspath, basename, dirname
+from sat.core.i18n import _
+
+file_parser = base.subparser.add_parser('file',
+ help = "File managment")
+
+file_parser.add_argument("-p", "--profile", action="store", type=str, default='@DEFAULT@',
+ help=_("Use PROFILE profile key (default: %(default)s)"))
+file_subparser = file_parser.add_subparsers()
+file_send = file_subparser.add_parser('send',
+ help = "Send a file to a contact")
+file_send.add_argument("file", type=str, nargs = '*',
+ help=_("A list of file"))
+file_send.add_argument("jid", type=str,
+ help=_("The destination jid"))
+file_send.add_argument("-b", "--bz2", action="store_true", default=False,
+ help=_("Make a bzip2 tarball"))
+
+file_send.set_defaults(func=lambda args : FileSend(args.profile, args.jid, args.file, args.bz2).go())
+
+
+file_recv = file_subparser.add_parser('recv',
+ help = "Receive a file to a contact")
+file_recv.add_argument("jids", type=str, nargs="*",
+ help=_("A list of destination jids"))
+file_recv.add_argument("-m", "--multiple", action="store_true", default=False,
+ help=_("Wait for a file to be sent by a contact"))
+file_recv.add_argument("-f", "--force", action="store_true", default=False,
+ help=_("Force overwritting of existing files"))
+
+file_recv.set_defaults(func=lambda args : FileRecv(args.profile,
+ args.jids,
+ args.multiple,
+ args.force).go())
+
+
+
+class FileSend(base.JPWithProfile):
+ def __init__(self, profile, dest_jid, files, bz2):
+ base.JPWithProfile.__init__(self,profile)
+ self.dest_jid = dest_jid
+ self.files = files
+ self.bz2 = bz2
+
+ def send_files(self):
+ """Send files to jabber contact"""
+ for file in self.files:
+ if not os.path.exists(file):
+ error (_(u"File [%s] doesn't exist !") % file)
+ exit(1)
+ if not self.bz2 and os.path.isdir(file):
+ error (_("[%s] is a dir ! Please send files inside or use compression") % file)
+ exit(1)
+
+ full_dest_jid = self._getFullJid(self.dest_jid)
+ if self.bz2:
+ tmpfile = (basename(self.files[0]) or basename(dirname(self.files[0])) ) + '.tar.bz2' #FIXME: tmp, need an algorithm to find a good name/path
+ if os.path.exists(tmpfile):
+ error (_("tmp file (%s) already exists ! Please remove it"), tmpfile)
+ exit(1)
+ warning(_("bz2 is an experimental option at an early dev stage, use with caution"))
+ #FIXME: check free space, writting perm, tmp dir, filename (watch for OS used)
+ info(_("Starting compression, please wait..."))
+ sys.stdout.flush()
+ bz2=tarfile.open(tmpfile, "w:bz2")
+ for file in self.files:
+ info(_("Adding %s"), file)
+ bz2.add(file)
+ bz2.close()
+ info(_("OK !"))
+ path = abspath(tmpfile)
+ self.transfer_data = self.bridge.sendFile(full_dest_jid, path, {}, self.profile)
+ else:
+ for file in self.files:
+ path = abspath(file)
+ self.transfer_data = self.bridge.sendFile(full_dest_jid, path, {}, self.profile) #FIXME: show progress only for last transfer_id
+
+ def run(self):
+ self.send_files()
+
+
+class FileRecv(base.JPAsk):
+ def __init__(self, profile, dest_jids, multiple, force, progress = False):
+ base.JPAsk.__init__(self,profile, start_mainloop = True)
+ self._dest_jids = dest_jids
+ self.multiple = multiple
+ self.force = force
+ self.progress = progress
+
+ def dest_jids(self):
+ return self._dest_jids
+
+ def confirm_type(self):
+ return "FILE_TRANSFER"
+
+ def ask(self, data):
+ answer_data = {}
+ answer_data["dest_path"] = os.getcwd()+'/'+data['filename']
+
+ if self.force or not os.path.exists(answer_data["dest_path"]):
+ self.answer(True, answer_data)
+ info(_("Accepted file [%(filename)s] from %(sender)s") % {'filename':data['filename'], 'sender':data['from']})
+ # self.transfer_data = self.confirm_id # Used by progress
+ else:
+ self.answer(False, answer_data)
+ warning(_("Refused file [%(filename)s] from %(sender)s: a file with the same name already exist") % {'filename':data['filename'], 'sender':data['from']})
+
+
+ if not self.multiple and not self.progress:
+ #we just accept one file
+ self.loop.quit()
diff --git a/frontends/src/jp/jp b/frontends/src/jp/jp
index 93853217..5cfad101 100755
--- a/frontends/src/jp/jp
+++ b/frontends/src/jp/jp
@@ -1,444 +1,12 @@
-#! /usr/bin/python
-# -*- coding: utf-8 -*-
-
-# jp: a SAT command line tool
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-
-from __future__ import with_statement
-from sat.core.i18n import _
-
-#consts
-name = u"jp"
-about = name+u""" v%s (c) Jérôme Poisson (aka Goffi) 2009, 2010, 2011, 2012
-
----
-"""+name+u""" Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (aka Goffi)
-This program comes with ABSOLUTELY NO WARRANTY;
-This is free software, and you are welcome to redistribute it
-under certain conditions.
----
-
-This software is a command line tool for jabber
-Get the latest version at http://www.goffi.org
-"""
-
-global pbar_available
-pbar_available = True #checked before using ProgressBar
-
-### logging ###
-import logging
-from logging import debug, info, error, warning
-logging.basicConfig(level=logging.DEBUG,
- format='%(message)s')
-###
-
-import sys
-import os
-from os.path import abspath, basename, dirname
-from optparse import OptionParser
-from sat.tools.jid import JID
-import gobject
-from sat_frontends.bridge.DBus import DBusBridgeFrontend
-from sat.core.exceptions import BridgeExceptionNoService, BridgeInitError
-from sat.tools.utils import clean_ustr
-import tarfile
-import tempfile
-import shutil
-try:
- from progressbar import ProgressBar, Percentage, Bar, ETA, FileTransferSpeed
-except ImportError, e:
- info (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar'))
- info (_('Progress bar deactivated\n--\n'))
- pbar_available=False
-
-
-
-
-class JP(object):
- def __init__(self):
- try:
- self.bridge=DBusBridgeFrontend()
- except BridgeExceptionNoService:
- print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
- sys.exit(1)
- except BridgeInitError:
- print(_(u"Can't init bridge"))
- sys.exit(1)
- self.transfer_data = None
-
- def check_options(self):
- """Check command line options"""
- usage=_("""
- %prog [options] [FILE1 FILE2 ...] JID
- %prog -w [options] [JID1 JID2 ...]
-
- %prog --help for options list
- """)
- version = unicode(self.bridge.getVersion())
- parser = OptionParser(usage=usage,version=about % version)
-
- parser.add_option("-p", "--profile", action="store", type="string", default='@DEFAULT@',
- help=_("Use PROFILE profile key (default: %default)"))
- parser.add_option("-b", "--bz2", action="store_true", default=False,
- help=_("Make a bzip2 tarball"))
- parser.add_option("-w", "--wait-file", action="store_true", default=False,
- help=_("Wait for a file to be sent by a contact"))
- parser.add_option("-m", "--multiple", action="store_true", default=False,
- help=_("Accept multiple files (you'll have to stop manually)"))
- parser.add_option("-f", "--force", action="store_true", default=False,
- help=_("Force overwritting of existing files"))
- parser.add_option("-g", "--progress", action="store_true", default=False,
- help=_("Show progress bar"))
- parser.add_option("-s", "--separate", action="store_true", default=False,
- help=_("Separate xmpp messages: send one message per line instead of one message alone."))
- parser.add_option("-n", "--new-line", action="store_true", default=False,
- help=_("Add a new line at the beginning of the input (usefull for ascii art ;))"))
- parser.add_option("--list-profiles", action="store_true", default=False,
- help=_("List available profiles"))
- parser.add_option("-c", "--create-profile", action="store", type="string", nargs=3,
- help=_("Create a profile (args: profile_name jid password)"))
- parser.add_option("--get-profile", action="store", type="string",
- help=_("Get profile informations (arg: profile_name)"))
- parser.add_option("--rm-profile", action="store", type="string",
- help=_("Remove profile"))
- parser.add_option("--connect", action="store_true", default=False,
- help=_("Connect the profile before doing anything else"))
- parser.add_option("--pipe-in", action="store_true", default=False,
- help=_("Wait for the reception of a pipe stream"))
- parser.add_option("--pipe-out", action="store_true", default=False,
- help=_("Pipe a stream out "))
-
- (self.options, args) = parser.parse_args()
- if self.options.list_profiles:
- for p in self.bridge.getProfilesList():
- info(p)
- exit(0)
- if self.options.create_profile or self.options.get_profile:
- self.start_loop = True
- return args
- if self.options.rm_profile:
- self.start_loop = False
- return args
-
- if len(args) < 1 and not self.options.wait_file:
- parser.error(_("You must specify the destination JID (Jabber ID)").encode('utf-8'))
-
- if self.options.wait_file or self.options.pipe_in:
- #several jid
- self.dest_jids = [arg.decode('utf-8') for arg in args]
- else:
- #one dest_jid, other args are files
- self.dest_jid = JID(args[-1].decode('utf-8'))
- self.files = args[:-1]
-
- if not pbar_available and self.options.progress:
- self.options.progress = False
- error (_("Option progress is not available, deactivated."))
-
- if self.options.progress or self.options.wait_file or self.options.connect or self.options.pipe_in:
- self.start_loop = True #We have to use loop for these options
- else:
- self.start_loop = False
-
-
- return args
-
- def create_profile(self):
- """Create a new profile"""
- profile, jid, password = self.options.create_profile
- if profile in self.bridge.getProfilesList():
- error("Profile %s already exists."%profile)
- exit(1)
- self.bridge.asyncCreateProfile(profile, lambda : self._create_profile(profile, jid, password), None)
-
- def get_profile(self):
- def setJID(jid):
- info("jid: %s"%jid)
- self.bridge.asyncGetParamA("Password", "Connection", profile_key=profile_name, callback=setPassword)
- def setPassword(password):
- info("pwd: %s"%password)
- self.loop.quit()
- profile_name = self.options.get_profile
- if profile_name not in self.bridge.getProfilesList():
- error("Profile %s doesn't exist."%profile_name)
- exit(1)
- self.bridge.asyncGetParamA("JabberID", "Connection", profile_key=profile_name, callback=setJID)
-
- def rm_profile(self):
- profile_name = self.options.rm_profile
- if profile_name not in self.bridge.getProfilesList():
- error("Profile %s doesn't exist."%profile_name)
- exit(1)
- self.bridge.deleteProfile(profile_name)
-
- def _create_profile(self, profile_name, jid, password):
- self.bridge.setParam("JabberID", jid, "Connection" ,profile_key=profile_name)
- self.bridge.setParam("Server", JID(jid).domain, "Connection", profile_key=profile_name)
- self.bridge.setParam("Password", password, "Connection", profile_key=profile_name)
- self.loop.quit()
-
- def check_jabber_status(self):
- """Check that jabber status is allright"""
- def cantConnect():
- error(_(u"Can't connect profile"))
- exit(1)
-
-
- self.profile = self.bridge.getProfileName(self.options.profile)
- if not self.profile:
- error(_("The profile asked doesn't exist"))
- exit(1)
-
- if self.options.connect: #if connection is asked, we connect the profile
- self.bridge.asyncConnect(self.profile, self.connected, cantConnect)
- return
- elif not self.bridge.isConnected(self.profile):
- error(_(u"Profile [%(profile)s] is not connected, please connect it before using jp, or use --connect option") % { "profile": self.profile })
- exit(1)
-
- self.connected()
-
- def check_jids(self):
- """Check jids validity, transform roster name to corresponding jids"""
- names2jid = {}
- nodes2jid = {}
-
- for contact in self.bridge.getContacts(self.options.profile):
- _jid, attr, groups = contact
- if attr.has_key("name"):
- names2jid[attr["name"].lower()] = _jid
- nodes2jid[JID(_jid).node.lower()] = _jid
-
- def expandJid(jid):
- _jid = jid.lower()
- if _jid in names2jid:
- expanded = names2jid[_jid]
- elif _jid in nodes2jid:
- expanded = nodes2jid[_jid]
- else:
- expanded = jid
- return unicode(expanded)
-
- def check(jid):
- if not jid.is_valid:
- error (_("%s is not a valid JID !"), self.dest_jid)
- exit(1)
-
- try:
- self.dest_jid = expandJid(self.dest_jid)
- check(self.dest_jid)
- except AttributeError:
- pass
- try:
- for i in range(len(self.dest_jids)):
- self.dest_jids[i] = expandJid(self.dest_jids[i])
- check(self.dest_jids[i])
- except AttributeError:
- pass
-
-
- def send_stdin(self):
- """Send incomming data on stdin to jabber contact"""
- header = "\n" if self.options.new_line else ""
-
- if self.options.separate: #we send stdin in several messages
- if header:
- self.bridge.sendMessage(self.dest_jid, header, profile_key=self.profile, callback=lambda: None, errback=lambda ignore: ignore)
- while (True):
- line = clean_ustr(sys.stdin.readline().decode('utf-8','ignore'))
- if not line:
- break
- self.bridge.sendMessage(self.dest_jid, line.replace("\n",""), profile_key=self.profile, callback=lambda: None, errback=lambda ignore: ignore)
- else:
- self.bridge.sendMessage(self.dest_jid, header + clean_ustr(u"".join([stream.decode('utf-8','ignore') for stream in sys.stdin.readlines()])),
- profile_key=self.profile, callback=lambda: None, errback=lambda ignore: ignore)
-
-
- def pipe_out(self):
- """Create named pipe, and send stdin to it"""
- tmp_dir = tempfile.mkdtemp()
- fifopath = os.path.join(tmp_dir,"pipe_out")
- os.mkfifo(fifopath)
- self.bridge.pipeOut(self._getFullJid(self.dest_jid), fifopath, {}, self.profile)
- with open(fifopath, 'w') as f:
- shutil.copyfileobj(sys.stdin, f)
- shutil.rmtree(tmp_dir)
-
-
- def send_files(self):
- """Send files to jabber contact"""
-
- for file in self.files:
- if not os.path.exists(file):
- error (_(u"File [%s] doesn't exist !") % file)
- exit(1)
- if not self.options.bz2 and os.path.isdir(file):
- error (_("[%s] is a dir ! Please send files inside or use compression") % file)
- exit(1)
-
- full_dest_jid = self._getFullJid(self.dest_jid)
- if self.options.bz2:
- tmpfile = (basename(self.files[0]) or basename(dirname(self.files[0])) ) + '.tar.bz2' #FIXME: tmp, need an algorithm to find a good name/path
- if os.path.exists(tmpfile):
- error (_("tmp file (%s) already exists ! Please remove it"), tmpfile)
- exit(1)
- warning(_("bz2 is an experimental option at an early dev stage, use with caution"))
- #FIXME: check free space, writting perm, tmp dir, filename (watch for OS used)
- info(_("Starting compression, please wait..."))
- sys.stdout.flush()
- bz2=tarfile.open(tmpfile, "w:bz2")
- for file in self.files:
- info(_("Adding %s"), file)
- bz2.add(file)
- bz2.close()
- info(_("OK !"))
- path = abspath(tmpfile)
- self.transfer_data = self.bridge.sendFile(full_dest_jid, path, {}, self.profile)
- else:
- for file in self.files:
- path = abspath(file)
- self.transfer_data = self.bridge.sendFile(full_dest_jid, path, {}, self.profile) #FIXME: show progress only for last transfer_id
-
-
- def _getFullJid(self, param_jid):
- """Return the full jid if possible (add last resource when find a bare jid"""
- _jid = JID(param_jid)
- if not _jid.resource:
- #if the resource is not given, we try to add the last known resource
- last_resource = self.bridge.getLastResource(param_jid, self.options.profile)
- if last_resource:
- return "%s/%s" % (_jid.bare, last_resource)
- return param_jid
-
-
- def askConfirmation(self, confirm_id, confirm_type, data, profile):
- """CB used for file transfer, accept files depending on parameters"""
- if profile != self.profile:
- debug("Ask confirmation ignored: not our profile")
- return
- answer_data={}
- if confirm_type == "FILE_TRANSFER":
- if not self.options.wait_file:
- return
- if self.dest_jids and not JID(data['from']).bare in [JID(_jid).bare for _jid in self.dest_jids]:
- return #file is not sent by a filtered jid
-
- answer_data["dest_path"] = os.getcwd()+'/'+data['filename']
-
- if self.options.force or not os.path.exists(answer_data["dest_path"]):
- self.bridge.confirmationAnswer(confirm_id, True, answer_data, profile)
- info(_("Accepted file [%(filename)s] from %(sender)s") % {'filename':data['filename'], 'sender':data['from']})
- self.transfer_data = confirm_id
- else:
- self.bridge.confirmationAnswer(confirm_id, False, answer_data, profile)
- warning(_("Refused file [%(filename)s] from %(sender)s: a file with the same name already exist") % {'filename':data['filename'], 'sender':data['from']})
-
-
- if not self.options.multiple and not self.options.progress:
- #we just accept one file
- self.loop.quit()
- elif confirm_type == "PIPE_TRANSFER":
- if not self.options.pipe_in:
- return
- if self.dest_jids and not JID(data['from']).bare in [JID(_jid).bare for _jid in self.dest_jids]:
- return #pipe stream is not sent by a filtered jid
-
- tmp_dir = tempfile.mkdtemp()
- fifopath = os.path.join(tmp_dir,"pipe_in")
- answer_data["dest_path"] = fifopath
- os.mkfifo(fifopath)
- self.bridge.confirmationAnswer(confirm_id, True, answer_data, profile)
- with open(fifopath, 'r') as f:
- shutil.copyfileobj(f, sys.stdout)
- shutil.rmtree(tmp_dir)
- self.loop.quit()
-
-
- def actionResult(self, action_type, action_id, data, profile):
- #FIXME
- info (_("FIXME: actionResult not implemented"))
-
- def confirmation_reply(self):
- """Auto reply to confirmations requests"""
- #we register incoming confirmation
- self.bridge.register("askConfirmation", self.askConfirmation)
-
- #and we ask those we have missed
- for confirm_id, confirm_type, data in self.bridge.getWaitingConf(self.profile):
- self.askConfirmation(confirm_id, confirm_type, data, self.profile)
-
- def progressCB(self):
- if self.transfer_data:
- transfer_id = self.transfer_data
- data = self.bridge.getProgress(transfer_id, self.profile)
- if data:
- if not data['position']:
- data['position'] = '0'
- if not self.pbar:
- #first answer, we must construct the bar
- self.pbar = ProgressBar(int(data['size']),[_("Progress: "),Percentage()," ",Bar()," ",FileTransferSpeed()," ",ETA()])
- self.pbar.start()
-
- self.pbar.update(int(data['position']))
- elif self.pbar:
- self.pbar.finish()
- if not self.options.multiple:
- self.loop.quit()
- return False
-
- return True
-
- def go(self):
- self.check_options()
- if self.options.create_profile:
- self.create_profile()
- elif self.options.get_profile:
- self.get_profile()
- elif self.options.rm_profile:
- self.rm_profile()
- else:
- self.check_jabber_status()
- if self.start_loop:
- self.loop = gobject.MainLoop()
- try:
- self.loop.run()
- except KeyboardInterrupt:
- info(_("User interruption: good bye"))
-
- def connected(self):
- """This is called when the profile is connected"""
- self.check_jids()
- if self.options.wait_file or self.options.pipe_in:
- self.confirmation_reply()
- else:
- if self.files:
- self.send_files()
- elif self.options.pipe_out:
- self.pipe_out()
- else:
- self.send_stdin()
-
- if self.options.progress:
- self.pbar = None
- gobject.timeout_add(10, self.progressCB)
-
- if self.start_loop and not self.options.progress and not self.options.wait_file and not self.options.pipe_in:
- self.loop.quit()
+#!/usr/bin/python
+import base
+import message
+import pipe
+import profile
+import file
if __name__ == "__main__":
- jp = JP()
- jp.go()
+ args = base.parser.parse_args()
+ args.func(args)
+
diff --git a/frontends/src/jp/message.py b/frontends/src/jp/message.py
new file mode 100644
index 00000000..012e1157
--- /dev/null
+++ b/frontends/src/jp/message.py
@@ -0,0 +1,49 @@
+import base
+import sys
+from sat.core.i18n import _
+
+message_parser = base.subparser.add_parser('message', help = "Send a message to a contact")
+message_parser.add_argument("-p", "--profile", action="store", type=str, default='@DEFAULT@',
+ help=_("Use PROFILE profile key (default: %(default)s)"))
+
+message_parser.add_argument("-s", "--separate", action="store_true", default=False,
+ help=_("Separate xmpp messages: send one message per line instead of one message alone."))
+message_parser.add_argument("-n", "--new-line", action="store_true", default=False,
+ help=_("Add a new line at the beginning of the input (usefull for ascii art ;))"))
+message_parser.add_argument("jid", type=str,
+ help=_("The destination jid"))
+
+def launch(args):
+ profile = args.profile
+ jp = Message(profile, args.jid, args.new_line, args.separate)
+ jp.go()
+
+message_parser.set_defaults(func=launch)
+
+class Message(base.JPWithProfile):
+ def __init__(self, profile, dest_jid, new_line = False, separate = False):
+ base.JPWithProfile.__init__(self,profile)
+ self.dest_jid = dest_jid
+ self.new_line = new_line
+ self.separate = separate
+
+
+ def send_stdin(self, dest_jid , new_line = False, separate = False):
+ """Send incomming data on stdin to jabber contact"""
+ header = "\n" if new_line else ""
+
+ if separate: #we send stdin in several messages
+ if header:
+ self.bridge.sendMessage(dest_jid, header, profile_key=self.profile, callback=lambda: None, errback=lambda ignore: ignore)
+ while (True):
+ line = base.clean_ustr(sys.stdin.readline().decode('utf-8','ignore'))
+ if not line:
+ break
+ self.bridge.sendMessage(dest_jid, line.replace("\n",""), profile_key=self.profile, callback=lambda: None, errback=lambda ignore: ignore)
+ else:
+ self.bridge.sendMessage(dest_jid, header + base.clean_ustr(u"".join([stream.decode('utf-8','ignore') for stream in sys.stdin.readlines()])), profile_key=self.profile, callback=lambda: None, errback=lambda ignore: ignore)
+
+ def run(self):
+ jids = self.check_jids([self.dest_jid])
+ jid = jids[0]
+ self.send_stdin(jid, self.new_line, self.separate)
diff --git a/frontends/src/jp/pipe.py b/frontends/src/jp/pipe.py
new file mode 100644
index 00000000..f0d53109
--- /dev/null
+++ b/frontends/src/jp/pipe.py
@@ -0,0 +1,71 @@
+import base
+
+import tempfile
+import os
+import shutil
+from sat.core.i18n import _
+
+
+parser = base.subparser.add_parser('pipe',
+ help = "File managment")
+
+parser.add_argument("-p", "--profile", action="store", type=str, default='@DEFAULT@',
+ help=_("Use PROFILE profile key (default: %(default)s)"))
+
+subparser = parser.add_subparsers()
+pipein = subparser.add_parser('out',
+ help = "Send a pipe to a contact")
+pipein.add_argument("jid", type=str,
+ help=_("The destination jid"))
+
+pipein.set_defaults(func=lambda args : PipeOut(args.profile, args.jid).go())
+
+pipeout = subparser.add_parser('in',
+ help = "Send a pipe to a contact")
+pipeout.add_argument("jids", type=str, nargs="*",
+ help=_("The destination jid"))
+
+pipeout.set_defaults(func=lambda args : PipeIn(args.profile, args.jids).go())
+
+class PipeOut(base.JPWithProfile):
+ def __init__(self, profile, dest_jid):
+ base.JPWithProfile.__init__(self,profile)
+ self.dest_jid = dest_jid
+
+ def pipe_out(self, dest_jid):
+ """Create named pipe, and send stdin to it"""
+ tmp_dir = tempfile.mkdtemp()
+ fifopath = os.path.join(tmp_dir,"pipe_out")
+ os.mkfifo(fifopath)
+ self.bridge.pipeOut(self._getFullJid(dest_jid), fifopath, {}, self.profile)
+ with open(fifopath, 'w') as f:
+ shutil.copyfileobj(sys.stdin, f)
+ shutil.rmtree(tmp_dir)
+
+ def run(self):
+ jids = self.check_jids([self.dest_jid])
+ jid = jids[0]
+ self.pipe_out(jid)
+
+class PipeIn(base.JPAsk):
+ def __init__(self, profile, dest_jids):
+ base.JPAsk.__init__(self,profile, start_mainloop = True)
+ self._dest_jids = dest_jids
+
+ def dest_jids(self):
+ return self._dest_jids
+
+ def confirm_type(self):
+ return "PIPE_TRANSFER"
+
+ def ask(self, data):
+ answer_data = {}
+ tmp_dir = tempfile.mkdtemp()
+ fifopath = os.path.join(tmp_dir,"pipe_in")
+ answer_data["dest_path"] = fifopath
+ os.mkfifo(fifopath)
+ self.answer(True, answer_data)
+ with open(fifopath, 'r') as f:
+ shutil.copyfileobj(f, sys.stdout)
+ shutil.rmtree(tmp_dir)
+ self.loop.quit()
diff --git a/frontends/src/jp/profile.py b/frontends/src/jp/profile.py
new file mode 100644
index 00000000..74d4e1b4
--- /dev/null
+++ b/frontends/src/jp/profile.py
@@ -0,0 +1,111 @@
+"""This module permits to manage profiles. It can list, create, delete
+and retrieve informations about a profile."""
+
+import base
+import sys
+
+from logging import debug, info, error, warning
+from sat.core.i18n import _
+
+profile_parser = base.subparser.add_parser('profile',
+ help = "Profile Commands")
+profile_subparser = profile_parser.add_subparsers()
+
+################################################################################
+# DELETE #
+################################################################################
+profile_delete = profile_subparser.add_parser('delete',
+ help = "Delete a profile")
+profile_delete.add_argument('profile', type=str,
+ help='the name of the profile')
+profile_delete.set_defaults(
+ func = lambda args : ProfileDelete(args.profile).go())
+
+class ProfileDelete(base.JP):
+ def __init__(self, profile_name):
+ base.JP.__init__(self)
+ self.profile_name = profile_name
+
+ def run(self):
+ if self.profile_name not in self.bridge.getProfilesList():
+ error("Profile %s doesn't exist."%self.profile_name)
+ exit(1)
+ self.bridge.deleteProfile(self.profile_name)
+
+################################################################################
+# INFO #
+################################################################################
+profile_info = profile_subparser.add_parser('info',
+ help = "Get informations about a profile")
+profile_info.add_argument('profile', type=str,
+ help='the name of the profile')
+profile_info.set_defaults(
+ func = lambda args : ProfileInfo(args.profile).go())
+
+class ProfileInfo(base.JP):
+ def __init__(self, profile_name):
+ base.JP.__init__(self)
+ self.profile_name = profile_name
+
+ def run(self):
+ def getJID(jid):
+ info("jid: %s"%jid)
+ self.bridge.asyncGetParamA("Password", "Connection", profile_key=self.profile_name, callback=getPassword)
+ def getPassword(password):
+ info("pwd: %s"%password)
+ self.loop.quit()
+ if self.profile_name not in self.bridge.getProfilesList():
+ error("Profile %s doesn't exist."%self.profile_name)
+ exit(1)
+
+ self.start_mainloop()
+ self.bridge.asyncGetParamA("JabberID", "Connection", profile_key=self.profile_name, callback=getJID)
+
+################################################################################
+# LIST #
+################################################################################
+profile_list = profile_subparser.add_parser('list',
+ help = "List profiles")
+profile_list.set_defaults(
+ func = lambda args : ProfileList().go())
+
+class ProfileList(base.JP):
+ def run(self):
+ for p in self.bridge.getProfilesList():
+ info(p)
+
+################################################################################
+# CREATE #
+################################################################################
+create_parser = profile_subparser.add_parser('create',
+ help = "Create a new profile")
+create_parser.add_argument('profile', type=str,
+ help='the name of the profile')
+create_parser.add_argument('jid', type=str,
+ help='the jid of the profile')
+create_parser.add_argument('password', type=str,
+ help='the password of the profile')
+create_parser.set_defaults(
+ func = lambda args : ProfileCreate(args.profile, args.jid, args.password).go())
+
+class ProfileCreate(base.JP):
+ def __init__(self, profile_name, jid, password):
+ base.JP.__init__(self)
+ self.profile_name = profile_name
+ self.jid = jid
+ self.password = password
+
+ def _create_profile(self, profile_name, jid, password):
+ self.bridge.setParam("JabberID", jid, "Connection" ,profile_key=profile_name)
+ self.bridge.setParam("Server", base.JID(jid).domain, "Connection", profile_key=profile_name)
+ self.bridge.setParam("Password", password, "Connection", profile_key=profile_name)
+ self.loop.quit()
+
+ def run(self):
+ """Create a new profile"""
+ if self.profile_name in self.bridge.getProfilesList():
+ error("Profile %s already exists."%self.profile_name)
+ exit(1)
+ self.start_mainloop()
+ self.bridge.asyncCreateProfile(self.profile_name, lambda : self._create_profile(self.profile_name, self.jid, self.password), None)
+