# -*- coding: cp1252 -*-
# Demo de los servicios Copiagenda y MensajeriaWeb.
#
# Características:
# * Implementación de clase para recuperar la agenda de contactos.
# * Implementación de clase para enviar un mensaje por web.
# * Uso de un diálogo con un control ListCtrl.


import sys
if "--noxp" in sys.argv:
    import win32gui
else:
    import winxpgui as win32gui
import win32api
import win32con
import struct, array
import commctrl
import Queue
import os

import win32gui_struct

import httplib, urllib

IDC_LOGIN = 1024
IDC_PASSWORD = 1025
IDC_NUMERO = 1026
IDC_MENSAJE = 1027
IDC_BUTTON_COPIAGENDA = 1028
IDC_BUTTON_ENVIO = 1029
IDC_LISTBOX = 1030

g_registeredClass = 0

g_iconPathName = os.path.abspath(os.path.join( os.path.split(sys.executable)[0], "C:\\Python25\\DLLs\\pyc.ico" ))
if not os.path.isfile(g_iconPathName):
    # Buscamos en el árbol del código.
    g_iconPathName = os.path.abspath(os.path.join( os.path.split(sys.executable)[0], "..\\PC\\pyc.ico" ))
    if not os.path.isfile(g_iconPathName):
        print "No se encuentra el icono"
        g_iconPathName = None

class _WIN32MASKEDSTRUCT:
    def __init__(self, **kw):
        full_fmt = ""
        for name, fmt, default, mask in self._struct_items_:
            self.__dict__[name] = None
            if fmt == "z":
                full_fmt += "pi"
            else:
                full_fmt += fmt
        for name, val in kw.items():
            if not self.__dict__.has_key(name):
                raise ValueError, "La estructura LVITEM no tiene un elemento '%s'" % (name,)
            self.__dict__[name] = val

    def __setattr__(self, attr, val):
        if not attr.startswith("_") and not self.__dict__.has_key(attr):
            raise AttributeError, attr
        self.__dict__[attr] = val

    def toparam(self):
        self._buffs = []
        full_fmt = ""
        vals = []
        mask = 0
        # Calculamos la máscara
        for name, fmt, default, this_mask in self._struct_items_:
            if this_mask is not None and self.__dict__.get(name) is not None:
                mask |= this_mask
        self.mask = mask
        for name, fmt, default, this_mask in self._struct_items_:
            val = self.__dict__[name]
            if fmt == "z":
                fmt = "Pi"
                if val is None:
                    vals.append(0)
                    vals.append(0)
                else:
                    str_buf = array.array("c", val+'\0')
                    vals.append(str_buf.buffer_info()[0])
                    vals.append(len(val))
                    self._buffs.append(str_buf)
            else:
                if val is None:
                    val = default
                vals.append(val)
            full_fmt += fmt
        return apply(struct.pack, (full_fmt,) + tuple(vals) )


class LVITEM(_WIN32MASKEDSTRUCT):
    _struct_items_ = [
        ("mask", "I", 0, None),
        ("iItem", "i", 0, None),
        ("iSubItem", "i", 0, None),
        ("state", "I", 0, commctrl.LVIF_STATE),
        ("stateMask", "I", 0, None),
        ("text", "z", None, commctrl.LVIF_TEXT),
        ("iImage", "i", 0, commctrl.LVIF_IMAGE),
        ("lParam", "i", 0, commctrl.LVIF_PARAM),
        ("iIdent", "i", 0, None),
    ]

class LVCOLUMN(_WIN32MASKEDSTRUCT):
    _struct_items_ = [
        ("mask", "I", 0, None),
        ("fmt", "i", 0, commctrl.LVCF_FMT),
        ("cx", "i", 0, commctrl.LVCF_WIDTH),
        ("text", "z", None, commctrl.LVCF_TEXT),
        ("iSubItem", "i", 0, commctrl.LVCF_SUBITEM),
        ("iImage", "i", 0, commctrl.LVCF_IMAGE),
        ("iOrder", "i", 0, commctrl.LVCF_ORDER),
    ]

class SMSWindow:

    MiAgenda = []
    
    def __init__(self):
        win32gui.InitCommonControls()
        self.hinst = win32gui.dllhandle

    def _RegisterWndClass(self):
        className = "MultienvioSMS"
        global g_registeredClass
        if not g_registeredClass:
            message_map = {}
            wc = win32gui.WNDCLASS()
            wc.SetDialogProc() # Creamos una clase de diálogo.
            wc.hInstance = self.hinst
            wc.lpszClassName = className
            wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
            wc.hCursor = win32gui.LoadCursor( 0, win32con.IDC_ARROW )
            wc.hbrBackground = win32con.COLOR_WINDOW + 1
            wc.lpfnWndProc = message_map # También se podría especificar una función WndProc.
            wc.cbWndExtra = win32con.DLGWINDOWEXTRA + struct.calcsize("Pi")
            icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
            wc.hIcon = win32gui.LoadImage(self.hinst, g_iconPathName, win32con.IMAGE_ICON, 0, 0, icon_flags)
            classAtom = win32gui.RegisterClass(wc)
            g_registeredClass = 1
        return className

    def _GetDialogTemplate(self, dlgClassName):
        style = win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT | win32con.WS_MINIMIZEBOX
        cs = win32con.WS_CHILD | win32con.WS_VISIBLE
        title = "Multienvío SMS"

        # Marco y título de la ventana
        dlg = [ [title, (0, 0, 300, 140), style, None, (8, "MS Sans Serif"), None, dlgClassName], ]

        # Cajas de texto estático
        dlg.append([130, "Usuario", -1, (5, 5, 50, 9), cs | win32con.SS_LEFT])
        dlg.append([130, "Password", -1, (60, 5, 50, 9), cs | win32con.SS_LEFT])
        dlg.append([130, "Destinatario", -1, (190, 106, 50, 9), cs | win32con.SS_LEFT])
        
        s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER

        # Controles de edición
        # Texto del mensaje
        dlg.append(['EDIT', "Escribe aquí tu mensaje", IDC_MENSAJE, (175, 5, 120, 95), s | win32con.ES_MULTILINE])
        # Login y password
        dlg.append(['EDIT', None, IDC_LOGIN, (5, 104, 50, 12), s | win32con.ES_NUMBER])
        dlg.append(['EDIT', None, IDC_PASSWORD, (60, 104, 50, 12), s | win32con.ES_PASSWORD])
        # Número de teléfono del destinatario
        dlg.append(['EDIT', None, IDC_NUMERO, (230, 104, 50, 12), s | win32con.ES_NUMBER])

        # Botones de Copiagenda y de Envío
        s = cs | win32con.WS_TABSTOP
        dlg.append([128, "Refrescar agenda", IDC_BUTTON_COPIAGENDA, (5, 120, 105, 14), s | win32con.BS_DEFPUSHBUTTON])
        s = win32con.BS_PUSHBUTTON | s
        dlg.append([128, "Enviar", IDC_BUTTON_ENVIO, (190, 120, 90, 14), s])

        return dlg

    def _DoCreate(self, fn):
        message_map = {
            win32con.WM_COMMAND: self.OnCommand,
            win32con.WM_INITDIALOG: self.OnInitDialog,
            win32con.WM_CLOSE: self.OnClose,
            win32con.WM_DESTROY: self.OnDestroy
        }
        dlgClassName = self._RegisterWndClass()
        template = self._GetDialogTemplate(dlgClassName)
        return fn(self.hinst, template, 0, message_map)

    def _SetupList(self): # Le quitamos el estilo "commctrl.LVS_SINGLESEL" para permitir multiselección
        child_style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | win32con.WS_HSCROLL | win32con.WS_VSCROLL
        child_style |= commctrl.LVS_SHOWSELALWAYS | commctrl.LVS_REPORT
        self.hwndList = win32gui.CreateWindow("SysListView32", None, child_style, 7, 7, 246, 156, self.hwnd, IDC_LISTBOX, self.hinst, None)

        child_ex_style = win32gui.SendMessage(self.hwndList, commctrl.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0)
        child_ex_style |= commctrl.LVS_EX_FULLROWSELECT
        win32gui.SendMessage(self.hwndList, commctrl.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, child_ex_style)

        # Añadimos una lista de imágenes (ImageList)
        il = win32gui.ImageList_Create(
                    win32api.GetSystemMetrics(win32con.SM_CXSMICON),
                    win32api.GetSystemMetrics(win32con.SM_CYSMICON),
                    commctrl.ILC_COLOR32 | commctrl.ILC_MASK,
                    1,
                    0)

        shell_dll = os.path.join(win32api.GetSystemDirectory(), "netplwiz.dll") # Aquí se puede cambiar el archivo del que extraemos el icono
        large, small = win32gui.ExtractIconEx(shell_dll, 3, 1)  # ...y aquí la posición dentro de ese archivo
        win32gui.ImageList_ReplaceIcon(il, -1, small[0])
        win32gui.DestroyIcon(small[0])
        win32gui.DestroyIcon(large[0])
        win32gui.SendMessage(self.hwndList, commctrl.LVM_SETIMAGELIST,
                             commctrl.LVSIL_SMALL, il)

        # Creamos las columnas del control.
        lvc = LVCOLUMN(mask = commctrl.LVCF_FMT | commctrl.LVCF_WIDTH | commctrl.LVCF_TEXT | commctrl.LVCF_SUBITEM)
        lvc.fmt = commctrl.LVCFMT_LEFT
        lvc.iSubItem = 0
        lvc.text = "Nombre"
        lvc.cx = 120
        win32gui.SendMessage(self.hwndList, commctrl.LVM_INSERTCOLUMN, 0, lvc.toparam())
        lvc.iSubItem = 1
        lvc.text = "Número"
        lvc.cx = 100
        win32gui.SendMessage(self.hwndList, commctrl.LVM_INSERTCOLUMN, 1, lvc.toparam())

        win32gui.UpdateWindow(self.hwnd)

    def ClearListItems(self):
        win32gui.SendMessage(self.hwndList, commctrl.LVM_DELETEALLITEMS)

    def AddListItem(self, data, *columns):
        num_items = win32gui.SendMessage(self.hwndList, commctrl.LVM_GETITEMCOUNT)
        item = LVITEM(text=data[3], iItem = num_items)
        new_index = win32gui.SendMessage(self.hwndList, commctrl.LVM_INSERTITEM, 0, item.toparam())

        numero=data[11]
        item = LVITEM(text=numero, iItem = new_index, iSubItem = 1)
        win32gui.SendMessage(self.hwndList, commctrl.LVM_SETITEM, 0, item.toparam())

        buf,extra = win32gui_struct.EmptyLVITEM(new_index, 0) 
        a=win32gui.SendMessage(self.hwndList, commctrl.LVM_GETITEMTEXT, new_index, buf)
        for n in extra :
            nombre = n.tostring()
            print nombre[0:a]

#        lvi = LVITEM(iItem = new_index, iSubItem = 0)
#        win32gui.SendMessage(self.hwndList, commctrl.LVM_GETITEMTEXT, 0, lvi.toparam())
#        print plvi.text

    def OnInitDialog(self, hwnd, msg, wparam, lparam):
        self.hwnd = hwnd
        # Centramos el diálogo en la pantalla
        desktop = win32gui.GetDesktopWindow()
        l,t,r,b = win32gui.GetWindowRect(self.hwnd)
        dt_l, dt_t, dt_r, dt_b = win32gui.GetWindowRect(desktop)
        centre_x, centre_y = win32gui.ClientToScreen( desktop, ( (dt_r-dt_l)/2, (dt_b-dt_t)/2) )
        win32gui.MoveWindow(hwnd, centre_x-(r/2), centre_y-(b/2), r-l, b-t, 0)
        self._SetupList()
        l,t,r,b = win32gui.GetClientRect(self.hwnd)

    def OnCommand(self, hwnd, msg, wparam, lparam):
        id = win32api.LOWORD(wparam)
        if id == IDC_BUTTON_COPIAGENDA:
            
            # Obtenemos el login del usuario
            loginHwnd = win32gui.GetDlgItem(self.hwnd, IDC_LOGIN)
            login = win32gui.GetWindowText(loginHwnd)

            # Obtenemos la password del usuario
            password = win32gui.GetDlgItem(self.hwnd, IDC_PASSWORD)
            pwd = win32gui.GetWindowText(password)

            copia=Copiagenda()
            self.MiAgenda = copia.RecuperaContactos(login, pwd)
            self.RellenaLista(self.MiAgenda)

        elif id == IDC_BUTTON_ENVIO:
            # Obtenemos el login del usuario
            loginhwnd = win32gui.GetDlgItem(self.hwnd, IDC_LOGIN)
            login = win32gui.GetWindowText(loginhwnd)

            # Obtenemos la password del usuario
            password = win32gui.GetDlgItem(self.hwnd, IDC_PASSWORD)
            pwd = win32gui.GetWindowText(password)

            # Obtenemos el texto del mensaje
            msgHwnd = win32gui.GetDlgItem(self.hwnd, IDC_MENSAJE)
            msg = win32gui.GetWindowText(msgHwnd)

            # Obtenemos el número del destinatario
            destHwnd = win32gui.GetDlgItem(self.hwnd, IDC_NUMERO)
            dest = win32gui.GetWindowText(destHwnd)
            
            if dest != "" :
                print "Enviando mensaje a " + dest
                sender=MensajeriaWeb()
                sender.EnviaMensaje(login, pwd, dest, msg)
            else :
                sel = win32gui.SendMessage(self.hwndList, commctrl.LVM_GETNEXTITEM, -1, commctrl.LVNI_SELECTED)
                while sel != -1 :
                    item = LVITEM(iItem = sel, iSubItem = 1)
                    i=0;
                    print "sel != -1"
                    sender=MensajeriaWeb()
                    for ct in self.MiAgenda :
                        if(i==sel) :
                            print "Enviando mensaje a " + ct[3] + " (" +ct[11] + ")"
                            numero=ct[11]
                            sender.EnviaMensaje(login, pwd, numero, msg)
                            break;
                        i = i + 1
                    # Buscamos el siguiente elemento seleccionado
                    sel = win32gui.SendMessage(self.hwndList, commctrl.LVM_GETNEXTITEM, sel, commctrl.LVNI_SELECTED)

    # These function differ based on how the window is used, so may be overridden
    def OnClose(self, hwnd, msg, wparam, lparam):
        raise NotImplementedError

    def OnDestroy(self, hwnd, msg, wparam, lparam):
        pass

    def RellenaLista(self, agenda):
        """Rellena el listctrl con los contactos obtenidos de copiagenda
            Entrada: La lista de contactos"""
        
        self.ClearListItems()
        agenda.sort()
        # Recorremos la agenda insertando los contactos en el listctrl
        for n in agenda :
            self.AddListItem(n, 2)

class Copiagenda:

    def ParseaRespuesta (self, codigo) :
        """Parsea el fichero de texto con los contactos separados por tabuladores
            Entrada: La cadena con el fichero
            Retorna: La lista de contactos o 0 en caso de error"""

        Contactos =[]
        delimitadorContacto = '"Title"'
        
        if codigo[:7]!= delimitadorContacto :
            return 0
        indice = codigo.find ('\n"')
        DescripCampos=(codigo[1:indice-2]).split('"\t"')
        codigo = codigo [indice+1:]
        
        indice = codigo.find ('\n"')
        while indice!=-1 :
            contacto = (codigo [1:indice-2]).split('"\t"')
            Contactos.append (contacto)
            codigo = codigo [indice+1:]
            indice = codigo.find ('\n"')
        else :
            indice = codigo.find ('\n')
            contacto = (codigo [1:indice-2]).split('"\t"')
            Contactos.append (contacto)

        return Contactos

    def RecuperaContactos (self, login, pwd) :
        """Realiza las transacciones HTTPS contra el servidor de Copiagenda
            Entrada:  login=cadena con el numero de telefono,
                      pwd=cadena con el password de acceso a la web
            Retorna: La lista de contactos o 0 en caso de error"""

        ##Valores a retornar
        ListaContactos = []
        
        ## Iniciamos login con HTTPS
        params = urllib.urlencode ({'TM_ACTION': 'LOGIN','TM_LOGIN':login, 'TM_PASSWORD':pwd})
        print params
        headers = {"Content-type":"application/x-www-form-urlencoded","Accept": "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*", "User-Agent" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)", "Connection" :  "Keep-Alive"}
        conn=httplib.HTTPSConnection("copiagenda.movistar.es")
        conn.request ("POST", "/cp/ps/Main/login/Agenda",params, headers)
        resp=conn.getresponse()
        print resp.status

        ## Nos redirecionan y nos dan una cookie
        headresp=resp.getheaders()
        cookie=headresp[1][1]
        cookie = cookie.replace(" Path=/, "," ")
        cookie = cookie.replace("; Domain=.movistar.es; Path=/","+")
        cookie = cookie.rstrip('+')
        headers2 = {"Accept": "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*", "User-Agent" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)", "Host" : "copiagenda.movistar.es", "Connection" :  "Keep-Alive","Cookie" : cookie}
        conn2=httplib.HTTPSConnection("copiagenda.movistar.es")
        conn2.request ("GET", "/cp/ps/Main/login/Verificacion?d=movistar.es",None, headers2)
        resp2=conn2.getresponse()
        print resp2.status
        respuesta= resp2.read()
        inic=respuesta.find("name=\"password")
        inic=respuesta.find("value=",inic)
        finic=respuesta.find(">",inic)
        pwdCodificado= respuesta[inic+6:finic]

        ## Nos piden que nos re-autentiquemos con los datos de usuario + la cookie y nos devuelven un token de sesion
        params3 = urllib.urlencode ({'password':pwdCodificado, 'u': login, 'd':'movistar.es'})
        headers3 = {"Content-type":"application/x-www-form-urlencoded", "Accept-Encoding" : "gzip, deflate", "Host" : "copiagenda.movistar.es" , "Referer" : "https://copiagenda.movistar.es/cp/ps/Main/login/Verificacion?d=movistar.es", "Accept": "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*", "User-Agent" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)", "Accept-Language" : "es", "Cache-Control" : "no-cache", "Connection" :  "Keep-Alive", "Cookie" : cookie}
        conn3=httplib.HTTPSConnection("copiagenda.movistar.es")
        conn3.request ("POST", "/cp/ps/Main/login/Authenticate",params3, headers3)
        resp3=conn3.getresponse()
        print resp3.status
        token = resp3.read()
        initoken=token.find("&t=")
        fintoken=token.find("\"",initoken)
        token=token[initoken:fintoken]

        ## Pedimos un exportado de los datos en fichero txt separado por tabuladores
        params4 = urllib.urlencode ({'fileFormat':'TEXT', 'charset': '8859_1', 'delimiter': 'TAB'})
        headers4 = {"Content-type":"application/x-www-form-urlencoded", "Accept-Encoding" : "gzip, deflate", "Host" : "copiagenda.movistar.es" , "Referer" : "https://copiagenda.movistar.es/cp/ps/Main/login/Verificacion?d=movistar.es", "Accept": "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*", "User-Agent" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)", "Accept-Language" : "es", "Cache-Control" : "no-cache", "Connection" :  "Keep-Alive", "Cookie" : cookie}
        conn4=httplib.HTTPSConnection("copiagenda.movistar.es")
        path4 = "/cp/ps/PSPab/preferences/ExportContacts?d=movistar.es&c=yes&u="+login+token
        conn4.request ("POST", path4 ,params4, headers4)
        resp4=conn4.getresponse()
        codHTML=resp4.read()
        ListaContactos=self.ParseaRespuesta (codHTML)
            
        ## dejamos todo como estaba.
        conn4.close()
        conn3.close()
        conn2.close()
        conn.close()
        return ListaContactos

class MensajeriaWeb:

    def EnviaMensaje(self, login, pwd, dest, msg) :
        """Realiza las transacciones HTTP contra el servidor de opensms
            Entrada:  login=cadena con el numero de telefono,
                      pwd=cadena con el password de acceso a la web
                      dest=cadena con el destinatario del mensaje
                      msg=cadena con el texto del mensaje"""

        retorno=0;
        # Iniciamos envío con HTTPS
        params1 = urllib.urlencode ({'TM_ACTION': 'AUTHENTICATE', 'TM_LOGIN': login})
        params2 = urllib.urlencode ({'TM_PASSWORD': pwd})
        params3 = urllib.urlencode ({'to': dest, 'message': msg})
        params = params1 + "&" + params2 + "&" + params3
        print params
        headers = {"Content-type":"application/x-www-form-urlencoded","Accept": "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*", "User-Agent" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)", "Connection" :  "Keep-Alive"}
        conn=httplib.HTTPSConnection("opensms.movistar.es")
        conn.request ("POST", "/aplicacionpost/loginEnvio.jsp",params, headers)
        resp=conn.getresponse()
        print resp.status
        respuesta=resp.read()
        print resp.getheaders()
        print respuesta

        # dejamos todo como estaba.
        conn.close()

        return retorno

class DemoDialog(SMSWindow):
    def DoModal(self):
        return self._DoCreate(win32gui.DialogBoxIndirect)

    def OnClose(self, hwnd, msg, wparam, lparam):
        win32gui.EndDialog(hwnd, 0)

def DemoModal():
    w=DemoDialog()
    w.DoModal()
    
if __name__=='__main__':
    DemoModal()
