jp: refactoring using ArgParse

This commit is contained in:
Dal 2014-02-05 14:35:26 +01:00
commit f4de26e4e8
7 changed files with 631 additions and 441 deletions

263
frontends/src/jp/base.py Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
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)

View file

@ -0,0 +1,9 @@
class MissingPlugin(Exception):
pass
def require(plugin_name):
suported_plugins = ["profile"]
if plugin_name not in Supported_plugins:
raise MissingPlugin

119
frontends/src/jp/file.py Normal file
View file

@ -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()

View file

@ -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 <http://www.gnu.org/licenses/>.
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)

View file

@ -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)

71
frontends/src/jp/pipe.py Normal file
View file

@ -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()

111
frontends/src/jp/profile.py Normal file
View file

@ -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)