class Sender(Plugin):
    """Classe base dei plugin che spediscono SMS via Internet."""

    codingManager = CodingManager.getInstance()
    """Istanza locale del gestore della codifica."""
    connectionManager = ConnectionManager.getInstance()
    """Istanza locale del gestore delle connessioni."""
    maxLength = 160
    """Lunghezza massima del messaggio singolo inviabile da un sito."""
    requiresRegistration = None
    """Cosa richiede questo plugin per funzionare?"""
    stop = False
    """Indicatore per interrompere l'invio"""
    perform = connectionManager.ePerform
    """Reimplementazione di curl perform"""

    def sendOne(self, number, text, dati = None, ui = None):
        """Spedisce un SMS con soli caratteri ASCII e di lunghezza massima maxLength
        con le credenziali specificate, supponendo Internet raggiungibile.
        """
        raise NotImplementedError()

    def send(self, proxy, number, text, dati = None, ui = None):
        """Spedisce un SMS (con le eventuali credenziali specificate)."""
        #Controllo pre-condizioni
        self.connectionManager.setCurlProxy(proxy)
        if self.connectionManager.isInternetReachable() == False:
            raise ConnectionError()
        text = self.replaceNonAscii(text)
        length = len(text)
        if length > self.maxLength*99:#massima lunghezza 99 messaggi maxLength caratteri ognuno
            raise TooLongMessageError()
        texts = self.splitText(text)
        self.stop = False
        for i in texts:
            #resetta la gauge
            if ui: ui.gaugeIncrement(0)
            self.sendOne(number, i, dati, ui)

    def replaceNonAscii(sender, text):
        """Modifica alcuni caratteri non-ASCII dalla stringa."""
        replace_map = (
                (u"à", "a'"), (u"á", "a'"), (u"è", "e'"), (u"é", "e'"),
                (u"í", "i'"), (u"ì", "i'"), (u"ó", "o'"), (u"ò", "o'"),
                (u"ú", "u'"), (u"ù", "u'"), (u"À", "A'"), (u"È", "E'"),
                (u"É", "E'"), (u"Ì", "I'"), (u"Ò", "O'"), (u"Ù", "U'"),
                (u"ç", "c"), (u"[", "("), (u"]", ")"), (u"{", "("),
                (u"}", ")"), (u"^", ""), (u"£", "L."), (u"\\", "BkSlash"),
                (u"|", "Pipe"), (u"§", "Par."), (u"°", "o"), (u"€", "EUR"),
                (u"~", "Tilde"), (u"`", "'"))
        for old, new in replace_map: text = text.replace(old, new)
        return text

    def splitText(self, text):
        """Spezza il testo in messaggi da maxLength caratteri numerati."""
        textLength = len(text)
        if textLength <= self.maxLength:
            result = [text]
        else:
            result = []
            remainingText = text
            #calcola il numero di messaggi totale e il prefisso da aggiungere
            n = self.countTexts(text)
            if (n<=9):
                format = "%(i)d/%(n)d "
            elif(n <= 99):
                format = "%(i)02d/%(n)02d "
            else:
                format = "%(i)03d/%(n)03d "
            i=1
            while len(remainingText)>0:
                prefix = format % {'i':i, 'n': n}
                prefixLength = len(prefix)
                pieceLength = self.maxLength - prefixLength
                result.append(prefix + remainingText[0:pieceLength])
                remainingText = remainingText[pieceLength:]
                i+=1
        return result

    def countTexts(self, text):
        """Ritorna il numero di SMS necessari per inviare il testo passato."""
        textLength = len(text)
        if (textLength <= self.maxLength):
            return 1
        if (textLength <= (self.maxLength - 4)*9):
            return int(math.ceil(textLength/(self.maxLength - 4.0)))
        elif(textLength <= (self.maxLength - 6)*99):
            return int(math.ceil(textLength/(self.maxLength - 6.0)))
        else:
            return int(math.ceil(textLength/(self.maxLength - 8.0)))

    def newCharCount(self,text):
        textLength = len(self.replaceNonAscii(text))
        textCount = self.countTexts(self.replaceNonAscii(text))
        if textCount == 2:
            return (textLength-self.maxLength)
        elif not textCount == 1:
            prefixLength = int(math.log10(textCount-1))*2+4
            return (textLength-(self.maxLength-prefixLength)*(textCount-1)) class CodingManager(Singleton):
    """Qualche metodo utile per la gestione delle stringhe Unicode."""

    stdinEncoding = "iso-8859-1"
    stdoutEncoding = "iso-8859-1"
    argvEncoding = "iso-8859-1"

    def __init__(self):
        """Costruttore di default."""
        testString = "ciao"
        try:
            if sys.stdout.encoding is not None:
                testString.encode(sys.stdout.encoding)
                self.stdoutEncoding = sys.stdout.encoding
        except (LookupError, AttributeError):
            pass
        try:
            if sys.stdin.encoding is not None:
                testString.decode(sys.stdin.encoding)
                self.stdinEncoding = sys.stdin.encoding
        except (LookupError, AttributeError):
            pass
        defaultLocale = locale.getdefaultlocale()[1]
        if defaultLocale is not None:
            try:
                testString.decode(defaultLocale)
                self.argvEncoding = defaultLocale
            except LookupError:
                pass

    def unicodeStdin(self, string):
        """Converte una stringa presa dallo standard input in Unicode."""
        return string.decode(self.stdinEncoding)

    def unicodeArgv(self, string):
        """Converte una stringa dagli argomenti in Unicode."""
        return string.decode(self.argvEncoding)

    def encodeStdout(self, unicodeString):
        """Converte una stringa Unicode in stringa stampabile su stdout"""
        return unicodeString.encode(self.stdoutEncoding, "replace")

    def quoteUnicode(self, unicodeString):
        """Converte una stringa Unicode in ASCII codificato."""
        return unicodeString.encode("unicode_escape", "replace")

    def unQuoteUnicode(self, string):
        """Converte una stringa da ASCII codificato a Unicode."""
        return string.decode("unicode_escape")

    def quoteBase64(self, bitString):
        """Converte una stringa di bit in ASCII codificato."""
        return bitString.encode("base64")

    def unQuoteBase64(self, string):
        """Converte una stringa da ASCII codificato a stringa di bit."""
        return string.decode("base64")

    def urlEncode(self, dictionary):
        """Converte il dizionario in una stringa in formato
        application/x-www-form-urlencoded"""
        #Per evitare errori di codifica, sostituisco con ? i
        #caratteri non-ascii
        safeDictionary = {}
        for k, v in dictionary.iteritems():
            safeDictionary[k.encode("ascii", "replace")] = v.encode("ascii", "replace")
        return urllib.urlencode(safeDictionary)

    def xmlEncode(self, unicodeString, encoding="ascii"):
        """
        Encode unicode_data for use as XML or HTML, with characters outside
        of the encoding converted to XML numeric character references.
        """
        result = unicodeString.replace("&", "&amp;")
        result = result.encode(encoding, 'xmlcharrefreplace')
        result = result.replace("<", "&lt;")
        result = result.replace(">", "&lt;")
        return result

    def iso88591ToUnicode(self, string):
        """Converte una stringa da iso-8859-1 a Unicode."""
        return string.decode("iso-8859-1")

    def unicodeToIso88591(self, string):
        """Converte una stringa da iso-8859-1 a Unicode."""
        return string.encode("iso-8859-1", "replace") class SiteAuthError(SenderError):
    """Errore di un Sender: username e password vengono rifiutate."""


    def __init__(self, site):
        """Costruttore standard."""
        SenderError.__init__(self, site)

    def __str__(self):
        return u"Nome utente e password non sono stati accettati dal sito." class SiteConnectionError(SenderError):
    """Errore per indicare un problema con la connessione ad un sito."""

    def __init__(self, site, msg = None):
        """Costruttore standard."""
        SenderError.__init__(self, site)
        self.msg = msg

    def __str__(self):
        s = u"Il sito " + self.site
        if 'Operation timed out after' in self.msg: self.msg = None
        if self.msg is None:
            s += u" non risponde.\n"
        else:
            s += u" ha risposto con l'errore:\n"+ self.msg + u"\n"
        s += (u"Per favore controlla che:\n" +
             u"1) Internet funzioni,\n" +
             u"2) il sito " + self.site + " sia raggiungibile,\n" +
             u"3) sia possibile inviare SMS da " + self.site + u".\n" +
             u"Se il problema persiste ed è possibile inviare SMS \n" +
             u"dal Web, controlla che non sia uscita una nuova \n" +
             u"versione sul sito ufficiale: https://github.com/sylarpowa/ermeSms")
        return s class SiteCustomError(SenderError):
    """Errore specifico di un Sender."""

    def __init__(self, site, msg):
        """Costruttore standard."""
        SenderError.__init__(self, site)
        self.msg = msg

    def __str__(self):
        return self.msg class SenderError(Exception):
    """Errore durante l'esecuzione di un Sender."""

    def __init__(self, site):
        """Costruttore standard."""
        Exception.__init__(self)
        self.site = site

    def __str__(self):
        msg = (u"Il sito " + self.site + u" si è rifiutato di mandare il tuo sms. \n"+
               u"Ci possono essere due ragioni: \n" +
               u"1) il sito " + self.site + u" ha un problema temporaneo \n"
               u"oppure \n" +
               u"2) il sito " + self.site + u" è stato modificato, pertanto \n"+
               u"MoioSMS necessita un aggiornamento. \n\n" +
               u"Se il problema persiste ed è possibile inviare SMS \n" +
               u"dal Web, controlla che non sia uscita una nuova \n" +
               u"versione sul forum del sito ufficiale: \n"
               u"http://www.ermesmsli.net/forum/")

        return (msg) class StopError(Exception):
    """Indica l'interruzione di invio da parte dell'utente"""

    def __init__(self):
        """Costruttore standard."""
        Exception.__init__(self)

    def __str__(self):
        return u"Invio interrotto per richiesta dell'utente" class ConnectionError(Exception):
    """Indica un problema con la connessione Internet."""


    def __init__(self):
        """Costruttore standard."""
        Exception.__init__(self)

    def __str__(self):
        return u"Errore di connessione. Internet funziona?" class CaptchaError(SenderError):
    """Errore per indicare un problema con la connessione ad un sito (scaricamento captcha)."""

    def __init__(self):
        """Costruttore standard."""
        SenderError.__init__(self, repr('Captcha'))

    def __str__(self):
        return u"Impossibile decodificare l'immagine captcha." class PreferenceManagerError(Exception):
    """Indica che il PreferenceManager ha un errore generico."""


    description = u""

    def __init__(self, description):
        Exception.__init__(self)
        self.description = description

    def __str__(self):
        return self.description class NotFoundError(Exception):
    """Indica che il nome o il numero in rubrica non è stato trovato."""


    name = ""

    def __init__(self, name):
        Exception.__init__(self)
        self.name = name

    def __str__(self):
        return self.name + u" non è in rubrica."
import threading
import os
import sys
from PyQt4.QtCore import SIGNAL
from ermesms.plugins.Sender import Sender
from ermesms.CodingManager import CodingManager
from ermesms.errors.SiteAuthError import SiteAuthError
from ermesms.errors.SiteConnectionError import SiteConnectionError
from ermesms.errors.SiteCustomError import SiteCustomError
from ermesms.errors.SenderError import SenderError
from ermesms.errors.StopError import StopError
from ermesms.errors.ConnectionError import ConnectionError
from ermesms.errors.CaptchaError import CaptchaError
from ermesms.errors.PreferenceManagerError import PreferenceManagerError
from ermesms.errors.NotFoundError import NotFoundError
# -*- coding: UTF-8 -*-




class SendMessage(threading.Thread):

    def __init__(self, mf, info):
        threading.Thread.__init__(self)
        self.offline = info['offline']
        self.dest = info['dest']
        self.senderName = info['sender']
        self.text = info['text']
        self.mf = mf
        self.pm = self.mf.pm
        self.masterKey = self.mf.masterKey

           
    def run(self):
        """Spedisce un messaggio e aggiorna la grafica."""
        #imposta senderName e identifica l'invio offline
        senderName = self.senderName
        sender = Sender.getPlugins()[senderName]
                       
        done = False
        hadError = False
        while done == False:
            try:
                reg = {}
                dn = self.dest
                if sender.requiresRegistration:
                    for i in sender.requiresRegistration:
                        reg[i] = self.pm.getAccount(senderName,i,
                                                    self.masterKey)
                if self.mf.isValid(dn)==False:
                    dn = self.pm.lookup(dn)
                if self.pm.isProxyEnabled():
                    proxy = self.pm.getProxy()
                else:
                    proxy = ''

                #resetta la gauge
                self.mf.gaugeIncrement(0)                     
              
                sender.send(proxy, dn, self.text, reg, self.mf)

                #log
                self.mf.emit(SIGNAL('logSave'), senderName, self.dest,
                               sender.replaceNonAscii(self.text))
                #salvo il gestore
                self.pm.setContactSender(self.dest,senderName)
                #aggiungo 1 ai mex inviati
                textCount=sender.countTexts(sender.replaceNonAscii(self.text))
                if self.pm.isSentSenderAvailable(senderName):
                    sentmessage = int(self.pm.getSentSender(senderName))+\
                                  textCount
                else: sentmessage = textCount
                self.pm.setSentSender(senderName,str(sentmessage))
                
                done = True
                
            except PreferenceManagerError:
                data = {'message' : "Immetti dei dati validi per accedere " + \
                    "al sito " + senderName }
                data['request'] = sender.requiresRegistration
                self.mf.emit(SIGNAL('passRequest'), data)
                data = self.mf.qReq.get(True)
                if not data:
                    done = True
                    hadError = u"L'ultimo SMS non è stato" +\
                               u" inviato a causa di un errore."                   
                else:
                    for i in sender.requiresRegistration:
                        self.pm.addAccount(i,data[i],senderName,self.masterKey)

            except SiteAuthError: