view 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 source

#! /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)