jp: refactoring:
- imports from sat_frontends.jp instead of local imports
- added __init__.py
- commands now inherits from a base class: each base.CommandBase instance is a subcommand
- new arguments are added in CommandBase.add_parser_options methods, starting point si CommandBase.run or CommandBase.connected if a profile connection is needed
- commands are exported using a __commands__ variable at the top of the module
- sub-subcommand are easily added by using an other CommandBase instance as parent instead of using a Jp instance. In this case, the parent subcommand must be the one exported, and have a subcommands iterable (see cmd_file or cmd_pipe for examples).
- options which are often used (like --profile) are automatically added on demand (use_profile=True, use_progress=True)
- commands are automatically loaded when there are in a module named cmd_XXX
- restored --connect option
- restored progress bar
- restored getVersion bridge call on jp --version
- fixed file and pipe commands
- fixed forgotten translations
- fixed non SàT compliant docstrings
- better about/version dialog
This commit is contained in:
parent
119ce72819
commit
7ca3c657f4
12 changed files with 662 additions and 590 deletions
0
frontends/src/jp/__init__.py
Normal file
0
frontends/src/jp/__init__.py
Normal file
|
|
@ -17,24 +17,8 @@
|
|||
# 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
|
||||
|
||||
|
|
@ -46,35 +30,40 @@ logging.basicConfig(level=logging.DEBUG,
|
|||
###
|
||||
|
||||
import sys
|
||||
import os
|
||||
from os.path import abspath, basename, dirname
|
||||
from argparse import ArgumentParser
|
||||
from sat.tools.jid import JID
|
||||
import locale
|
||||
import os.path
|
||||
import argparse
|
||||
import gobject
|
||||
from glob import iglob
|
||||
from importlib import import_module
|
||||
from sat.tools.jid import JID
|
||||
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
|
||||
from sat.core import exceptions
|
||||
import sat_frontends.jp
|
||||
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
|
||||
import progressbar
|
||||
except ImportError:
|
||||
info (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar'))
|
||||
info (_('Progress bar deactivated\n--\n'))
|
||||
progressbar=None
|
||||
|
||||
#consts
|
||||
prog_name = u"jp"
|
||||
description = """This software is a command line tool for XMPP.
|
||||
Get the latest version at http://sat.goffi.org"""
|
||||
|
||||
copyleft = 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.
|
||||
"""
|
||||
|
||||
|
||||
#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
|
||||
def unicode_decoder(arg):
|
||||
# Needed to have unicode strings from arguments
|
||||
return arg.decode(locale.getpreferredencoding())
|
||||
|
||||
|
||||
|
||||
class JP(object):
|
||||
class Jp(object):
|
||||
"""
|
||||
This class can be use to establish a connection with the
|
||||
bridge. Moreover, it should manage a main loop.
|
||||
|
|
@ -83,64 +72,112 @@ class JP(object):
|
|||
specify what kind of operation you want to perform.
|
||||
|
||||
"""
|
||||
def __init__(self, start_mainloop = False):
|
||||
def __init__(self):
|
||||
try:
|
||||
self.bridge=DBusBridgeFrontend()
|
||||
except BridgeExceptionNoService:
|
||||
self.bridge = DBusBridgeFrontend()
|
||||
except exceptions.BridgeExceptionNoService:
|
||||
print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
|
||||
sys.exit(1)
|
||||
except BridgeInitError:
|
||||
except excpetions.BridgeInitError:
|
||||
print(_(u"Can't init bridge"))
|
||||
sys.exit(1)
|
||||
|
||||
self._start_loop = start_mainloop
|
||||
self.parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=description)
|
||||
|
||||
def run(self):
|
||||
raise NotImplementedError
|
||||
self._make_parents()
|
||||
self.add_parser_options()
|
||||
self.subparsers = self.parser.add_subparsers(title=_('Available commands'), dest='subparser_name')
|
||||
self._auto_loop = False # when loop is used for internal reasons
|
||||
self.need_loop = False # to set by commands when loop is needed
|
||||
self._progress_id = None # TODO: manage several progress ids
|
||||
self.quit_on_progress_end = True # set to False if you manage yourself exiting, or if you want the user to stop by himself
|
||||
|
||||
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()
|
||||
@property
|
||||
def version(self):
|
||||
return self.bridge.getVersion()
|
||||
|
||||
def _loop_start(self):
|
||||
@property
|
||||
def progress_id(self):
|
||||
return self._progress_id
|
||||
|
||||
@progress_id.setter
|
||||
def progress_id(self, value):
|
||||
self._progress_id = value
|
||||
|
||||
def _make_parents(self):
|
||||
self.parents = {}
|
||||
|
||||
profile_parent = self.parents['profile'] = argparse.ArgumentParser(add_help=False)
|
||||
profile_parent.add_argument("-p", "--profile", action="store", type=str, default='@DEFAULT@', help=_("Use PROFILE profile key (default: %(default)s)"))
|
||||
profile_parent.add_argument("-c", "--connect", action="store_true", help=_("Connect the profile before doing anything else"))
|
||||
|
||||
progress_parent = self.parents['progress'] = argparse.ArgumentParser(add_help=False)
|
||||
if progressbar:
|
||||
progress_parent.add_argument("-g", "--progress", action="store_true", help=_("Show progress bar"))
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument('--version', action='version', version=("%(name)s %(version)s %(copyleft)s" % {'name': prog_name, 'version': self.version, 'copyleft': copyleft}))
|
||||
|
||||
def import_commands(self):
|
||||
""" Automaticaly import commands to jp
|
||||
looks from modules names cmd_*.py in jp path and import them
|
||||
|
||||
"""
|
||||
path = os.path.dirname(sat_frontends.jp.__file__)
|
||||
modules = (os.path.splitext(module)[0] for module in map(os.path.basename, iglob(os.path.join(path, "cmd_*.py"))))
|
||||
for module_name in modules:
|
||||
module = import_module("sat_frontends.jp."+module_name)
|
||||
try:
|
||||
self.import_command_module(module)
|
||||
except ImportError:
|
||||
continue
|
||||
|
||||
def import_command_module(self, module):
|
||||
""" Add commands from a module to jp
|
||||
@param module: module containing commands
|
||||
|
||||
"""
|
||||
try:
|
||||
for classname in module.__commands__:
|
||||
cls = getattr(module, classname)
|
||||
except AttributeError:
|
||||
warning(_("Invalid module %s") % module)
|
||||
raise ImportError
|
||||
cls(self)
|
||||
|
||||
|
||||
def run(self, args=None):
|
||||
self.args = self.parser.parse_args(args)
|
||||
self.args.func()
|
||||
if self.need_loop or self._auto_loop:
|
||||
self._start_loop()
|
||||
|
||||
def _start_loop(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 stop_loop(self):
|
||||
try:
|
||||
self.loop.quit()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
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 quit(self, errcode=0):
|
||||
self.stop_loop()
|
||||
if errcode:
|
||||
sys.exit(errcode)
|
||||
|
||||
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
|
||||
@param profile: profile name
|
||||
@param jids: list of jids
|
||||
@return: List of jids
|
||||
|
||||
"""
|
||||
names2jid = {}
|
||||
nodes2jid = {}
|
||||
|
|
@ -151,7 +188,7 @@ class JPWithProfile(JP):
|
|||
names2jid[attr["name"].lower()] = _jid
|
||||
nodes2jid[JID(_jid).node.lower()] = _jid
|
||||
|
||||
def expandJid(jid):
|
||||
def expand_jid(jid):
|
||||
_jid = jid.lower()
|
||||
if _jid in names2jid:
|
||||
expanded = names2jid[_jid]
|
||||
|
|
@ -164,100 +201,204 @@ class JPWithProfile(JP):
|
|||
def check(jid):
|
||||
if not jid.is_valid:
|
||||
error (_("%s is not a valid JID !"), jid)
|
||||
exit(1)
|
||||
self.quit(1)
|
||||
|
||||
dest_jids=[]
|
||||
try:
|
||||
for i in range(len(jids)):
|
||||
dest_jids.append(expandJid(jids[i]))
|
||||
dest_jids.append(expand_jid(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
|
||||
def connect_profile(self, callback):
|
||||
""" Check if the profile is connected
|
||||
@param callback: method to call when profile is connected
|
||||
@exit: - 1 when profile is not connected and --connect is not set
|
||||
- 1 when the profile doesn't exists
|
||||
- 1 when there is a connection error
|
||||
"""
|
||||
# FIXME: need better exit codes
|
||||
|
||||
def cant_connect():
|
||||
error(_(u"Can't connect profile"))
|
||||
exit(1)
|
||||
self.quit(1)
|
||||
|
||||
self.profile = self.bridge.getProfileName(self.args.profile)
|
||||
|
||||
self.profile = self.bridge.getProfileName(self.profile_name)
|
||||
if not self.profile:
|
||||
error(_("The profile asked doesn't exist"))
|
||||
exit(1)
|
||||
error(_("The profile [%s] doesn't exist") % self.args.profile)
|
||||
self.quit(1)
|
||||
|
||||
if self.bridge.isConnected(self.profile):
|
||||
print "Already connected"
|
||||
else:
|
||||
self._start_loop = True
|
||||
self.bridge.asyncConnect(self.profile, self._run, cantConnect)
|
||||
if self.args.connect: #if connection is asked, we connect the profile
|
||||
self.bridge.asyncConnect(self.profile, callback, cant_connect)
|
||||
self._auto_loop = True
|
||||
return
|
||||
self.run()
|
||||
|
||||
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 })
|
||||
self.quit(1)
|
||||
|
||||
def _getFullJid(self, param_jid):
|
||||
"""Return the full jid if possible (add last resource when find a bare jid"""
|
||||
callback()
|
||||
|
||||
def get_full_jid(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)
|
||||
last_resource = self.bridge.getLastResource(param_jid, self.profile)
|
||||
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()
|
||||
def watch_progress(self):
|
||||
self.pbar = None
|
||||
gobject.timeout_add(10, self._progress_cb)
|
||||
|
||||
def _progress_cb(self):
|
||||
if self.progress_id:
|
||||
data = self.bridge.getProgress(self.progress_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.ProgressBar(int(data['size']),
|
||||
[_("Progress: "),progressbar.Percentage(),
|
||||
" ",
|
||||
progressbar.Bar(),
|
||||
" ",
|
||||
progressbar.FileTransferSpeed(),
|
||||
" ",
|
||||
progressbar.ETA()])
|
||||
self.pbar.start()
|
||||
|
||||
self.pbar.update(int(data['position']))
|
||||
|
||||
elif self.pbar:
|
||||
self.pbar.finish()
|
||||
if self.quit_on_progress_end:
|
||||
self.quit()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class CommandBase(object):
|
||||
|
||||
class JPAsk(JPWithProfile):
|
||||
def confirm_type(self):
|
||||
"""Must return a string containing the confirm type. For instance,
|
||||
FILE_TRANSFER or PIPE_TRANSFER, etc.
|
||||
def __init__(self, host, name, use_profile=True, use_progress=False, help=None, **kwargs):
|
||||
""" Initialise CommandBase
|
||||
@param host: Jp instance
|
||||
@param name: name of the new command
|
||||
@param use_profile: if True, add profile selection/connection commands
|
||||
@param use_progress: if True, add progress bar activation commands
|
||||
@param help: help message to display
|
||||
@param **kwargs: args passed to ArgumentParser
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
raise NotImplemented
|
||||
try: # If we have subcommands, host is a CommandBase and we need to use host.host
|
||||
self.host = host.host
|
||||
except AttributeError:
|
||||
self.host = host
|
||||
|
||||
def dest_jids(self):
|
||||
return None
|
||||
parents = kwargs.setdefault('parents', set())
|
||||
if use_profile:
|
||||
#self.host.parents['profile'] is an ArgumentParser with profile connection arguments
|
||||
parents.add(self.host.parents['profile'])
|
||||
if use_progress:
|
||||
parents.add(self.host.parents['progress'])
|
||||
|
||||
def _askConfirmation(self, confirm_id, confirm_type, data, profile):
|
||||
self.parser = host.subparsers.add_parser(name, help=help, **kwargs)
|
||||
if hasattr(self, "subcommands"):
|
||||
self.subparsers = self.parser.add_subparsers()
|
||||
else:
|
||||
self.parser.set_defaults(func=self.run)
|
||||
self.add_parser_options()
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self.host.args
|
||||
|
||||
@property
|
||||
def need_loop(self):
|
||||
return self.host.need_loop
|
||||
|
||||
@need_loop.setter
|
||||
def need_loop(self, value):
|
||||
self.host.need_loop = value
|
||||
|
||||
@property
|
||||
def profile(self):
|
||||
return self.host.profile
|
||||
|
||||
@property
|
||||
def progress_id(self):
|
||||
return self.host.progress_id
|
||||
|
||||
@progress_id.setter
|
||||
def progress_id(self, value):
|
||||
self.host.progress_id = value
|
||||
|
||||
def add_parser_options(self):
|
||||
try:
|
||||
subcommands = self.subcommands
|
||||
except AttributeError:
|
||||
# We don't have subcommands, the class need to implements add_parser_options
|
||||
raise NotImplementedError
|
||||
|
||||
# now we add subcommands to ourself
|
||||
for cls in subcommands:
|
||||
cls(self)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self.args.profile:
|
||||
self.host.connect_profile(self.connected)
|
||||
except AttributeError:
|
||||
# the command doesn't need to connect profile
|
||||
pass
|
||||
try:
|
||||
if self.args.progress:
|
||||
self.host.watch_progress()
|
||||
except AttributeError:
|
||||
# the command doesn't use progress bar
|
||||
pass
|
||||
|
||||
def connected(self):
|
||||
if not self.need_loop:
|
||||
self.host.stop_loop()
|
||||
|
||||
|
||||
class CommandAnswering(CommandBase):
|
||||
#FIXME: temp, will be refactored when progress_bar/confirmations will be refactored
|
||||
|
||||
def _ask_confirmation(self, confirm_id, confirm_type, data, profile):
|
||||
""" Callback used for file transfer, accept files depending on parameters"""
|
||||
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()]:
|
||||
if confirm_type == self.confirm_type:
|
||||
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)
|
||||
self.ask(data, confirm_id)
|
||||
|
||||
def ask(self):
|
||||
"""
|
||||
The return value is used to answer to the bridge.
|
||||
:rtype: (bool, dict)
|
||||
@return: bool or 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):
|
||||
def connected(self):
|
||||
"""Auto reply to confirmations requests"""
|
||||
#we register incoming confirmation
|
||||
self.bridge.register("askConfirmation", self._askConfirmation)
|
||||
self.need_loop = True
|
||||
super(CommandAnswering, self).connected()
|
||||
# we watch confirmation signals
|
||||
self.host.bridge.register("ask_confirmation", self._ask_confirmation)
|
||||
|
||||
#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)
|
||||
|
||||
|
||||
for confirm_id, confirm_type, data in self.host.bridge.getWaitingConf(self.profile):
|
||||
self._ask_confirmation(confirm_id, confirm_type, data, self.profile)
|
||||
|
|
|
|||
126
frontends/src/jp/cmd_file.py
Normal file
126
frontends/src/jp/cmd_file.py
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#! /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 logging import debug, info, error, warning
|
||||
|
||||
import base
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import tarfile
|
||||
#import tempfile
|
||||
from sat.core.i18n import _
|
||||
|
||||
__commands__ = ["File"]
|
||||
|
||||
class Send(base.CommandBase):
|
||||
def __init__(self, host):
|
||||
super(Send, self).__init__(host, 'send', use_progress=True, help=_('Send a file to a contact'))
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument("files", type=str, nargs = '+', help=_("A list of file"))
|
||||
self.parser.add_argument("jid", type=base.unicode_decoder, help=_("The destination jid"))
|
||||
self.parser.add_argument("-b", "--bz2", action="store_true", help=_("Make a bzip2 tarball"))
|
||||
|
||||
def connected(self):
|
||||
"""Send files to jabber contact"""
|
||||
self.need_loop=True
|
||||
super(Send, self).connected()
|
||||
self.send_files()
|
||||
|
||||
def send_files(self):
|
||||
|
||||
for file_ in self.args.files:
|
||||
if not os.path.exists(file_):
|
||||
error (_(u"file [%s] doesn't exist !") % file_)
|
||||
self.host.quit(1)
|
||||
if not self.args.bz2 and os.path.isdir(file_):
|
||||
error (_("[%s] is a dir ! Please send files inside or use compression") % file_)
|
||||
self.host.quit(1)
|
||||
|
||||
full_dest_jid = self.host.get_full_jid(self.args.jid)
|
||||
|
||||
if self.args.bz2:
|
||||
tmpfile = (os.path.basename(self.args.files[0]) or os.path.basename(os.path.dirname(self.args.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)
|
||||
print _(u"Starting compression, please wait...")
|
||||
sys.stdout.flush()
|
||||
bz2 = tarfile.open(tmpfile, "w:bz2")
|
||||
for file_ in self.args.files:
|
||||
print _(u"Adding %s") % file_
|
||||
bz2.add(file_)
|
||||
bz2.close()
|
||||
print _(u"Done !")
|
||||
path = os.path.abspath(tmpfile)
|
||||
self.progress_id = self.host.bridge.sendFile(full_dest_jid, path, {}, self.profile)
|
||||
else:
|
||||
for file_ in self.args.files:
|
||||
path = os.path.abspath(file_)
|
||||
self.progress_id = self.host.bridge.sendFile(full_dest_jid, path, {}, self.profile) #FIXME: show progress only for last progress_id
|
||||
|
||||
|
||||
class Receive(base.CommandAnswering):
|
||||
confirm_type = "FILE_TRANSFER"
|
||||
|
||||
def __init__(self, host):
|
||||
super(Receive, self).__init__(host, 'recv', use_progress=True, help=_('Wait for a file to be sent by a contact'))
|
||||
|
||||
@property
|
||||
def dest_jids(self):
|
||||
return self.args.jids
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument("jids", type=base.unicode_decoder, nargs="*", help=_('Jids accepted (none means "accept everything")'))
|
||||
self.parser.add_argument("-m", "--multiple", action="store_true", help=_("Accept multiple files (you'll have to stop manually)"))
|
||||
self.parser.add_argument("-f", "--force", action="store_true", help=_("Force overwritting of existing files"))
|
||||
|
||||
|
||||
def ask(self, data, confirm_id):
|
||||
answer_data = {}
|
||||
answer_data["dest_path"] = os.path.join(os.getcwd(), data['filename'])
|
||||
|
||||
if self.args.force or not os.path.exists(answer_data["dest_path"]):
|
||||
self.host.bridge.confirmationAnswer(confirm_id, True, answer_data, self.profile)
|
||||
info(_("Accepted file [%(filename)s] from %(sender)s") % {'filename':data['filename'], 'sender':data['from']})
|
||||
self.progress_id = confirm_id
|
||||
else:
|
||||
self.host.bridge.confirmationAnswer(confirm_id, False, answer_data, self.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.args.multiple:
|
||||
self.host.quit()
|
||||
|
||||
if not self.args.multiple and not self.args.progress:
|
||||
#we just accept one file
|
||||
self.host.quit()
|
||||
|
||||
def run(self):
|
||||
super(Receive, self).run()
|
||||
if self.args.multiple:
|
||||
self.host.quit_on_progress_end = False
|
||||
|
||||
|
||||
class File(base.CommandBase):
|
||||
subcommands = (Send, Receive)
|
||||
|
||||
def __init__(self, host):
|
||||
super(File, self).__init__(host, 'file', use_profile=False, help=_('File sending/receiving'))
|
||||
61
frontends/src/jp/cmd_message.py
Normal file
61
frontends/src/jp/cmd_message.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#! /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 sat_frontends.jp import base
|
||||
import sys
|
||||
from sat.core.i18n import _
|
||||
from sat.tools.utils import clean_ustr
|
||||
|
||||
__commands__ = ["Message"]
|
||||
|
||||
|
||||
class Message(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
super(Message, self).__init__(host, 'message', help=_('Send a message to a contact'))
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument("-s", "--separate", action="store_true", help=_("Separate xmpp messages: send one message per line instead of one message alone."))
|
||||
self.parser.add_argument("-n", "--new-line", action="store_true", help=_("Add a new line at the beginning of the input (usefull for ascii art ;))"))
|
||||
self.parser.add_argument("jid", type=str, help=_("The destination jid"))
|
||||
|
||||
def connected(self):
|
||||
super(Message, self).connected()
|
||||
jids = self.host.check_jids([self.args.jid])
|
||||
jid = jids[0]
|
||||
self.send_stdin(jid)
|
||||
|
||||
def send_stdin(self, dest_jid):
|
||||
"""Send incomming data on stdin to jabber contact
|
||||
@param dest_jid: destination jid"""
|
||||
header = "\n" if self.args.new_line else ""
|
||||
|
||||
if self.args.separate: #we send stdin in several messages
|
||||
|
||||
if header:
|
||||
self.host.bridge.sendMessage(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.host.bridge.sendMessage(dest_jid, line.replace("\n",""), profile_key=self.host.profile, callback=lambda: None, errback=lambda ignore: ignore)
|
||||
|
||||
else:
|
||||
self.host.bridge.sendMessage(dest_jid, header + clean_ustr(u"".join([stream.decode('utf-8','ignore') for stream in sys.stdin.readlines()])), profile_key=self.host.profile, callback=lambda: None, errback=lambda ignore: ignore)
|
||||
87
frontends/src/jp/cmd_pipe.py
Normal file
87
frontends/src/jp/cmd_pipe.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#! /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 sat_frontends.jp import base
|
||||
|
||||
import tempfile
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
from sat.core.i18n import _
|
||||
|
||||
__commands__ = ["Pipe"]
|
||||
|
||||
class PipeOut(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
super(PipeOut, self).__init__(host, 'out', help=_('Pipe a stream out'))
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument("jid", type=base.unicode_decoder, help=_("The destination jid"))
|
||||
|
||||
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.host.bridge.pipeOut(self.host.get_full_jid(self.args.jid), fifopath, {}, self.profile)
|
||||
with open(fifopath, 'w') as f:
|
||||
shutil.copyfileobj(sys.stdin, f)
|
||||
shutil.rmtree(tmp_dir)
|
||||
self.host.quit()
|
||||
|
||||
def connected(self):
|
||||
# TODO: check_jids
|
||||
self.need_loop = True
|
||||
super(PipeOut, self).connected()
|
||||
self.pipe_out()
|
||||
|
||||
|
||||
class PipeIn(base.CommandAnswering):
|
||||
confirm_type = "PIPE_TRANSFER"
|
||||
|
||||
def __init__(self, host):
|
||||
super(PipeIn, self).__init__(host, 'in', help=_('Wait for the reception of a pipe stream'))
|
||||
|
||||
@property
|
||||
def dest_jids(self):
|
||||
return self.args.jids
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument("jids", type=base.unicode_decoder, nargs="*", help=_('Jids accepted (none means "accept everything")'))
|
||||
|
||||
def ask(self, data, confirm_id):
|
||||
answer_data = {}
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
fifopath = os.path.join(tmp_dir,"pipe_in")
|
||||
answer_data["dest_path"] = fifopath
|
||||
os.mkfifo(fifopath)
|
||||
self.host.bridge.confirmationAnswer(confirm_id, True, answer_data, self.profile)
|
||||
with open(fifopath, 'r') as f:
|
||||
shutil.copyfileobj(f, sys.stdout)
|
||||
shutil.rmtree(tmp_dir)
|
||||
self.host.quit()
|
||||
|
||||
|
||||
class Pipe(base.CommandBase):
|
||||
subcommands = (PipeOut, PipeIn)
|
||||
|
||||
def __init__(self, host):
|
||||
super(Pipe, self).__init__(host, 'pipe', use_profile=False, help=_('Stream piping through XMPP'))
|
||||
115
frontends/src/jp/cmd_profile.py
Normal file
115
frontends/src/jp/cmd_profile.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#! /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/>.
|
||||
|
||||
"""This module permits to manage profiles. It can list, create, delete
|
||||
and retrieve informations about a profile."""
|
||||
|
||||
from logging import debug, info, error, warning
|
||||
from sat.core.i18n import _
|
||||
from sat_frontends.jp import base
|
||||
from sat.tools.jid import JID
|
||||
|
||||
__commands__ = ["Profile"]
|
||||
|
||||
PROFILE_HELP = _('The name of the profile')
|
||||
|
||||
|
||||
class ProfileDelete(base.CommandBase):
|
||||
def __init__(self, host):
|
||||
super(ProfileDelete, self).__init__(host, 'delete', use_profile=False, help=_('Delete a profile'))
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument('profile', type=str, help=PROFILE_HELP)
|
||||
|
||||
def run(self):
|
||||
super(ProfileDelete, self).run()
|
||||
if self.args.profile not in self.host.bridge.getProfilesList():
|
||||
error("Profile %s doesn't exist." % self.args.profile)
|
||||
self.host.quit(1)
|
||||
self.host.bridge.deleteProfile(self.args.profile)
|
||||
|
||||
|
||||
class ProfileInfo(base.CommandBase):
|
||||
def __init__(self, host):
|
||||
super(ProfileInfo, self).__init__(host, 'info', use_profile=False, help=_('Get informations about a profile'))
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument('profile', type=str, help=PROFILE_HELP)
|
||||
|
||||
def run(self):
|
||||
super(ProfileInfo, self).run()
|
||||
self.need_loop = True
|
||||
|
||||
def getPassword(password):
|
||||
print "pwd: %s" % password
|
||||
self.host.quit()
|
||||
|
||||
def getJID(jid):
|
||||
print "jid: %s" % jid
|
||||
self.host.bridge.asyncGetParamA("Password", "Connection", profile_key=self.args.profile, callback=getPassword)
|
||||
|
||||
if self.args.profile not in self.host.bridge.getProfilesList():
|
||||
error("Profile %s doesn't exist." % self.args.profile)
|
||||
self.host.quit(1)
|
||||
|
||||
self.host.bridge.asyncGetParamA("JabberID", "Connection", profile_key=self.args.profile, callback=getJID)
|
||||
|
||||
|
||||
class ProfileList(base.CommandBase):
|
||||
def __init__(self, host):
|
||||
super(ProfileList, self).__init__(host, 'list', use_profile=False, help=_('List profiles'))
|
||||
|
||||
def add_parser_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
super(ProfileList, self).run()
|
||||
for profile in self.host.bridge.getProfilesList():
|
||||
print profile
|
||||
|
||||
|
||||
class ProfileCreate(base.CommandBase):
|
||||
def __init__(self, host):
|
||||
super(ProfileCreate, self).__init__(host, 'create', use_profile=False, help=_('Create a new profile'))
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument('profile', type=str, help=_('the name of the profile'))
|
||||
self.parser.add_argument('jid', type=str, help=_('the jid of the profile'))
|
||||
self.parser.add_argument('password', type=str, help=_('the password of the profile'))
|
||||
|
||||
def _profile_created(self):
|
||||
self.host.bridge.setParam("JabberID", self.args.jid, "Connection" ,profile_key=self.args.profile)
|
||||
self.host.bridge.setParam("Server", JID(self.args.jid).domain, "Connection", profile_key=self.args.profile)
|
||||
self.host.bridge.setParam("Password", self.args.password, "Connection", profile_key=self.args.profile)
|
||||
self.host.quit()
|
||||
|
||||
def run(self):
|
||||
"""Create a new profile"""
|
||||
self.need_loop = True
|
||||
if self.args.profile in self.host.bridge.getProfilesList():
|
||||
error("Profile %s already exists." % self.args.profile)
|
||||
self.host.quit(1)
|
||||
self.host.bridge.asyncCreateProfile(self.args.profile, self._profile_created, None)
|
||||
|
||||
|
||||
class Profile(base.CommandBase):
|
||||
subcommands = (ProfileDelete, ProfileInfo, ProfileList, ProfileCreate)
|
||||
|
||||
def __init__(self, host):
|
||||
super(Profile, self).__init__(host, 'profile', use_profile=False, help=_('Profile commands'))
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#! /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/>.
|
||||
|
||||
|
||||
|
||||
class MissingPlugin(Exception):
|
||||
pass
|
||||
|
||||
def require(plugin_name):
|
||||
suported_plugins = ["profile"]
|
||||
if plugin_name not in Supported_plugins:
|
||||
raise MissingPlugin
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
#! /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 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()
|
||||
|
|
@ -17,13 +17,9 @@
|
|||
# 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/>.
|
||||
|
||||
import base
|
||||
import message
|
||||
import pipe
|
||||
import profile
|
||||
import file
|
||||
from sat_frontends.jp import base
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = base.parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
jp = base.Jp()
|
||||
jp.import_commands()
|
||||
jp.run()
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
#! /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/>.
|
||||
|
||||
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)
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
#! /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/>.
|
||||
|
||||
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()
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
#! /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/>.
|
||||
|
||||
"""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)
|
||||
|
||||
Loading…
Reference in a new issue