MSN.py :  » IRC » Pebrot » pebrot-0.8.9 » pypebrot » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » IRC » Pebrot 
Pebrot » pebrot 0.8.9 » pypebrot » MSN.py
#!/usr/bin/env python 

import sys
import os
import re
import socket
import md5
import urllib
import select
import errno
import time
import locale
import random

from string import *
import gettext

try:
    # If we are executing from source just try to load locales
    sourceLocales= sys.path[0] + '/i18n'
    if os.access( sourceLocales, os.F_OK ):
        gettext.install( 'pebrot', sourceLocales )
    else: # We are executing from a correct install (so using default locales
          # dir)   
        gettext.install( 'pebrot' )
except IOError: # Lets try to not crash ;)
    c= gettext.NullTranslations()
    c.install()


######## Options ########
DEBUG= 0
VERBOSE= 0
CLOSE_EMPTY= 0     # 1 -> Close chat session when no buddies there
SELECT_TIMEOUT= 0.01
PAL_CONNECT_TIMEOUT= 20
DOWNLOAD_DIR= os.getenv('HOME') + '/'
LOG_CHATS= 0
LOG_DIR= os.getenv('HOME') + '/'
MY_IP= None
#########################


SEND_PORT= 6891
# Lets use system's encoding (if not just use 'latin-1')
ENCODING= 'latin-1'
loc= locale.getdefaultlocale()[1]
if loc:
    ENCODING= loc

MSG_HEADER= 'MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n' 
TYPING_HEADER= 'MIME-Version: 1.0\r\nContent-Type: text/x-msmsgscontrol\r\nTypingUser: ' 
SEND_HEADER= 'MIME-Version: 1.0\r\nContent-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\nApplication-Name: File Transfer\r\nApplication-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\nInvitation-Command: INVITE\r\nInvitation-Cookie: %d\r\nApplication-File: %s\r\nApplication-FileSize: %d\r\n\r\n'
SEND_HEADER2= 'MIME-Version: 1.0\r\nContent-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\nInvitation-Command: ACCEPT\r\nInvitation-Cookie: %d\r\nIP-Address: %s\r\nPort: %d\r\nAuthCookie: %d\r\nLaunch-Application: FALSE\r\nRequest-Data: IP-Address:\r\n\r\n'
SEND_ACCEPT= 'MIME-Version: 1.0\r\nContent-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\nInvitation-Command: ACCEPT\r\nInvitation-Cookie: %d\r\nLaunch-Application: FALSE\r\nRequest-Data: IP-Address:\r\n\r\n'
SEND_REJECT= 'MIME-Version: 1.0\r\nContent-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\nInvitation-Command: CANCEL\r\nInvitation-Cookie: %d\r\nCancel-Code: REJECT\r\n\r\n'
SEND_CANCEL= 'MIME-Version: 1.0\r\nContent-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\nInvitation-Command: CANCEL\r\nInvitation-Cookie: %d\r\nCancel-Code: TIMEOUT\r\n\r\n'


FILE_CHUNK_SIZE= 2048
BUFF_LEN= 80192
HOST= 'messenger.hotmail.com'
PORT= 1863
MAX_LEN= 1664 - len(MSG_HEADER) #Message's max length
DISPLAYNAME_MAX= 386

LOG_CHAT_INIT= _('-- Starting chat session with %s on %s --')
LOG_CHAT_END= _('-- Closing chat session on %s --')

MSGS= {  
         'VER':['id', 'proto1', 'proto2', 'proto3', 'proto4'],
         'INF':['id', 'secPack'],
         'XFR':['id', 'type', 'ipPort1', 'param2', 'value2'],
         'USR':['id', 'type', 'arg1', 'arg2', 'arg3', 'arg4'],
         'SYN':['id', 'version', 'numPeople', 'numGroups'],
         'GTC':['value'],
         'BLP':['list'],
         'CHG':['id', 'state'],
         'LSG':['id', 'group', 'number'],
         'ILN':['id', 'state', 'passport', 'name'],
         'CHL':['id', 'hash'],
         'QRY':['id'],
         'MSG':['arg1', 'arg2', 'arg3', 'arg4'],
         'NLN':['state', 'passport', 'name'],
         'FLN':['passport'],
         'OUT':['type'],
         'CAL':['id', 'ringing', 'sesionId'],
         'RNG':['sessionId', 'ipPort', 'cki', 'hash', 'passport', 'name'],
         'JOI':['passport', 'name'],
         'ACK':['id'],
         'NAK':['id'],
         'IRO':['id', 'num', 'total', 'passport', 'name'],
         'ANS':['id', 'ok'],
         'ADD':['id', 'list', 'number', 'passport1', 'passport2'],
         'REM':['id', 'list', 'number', 'passport'],
         'BYE':['arg', 'idleclose'],
         'REA':['id', 'listNum', 'passport', 'name'],
         'PRP':['type', 'phone'],
         'LST':['passport', 'nick', 'lists', 'groups'],
         'CVR':['id', 'num1', 'num2', 'num3', 'url1', 'url2'],
         'TFR':[],
         'CCL':[],
         'FIL':['size'],
         'QNG':[],
         'NOT':['size'],
         'UUX':['id', 'length'],
       }

ERRORS= {      
            200: _('Invalid syntax'),
            201: _('Invalid parameter'),
            205: _('Invalid user'),
            206: _('Domain name missing'),
            207: _('Already logged in'),
            208: _('Invalid username'),
            209: _('Nickname change illegal'),
            210: _('User list full'),
            215: _('User already on list'),
            216: _('User already not on list'),
            217: _('User not online'),
            218: _('Already in mode'),
            219: _('User is in the opposite list'),
            223: _('Too many groups'),
            224: _('Invalid group'),
            225: _('User not in group'),
            229: _('Group name too long'),
            230: _('Cannot remove group zero'),
            231: _('Invalid group'),
            280: _('Switchboard failed'),
            281: _('Transfer to switchboard failed'),
            300: _('Required field missing'),
            301: _('Too many hits to a FND'),
            302: _('Not logged in'),
            500: _('Internal server error'),
            501: _('Database server error'),
            502: _('Command disabled'),
            510: _('File operation failed'),
            520: _('Memory allocation failed'),
            540: _('Challenge response failed'),
            600: _('Server is busy'),
            601: _('Server is unavaliable'),
            602: _('Peer nameserver is down'),
            603: _('Database connection failed'),
            604: _('Server is going down'),
            605: _('Server unavaliable'),
            707: _('Could not create connection'),
            710: _('Bad CVR parameters sent'),
            711: _('Write is blocking'),
            712: _('Session is overloaded'),
            713: _('Too many active users'),
            714: _('Too many sessions'),
            715: _('Not expected'),
            717: _('Bad friend file'),
            731: _('Not expected'),
            800: _('Friendly name changes too rapidly'),
            910: _('Server too busy'),
            911: _('Authentication failed'),
            912: _('Authentication failed'),
            913: _('Not allowed when offline'),
            914: _('Server unavailable'),
            915: _('Server unavailable'),
            916: _('Server unavailable'),
            917: _('Authentication failed'),
            918: _('Server too busy'),
            919: _('Server too busy'),
            920: _('Not accepting new users'),
            921: _('Server too busy'),
            922: _('Server too busy'),
            923: _('Kids Passport without parental consent'),
            924: _('Passport account not yet verified'),
        }

STATES= {
            'NLN': _('Online'),
            'FLN': _('Offline'),
            'HDN': _('Appear Offline'),
            'IDL': _('Idle'),
            'AWY': _('Away'),
            'BSY': _('Busy'),
            'BRB': _('Be Right Back'),
            'PHN': _('On the Phone'),
            'LUN': _('Out to Lunch'),
        }

LISTS= {
            'AL': _('ALLOW LIST'),
            'BL': _('BLOCK LIST'),
            'FL': _('FORWARD LIST'),
            'RL': _('REVERSE LIST'),
       }

# Different socket types
SOCK_MAIN= 0       
SOCK_CHAT= 1       
SOCK_SEND= 2       
SOCK_RECV= 3       

# States of a file send object
SEND_CREATED= 0
SEND_LISTEN= 1
SEND_CONNECTED= 2
SEND_SENDING= 3
SEND_FINISHED= 4

# States of a file recv socket
RECV_CREATED= 0
RECV_CONNECTING= 1
RECV_CONNECTED= 2
RECV_ACCEPTED= 3

# Chat states
CHAT_CONNECTING= 0
CHAT_WAITING_REPLY= 1
CHAT_WAITING_PALS= 2
CHAT_OK= 3

# Some print funcs
# Should be overriden by descending classes (or not...)
class ExParrot:
    def pVerbose( self, level, st ):
        if level >= VERBOSE:
            sys.stdout.write( st )
            sys.stdout.flush()

    def pDebug( self, st ):
        if DEBUG:
            print >>sys.stderr, ( str(st) + '\n' )

    def pError( self, st ):
        print >>sys.stderr, ( _('\nError: %s\n') % st )



# Basic communication class
# Provides some basic for functions for sending MSN's protocol messages 
class Comm(ExParrot):
    def __init__( self, id=0 ):
        self.s= socket.socket( socket.AF_INET, socket.SOCK_STREAM )
        self.ready= 0
        self.id= id
        self.host= ''
        self.port= -1

    def renewSocket( self ):
        self.s= socket.socket( socket.AF_INET, socket.SOCK_STREAM )

    def sendSock( self, sock, cmd, args ):
        'send msn command to socket'

        try:
            if self.ready:
                sock.send( '%s %s %s' % ( cmd, self.id, args ) )
                self.pDebug( 'send: %s %s %s' % (cmd, self.id, args) )
                #rep= self.inputs.readFrom( sock )
                #msg= Msg( rep )
                self.id= self.id + 1
                #return msg
        except socket.error, e:
            raise SocketError, (sock, e)
    
    def send( self, cmd, args ):
        'send msn command via default socket'
        self.sendSock( self.s, cmd, args )

    def sendRaw( self, cmd ):
        'send msn commands without id, send cmd as it is'
        try:
            if self.ready:
                self.s.send( cmd )
                self.pDebug( 'send: ' +  cmd )
        except socket.error, e:
            raise SocketError, (self.s, e)

    def connect_ex( self ):
        try:
            self.s.setblocking( 0 )     
            return self.s.connect_ex( (self.host, self.port) )
        except socket.error, e:
            raise SocketError, (self.s, e)


################################################################################
# Class for MSN protocol errors
class MSNError(Exception):

    def __init__( self, code, id ):
        self.code= int(code)
        self.id= int(id)
        self.text= ERRORS[self.code]

        #print >>sys.stderr, "MSNError: %d %d" % (self.code, self.id)

    def __str__( self ):
        return self.text

# Exception raised when other side closed socket
class SocketClosed(Exception):
    def __init__( self, socket ):
        self.socket= socket

    def __str__( self ):
        return str(self.socket)

# Exception raised on socket error
class SocketError(Exception):
    def __init__( self, socket, error ):
        self.socket= socket
        self.error= error

    def errno( self ):
        return self.error[0]

    def errorMsg( self ):
        return self.error[1]

    def __str__( self ):
        return str(self.socket) + ' - Error: ' + str(self.error) 

################################################################################

# Allows waiting for input on several sockets. 
class Inputs(ExParrot):
    
    def __init__( self, list ):
        self.fileDescs= {}
        for f in list:
            self.fileDescs[f]= ('', SOCK_MAIN)

    def add( self, fileDesc, type ):
    #"type can be MAIN, CHAT, FILE_SEND"
        self.fileDescs[fileDesc]= ('', type)

    def has_key( self, key ):
        return self.fileDescs.has_key( key )

    def remove( self, fileDesc ):    
        if self.fileDescs.has_key( fileDesc ):
            del( self.fileDescs[fileDesc] )
    
    def line( self, fd, peek=0 ):
        self.pDebug( 'line' )
        text= self.fileDescs[fd][0]

        # avoid inf. loop when no data ready
        if ( len(text)>0 and find(text, '\r\n') != -1 ):
            self.pDebug( 'Try to fill= ' + text )
            self.pDebug( len(text) )
            #text+= fd.recv( BUFF_LEN )
            #self.fileDescs[fd]+= fd.recv( BUFF_LEN )
            #self.pDebug( 'LEIDO en while=' + text )
            l= split( text, '\r\n', 1 )[0]

            if not peek:
                remain= text[len(l)+2:]
                self.fileDescs[fd]= (remain,  self.fileDescs[fd][1])
            return l

        else:
            return None

    def selectRead( self ):
        #self.pDebug( 'fds= ' +  str(self.fileDescs) )
        try:
            res= select.select( self.fileDescs.keys(), [], [], SELECT_TIMEOUT )[0]
        except select.error:    
            return None

        #self.pDebug( 'RES= ' + str(res) )
        if len(res) == 0:
            return None
        
        #if res[0] == sys.stdin:
            #return ( sys.stdin, raw_input())
        #else:
        try:
            st= res[0].recv( BUFF_LEN )
        except socket.error, e:    
            raise SocketError, (res[0], e)
        
        if st == '': # peer closed socket 
            res[0].close()
            raise SocketClosed, res[0]
        
        self.pDebug( 'sock=%s, 1st=%s' % (str(res[0]), st) )
        self.fileDescs[res[0]]= (self.fileDescs[res[0]][0] + st
             , self.fileDescs[res[0]][1] )
        return res     
    
    def read( self ):
        global BUFF_LEN

        #self.pDebug( "self.fileDescs= " + str(self.fileDescs) )
        for f in self.fileDescs.items():
            if f[1][0]:
                if f[1][0][0] in ['\0', '\1']: 
                    size1= ord( f[1][0][1] )
                    size2= ord( f[1][0][2] )
                    self.pDebug( 'ANTES 1' )
                    l= self.getChars( f[0], size1+size2*256 + 3 )
                    self.pDebug( 'DESPUES 1' )
                else: 
                    l= self.line( f[0] )
                
                if l:
                    self.pDebug( "getChars1= " + l )
                    tuple= ( f[0], l )
                    return tuple
        
            
        res= self.selectRead()
        
        if res== None:
            return None

        buff= self.fileDescs[res[0]][0]

        # This is a chunk from a filesend (dont try to read a line)
        if buff[0] in ['\0', '\1']:   
            self.pDebug( 'estoy en el if' )
            size1= ord( buff[1] )
            size2= ord( buff[2] )
            #self.pDebug( 'ANTES' )
            l= self.getChars( res[0], size1+size2*256 + 3 )
            #self.pDebug( 'DESPUES' )
        else:
            #self.pDebug( 'en line' )
            l= self.line( res[0] )

        if l:
            #self.pDebug( "readed in getChars2= %d" % len(l) )
            #self.pDebug( "getChars2= " + l )
            return ( res[0], l )
        else:
            return None

    def type( self, fDesc ):
        return self.fileDescs[fDesc][1]

    def getChars( self, fd, numChars ):
        self.pDebug( 'getChars!!!!, fd=%s, numChars= %d' % (fd, numChars) ) 
        self.selectRead()    
        text= self.fileDescs[fd][0]

        if len(text) < numChars: # not enough chars
            self.pDebug( 'there are only %d chars' % len(text) )
            self.pDebug( 'they are= %s' % text )
            #self.fileDescs[fd]+= fd.recv( BUFF_LEN )
            #text+= fd.recv( BUFF_LEN )
            return None
        else:    
            chars= text[:numChars]
            self.fileDescs[fd]= (text[numChars:], self.fileDescs[fd][1])
            return chars

    def hasSubstr( self, fd, subst ):
        return find( self.fileDescs[fd][0], subst )

    def readFrom( self, fd, timeout= -1 ):
        global BUFF_LEN
        if ( self.fileDescs[fd][0] ):
            l= self.line( fd )
            return l
        
        if timeout == -1:
            res= select.select( [fd], [], [], SELECT_TIMEOUT )[0]
        else:
            res= select.select( [fd], [], [], timeout )[0]

        if fd not in res:
            return None
        
        st= fd.recv( BUFF_LEN )
        self.pDebug( '2st=' + st )
        self.fileDescs[fd][0]= self.fileDescs[fd][0] + st
        l= self.line( fd )
        return l
        
    def peekFrom( self, fd ):
        global BUFF_LEN
        if ( self.fileDescs[fd][0] ):
            l= self.line( fd, 1 )
            return l

        st= fd.recv( BUFF_LEN )
        self.pDebug( 'st=' + st )
        self.fileDescs[fd][0]= self.fileDescs[fd][0] + st
        l= self.line( fd, 1 )
        self.pDebug( 'postST=' + self.fileDescs[fd][0] )
        return l

    def strReturn( self, fd, string ):
        self.fileDescs[fd]=( string + self.fileDescs[fd][0], self.fileDescs[fd][1] )
        
        
################################################################################

# Does the MSN message parsing 
class Msg(ExParrot):
    def __init__( self, st ):
        self.pDebug( 'rec: ' + st )
        self.st= st
        if not st:    
            self.type= ''
            return

        self.fields= {}
        parts= split( st, ' ' )
        self.type= parts[0]

        if self.type.isdigit() and len(self.type) == 3:
            raise MSNError, (self.type, int(parts[1]) )
        elif ( self.type not in ['GTC', 'BPR'] ):
            maxLen= len( parts )
            if self.type in MSGS:
                expLen= len( MSGS[self.type] )+1
                if ( expLen < maxLen ):
                    maxLen= expLen

                for i in range( 1, maxLen ):
                    self.fields[MSGS[self.type][i-1]]= parts[i]
            else:        
                self.pDebug( 'Unknown message type: %s' % (self.type) )
                
                
                
    def __str__( self ):        
        return self.st
        
    def __repr__():
        return self.type + '=' + str( self.fields )
    
################################################################################

# Main class. Represents an MSN session.
class Session( Comm ):

    def __init__( self ):
        Comm.__init__( self )
        self.user= None
        self.name= None
        self.inputs= Inputs( [self.s])
        self.usersOnline= {}
        self.chats= {}
        self.sendFile= {}
        self.recvFile= {}
        self.pendingChats= {}
        self.connectingChats= {}
        self.connectingFileRecvs= {}
        self.requestedList= {}
        self.running= 1
        self.listShown= None
        self.lists= { 'FL':{}, 'AL':{}, 'BL':{}, 'RL':{} } 
        self.listOps= {}
        self.lstNum= 0
        self.lstTotal= 0

        # For hiding socket error when server closes idle chats
        self.hideSocketError= 0

    def connect( self, user, passwd, state ):
        self.user= user
        self.passwd= passwd
        self.state= state

        self.pVerbose( 1, _('Connecting to %s... ') % HOST )
        self.pDebug( 'CONNECTING %s:%d' % ( HOST, PORT) )
        self.s.connect((HOST, PORT))
        self.ready= 1
        #self.s.setblocking( 0 )

        self.send( 'VER', 'MSNP8 CVRO\r\n' )

    def reconnect( self ):
        self.inputs.remove( self.s )
        self.renewSocket()
        self.inputs.add( self.s, SOCK_MAIN )
        self.connect( self.user, self.passwd, self.state )

    # Must be connected for this to work
    def myIP( self ):
        if MY_IP:
            return MY_IP
        else:    
            return self.s.getsockname()[0]

    def addInput( self, input, type ):
        self.inputs.add( input, type )

    def socketType( self, sock ):
        if sock == self.s:
            return SOCK_MAIN
        elif self.chats.has_key( sock ):
            return SOCK_CHAT
        elif self.sendFile.has_key( sock ):
            return SOCK_SEND
        elif self.recvFile.has_key( sock ):
            return SOCK_RECV
        else:
            return None
    
    def removeInput( self, input ):
        if self.inputs.has_key( input ):
            self.inputs.remove( input )
    
    def newChat( self, passport=None ):
        chat= Chat( self, int(self.id) )
        chat.created= 1
        chat.state= CHAT_CONNECTING
        self.chats[chat.s]= chat
        self.pendingChats[str(self.id)]= chat
        if passport:
            chat.addUser( passport )
        self.send( 'XFR', 'SB\r\n' )

        return chat

    def sendChatMsg( self, chat, msg ):
        if chat.state == CHAT_OK:
            try:
                if chat.listInactiveUsers():
                    self.addConnectingChat( chat, 'NORMAL' )
                    chat.inviteInactive()
                    chat.addAction( 'MSG', msg )
                else:
                    self.writeTo( chat, msg )
                    self.msgFrom( chat, self.user, msg )
                    self.isTyping= 0
            except SocketError:
                self.delConnectingChat( chat )
                self.reconnectChat( chat )
                chat.addAction( 'MSG', msg )
        else:
            chat.addAction( 'MSG', msg )

    def sendChatFile( self, chat, file ):
        if chat.state == CHAT_OK:
            try: 
                if chat.listInactiveUsers():
                    self.addConnectingChat( chat, 'NORMAL' )
                    chat.inviteInactive()
                    chat.addAction( 'FILE_SEND', file )
                else:
                    self.sendFileRequest( chat, file )
                    self.pInfo( _('Request for sending \'%s\'...')
                                % file )
            except SocketError:
                self.delConnectingChat( chat )
                self.reconnectChat( chat )
                chat.addAction( 'FILE_SEND', file )
        else:
            chat.addAction( 'FILE_SEND', file )

    #def delConnectingSocket( self, sock ):
        #if self.connectingChats.has_key( sock ):
            #del( self.connectingChats[sock] )
        #elif self.connectingFileRecvs.has_key( sock ):
            #del( self.connectingFileRecvs[sock] )

    def addConnectingChat( self, chat, type ):
        self.pDebug( 'addConnectingChat: %s %s' % (str(chat), type) )
        chat.state= CHAT_CONNECTING
        self.connectingChats[chat.s]= (chat, type)

    def delConnectingChat( self, chat ):
        self.pDebug( 'connectingChats: %s' % str(self.connectingChats) )
        if self.connectingChats.has_key( chat.s ):
            del( self.connectingChats[chat.s] )

    def addConnectingFileRecv( self, fRecv ):
        self.pDebug( 'addConnectingFileRecv: %s' % (str(fRecv)) )
        fRecv.state= RECV_CONNECTING
        self.connectingFileRecvs[fRecv.s]= fRecv

    def delConnectingFileRecv( self, fRecv ):
        #self.pDebug( 'connectingfRecv: %s' % str(self.connectingChats) )
        if self.connectingFileRecvs.has_key( fRecv.s ):
            del( self.connectingFileRecvs[fRecv.s] )

    def reconnectChat( self, chat ):
        #self.inputs.remove( chat.s )
        self.pDebug( 'reconnectChat')
        del self.chats[chat.s]
        chat.renewSocket()
        
        self.chats[chat.s]= chat
        self.pendingChats[str(self.id)]= chat
        self.send( 'XFR', 'SB\r\n' )
        chat.connected= None
    
    def closeChat( self, chat ):
        chat.logText( LOG_CHAT_END % (time.asctime()) )

        self.pDebug( 'chat.sendFile: %s' %  str(chat.sendFile) )
        # Get rid of uploads/downloads
        for fSend in chat.sendFile.values():
            self.removeFileSend( fSend )

        for fRecv in chat.recvFile.values():
            self.removeFileRecv( fRecv )

        chat.send( 'OUT', '\r\n' )
        self.delChat(chat)

    def delChat( self, chat ):
        del( self.chats[chat.s] )
        self.delConnectingChat( chat )
        self.inputs.remove( chat.s )
        chat.s.close()
    
    def hasChat( self, passport ):
        for chat in self.chats.items():
            if chat[1].users == passport:
                return chat[1].active
        
        return 0

    #def chatUser( self, passport ):
        #for chat in self.chats.items():
            #if chat[1].user == passport:
                #return chat[1]

    def changeState( self, state ):
        ## Added to allow Pebrot to stay alive offline
        if state == "FLN":
            self.send ( 'OUT', '\r\n' )
            self.state = 'FLN'
            self.pInfo( 'You have been logged out. Type \\q to exit or \\s nln to reconnect.' )
        else:
            self.send( 'CHG', '%s\r\n' % state )

    def challenge( self, msg ):
        chunk= md5.new( msg.fields['hash']+ 'Q1P7W2E4J9R8U3S5' ).hexdigest()
        self.send( 'QRY', 'msmsgs@msnmsgr.com 32\r\n%s' % chunk )

    def ping( self ):
        self.sendRaw( 'PNG\r\n' )

    def requestInfo( self, infoId= 0 ):
        self.send( 'SYN', '%d\r\n' % infoId );
    
    def addUser( self, passport, list ):    
        '''Add user to list.
    
            List can be one from  import 
                AL: allow list
                BL: block list
                FL: forward list
                RL: reverse list'''

        self.listOps[int(self.id)]= (passport, list, 'ADD')
        self.send( 'ADD', ' %s %s %s\r\n' % (list, passport, passport) )

    def delUser( self, passport, list ):    
        self.listOps[int(self.id)]= (passport, list, 'REM')
        self.send( 'REM', ' %s %s\r\n' % (list, passport) )

    def acceptChat( self, msg ):    
        chat= Chat( self )
        self.inputs.add( chat.s, SOCK_CHAT )
        self.pDebug( "self.inputs=" + str(self.inputs.fileDescs) )
        chat.addUser( msg.fields['passport'])
        chat.accept( msg )
        self.chats[chat.s]= chat
        
    def writeTo( self, chat, msg ):    
        chat.write( msg )

    def changeName( self, name ):
        name= name[:DISPLAYNAME_MAX]
        name= unicode( name, ENCODING, 'replace' )
        name= urllib.quote(name.encode( 'utf-8' ))
        self.send( 'REA', ' %s %s\r\n' % (self.user, name) )
     
    def onlyOnline( self, x ):
        #print >>sys.stderr, x, 'esta a', self.usersOnline.has_key( x )
        return self.usersOnline.has_key( x )
    
    def listInactiveUsers( self, chat ):
        list= chat.listInactiveUsers()
        #print >>sys.stderr, 'list2= ' + str(list)
        return filter( self.onlyOnline, list )

    def treatProtoError( self, exc ):
        if exc.code == 201:
            info= self.listOps[exc.id]
            self.addUserError( info[0], info[1] )
        elif exc.code == 215:
            info= self.listOps[exc.id]
            self.userOnListError( info[0], info[1] )
        elif exc.code == 216:
            info= self.listOps[exc.id]
            self.delUserError( info[0], info[1] )
        elif exc.code in [205, 209, 217, 219, 224, 231, 500]:
            self.protoError( exc.code )
        #fix crash when adding invalid usernames
        elif exc.code == 208:
            self.pError( _('The user may not be added because no account exists by that name.') )
        else:
            self.fatalProtoError( exc.code )


    def whatMSG( self, text ):
        m= re.compile(r'Content-Type: *[a-z]+/([^; \r]+)[; \r]').search( text )
        if not m:
            return 
        type= m.group( 1 )
        self.pDebug( 'type= %s' % type )

        if type == 'x-msmsgsinitialemailnotification':
            m= re.compile(r'Inbox-Unread: *([0-9]+)').search( text )
            if m:
                self.gotMail( m.group(1) )        
        elif type == 'x-msmsgsemailnotification':
            if find( text, 'From:') != -1:
                mail= re.compile(r'From: *(.+)\r\n').search( text )
                name= re.compile(r'From-Addr: *(.+)\r\n').search( text )
                self.incomingMail( mail.group(1), name.group(1) )        

    def addFileSend( self, fSend ):
        self.sendFile[fSend.s]= fSend
        fSend.chat.addFileSend( fSend )

    def addFileRecv( self, fRecv ):
        self.recvFile[fRecv.s]= fRecv
        fRecv.chat.addFileRecv( fRecv )

    def sendFileRequest( self, chat, fileName ):
        fSend= chat.sendFileRequest( fileName )
        self.addFileSend( fSend )

    def initSendFile( self, fSend ):
        fSend.state= SEND_LISTEN
        #self.addInput( fSend.s, SOCK_SEND )
        fSend.init()

    def removeFileSend( self, fSend ):    
        self.pDebug( 'self.sendFile: %s' % str(self.sendFile) )
        #if fSend.s.fileno() != -1 and  fSend.state \
        #in [SEND_CONNECTED, SEND_SENDING]:
        #fSend.cancel()
        del( self.sendFile[fSend.s] )
        fSend.chat.removeFileSend( fSend )
        if self.inputs.has_key( fSend.s ):
            self.inputs.remove( fSend.s )
        self.pDebug( 'FIN self.sendFile: %s' % str(self.sendFile) )
        
    def removeFileRecv( self, fRecv ):
        del( self.recvFile[fRecv.s] )
        fRecv.chat.removeFileRecv( fRecv )
        if self.inputs.has_key( fRecv.s ):
            self.inputs.remove( fRecv.s )
        fRecv.s.close()
        self.pDebug( 'self.recvFile: %s' % str(self.recvFile) )

        # Let's check if it was still connecting
        if self.connectingFileRecvs.has_key( fRecv.s ):
            del( self.connectingFileRecvs[fRecv.s] )


    def crash( self ):
        self.send( 'asdas', 'kk' )

    def step( self ):
        if len(self.sendFile) > 0:
            for fSend in self.sendFile.values():
                if fSend.state == SEND_LISTEN:
                    #self.pDebug( 'en el listen' )
                    self.pDebug( 'en el listen, socket = %s, fSend=%s' 
                        % (str(fSend.s), str(fSend)) )
                    try:
                        fSend.s.setblocking( 0 )
                        self.pDebug( 'antes' )
                        sock, dir= fSend.s.accept()
                        self.pDebug( 'despues' )
                        self.pDebug( 'sock= %s, dir= %s' % (str(sock), str(dir)) )
                        #del( self.sendFile[fSend.s] )
                        self.removeFileSend( fSend )
                        fSend.s= sock
                        self.addInput( fSend.s, SOCK_SEND )
                        self.addFileSend( fSend )
                        fSend.state= SEND_CONNECTED
                    except socket.error, e:
                        self.pDebug( 'Esperando1... %s' % (str(e)) )
                elif fSend.state == SEND_SENDING:
                    try:
                        fSend.s.setblocking( 0 )
                        resul= fSend.sendChunk()
                        if resul:
                            self.chunkSent( fSend )
                    except socket.error, e:
                        self.pDebug( 'Esperando2... %s' % (str(e)) )

        if len(self.connectingChats) > 0:
            for elem in self.connectingChats.values():
                chat= elem[0]

                self.pDebug( 'Chat: %s - state: %d' % (str(chat), chat.state) )
                if chat.state == CHAT_CONNECTING:
                    resul= chat.connect_ex()
                    if resul != 0:
                        self.pDebug( 'connecting chat resul= %d, %s' % 
                                     (resul, errno.errorcode[resul]) )
                    if resul in [0, errno.EISCONN]:
                        chat.ready= 1
                        if elem[1] == 'NORMAL':
                            self.inputs.add( chat.s, SOCK_CHAT )
                            chat.send( 'USR', '%s %s\r\n' % (self.user, chat.hash) )
                            self.pDebug( 'USR sent' )
                        else:
                            #self.s.setblocking( 0 )
                            chat.send( 'ANS', '%s %s %s\r\n' % (self.user
                                    , chat.hash, chat.sessionId) )
                        chat.state= CHAT_WAITING_REPLY            
                        #self.delConnectingChats( chat )
                elif chat.state == CHAT_WAITING_PALS: 
                    chat.checkInactive()
                elif chat.state == CHAT_OK:    
                    self.delConnectingChat( chat )
                    chat.processActions()

        if len(self.connectingFileRecvs) > 0:
            self.pDebug( 'self.connectingFileRecvs: %s' 
                         % str(self.connectingFileRecvs) )
            for fRecv in self.connectingFileRecvs.values():
                self.pDebug( 'fRecv: %s, state: %d' % (str(fRecv), fRecv.state) ) 
                if fRecv.state == RECV_CONNECTING:
                    self.pDebug( 'antes' )
                    resul= fRecv.connect_ex()
                    self.pDebug( 'despues' )
                    if resul != 0:
                        self.pDebug( 'connecting file recv resul= %d, %s' % 
                                     (resul, errno.errorcode[resul]) )
                    if resul in [0, errno.EISCONN]:
                        fRecv.ready= 1
                        fRecv.s.setblocking( 1 )
                        fRecv.state= RECV_CONNECTED
                        fRecv.sendRaw( 'VER MSNFTP\r\n' )
                        self.delConnectingFileRecv( fRecv )
                    

        self.processMsgs()

    def sslSendRecv( self, host, port, st ):
        sock= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect( (host, port) )
        ssl= socket.ssl( sock )
        self.pDebug( 'request: ' + st )
        ssl.write( st )
        self.pDebug( 'AFTER SEND' )
    
        recv= ''
        try:
            while (1):
                recv+= ssl.read()
        except socket.sslerror, e:
            if e[0] not in [5,8]:
                raise e
        
        return recv

    def sslLogin( self, params ):
        recv= self.sslSendRecv( 'nexus.passport.com', 443, 'GET /rdr/pprdr.asp HTTP/1.0\r\n\r\n' )

        self.pDebug( 'SSL response: ' + recv )
        resul= re.compile(r'DALogin=([^,]+),').search( recv )
        dir= resul.group(1)
        self.pDebug( 'daLogin: ' + str(dir) )
        elem= split( dir, '/' )

        user= urllib.quote( self.user )
        header= 'Authorization: Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in=' + user + ',pwd=' + self.passwd + ',' + params 
        req= 'GET /%s HTTP/1.0\r\n%s\r\n\r\n' % (elem[1], header)

        retry= 1
        while (retry):
            recv= self.sslSendRecv( elem[0], 443, req )
            self.pDebug( 'AFTER SEND' )

            self.pDebug( 'SSL response: ' + str(recv) )
            if recv[:12] == 'HTTP/1.1 302':
                resul= re.compile(r'Location: https://([^/]+)/([^ ]+)\r\n').search( recv )
                elem[0]= resul.group(1)
                elem[1]= resul.group(2)
            else:
                retry= 0

        resul= re.search( "from-PP='([^']+)'", recv )
        param='t=&p='
        if resul:
            param= resul.group( 1 )
            self.pDebug( 'PARAM: ' + param )

        return param
        

    def processMsgs( self ):
        try:
            res= self.inputs.read()
        except SocketClosed, e: # the other side closed socket
            self.pDebug( 'Socket error' )
            self.pDebug( 'Error from: %s' % str(e.socket) )
            if self.sendFile.has_key( e.socket ):
                self.pDebug( 'UNO' ) 
                self.removeFileSend( self.sendFile[e.socket] )
            elif self.recvFile.has_key( e.socket ):
                self.pDebug( 'DOS' ) 
                self.removeFileRecv( self.recvFile[e.socket] )
            else:
                self.pDebug( 'TRES' )
                self.removeInput( e.socket )
            self.pDebug( 'peer closed socket - ' + str(e) )
            return None
            

        if res == None:
            return
        else:
            sockType= self.inputs.type(res[0])

        #print >>sys.stderr, 'RECIBIDO= ', res
        #print >>sys.stderr, 'SOCK_TYPE= ', sockType

        if sockType == SOCK_MAIN: # Message from Main server
            try:
                msg= Msg( res[1] )
            except MSNError, e:    
                self.treatProtoError( e )
                self.pDebug( 'debug= ' + str(res[0]) )
                return

            if ( msg.type == 'CHL' ):
                self.challenge( msg )
            elif ( msg.type == 'QNG' ):
                self.qngReceived()

                if not self.listShown:
                    self.palListComplete()
                    self.listShown= 1

            elif ( msg.type == 'VER' ):
                self.send( 'CVR', '0x0409 winnt 5.1 i386 MSNMSGR 5.0.0540 MSMSGS %s\r\n' % self.user )
            elif ( msg.type == 'INF' ):
                self.send( 'USR', 'MD5 I %s\r\n' % self.user )
            elif ( msg.type == 'ILN' ):
                name= unicode( urllib.unquote(msg.fields['name']), 'utf-8'
                        , 'replace' )
                name= name.encode( ENCODING, 'replace' )
                self.usersOnline[msg.fields['passport']]= {
                    'name': name , 'state':msg.fields['state']}
                self.userOn( msg.fields['passport'] )
            elif ( msg.type == 'CVR' ):
                self.send( 'USR', 'TWN I %s\r\n' % self.user )
            elif ( msg.type == 'NLN' ):
                name= unicode( urllib.unquote(msg.fields['name']), 'utf-8'
                        , 'replace' )
                name= name.encode( ENCODING, 'replace' )

                wasOnline = self.usersOnline.has_key( msg.fields['passport'] )
                self.usersOnline[msg.fields['passport']]= {
                    'name': name , 'state':msg.fields['state']}

                if wasOnline:
                    self.userChangesState( msg )
                else:
                    self.userConnected( msg )
                    

            elif ( msg.type == 'FLN' ):
                if self.usersOnline.has_key( msg.fields['passport'] ):
                    del( self.usersOnline[msg.fields['passport']] )
                    self.userOff( msg.fields['passport'] )
            elif ( msg.type == 'QRY' ):
                pass
            elif ( msg.type == 'XFR' ):
                host= split( msg.fields['ipPort1'], ':' )    
                self.pDebug( 'CONNECTING %s:%s' % (host[0], host[1]) )

                # Main server (Notification server)
                if msg.fields['type'] == 'NS':
                    self.pVerbose( 1, _('done\n') )
                    self.pVerbose( 1, _('Connecting to %s... ') % host[0] )
                    self.inputs.remove( self.s )
                    self.s= socket.socket( socket.AF_INET, socket.SOCK_STREAM )
                    self.inputs.add( self.s, SOCK_MAIN )
                    self.s.connect( (host[0], int( host[1])) )    
                    self.ready= 1
                    #self.pDebug( 'Socket: ' + str(self.s) )
                    #self.pDebug( 'Inputs: ' + str(self.inputs.fileDescs) )
                    #self.send( 'USR', 'MD5 I %s\r\n' % self.user )
                    self.send( 'VER', 'MSNP8 CVRO\r\n' )
                else: # Per chat server (Switchboard server)
                    c= self.pendingChats[msg.fields['id']] 
                    del( self.pendingChats[msg.fields['id']] )
                    #c.hash= msg.fields['value2']
                    self.addConnectingChat( c, 'NORMAL' )

                    c.host= host[0]
                    c.port= int(host[1])
                    c.hash= msg.fields['value2']
                    c.connect_ex()

                    #if resul == 0: 
                        #self.chatConnectError( c )
                        #return


            elif ( msg.type == 'USR' ):
                if msg.fields['type'] == 'MD5':
                    hash= msg.fields['arg2']
                    chunk= md5.new( hash + self.passwd ).hexdigest()
                    self.send( 'USR', 'MD5 S %s\r\n' % chunk )
                elif msg.fields['type'] == 'TWN': 
                    text= split(msg.st)[4]
                    #text= urllib.unquote( text )
                    #self.pageParams= replace( text, ',', '&' )
                    self.pDebug( 'PARAMS: ' +  text )
                    param= self.sslLogin( text )
                    self.send( 'USR', 'TWN S %s\r\n' % param )
                else:    
                    name= unicode( urllib.unquote(msg.fields['arg2']), 'utf-8'
                            , 'replace' )
                    name= name.encode( ENCODING, 'replace' )
                    self.name= name
                    self.pVerbose( 1, _('done\n') )
                    #self.changeState( self.state )
                    self.requestInfo()
                    #self.ping()
            elif ( msg.type == 'ADD' ):
                l = msg.fields['list']
                p = msg.fields['passport1']
                self.lists[l][p]= ''
                self.userAdded( p, l )
            elif ( msg.type == 'REM' ):
                l = msg.fields['list']
                p = msg.fields['passport']
                if self.lists[l].has_key( p ):
                    del( self.lists[l][p] )
                self.userRemoved( p, l )
            elif ( msg.type == 'OUT' ):
                if msg.fields['type'] == 'OTH':
                    self.otherLogin()
                elif msg.fields['type'] == 'SSD':
                    self.serverDown()
            elif ( msg.type == 'RNG' ):
                self.invitedBy( msg )
            elif ( msg.type == 'CHG' ):
                self.state= msg.fields['state'] 
                self.selfStateChanged( msg.fields['state'] )
            elif ( msg.type == 'REA' ):
                name= unicode( urllib.unquote(msg.fields['name']), 'utf-8'
                        , 'replace' )
                name= name.encode( ENCODING, 'replace' )
                self.nameChanged( name )
            elif ( msg.type == 'SYN' ):
                if msg.fields.has_key('numPeople'): # Ok, we are gonna receive some LSTs 
                    self.lstTotal= int( msg.fields['numPeople'] )
                    self.lstNum= 0
                    self.pDebug( 'lstTotal= %d' % self.lstTotal )
                #else: # List is up-to-date 
                    #self.changeState( self.state )
                    #self.ping()
            elif ( msg.type == 'LST' ):
                listNums= int( msg.fields['lists'] )
                if listNums == 8:
                    self.userAddedYou( msg.fields['passport'] )

                if listNums & 1:
                    self.lists['FL'][msg.fields['passport']]= ''
                if listNums & 2:
                    self.lists['AL'][msg.fields['passport']]= ''
                if listNums & 4:
                    self.lists['BL'][msg.fields['passport']]= ''
                if listNums & 8:
                    self.lists['RL'][msg.fields['passport']]= ''

                # This tries to fix a problem with no receiving ILNs
                # Just doing the initial CHG after receiving the complete list
                self.lstNum += 1
                self.pDebug( 'lstNum= %d' % self.lstNum )
                if self.lstNum == self.lstTotal:
                    self.changeState( self.state )
                    self.ping()

                #if msg.fields.has_key( 'passport' ):
                    #self.requestedList[msg.fields['passport']]= ''
                #if msg.fields['num'] == msg.fields['listSize']:
                    #self.listUpdated( self.requestedList, msg.fields['list'] )

                
            elif ( msg.type == 'NOT' ): # Notification (MSN Mobile, alert...)
                more= self.inputs.getChars( res[0], int(msg.fields['size']) )

                # there's only part of notification, return what we read 
                # (NOT line)
                if not more:
                    self.inputs.strReturn( res[0], msg.st + '\r\n' )

            elif ( msg.type == 'MSG' ): # Mail notification
                more= self.inputs.getChars( res[0], int(msg.fields['arg3']) )

                # there's only part of msg, return what we read (MSG line)
                if not more:
                    self.inputs.strReturn( res[0], msg.st + '\r\n' )
                    return

                self.pDebug( "more="+ more )
                self.whatMSG( more )        

        elif sockType == SOCK_CHAT: # Message from any chat server
            #print >>sys.stderr, 'AUA'
            try:
                msg= Msg( res[1] )
            except MSNError, e:    
                if e.code == '216':   # When a user disconnects during chat
                    res[0].close()
                    self.inputs.remove( res[0] )
                self.treatProtoError( e )
                self.pDebug( 'debug2= ' + str(res[0]) )
                return
            #print >>sys.stderr, 'MSG=', msg

            if ( msg.type == 'BYE' ):
                if self.chats.has_key( res[0] ):
                    chat= self.chats[res[0]]
                    if msg.fields.has_key('idleclose'):
                        chat.delUser( msg.fields['arg'], msg.fields['idleclose'] )
                    else:
                        chat.delUser( msg.fields['arg'] )
                    if len(chat.users) == 0 and CLOSE_EMPTY:
                        self.pDebug( 'chat borrado!' )
                        self.inputs.remove( res[0] )
                        del( self.chats[res[0]] ) 
                    self.userLeavesChat( chat, msg.fields['arg'] )
            elif ( msg.type == 'IRO' ):
                chat= self.chats[res[0]]
                chat.activateUser( msg.fields['passport'] )
            elif ( msg.type == 'ANS' ):
                chat= self.chats[res[0]]
                chat.active= 1
                chat.state= CHAT_OK
                self.joinToChat( chat )
            elif ( msg.type == 'MSG' ):
                chat= self.chats[res[0]]
                more= self.inputs.getChars( res[0]
                        , int(msg.fields['arg3']) )

                # there's only part of msg, return what we read (MSG line)
                if not more:        
                    self.inputs.strReturn( res[0], msg.st + '\r\n' )
                    return

                parts= split(more, '\r\n\r\n', 1)
                heads= parts[0]
                #self.pDebug( 'heads=' + heads )
                if len(parts) > 1:
                    text= unicode( parts[1], 'utf-8', 'replace' )
                    resul= re.compile(r'Content-Type: *[a-z]+/([^; \r]+)[; \r]?').search( heads )
                    if not resul:
                        return 
                        
                    type= resul.group( 1 )

                    self.pDebug( 'type= ' + type )

                    # Typing notification
                    if type == 'x-msmsgscontrol':
                        self.typing( chat, msg.fields['arg1'] )
                    # Message
                    elif type == 'plain':
                        #chat.activateUser( msg.fields['arg1'] )
                        self.msgFrom( chat, msg.fields['arg1']
                                    , text.encode( ENCODING, 'replace' ) )
                    # Response for file send 
                    elif type == 'x-msmsgsinvite':
                        search= re.compile(r'Invitation-Command: (\w+)').search(text)
                        if not search:
                            return 

                        result= search.group(1)
                        search= re.compile(r'Invitation-Cookie: (\d+)').search(text)
                        if not search:
                            return

                        cookie= int(search.group(1))
                        cancelCode= re.compile(r'Cancel-Code: FTTIMEOUT').search(text)

                        self.pDebug( 'result= ' + result )
                        if result == 'ACCEPT':
                            # Peer sends info for file downloading    
                            search= re.compile(r'AuthCookie: (\d+)').search(text)
                            if search:
                                authCookie= int(search.group(1))
                                search= re.compile(r'IP-Address: ([^\r]+)\r').search(text)
                                if not search:
                                    return
                                ip= search.group(1)
                                search= re.compile(r'Port: (\d+)').search(text)
                                if not search:
                                    return
                                port= int(search.group(1))

                                fRecv= self.chats[res[0]].getFileRecv( cookie )
                                fRecv.init( authCookie, ip, port )
                                self.addInput( fRecv.s, SOCK_RECV )
                                self.pDebug( 'AuthCookie= %d, IP= %s, port= %d'
                                    % (authCookie, ip, port) )
                            # Peer has accepted file send
                            else:
                                self.pDebug( 'Accepted cookie=' + str(cookie) )
                                fSend= self.chats[res[0]].getFileSend( cookie )
                                if fSend:
                                    self.initSendFile( fSend )
                                    self.sendFileAccepted( fSend )

                        elif result == 'CANCEL':    
                            if cancelCode:
                                pass
                            else:
                                self.pDebug( 'File cancelled' )
                                chat= self.chats[res[0]]
                                if chat.sendFile.has_key( cookie ):
                                    fSend= chat.getFileSend( cookie )
                                    self.removeFileSend( fSend )
                                    self.sendFileRejected( fSend )
                                elif chat.recvFile.has_key( cookie ):    
                                    fRecv= chat.getFileRecv( cookie )
                                    self.removeFileRecv( fRecv )
                                    self.recvFileCancelled( fRecv )

                        elif result == 'INVITE':    
                            chat= self.chats[res[0]]
                            search= re.compile(r'Application-File: ([^\r]+)\r').search(text)
                            if not search:
                                return
                            fileName= search.group(1)
                            fileName= fileName.encode( ENCODING, 'replace' )
                            search= re.compile(r'Application-FileSize: (\d+)').search(text)
                            if not search:
                                return
                            size= int(search.group(1))
                            self.pDebug( 'MSG INVITE= %s %s' % (fileName, size) )    

                            user= msg.fields['arg1']
                            #print >>sys.stderr, str(msg.fields)
                            fRecv= FileRecv( self, chat, user, cookie, fileName
                                            , size )
                            self.addFileRecv( fRecv )
                            self.recvFileRequest( fRecv )

            elif ( msg.type == 'USR' ):
                self.pDebug( 'USR received' )
                chat= self.chats[res[0]]
                chat.inviteInactive()
                #chat.processActions()
                #print >>sys.stderr, 'CHAT=', chat
                #self.pDebug( 'CHATS:' + str(self.chats) )
                #self.pDebug( 'CHAT:' + str(chat) )
                #self.pDebug( 'USERS:' + str(chat.users) )
                #chat.send( 'CAL', '%s\n' % chat.nextNotActive() )
            elif ( msg.type == 'CAL' ):
                pass
            elif ( msg.type == 'JOI' ):
                chat= self.chats[res[0]]
                chat.activateUser( msg.fields['passport'] )
                self.userAcceptChat( chat, msg.fields['passport'] )
            elif ( msg.type == 'NAK' ):
                self.msgNotReceived( self.chats[res[0]] )
            else:    
                self.pDebug( 'NOT IMPLEMENTED' )

        elif sockType == SOCK_SEND: # Message from file send
            #self.pDebug( 'lala=' + str(res))
            try:
                msg= Msg( res[1] )
            except MSNError, e:    
                self.treatProtoError( e )
                self.pDebug( 'debug= ' + str(res[0]) )
                return

            fSend= self.sendFile[res[0]]
            if ( msg.type == 'VER' ):
                fSend.sendRaw( 'VER MSNFTP\r\n' )
            elif ( msg.type == 'USR' ):
                fSend.sendRaw( 'FIL %d\r\n' % (fSend.fileSize()) )
            elif ( msg.type == 'TFR' ):
                fSend.state= SEND_SENDING
            elif ( msg.type == 'CCL' ):
                self.removeFileSend( fSend )
                ##self.inputs.remove( fSend.s )
                self.sendFileCancelled( fSend )
            elif ( msg.type == 'BYE'):
                ##self.removeInput( fSend.s )
                self.removeFileSend( fSend )
                self.sendFileFinished( fSend )
            else:    
                self.pDebug( 'NOT IMPLEMENTED' )

        elif sockType == SOCK_RECV: # Message from file recv
            self.pDebug( 'SOCK_RECV' )

            fRecv= self.recvFile[res[0]]
            if res[1][:3] == 'VER':
                fRecv.sendRaw( 'USR %s %d\r\n' % (self.user, fRecv.authCookie) )
            elif res[1][:3] == 'FIL':
                parts= split( res[1] )
                fRecv.size= int(parts[1])
                self.pDebug( 'size= %d' % fRecv.size )
                fRecv.sendRaw( 'TFR\r\n' )
            elif res[1][0] == '\0':    
                self.pDebug( 'chunk size= %d' % (len(res[1]))  )
                fRecv.recvChunk( res[1] )
                self.chunkReceived( fRecv )
                if fRecv.received == fRecv.size:
                    self.pDebug( 'Full file received' )
                    fRecv.close()
                    self.removeFileRecv( fRecv )
                    self.recvFileFinished( fRecv )
            elif res[1][0] == '\1':    
                self.removeFileRecv( fRecv )
                self.recvFileCancelled( fRecv )
            else:
                self.pDebug( 'NOT IMPLEMENTED' )

    def loopMsg( self ):
        while self.running:
            self.processMsgs()
                
    
    ########## CALLBACKS ############

    def userOn( self, passport ):
        self.pDebug( 'userOn -> NO CALLBACK' )

    def selfStateChanged( self, state ):        
        self.pDebug( 'selfStateChanged-> NO CALLBACK' )
    
    def userChangesState( self, msg ):
        self.pDebug( 'userChangesState -> NO CALLBACK' )

    def userConnected( self, msg ):        
        self.pDebug( 'userConnected -> NO CALLBACK' )
    
    def userAdded( self, passport, list ):
        self.pDebug( 'userAdded -> NO CALLBACK' )
    
    def userRemoved( self, passport, list ):
        self.pDebug( 'userRemoved -> NO CALLBACK' )
    
    def userOff( self, passport ):
        self.pDebug( 'userOff -> NO CALLBACK' )

    def serverDown( self ):
        self.pDebug( 'serverDown -> NO CALLBACK' )

    def otherLogin( self ):
        self.pDebug( 'otherLogin -> NO CALLBACK' )

    def userAcceptChat( self, chat, passport ):
        self.pDebug( 'userAcceptChat-> NO CALLBACK' )

    def invitedBy( self, msg ):    
        self.pDebug( 'invitedBy -> NO CALLBACK' )

    def userLeavesChat( self, chat, user ):
        self.pDebug( 'userLeavesChat -> NO CALLBACK' )
        
    def msgFrom( self, chat, passport, msg ):
        self.pDebug( 'msgFrom -> NO CALLBACK' )
        
    def typing( self, chat, passport ):
        self.pDebug( 'typing -> NO CALLBACK' )
        
    def gotMail( self, numUnread ):
        self.pDebug( 'gotMail -> NO CALLBACK' )
        
    def incomingMail( self, mailFrom, nameFrom ):
        self.pDebug( 'incomingMail -> NO CALLBACK' )
        
    def protoError( self, code ):
        self.pDebug( 'protoError -> NO CALLBACK' )
    
    def fatalProtoError( self, code ):
        self.pDebug( 'fatalProtoError -> NO CALLBACK' )

    def addUserError( self, passport, list ):        
        self.pDebug( 'addUserError -> NO CALLBACK' )
    
    def delUserError( self, passport, list ):        
        self.pDebug( 'delUserError -> NO CALLBACK' )
    
    def userOnListError( self, passport, list ):        
        self.pDebug( 'userOnListError -> NO CALLBACK' )
    
    def nameChanged( self, name ):
        self.pDebug( 'nameChanged-> NO CALLBACK' )

    def msgNotReceived( self, chat ):
        self.pDebug( 'msgNotReceived -> NO CALLBACK' )

    def joinToChat( self, chat ):
        self.pDebug( 'joinToChat -> NO CALLBACK' )
        
    def chatConnectError( self, chat ):
        self.pDebug( 'errorChatConnect -> NO CALLBACK' )
    
    def sendFileAccepted( self, fSend ):
        self.pDebug( 'sendFileAccepted -> NO CALLBACK' )
        
    def sendFileRejected( self, fSend ):
        self.pDebug( 'sendFileRejected -> NO CALLBACK' )

    def sendFileCancelled( self, fSend ):
        self.pDebug( 'sendFileCancelled -> NO CALLBACK' )

    def sendFileFinished( self, fSend ):
        self.pDebug( 'sendFileFinished -> NO CALLBACK' )

    def chunkSent( self, fSend ):
        self.pDebug( 'chunkSent -> NO CALLBACK' )

    def recvFileRequest( self, fRecv ):
        self.pDebug( 'recvFileRequest -> NO CALLBACK' )

    def recvFileFinished( self, fRecv ):
        self.pDebug( 'recvFileFinished -> NO CALLBACK' )

    def recvFileCancelled( self, fRecv ):
        self.pDebug( 'recvFileCancelled -> NO CALLBACK' )

    def chunkReceived( self, fRecv ):
        self.pDebug( 'chunkReceived -> NO CALLBACK' )

    def listUpdated( self, list, type ):
        self.pDebug( 'listUpdated -> NO CALLBACK' )

    def palListComplete( self ):
        self.pDebug( 'palListComplete-> NO CALLBACK' )
        
    def qngReceived( self ):
        self.pDebug( 'qngReceived -> NO CALLBACK' )

    def userAddedYou( self, passport ):
        self.pDebug( 'userAddedYou -> NO CALLBACK' )


################################################################################

class Chat( Comm ):
    def __init__( self, session, id=0 ):
        self.id= id
        Comm.__init__( self )
        self.session= session
        self.users= {}
        self.active= None
        self.connected= None
        self.written= 0
        self.created= 0
        self.sendFile= {}
        self.recvFile= {}
        self.logFile= None
        self.actions= []

    def logText( self, text='' ):
        if LOG_CHATS and self.logFile != None:
            self.logFile.write( text + '\n' )
            self.logFile.flush()

    def addUser( self, user ):
        # Create log file with first pal name
        if LOG_CHATS: 
            if self.logFile == None:
                self.logFile= open( LOG_DIR + user, 'a' )
                self.logText()
                self.logText( LOG_CHAT_INIT % (user, time.asctime()) )
        
        self.users[user]= ''
        self.hideSocketError= 1

    def nextNotActive( self ):
        for f in self.users.keys():
            if not self.users[f]:
                return f
        return None

    def activateUser( self, user ):
        self.users[user]= 'ONLINE'

    def deactivateUser( self, user ):
        self.users[user]= ''

    def typingUser( self, user ):
        self.users[user]= 'TYPING'
        
    def isTyping( self, user ):    
        return self.users[user] == 'TYPING'

    # idleClose is 1 when server closed socket when idle after 5 min.
    def delUser( self, user, idleClose=0 ):
        del( self.users[user] )
        self.pDebug('self.delUser: ' + str(self.listUsers()))
        if len(self.listUsers()) == 0 or idleClose:
            self.hideSocketError= 1
        else:    
            self.hideSocketError= 0
   
    def getUsers( self ):
        return self.users.items()

    def listUsers( self ):
        return self.users.keys()

    def listInactiveUsers( self ):
        l= []
        for u in self.users.items():
            if not u[1]:
                l.append( u[0] )
        return l

    def addFileSend( self, fSend ):
        self.sendFile[fSend.cookie]= fSend

    def getFileSend( self, cookie ):
        cookie= int(cookie)
        if self.sendFile.has_key( cookie ):
            return self.sendFile[cookie]
        else:    
            return None

    def getFileRecv( self, cookie ):
        cookie= int(cookie)
        if self.recvFile.has_key( cookie ):
            return self.recvFile[int(cookie)]
        else:    
            return None

    def removeFileSend( self, fSend ):
        del( self.sendFile[fSend.cookie] )

    def addFileRecv( self, fRecv ):
        self.recvFile[fRecv.cookie]= fRecv

    def removeFileRecv( self, fRecv ):
        del( self.recvFile[fRecv.cookie] )

    def closeFileRecv( self, fRecv ):
        fRecv.s.close()
        self.session.removeFileRecv( fRecv )

    def cancelFileSend( self, fSend ):
        fSend.cancel()
        self.session.removeFileSend( fSend )

    def sendFileRequest( self, fileName ):
        rand= random.randrange( sys.maxint )
        stats= os.stat( fileName )
        msg= SEND_HEADER % (rand, os.path.basename(fileName), stats[6] )
        nBytes= len(msg)
        self.send( 'MSG', 'N %d\r\n%s' % (nBytes, msg) )
        
        fSend= FileSend( self.session, self, rand, fileName )
        self.addFileSend( fSend )

        return fSend

    def initSendFile( self, cookie ):    
        fSend= self.sendFile[cookie]
        fSend.init()

    def invite( self, passport ):
        self.send( 'CAL', '%s\r\n' % (passport) )

    def accept( self, msg ):
        self.user= msg.fields['passport']
        self.id= 1
        self.active= None
        host= split( msg.fields['ipPort'], ':' )    
        self.pDebug( 'CONNECTING %s:%s' % (host[0], host[1]) )

        self.host= host[0]
        self.port= int( host[1] )
        self.hash= msg.fields['hash']
        self.sessionId= msg.fields['sessionId']
        self.session.addConnectingChat( self, 'INVITED' )
        self.connect_ex()
            

    # Invite pals who are out from chat (e.g. timeout kicked)
    def inviteInactive( self ):
        self.state= CHAT_WAITING_PALS
        self.t0Invited= time.time()
        list= self.listInactiveUsers()
        for user in list:
            self.invite( user )

    def checkInactive( self ):
        t= time.time()
        if (t - self.t0Invited) < PAL_CONNECT_TIMEOUT:
            list= self.listInactiveUsers()
            if not list:
                self.state= CHAT_OK
        else:
            self.session.pError( _('Timeout while waiting for pals to connect'))
            self.session.delConnectingChat( self )
            

    def typingNotification( self ):
        self.pDebug('typingNotification')
        self.pDebug(self.ready)
        if self.numOnlineUsers() > 0:
            chunk= TYPING_HEADER + self.session.user + '\r\n\r\n\r\n'
            self.send( 'MSG', 'U %d\r\n%s' % ( len( chunk ), chunk ) )

        
    def write( self, text ):
        pieces= []
        if len(text) > MAX_LEN:
            while len(text) > 0:
                pieces.append( text[:MAX_LEN] )
                text= text[MAX_LEN:]
        else:
            pieces.append( text )
        
        for part in pieces:
            part= unicode( part, ENCODING, 'replace' )
            part= part.encode( 'utf-8' )
            chunk= MSG_HEADER + part
            self.send( 'MSG', 'N %d\r\n%s' % ( len( chunk ), chunk ))  

    def addAction( self, type, arg ):
        self.actions.append( (type, arg) )

    def processActions( self ):
        for elem in self.actions:
            if elem[0] == 'FILE_SEND':
                self.session.sendFileRequest( self, elem[1] )
                self.session.pInfo( _('Request for sending \'%s\'...') 
                                    % elem[1] )
            elif elem[0] == 'MSG':
                try:
                    self.session.writeTo( self, elem[1] )
                except SocketError, e:    
                    if e.errno() == errno.EAGAIN:
                        pass
                    else:
                        raise e
                self.session.msgFrom( self, self.session.user, elem[1] )
                self.session.isTyping= 0

        self.actions=[]

    def numOnlineUsers( self ):
        n = 0
        for state in self.users.values():
            if state == 'ONLINE':
                n += 1
    
        return n
    
###############################################################################

class FileSend( Comm ):
    def __init__( self, session, chat, cookie, fileName ):
        Comm.__init__( self )
        self.ready= 1
        self.cookie= cookie
        self.session= session
        self.chat= chat
        self.fileName= fileName
        self.file= None
        self.size= 0
        self.sent= 0
        self.port= SEND_PORT
        self.state= SEND_CREATED
        self.s= socket.socket( socket.AF_INET, socket.SOCK_STREAM )

        
    def init( self ):
        self.file= open( self.fileName )
        self.size= os.stat( self.fileName )[6]

        self.s.setblocking( 0 )

        trying= 1
        while trying:
            try:
                self.pDebug( 'Trying port %d...' % (self.port) )
                self.s.bind( ('', self.port) )
                trying= 0
            except socket.error, e:
                if e[0] == errno.EADDRINUSE:
                    self.pDebug( str(e) )
                    self.port+= 1
                else:
                    raise SocketError, (self.s, e)
                    
        self.pDebug( 'Using port= %d' % (self.port) )
        self.s.setblocking( 1 )
        self.s.listen( 1 )

        myIP= self.session.myIP()
        #self.session.pMsg( 'ME %s:%d' % (myIP, myPort) )
        authCookie= random.randrange( sys.maxint )
        msg= SEND_HEADER2 % (self.cookie, myIP, self.port, authCookie)
        nBytes= len(msg)
        self.chat.send( 'MSG', 'N %d\r\n%s' % (nBytes, msg) )

        #self.session.pMsg( 'ip= %s:%d' % (self.s.) )

    def fileSize( self ):
        return self.size

    def sendChunk( self ):
        self.pDebug( 'sendChunk, state= %d' % self.state )

        try:
            res= select.select( [], [self.s], [], 0 )[1]
        except select.error:
            return None

        self.pDebug( 'res= %s' % str(res) )
        if not res:
            self.pDebug( 'Socket not ready for sending' )
            return None

        chunkSize= FILE_CHUNK_SIZE
        if self.sent+(FILE_CHUNK_SIZE-3) > self.size:
            self.pDebug( 'en el if' )
            chunkSize= (self.size - self.sent) + 3
            self.state= SEND_FINISHED

        chunk= self.file.read( chunkSize - 3  )

        div= (chunkSize - 3) / 256
        mod= (chunkSize - 3) % 256
        
        #self.pDebug( 'sending Chunk: ' + chunk )
        self.sent= self.sent + chunkSize - 3
        try:    
            self.sendRaw( '\0' + chr(mod) + chr(div) + chunk )
            return 1
        except SocketError, e:    
            self.session.pError( _('Error while sending \'%s\' (%s)')
                % (self.fileName, str(e)) )
            self.session.removeFileSend( self )
            return None

    def getPeer( self ):
        return self.s.getpeername()

    def cancel( self ):
        if self.state == SEND_SENDING:
            self.pDebug( 'CORTADO' )
            self.sendRaw( '\1\0\0' )
        else:    
            msg= SEND_CANCEL % (self.cookie)
            nBytes= len(msg)
            self.chat.send( 'MSG', 'N %d\r\n%s' % (nBytes, msg) )
        
################################################################################

class FileRecv( Comm ):
    def __init__( self, session, chat, user, cookie, fileName, size ):
        Comm.__init__( self )
        self.user= user
        self.session= session
        self.chat= chat
        self.cookie= cookie
        self.fileName= fileName
        self.size= size
        self.file= None
        self.received= 0
        self.state= RECV_CREATED
        self.s= socket.socket( socket.AF_INET, socket.SOCK_STREAM )

    def init( self, authCookie, host, port ):
        self.authCookie= authCookie
        self.host= host
        self.port= port

        try:
            self.file= open( DOWNLOAD_DIR + self.fileName, 'w' )
        except IOError, e:
            self.session.pError( e[1] )

        self.ready= 1
        self.session.addConnectingFileRecv( self )

    def accept( self ):
        msg= SEND_ACCEPT % (self.cookie)
        nBytes= len(msg)
        self.chat.send( 'MSG', 'N %d\r\n%s' % (nBytes, msg) )
        self.state= RECV_ACCEPTED

    def reject( self ):
        msg= SEND_REJECT % (self.cookie)
        nBytes= len(msg)
        self.chat.send( 'MSG', 'N %d\r\n%s' % (nBytes, msg) )

        self.session.removeFileRecv( self )

    def recvChunk( self, chunk ):    
        size1= ord( chunk[1] )
        size2= ord( chunk[2] )
        
        self.received= self.received + (size1 + size2*256)
        self.pDebug( 'size1= %d, size2= %d' % (size1, size2) )
        self.file.write( chunk[3:] )

    def close( self ):
        self.sendRaw( 'BYE 16777989\r\n' )
        self.s.close()
        self.file.close()

www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.