Mercurial > libervia-backend
diff frontends/src/jp/jp @ 814:59c7bc51c323
jp: refactoring using ArgParse
author | Dal <kedals0@gmail.com> |
---|---|
date | Wed, 05 Feb 2014 14:35:26 +0100 |
parents | 1fe00f0c9a91 |
children | f8d534ed1d1e |
line wrap: on
line diff
--- a/frontends/src/jp/jp Tue Feb 04 18:54:06 2014 +0100 +++ b/frontends/src/jp/jp Wed Feb 05 14:35:26 2014 +0100 @@ -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 = {} +#!/usr/bin/python - 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() - +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) +