diff frontends/src/jp/base.py @ 814:59c7bc51c323

jp: refactoring using ArgParse
author Dal <kedals0@gmail.com>
date Wed, 05 Feb 2014 14:35:26 +0100
parents frontends/src/jp/jp@1fe00f0c9a91
children c39117d00f35
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/frontends/src/jp/base.py	Wed Feb 05 14:35:26 2014 +0100
@@ -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)
+
+