#!/usr/bin/env python
# -*- coding: latin1 -*-

import sys
import time
import re
import getopt
import errno
from string import *
import curses.ascii
import curses.wrapper
import os
import signal
import socket
import shutil
import struct
import fcntl
import termios
import urllib
import commands
    
from __init__ import VERSION
import MSN
import ui


### Options #####
SEND_TYPING= 1
SHOW_INVITED= 1
AUTO_LIST= 1
AUTO_LIST_ON_0= 0
AUTO_LIST_ON_0_CLEAR= 0
AUTO_LIST_LAST_MOVE= 0
BEEP_ON_MSG= 0
BEEP_ON_NEW_CHAT= 0
MAX_NAME_SIZE= 0
TIMESTAMP= 1
TIMESTAMP_FORMAT= '[%H:%M]'
DELETE_KEY= -1    # keycode of the BACKSPACE key in your system
MONOCHROME= 0
CHAT_TITLE= 'user'
CHAT_TITLE_USE_ALIAS= 1
PING_TIME= 0
IDLE_TIME= 0
IDLE_STATE= 'IDL'
AUTO_UNIDLE= 0
AUTO_ADD_TO_ALLOWED= 1

EXEC_ON_MSG= ''
EXEC_ON_NEW_CHAT= ''
EXEC_ON_FILE_RECV_BEGIN= ''
EXEC_ON_FILE_RECV_END= ''
EXEC_ON_PAL_STATE_NLN= ''
EXEC_ON_PAL_STATE_CHANGE= ''
EXEC_ON_PAL_CONNECTED= ''
EXEC_ON_PAL_DISCONNECTED= ''
#################

PROG_NAME= 'Pebrot'
EXE_NAME= 'pebrot.py'
INIT_STATE= 'NLN'
CONFIG_DIR= os.getenv('HOME') + '/.pebrot/'
CONFIG_FILE= CONFIG_DIR + 'pebrotrc'
LOGO_FILE= sys.prefix + '/share/doc/pebrot/logos/pebrot.txt'
MAIN_LOG_DIR= CONFIG_DIR + 'logs/'
MSN.DEBUG= 0
MSN.VERBOSE= 1
LISTLINE= '----------%s----------'
IP_URL= 'http://simple.showmyip.com'

USER=''
PASSWD=''

HELPMSG= _(r'''Commands begin with '\' or '/', everything else are messages.

    \number     Create chat to user with this number.
    \c          Close active chat.
    tab         Change active chat.
    \i number   Invite user to active chat.
    \l          List connected users.
    \l list     Show requested list. list must be one of the following:
                  fl: Forward list, users on your contact list
                  rl: Reverse list, users who have you on their contact list
                  al: Allow list, users allowed to see your status
                  bl: Block list, users not allowed to see your status
    \u          List users on active chat.
    \n          Change nick.
    \a user     Add user to buddy list.
    \b user     Block user.
    \r user     Remove user from buddy list.
    \o user     Open chat window with a user not on your buddy list.
    \s state    Change state. state must be one of the following:
                  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
    \f filePath Send file.
    \fa         Accept file for download.
    \fr pos     Reject file for download. pos specifies its position on chat
                  bar (1,2,3....). Can input several numbers at once, just
                  separate them with spaces.
                  If executed without any pos just rejects all file downloads
    \fc pos     Cancel file send. pos works like in '\fr'
    \!          Execute shell command.
    \e          Erase chat window contents.
    \d file     Dump chat text to file. If no file is supplied it will use a
                  temporal file name.
    Ctrl-l      Redraw screen.
    \h \?       This help.
    \q Ctrl-d   Quit.''')

USAGE= _(r'''%s - version %s

usage: %s [-d] [-m] [-u user] [-s state]
-d        debug output
-u user   log in with alternate user
-m        monochrome. Don't use colors
-s state  initial state. state must be one of the following:
              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
''') % (PROG_NAME, VERSION, EXE_NAME)

VER_MSG= _('''Version - %s
Enter \h for help.\n\n''') % VERSION


IMG_COLORS= {
                'R': 'RED',
                'G': 'GREEN',
                'Y': 'YELLOW',
                'W': 'WHITE',
                'B': 'BLUE',
                'C': 'CYAN',
                'M': 'MAGENTA',
            }

COLORABLE= [
                'DEFAULT',
                'INFO',
                'INFO_DELIM',
                'ERROR',
                'ERROR_DELIM',
                'CHAT_BAR',
                'CHAT_BAR_NAME',
                'CHAT_BAR_NAME_TYPING',
                'STATE_BAR',
                'STATE_BAR_WRITTEN',
                'STATE_BAR_NOT_WRITTEN',
                'MY_NAME',
                'OTHERS_NAME',
                'LIST_DELIM',
                'LIST_NUM',
                'LIST_PASS',
                'LIST_STATE',
                'LIST_PARENS',
                'LIST_NAME',
                'TIMESTAMP'
        ]


def onlyCreated( x ):
    return x.created

class TextMSN(MSN.Session):
    def __init__( self, stdscr ):
        MSN.Session.__init__( self )

        self.stdscr= stdscr
        self.usersList= {}
        self.firstKeyPress= 1
        self.altPressed= 0
        self.history= []
        self.historyIndex= 0
        self.accounts= {}
        self.COLORS= {}
        self.alias= {}
        self.lastPing= 0
        self.lastStartedTyping= time.time()
        self.typingCommand= 0
        self.needToSendTypingNotif= 1
        self.needToResetTypingNotif= 0
        self.idle= 0

        # To handle terminal resize
        signal.signal(signal.SIGWINCH, self.handleResize)

        # To handle defunct childs
        signal.signal(signal.SIGCHLD, self.handleDefunct)

        curses.use_env( 1 )

        # Lets add chat 0 (the one in window 0 )
        chat= MSN.Chat( self )
        chat.ready= 0
        self.chats['']= chat
        self.sortedChats= [chat]
        self.activeChat= chat

        # set alarm handler for typing notifications
        signal.signal( signal.SIGALRM, self.alarmHandler )
        self.lastPing= time.time()
       
        self.defineColors()
        if curses.has_colors() and not MONOCHROME:
            self.setColors( curses.COLOR_BLACK )
        self.defaultColors()

        (self.maxY, self.maxX)= self.stdscr.getmaxyx()
        #self.StateBar= curses.newwin( 1, self.maxX, 0, 0 )
        self.StateBar= ui.MyScrollWin( 1, self.maxX, 0, 0 )
        self.Main= ui.MyWinStack( self.maxY-4, self.maxX, 1, 0 )
        self.ChatBar= ui.MyScrollWin( 1, self.maxX, self.maxY-3, 0 )
        self.Text= ui.MyInputWin( 2, self.maxX, self.maxY-2, 0 )

        #print >>sys.stderr, 'COLORS:', str(self.COLORS)
        #print >>sys.stderr, 'DEFAULT:', self.COLORS['DEFAULT']
        #Add initial chat
        self.Main.newWin( chat )


    def defineColors( self ):
        colors= ['WHITE', 'BLUE', 'RED', 'CYAN', 'YELLOW', 'GREEN', 'MAGENTA' ]
        for f in (COLORABLE + colors):
            self.COLORS[f]= ''

    def defaultColors( self ):
        self.COLORS['DEFAULT']= self.COLORS['WHITE']
        self.COLORS['INFO']= self.COLORS['CYAN']
        self.COLORS['INFO_DELIM']= self.COLORS['RED']
        self.COLORS['ERROR']= self.COLORS['YELLOW']
        self.COLORS['ERROR_DELIM']= self.COLORS['RED']
        self.COLORS['CHAT_BAR']= self.COLORS['BLUE']
        self.COLORS['CHAT_BAR_NAME']= self.COLORS['BLUE']
        self.COLORS['CHAT_BAR_NAME_TYPING']= self.COLORS['GREEN']
        self.COLORS['STATE_BAR']= self.COLORS['BLUE']
        self.COLORS['STATE_BAR_WRITTEN']= self.COLORS['YELLOW']
        self.COLORS['STATE_BAR_NOT_WRITTEN']= self.COLORS['BLUE']
        self.COLORS['MY_NAME']= self.COLORS['BLUE']
        self.COLORS['OTHERS_NAME']= self.COLORS['RED']
        self.COLORS['LIST_DELIM']= self.COLORS['WHITE']
        self.COLORS['LIST_NUM']= self.COLORS['RED']
        self.COLORS['LIST_PASS']= self.COLORS['BLUE']
        self.COLORS['LIST_STATE']= self.COLORS['GREEN']
        self.COLORS['LIST_PARENS']= self.COLORS['WHITE']
        self.COLORS['LIST_NAME']= self.COLORS['WHITE']
        self.COLORS['TIMESTAMP']= self.COLORS['WHITE']

    def initBarColors( self ):
        if curses.has_colors() and not MONOCHROME:
            self.ChatBar.attrset( self.COLORS['CHAT_BAR'] )
            self.StateBar.attrset( self.COLORS['STATE_BAR'] )
            self.Text.setAttr( self.COLORS['DEFAULT'] )

        #self.showChatBar()
        #self.showStateBar()

    def termSize( self, ):
        try:
            return int(os.environ["LINES"]), int(os.environ["COLUMNS"])
        except KeyError:
            height, width = struct.unpack(
                "hhhh", fcntl.ioctl(0, termios.TIOCGWINSZ ,"\000"*8))[0:2]
            if not height: 
                return 25, 80
            return height, width        

    def reinitCurses( self ):
        while 1:
            try: 
                curses.endwin(); 
                self.stdscr= curses.initscr()
                break
            except curses.error, e: 
                time.sleep(1)


    def handleDefunct( self, a, b ):
        # Let's clean those zombie sons >:-]
        try:
            os.wait()
        except OSError:
            pass
    
    def handleResize( self, a, b ):
        #stdscr= curses.initscr()
        #self.stdscr= stdscr
        #size= self.termSize()
        #(self.maxY, self.maxX)= size[0], size[1]
        self.reinitCurses()
        
        try: 
            self.maxY, self.maxX= self.stdscr.getmaxyx()
        #print >>sys.stderr, 'maxX= %d, maxY= %d' % (self.maxX, self.maxY)
            if self.maxY-3 >= 6:
                self.StateBar.resize( 1, self.maxX, 0, 0 )
                self.Main.resize( self.maxY-4, self.maxX, 1, 0 )
                self.ChatBar.resize( 1, self.maxX, self.maxY-3, 0 )
                self.Text.resize( 2, self.maxX, self.maxY-2, 0 )

                self.refreshAll()
        except curses.error, e: 
            pass


    def setColors( self, background ):
        try:
            import usedefault
            usedefault.use_default_colors()
            back= -1
        except ImportError:
            back= background
        
        curses.init_pair( 1, curses.COLOR_WHITE, back )
        curses.init_pair( 2, curses.COLOR_BLUE, back )
        curses.init_pair( 3, curses.COLOR_RED, back )
        curses.init_pair( 4, curses.COLOR_CYAN, back )
        curses.init_pair( 5, curses.COLOR_YELLOW, back )
        curses.init_pair( 6, curses.COLOR_GREEN, back )
        curses.init_pair( 7, curses.COLOR_MAGENTA, back )
        curses.init_pair( 8, curses.COLOR_MAGENTA, back )

        self.COLORS['WHITE']= curses.color_pair( 1 )
        self.COLORS['BLUE']= curses.color_pair( 2 )
        self.COLORS['RED']= curses.color_pair( 3 )
        self.COLORS['CYAN']= curses.color_pair( 4 )
        self.COLORS['YELLOW']= curses.color_pair( 5 )
        self.COLORS['GREEN']= curses.color_pair( 6 )
        self.COLORS['MAGENTA']= curses.color_pair( 7 )

        #self.stdscr.touchwin()
        #self.stdscr.refresh()
        #self.stdscr.bkgdset( curses.color_pair(8) )


    def pActive( self, text, color= None, refresh= 1 ):
        #(y,x)= curses.getsyx()
        if color == None:
            color= 'DEFAULT'
            
        if curses.has_colors() and not MONOCHROME:
            self.Main.addstr( self.activeChat, '%s' % (text)
                , self.COLORS[color] )
        else:    
            self.Main.addstr( self.activeChat, '%s' % (text) )
        #curses.setsyx( y, x )
        if refresh:
            self.refreshWin()
            self.Text.refresh()

    def pWin( self, chat, text, color= None, refresh= 1 ):
        if color == None:
            color= 'DEFAULT'

        try:
            if curses.has_colors() and not MONOCHROME:
                self.Main.addstr( chat, text, self.COLORS[color] )
            else:    
                self.Main.addstr( chat, text )
        except TypeError, e:
            self.pError( _('Type error when displaying text: %s') % str(e) )
            
        #curses.setsyx( y, x )
        if refresh and self.activeChat == chat:
            #self.pDebug( 'REFRESH' )
            self.refreshWin( chat )
            self.Text.refresh()
        
    def pVerbose( self, level, st ):
        if level >= MSN.VERBOSE:
            self.pWin( self.activeChat, st )

    def refreshWin( self, chat= None ):
        if not chat:
            chat= self.activeChat
        self.Main.refresh( chat )

    def refreshAll( self ):
        #self.stdscr.refresh()
        #print >>sys.stderr, '-- STATE BAR --'
        #self.initBarColors()
        self.showStateBar()
        #print >>sys.stderr, '-- MAIN --'
        self.refreshWin()
        #print >>sys.stderr, '-- CHAT BAR --'
        self.showChatBar()
        #print >>sys.stderr, '-- TEXT --'
        self.Text.refresh()

    def addHistory( self, line ):
        self.history.append( line )
        self.historyIndex= len( self.history ) 

    def showPrevHistory( self ):    
        if self.historyIndex > 0:
            self.historyIndex= self.historyIndex - 1 

            self.Text.erase()
            self.Text.addstr( self.history[self.historyIndex] )
            self.Text.refresh()


    def showNextHistory( self ):
        if self.historyIndex < len( self.history ) - 1:
            self.historyIndex= self.historyIndex + 1

            self.Text.erase()
            self.Text.addstr( self.history[self.historyIndex] )
            self.Text.refresh()

            
    def pInfo( self, text, chat= None ):
        if not chat:
            chat= self.activeChat

        self.pWin( chat, '>> ', 'INFO_DELIM' )
        self.pWin( chat, text, 'INFO' )
        self.pWin( chat, ' <<\n', 'INFO_DELIM' )

    def aliasOrName( self, passport ):
        if self.alias.has_key( passport ):
            name= self.alias[passport]
        else:    
            try:
                name= self.usersList[passport]['name']
            except KeyError:
                name= passport
        
        return name

    def pChat( self, chat, passport, msg ):
        if passport == self.user:
            name= self.name
            color= 'MY_NAME'
            chat.written= 0
        else:    
            name= self.aliasOrName( passport )
                
            color= 'OTHERS_NAME'
            if BEEP_ON_MSG:
                curses.beep()    

            if EXEC_ON_MSG:    
                escMsg= re.escape( msg )
                escName= re.escape( name )
                self.execAction( EXEC_ON_MSG, {'$passport':passport
                    , '$user':escName, '$msg':escMsg} )

            chat.written= 1
            chat.activateUser( passport )

        if MAX_NAME_SIZE > 0:    
            name= name[:MAX_NAME_SIZE]
            
        if TIMESTAMP:    
            self.pWin( chat, '%s ' % time.strftime(TIMESTAMP_FORMAT
                                        , time.localtime()), 'TIMESTAMP' )

        self.pWin( chat, '%s: ' % name, color )
        self.pWin( chat, '%s\n' % msg )
        chat.logText( '%s %s: %s' % (time.strftime('[%H:%M:%S]'
                                    , time.localtime()) , passport, msg) )
        
                
    def pError( self, text, chat= None ):
        if not chat:
            chat= self.activeChat

        self.pWin( chat, '>> ', 'ERROR_DELIM' )
        self.pWin( chat, text, 'ERROR' )
        self.pWin( chat, ' <<\n', 'ERROR_DELIM' ) 

    def fatalError( self, msg ):
        #curses.endwin()
        #print >>sys.stderr, 'Error:', msg
        #self.refreshWin()
        #sys.exit()

        raise _('Error: ') + msg
        
    def usage( self ):
        print sys.stderr.write( USAGE + '\n' )

    def parseArgs( self ):
        global USER, INIT_STATE, MONOCHROME
    
        args= split( '-m -d -u -s' )
        try:    
            opts, args= getopt.getopt( sys.argv[1:], 'dmu:s:', args )
        except getopt.GetoptError:
            curses.endwin()
            self.usage()
            #self.refreshWin()
            sys.exit( 1 )
        
        for o, a in opts:
            if o == '-d':
                MSN.DEBUG= 1
            if o == '-m':
                MONOCHROME= 1
            if o == '-s':    
                a= upper( a )
                if not MSN.STATES.has_key( a ):
                    curses.endwin()
                    self.usage()
                    #self.refreshWin()
                    sys.exit( 1 )
                INIT_STATE= a
            if o == '-u':    
                USER= a


    def readConfig( self, file ):
        global USER, PASSWD, INIT_STATE, SHOW_INVITED, AUTO_LIST, BEEP_ON_MSG \
                , BEEP_ON_NEW_CHAT, SEND_TYPING, MAX_NAME_SIZE \
                , AUTO_LIST_ON_0, AUTO_LIST_ON_0_CLEAR, AUTO_LIST_LAST_MOVE \
                , TIMESTAMP, TIMESTAMP_FORMAT, DELETE_KEY, CHAT_TITLE \
                , CHAT_TITLE_USE_ALIAS, PING_TIME, IDLE_TIME, IDLE_STATE \
                , AUTO_UNIDLE, LOGO_FILE, EXEC_ON_MSG, EXEC_ON_NEW_CHAT \
                , EXEC_ON_FILE_RECV_BEGIN, EXEC_ON_FILE_RECV_END \
                , EXEC_ON_PAL_STATE_NLN, EXEC_ON_PAL_STATE_CHANGE \
                , EXEC_ON_PAL_CONNECTED, EXEC_ON_PAL_DISCONNECTED \
                , AUTO_ADD_TO_ALLOWED
                

        try:
            f= open( file )
        except IOError:
            self.fatalError( _('Opening config file \'%s\'') % file )
    
        l= f.readline()
        lastUser=''
        while l:
            l= strip(l)
            if len(l) > 0:
                if l[0] == '#':
                    self.pDebug( 'comment' )
                else:
                    (opt, value)= split(l, '=', 1)
                    opt= strip( opt )
                    value= strip( value )
                    if opt == 'user':
                        if USER == '':
                            USER= value
                        lastUser= value    
                        self.accounts[value]= ''
                    elif opt == 'password':
                        if PASSWD == '':
                            PASSWD= value
                        if lastUser == '':   
                            if USER: # To catch error later (no user defined)
                                self.fatalError( _('Reading config file \'%s\', password \'%s\' must have a preceding \'user\' option') % (file, value) )
                        else:
                            self.accounts[lastUser]= value
                        lastUser= ''    
                    elif opt == 'state':
                        INIT_STATE= upper( value )
                    elif opt == 'encoding':
                        MSN.ENCODING= value
                    elif opt == 'show_invited':
                        if value == '1':
                            SHOW_INVITED= 1        
                        elif value == '0':    
                            SHOW_INVITED= 0        
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'auto_list':
                        if value == '1':
                            AUTO_LIST= 1        
                        elif value == '0':    
                            AUTO_LIST= 0        
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'beep_on_msg':
                        if value == '1':
                            BEEP_ON_MSG= 1        
                        elif value == '0':    
                            BEEP_ON_MSG= 0        
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'beep_on_new_chat':
                        if value == '1':
                            BEEP_ON_NEW_CHAT= 1        
                        elif value == '0':    
                            BEEP_ON_NEW_CHAT= 0        
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'send_typing':
                        if value == '1':
                            SEND_TYPING= 1        
                        elif value == '0':    
                            SEND_TYPING= 0        
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'download_dir':
                        MSN.DOWNLOAD_DIR= value
                        if MSN.DOWNLOAD_DIR[-1] != '/':
                            MSN.DOWNLOAD_DIR= MSN.DOWNLOAD_DIR + '/'
                        
                    elif opt == 'max_name_size':
                        try:
                            MAX_NAME_SIZE= int(value)
                        except ValueError:
                            self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) )
                    elif opt == 'auto_list_on_0':
                        if value == '1':
                            AUTO_LIST_ON_0= 1        
                        elif value == '0':    
                            AUTO_LIST_ON_0= 0        
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'auto_list_on_0_clear':
                        if value == '1':
                            AUTO_LIST_ON_0_CLEAR= 1
                        elif value == '0':    
                            AUTO_LIST_ON_0_CLEAR= 0
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'auto_list_last_move':
                        if value == '1':
                            AUTO_LIST_LAST_MOVE= 1
                        elif value == '0':    
                            AUTO_LIST_LAST_MOVE= 0
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'pal_connect_timeout':
                        try:
                            MSN.PAL_CONNECT_TIMEOUT= int(value)
                        except ValueError:
                            self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) )
                    elif opt == 'timestamp':
                        if value == '1':
                            TIMESTAMP= 1
                        elif value == '0':    
                            TIMESTAMP= 0
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'log_chats':
                        if value == '1':
                            MSN.LOG_CHATS= 1
                        elif value == '0':    
                            MSN.LOG_CHATS= 0
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'delete_key':
                        try:
                            if value[:2] == '0x':
                                DELETE_KEY= int(value[2:], 16)
                            else:    
                                DELETE_KEY= int(value)
                        except ValueError:
                            self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) )
                        
                    elif opt == 'timestamp_format':
                        TIMESTAMP_FORMAT= value
                    elif opt == 'chat_title':
                        if value in ['number', 'user', 'both']:
                            CHAT_TITLE= value
                        else:    
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values number, user or both') % (file, opt) )
                    elif opt == 'alias':
                        l= split( value, ' ', 1 )

                        if len(l) == 2:
                            self.alias[l[0]]= l[1]
                        else:
                            self.fatalError( _('Reading config file \'%s\', option \'%s\': malformed alias') % (file, opt) )
                    elif opt == 'chat_title_use_alias':
                        if value == '1':
                            CHAT_TITLE_USE_ALIAS= 1
                        elif value == '0':
                            CHAT_TITLE_USE_ALIAS= 0
                        else:
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'ping_time':
                        try:
                            PING_TIME= int(value)
                        except ValueError:
                            self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) )
                    elif opt == 'idle_time':
                        try:
                            IDLE_TIME= int(value)
                        except ValueError:
                            self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) )
                    elif opt == 'idle_state':
                        IDLE_STATE= upper(value)
                        if not MSN.STATES.has_key( IDLE_STATE ):
                            self.fatalError( _('Reading config file \'%s\', \'%s\' must be a valid state') % (file, opt) )
                    elif opt == 'auto_add_to_allowed':
                        if value == '1':
                            AUTO_ADD_TO_ALLOWED= 1
                        elif value == '0':
                            AUTO_ADD_TO_ALLOWED= 0
                        else:
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'auto_unidle':
                        if value == '1':
                            AUTO_UNIDLE= 1
                        elif value == '0':
                            AUTO_UNIDLE= 0
                        else:
                            self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) )
                    elif opt == 'logo_file':
                        if value != '' and value[0] == '/':
                            LOGO_FILE= value
                        else:    
                            LOGO_FILE= sys.prefix + '/share/doc/pebrot/' + value
                    elif opt == 'my_ip':
                        MSN.MY_IP= value
                    elif opt[:4] == 'col_':
                        if not curses.has_colors():
                            l= f.readline()
                            continue
                            
                        if self.COLORS.has_key( opt[4:].upper() ):
                            color= value.upper()
                            #print >>sys.stderr, 'color=', color
                            if not self.COLORS.has_key( color ):
                                self.fatalError( _('Reading config file \'%s\', \'%s\' has a non valid color value (%s)') % (file, opt, value) )

                            self.COLORS[opt[4:].upper()]= self.COLORS[color]
                        else:    
                            self.fatalError( _('Reading config file \'%s\', \'%s\' is not a valid color option') % (file, opt) )
                    elif opt[:8] == 'exec_on_':
                        opt= opt.upper()
                        try:
                            exec(opt)
                        except NameError:    
                            self.fatalError( _('Reading config file \'%s\', \'%s\' is not a valid option') % (file, opt) )
                        
                        exec( '%s= \'%s\'' % (opt, value), globals() )
                    else:
                        self.fatalError( _('Reading config file \'%s\', unknown option \'%s\' ')
                            % (file, opt) )
            l= f.readline()


        #MSN.Session.__init__( self )


        #print >>sys.stderr, 'COLORS=', self.COLORS
        #self.showChatBar()
        #self.showStateBar()

    def connect( self, user, passwd, state ):
        self.pVerbose( 1, _('User: %s\n') % user )
        MSN.Session.connect( self, user, passwd, state )


    def getPassword( self ):
        if USER == '':
            self.fatalError( _('No user or password, you should take a look at README ;)') )

        if self.accounts.has_key( USER ) and self.accounts[USER]:
            passwd= self.accounts[USER]
        else:    
            self.pActive( _('Enter password for %s: ') % USER )
            passwd= strip( self.Text.getLine( self.handleInput, hidden= 1 ) ) 
            self.pActive('\n') 
        
        return passwd
    
    def printUsers( self, chat= None, hash=None, title= _('=| CONNECTED |=')
                    , numbers=1, state=1, name=1  ):
        if not chat:
            chat= self.activeChat
            
        if hash:
            users= hash.keys()
        else:    
            users= self.usersList.keys()

        i= 0
        users.sort()

        self.pWin( chat, '\n', refresh= 0 )
        self.pWin( chat, (LISTLINE % title) + '\n', color= 'LIST_DELIM' ) 
        for pal in users:
            if numbers:
                self.pWin( chat, str(i), 'LIST_NUM', 0 )
                self.pWin( chat, '- ', 'DEFAULT', 0 )
            self.pWin( chat, '%s ' % pal, 'LIST_PASS', 0 )
            if state:
                self.pWin( chat, '(', 'LIST_PARENS', 0 )
                self.pWin( chat, MSN.STATES[self.usersList[pal]['state']]
                    , 'LIST_STATE', 0 )
                self.pWin( chat, ')', 'LIST_PARENS', 0 )
            if name:
                self.pWin( chat, ': ', 'LIST_PARENS', 0 )
                if self.alias.has_key( pal ):
                    name= self.alias[pal]
                else:    
                    name= replace(self.usersList[pal]['name'], '\r', '')
                self.pWin( chat, '%s' % name, 'LIST_NAME', 0 )
            self.pWin( chat, '\n' )        
            #content= content + '%d- %s (%s): %s\n' % ( i
                #, pal
                #, MSN.STATES[self.usersList[pal]['state']]
                #, self.usersList[pal]['name'] )
            i= i + 1    

        #self.pMain( (LISTLINE  % ('-' * len(title))) + '\n' )
        if TIMESTAMP:
            self.pWin( chat, '------------= %s =------------\n' % time.strftime(TIMESTAMP_FORMAT
                                         , time.localtime()), 'TIMESTAMP' )
        else:
            self.pWin( chat, (LISTLINE  % ('-' * len(title))) + '\n', refresh= 0
                    , color= 'LIST_DELIM' )
        self.refreshWin()
        self.Text.refresh()

    def printList( self, chat, title, content ):
        self.pWin( chat, '\n' )
        self.pWin( chat,  (LISTLINE % title) + '\n' )
        self.pWin( chat, content + '\n' )
        self.pWin( chat, (LISTLINE  % ('-' * len(title))) + '\n' )
    
    def updateUsersList( self ):
        self.usersList= {}
        for user in self.usersOnline.keys():
            self.usersList[user]= self.usersOnline[user]

    def nextChat( self ):
        if SHOW_INVITED:
            list= self.sortedChats
        else:
            list= filter( onlyCreated, self.sortedChats )

        elems= len(list)    

        if elems == 0:
            return None
        else:
            if self.activeChat in list:
                i= list.index( self.activeChat )
                return list[(i+1)%elems]
            else:
                return list[-1]

    def dumpFileName( self ):
        pref= 'pebrot-dump'
        name= pref
        i= 0
        while( 1 ):
            if not os.access( name, os.F_OK ):
                return name
            else:    
                name= pref + '-' + str(i)
                i+= 1

    def helpInfo( self ):
        self.printList( self.activeChat, _('=| HELP |='), HELPMSG )

    def processInput( self, st ):
        st= strip( st )
        parts= split( st )
        if len(parts) == 0:
            return
        
		    
        # Added to allow Pebrot to remain offline
	if self.state == 'FLN':
            if parts[0][1:] == 'q':
                sys.exit()
            elif parts[0][1:] == 's':
                if len(parts)<2:
                    self.pInfo( _('state: %s') % MSN.STATES[self.state] )
                else:
                    state= upper( parts[1] )
                    if state != 'FLN' and MSN.STATES.has_key( state ):
                    	self.state = state
                    	self.reconnect()
                        self.idle= 0
                        #self.lastStartedTyping= time.time()
                        #self.alarmExecAndProgramNext()
                    else:
                        self.helpInfo()
		return
	    else:
                self.pError( _('Error: Must be online to send commands or messages.') )
	        return
        # End Edit

        if parts[0][0] in ['\\', '/']:
            command= parts[0][1:]
            self.pDebug( 'com=' + command )
            if command.isdigit():
                if ( int(command) >= len(self.usersList) ):
                    self.pError( _('Error: Select an existing user number') )
                    return
                
                if self.state in ['HDN', 'FLN']:
                    self.pError( _('Error: Must be online for talking') )
                    return

                allUsers= self.usersList.keys()
                allUsers.sort()
                user= allUsers[int(command)]
                if user == self.user:
                    self.pInfo( _('You can\'t talk to yourself') )
                    return

                chat= self.newChat( user )
                self.sortedChats.append( chat )

                self.activeChat= chat
                self.Main.newWin( chat )
                self.refreshWin()
                self.showChatBar()
                self.showStateBar()

            elif command == 'h' or command == '?':
                self.helpInfo()

            elif command == 'l':
                if len(parts) == 1:
                    self.printUsers()
                elif len(parts) == 2:
                    list= upper( parts[1] )
                    if list in ['FL', 'RL', 'AL', 'BL']:
                        self.printUsers( hash= self.lists[list]
                            , title= (_('=| USERS ON %s |=') % MSN.LISTS[list])
                            , numbers=0, state=0, name=0 )
                    else:    
                        self.helpInfo()
                else:
                    self.helpInfo()

            elif command == 'i':
                if len(parts)<2: 
                    self.helpInfo()
                    return
                if self.activeChat == self.sortedChats[0]:
                    self.pInfo( _('Must be on active chat to invite') )
                    return

                allUsers= self.usersList.keys()
                allUsers.sort()
                user= allUsers[int(parts[1])]
                self.activeChat.invite( user )

            elif command == 'c':
                if self.activeChat != self.sortedChats[0]:
                    self.pDebug( _('closing chat %s') % str(self.activeChat) )

                    try:
                        self.closeChat( self.activeChat )
                    except MSN.SocketError, e:    
                        pass
                    self.sortedChats.remove( self.activeChat )
                    self.Main.delWin( self.activeChat )
                    self.activeChat= self.nextChat()
                    #if len(self.chats.keys()) == 0:
                        #self.activeChat= None
                    self.refreshWin()    
                    self.showChatBar()    
                    self.showStateBar()

            elif command == 'a':
                if len(parts)<2: 
                    self.helpInfo()
                else:    
                    self.delUser( parts[1], 'BL' )
                    self.addUser( parts[1], 'AL' )
                    self.addUser( parts[1], 'FL' )

            elif command == 'b':
                if len(parts)<2: 
                    self.helpInfo()
                else:    
                    self.delUser( parts[1], 'AL' )
                    self.addUser( parts[1], 'BL' )

            elif command == 'r':
                if len(parts)<2: 
                    self.helpInfo()
                else:    
                    self.delUser( parts[1], 'AL' )
                    self.delUser( parts[1], 'FL' )

            elif command == 'u':
                if self.activeChat != self.sortedChats[0]:
                    self.showChatUsers( self.activeChat )

            elif command == 'o':
                if len(parts)<2:
                    self.helpInfo()
                else:
                    if self.state in ['HDN', 'FLN']:
                        self.pError( _('Error: Must be online for talking') )
                        return

                    user= parts[1]
                    if user == self.user:
                        self.pInfo( _('You can\'t talk to yourself') )
                        return
                    if not "@" in user:
                        self.pError( _('You must use give the full passport address. Ex.: somebody@example.com') )
                        return

                    chat= self.newChat( user )
                    self.sortedChats.append( chat )

                    self.activeChat= chat
                    self.Main.newWin( chat )
                    self.refreshWin()
                    self.showChatBar()
                    self.showStateBar()
            
            elif command == 'n':
                if len(parts) == 1: 
                    self.pInfo( _('Name: %s') % self.name )
                else:    
                    self.changeName( join(parts[1:], ' ') )
                            
            elif command == 's':
                if len(parts)<2: 
                    self.pInfo( _('state: %s') % MSN.STATES[self.state] )
                else:
                    state= upper( parts[1] )
                    if MSN.STATES.has_key( state ):
                        self.changeState( state )
                        self.idle= 0
                        self.lastStartedTyping= time.time()
                        self.alarmExecAndProgramNext()
                    else:    
                        self.helpInfo()

            elif command == 'p':
                self.ping()

            elif command == 'f':
                if self.activeChat == self.sortedChats[0]:
                    self.pError( _('Must be connected to a chat for file sending') )
                    return    

                if len(parts)<2: 
                    self.helpInfo()
                    return    

                file= join( parts[1:] )
                if not os.access( file, os.R_OK ):
                    self.pError( _('Can\'t open \'%s\'') % file )
                    return

                self.sendChatFile( self.activeChat, file )
                
            elif command == 'fa':
                for fRecv in self.activeChat.recvFile.values():
                    if fRecv.state != MSN.RECV_ACCEPTED:
                        fRecv.accept()
                        self.pInfo( _('Receiving \'%s\' accepted') 
                                    % (fRecv.fileName) )

            elif command == 'fr':
                if len(parts) == 1:
                    for fRecv in self.activeChat.recvFile.values():
                        fRecv.reject()
                        self.pInfo( _('Receiving \'%s\' rejected') 
                                    % (fRecv.fileName) )
                        self.showChatBar()            
                else:                    
                    recvs= self.activeChat.recvFile.values()
                    for num in parts[1:]:
                        num= int(num)
                        if num >= 1 and num <= len(recvs):
                            self.activeChat.closeFileRecv( recvs[num-1] )
                            self.pInfo( _('Aborting downloading number %d') 
                                          % num )
                            self.showChatBar()              
                        else:                  
                            if len(recvs) == 0:
                                self.pError( _('There are no downloads to cancel') ) 
                            else:
                                self.pError( _('Number must be between 1 and %d')
                                            % (len(recvs)) )
                    self.showChatBar()        

            elif command == 'fc':
                if len(parts) == 1:
                    for fSend in self.activeChat.sendFile.values():
                        fSend.cancel()
                        self.removeFileSend( fSend )
                        self.pInfo( _('Sending \'%s\' rejected') 
                                    % (fSend.fileName) )
                        self.showChatBar()            
                else:                    
                    sends= self.activeChat.sendFile.values()
                    for num in parts[1:]:
                        num= int(num)
                        if num >= 1 and num <= len(sends):
                            self.activeChat.cancelFileSend( sends[num-1] )
                            self.pInfo( _('Cancelling sending number %d') 
                                          % num )
                            self.showChatBar()              
                        else:                  
                            if len(sends) == 0:
                                self.pError( _('There are no downloads to cancel') ) 
                            else:
                                self.pError( _('Number must be between 1 and %d')
                                            % (len(sends)) )
                    self.showChatBar()        

            elif command == '!':
                com= join( parts[1:] )
                (stdin, out)= os.popen4(com, 'r')
                self.pActive( out.read() + '\n' )

            elif command == 'e':
                self.Main.erase( self.activeChat )
                self.Main.toBottom( self.activeChat )
                self.refreshWin()
                self.Text.refresh()
            
            elif command == 't':
                self.pActive( str(self.termSize()) )
            
            elif command == 'crash':
                self.crash()

            elif command == 'ex':
                self.subExec( self.replaceVars( 
                    'echo $user es una $prueba'
                    , {'$user':'de aqui', '$prueba':'kk' } ) )
            
	    ## added for pebrot no error message shutdown
	    elif command == 'rec':
	        if self.offline == 1:
                    self.crash()
		    self.offline = 0

            elif command == 'd':
                if len( parts ) == 1:
                    name= self.dumpFileName() 
                else:    
                    name= parts[1]

                try:
                    f= open( name, 'w' )
                except IOError:
                    self.pError( _('Can\'t open file \'%s\'') % name )
                    return 
                self.Main.dump( self.activeChat, f )
                self.pInfo( _('Chat dumped to file %s') % name )

            elif command == 'color':
	        if len( parts ) == 1:
		    self.pInfo( _('The colors are: %s') % self.COLORS )
		elif len ( parts ) == 3:
		    #self.pInfo( _('%s') % parts[1] + ' and ' + parts[2] )
		    self.COLORS[ parts[1] ] = self.COLORS[ parts[2] ]
		else:
		     self.pInfo( _('You messed up somehow and i can\'t fix it. :-P') )
	    elif command == 'q':
                sys.exit()
            else:
                self.pError( _(r'Error: Unknown command: %s') % parts[0] )
        else: # It's a message, lets send it
            if AUTO_UNIDLE and self.idle:
                self.changeState( 'NLN' );
                self.idle= 0
                self.lastStartedTyping= time.time()
                self.alarmExecAndProgramNext()

            if self.activeChat == self.sortedChats[0]:
                self.pError( _('Error: You are not connected to any chat.') )
            else:
                self.sendChatMsg( self.activeChat, st )
                    


    # Invite pals who are out from chat (e.g. timeout kicked)
    def inviteInactive( self ):
        list= self.listInactiveUsers( self.activeChat )
        if list:
            t0= time.time()
            for user in list:
                self.activeChat.invite( user )
            t1= time.time()    
            while self.listInactiveUsers( self.activeChat ) and \
                  (t1-t0) < MSN.PAL_CONNECT_TIMEOUT:
                self.step()
                t1= time.time()    
                self.pDebug( 'Waiting for pals to connect...(t1-t0= %s)' 
                                % (t1-t0) )

            if (t1-t0) >= MSN.PAL_CONNECT_TIMEOUT:
                self.pError( _('Timeout while waiting for pals to connect') )
    
    def showStateBar( self ):
        self.StateBar.erase()
        if curses.has_colors() and not MONOCHROME:
            self.StateBar.attrset( self.COLORS['STATE_BAR'] )
    
        if SHOW_INVITED:
            list= self.sortedChats 
        else:
            list= filter( onlyCreated, self.sortedChats )
        
        #list= [None] + list

        if curses.has_colors() and not MONOCHROME:
            cBar= self.COLORS['STATE_BAR']
            cWritten= self.COLORS['STATE_BAR_WRITTEN']
            cNormal= self.COLORS['STATE_BAR_NOT_WRITTEN']
        else:    
            cBar= cNormal= cWritten= 0

        #self.pMain( str(list) )
        i= 0
        for chat in list:
            if chat.written:
                color= cWritten
                if curses.has_colors() and not MONOCHROME:
                    plus= ''
                else:
                    plus= '+'
            else:    
                color= cNormal
                plus= ''

            if chat == self.activeChat:
                self.StateBar.addstr( '[', cBar )

            if CHAT_TITLE == 'number' or CHAT_TITLE == 'both':
                self.StateBar.addstr( '%d' % i, color )
            
            if CHAT_TITLE == 'both':
                self.StateBar.addstr( '-', color )
            
            if (CHAT_TITLE == 'user' or CHAT_TITLE == 'both'):
                users= chat.listUsers()
                if len( users ) == 0:
                    if chat == self.sortedChats[0]:
                        title= _('Main')
                    else:    
                        title= _('Empty')
                elif len( users ) == 1:
                    if CHAT_TITLE_USE_ALIAS and \
                    self.alias.has_key( users[0] ):
                        title= replace( self.alias[users[0]], ' ', '_' )
                    else:    
                        title= users[0].split('@')[0]
                else:
                    title= _('Multiple')
                self.StateBar.addstr( '%s' % title, color )

            self.StateBar.addstr( plus, color )

            if chat == self.activeChat:
                self.StateBar.addstr( ']', cBar )
            i= i + 1    

            if list:
                self.StateBar.addstr( ' ' )

        self.StateBar.line()
        self.StateBar.toTop()
        self.StateBar.refresh()
        self.Text.refresh()

    # Find public ip using http://simple.showmyip.com
    def getPublicIp( self ):
        self.pActive( _('Querying public ip from simple.showmyip.com ') )
        ip= ''
        for i in range(10):
            try:
                st= urllib.urlopen( IP_URL ).read().strip()
                ip= split( st )[0]
            except IOError:
                pass
            self.pActive( '.' )

            if ip != '':
                self.pActive( ' %s\n' % ip )
                return ip
        
        self.pActive( _(' ERROR: can\'t read page\n') )

    def showChatBar( self ):
        self.ChatBar.erase()
        if curses.has_colors() and not MONOCHROME:
            self.ChatBar.attrset( self.COLORS['CHAT_BAR'] )

        if self.activeChat != self.sortedChats[0]:
            for fSend in self.activeChat.sendFile.values():
                try:
                    percent= (float(fSend.sent)/fSend.size)*100
                    self.pDebug( '%s= (up: %d)' % ( str(self.sendFile)
                        , percent) )
                    self.ChatBar.addstr( _('(up: %d%%)') % percent )
                except ZeroDivisionError:
                    pass
            for fSend in self.activeChat.recvFile.values():
                #asd
                try:
                    percent= (float(fSend.received)/fSend.size)*100
                    self.pDebug( '%s= (dl: %d)' % ( str(self.recvFile)
                        , percent) ) 
                    self.ChatBar.addstr( _('(dl: %d%%)') % percent )
                except ZeroDivisionError:
                    pass
            
            users= self.activeChat.getUsers()
            self.pDebug( 'USERS= ' +  str(users) )
            self.ChatBar.addstr( '>>' )
            for u in users:
                user= u[0].split('@')[0]
                if curses.has_colors() and not MONOCHROME:
                    if u[1] == 'TYPING':
                        color= self.COLORS['CHAT_BAR_NAME_TYPING']
                    else:
                        color= self.COLORS['CHAT_BAR_NAME']
                    self.ChatBar.addstr( ' ' + user, color )
                else:    
                    if u[1] == 'TYPING':
                        self.ChatBar.addstr( ' *' + user )
                    else:
                        self.ChatBar.addstr( ' ' + user )

        self.ChatBar.line()
        self.ChatBar.toTop()
        self.ChatBar.refresh()
        self.Text.refresh()

    def activateChat( self, num ):
        if num < len(self.sortedChats):
            self.activeChat= self.sortedChats[num]
            self.refreshWin()    
            self.showChatBar()    
            self.showStateBar()
    
    def handleInput( self, c ):
        if c in [curses.ascii.BS, curses.ascii.DEL, curses.KEY_BACKSPACE
                , DELETE_KEY]:
            self.Text.delChar()
            self.Text.refresh()
        elif self.altPressed:
            if c >= 0x30 and c <= 0x39:    
                self.activateChat( c - 0x30 )
            elif c == 0x60: # Key left from number 1
                self.activateChat( 0 )
        elif c == 0x14a: # supr
            self.Text.suprChar()
            self.Text.refresh()
        elif c == curses.KEY_LEFT:    
            self.Text.cursBack()
            self.Text.refresh()
        elif c == curses.KEY_RIGHT:    
            self.Text.cursForward()
            self.Text.refresh()
        elif c == curses.KEY_UP:
            self.showPrevHistory()
        elif c == curses.KEY_DOWN:
            self.showNextHistory()
        elif c == curses.KEY_PPAGE:
            self.Main.scrollUp( self.activeChat, 5 )
            self.Text.refresh()
        elif c == curses.KEY_NPAGE:
            self.Main.scrollDown( self.activeChat, 5 )
            self.Text.refresh()
        elif c == 0x9: # tab
            self.activeChat= self.nextChat()
            self.refreshWin()
            self.showChatBar()
            self.showStateBar()
        elif c == curses.KEY_HOME: # Begin   
            self.Text.cursBegin()
            self.Text.refresh()
        elif c == curses.KEY_END: # End
            self.Text.cursEnd()
            self.Text.refresh()
        elif c == 0x1b: # Alt pulsado
            self.altPressed= 1
        elif c == 0xc: # Ctrl-l (redraw window)  
            self.reinitCurses()
            self.refreshAll()
        elif c == 0x4: # Ctrl-d   
            sys.exit(0)
        elif c > 255:    
            pass
        else:
            if c in [0x5c, 0x2f] and self.firstKeyPress: 
                # '\' for not sending typing notification with commands
                self.typingCommand= 1                    
            elif c != curses.ERR and SEND_TYPING and not self.typingCommand \
            and self.needToSendTypingNotif and self.activeChat:
                self.firstKeyPress= 0
                self.needToSendTypingNotif= 0
                self.lastStartedTyping= time.time()

                # This alarm is for self.needToSendTypingNotif= 1 and sending
                # a new typingNotification after 5 more seconds
                signal.alarm( 5 )
                self.needToResetTypingNotif= 1

                try:
                    self.activeChat.typingNotification()
                except MSN.SocketError, e:    
                    pass
                    #self.pError( 'Socket Error 3: %s' % e[1] )

            self.altPressed= 0
            return 0
            
        if c != 0x1b:
            self.altPressed= 0
        return 1

    def alarmExecAndProgramNext( self ):
        t= time.time()

        self.pDebug( 'alarmExecAndProgramNext: lastPing= %s, t= %s' % 
            (str(self.lastPing), str(t)) )
        # Setting to 600 as a failsafe, and because python appears not to have
        # support for infinity
        minTimeTillNext= 600

        # Usage: a three element list per time-out action
        #   first element - should return true or false, according to whether
        #        the action should be considered.
        #   second element - should return the time the action is to be
        #        performed at. As with all deadlines, the action may be taken
        #        after it has passed, but never before.
        #   third element - the function to call to perform the action.
        for [getcond, getdeadline, action] in [
                [
                    lambda: PING_TIME,
                    lambda: self.lastPing + PING_TIME,
                    self.pingWrap
                ],
                [
                    lambda: IDLE_TIME and self.state == 'NLN',
                    lambda: self.lastStartedTyping + IDLE_TIME,
                    self.idleTimeOut
                ],
                [
                    lambda: self.needToResetTypingNotif,
                    lambda: self.lastStartedTyping + 5,
                    self.resetTypingNotif
                ]
            ]:
            if getcond() and (t >= getdeadline()):
                # action should change the deadline and/or cond, 
                # which is why we use lambdas
                #self.pInfo( 'action %s called on %d' % (action, time.time()) )
                action()

            # Reset t in case action() takes a long time -
            t = time.time() 
            delta= getdeadline() - t
            #self.pInfo( '        %s - %s (%d)' % (delta, action, bool(getcond())) )
            if getcond() and delta < minTimeTillNext:
                minTimeTillNext= delta


        #self.pInfo( 'minTimeTillNext= %s' % minTimeTillNext )
        timeTillNext= int(minTimeTillNext) + 1
        if timeTillNext <= 0:
            timeTillNext= 1
        #self.pInfo( 'alarm set to %d' % timeTillNext)
        signal.alarm(timeTillNext)

    def alarmHandler( self, signum, frame ):    
        self.alarmExecAndProgramNext()            

    def pingWrap( self ):
        # It's time to ping server
        self.ping()
        #self.pInfo( 'PING - %s' % str(t) )
        self.lastPing= time.time()

    def resetTypingNotif( self ):
        #self.pInfo( 'resetTypingNotif' )
        self.needToResetTypingNotif= 0
        self.needToSendTypingNotif= 1

        for chat in self.chats.values():
            for user in chat.listUsers():
                if chat.isTyping( user ):
                    chat.activateUser( user ) 
        self.showChatBar()
                
    def idleTimeOut( self ):
        # TODO - allow other actions (e.g. shell scripts)
        self.idle= 1
        self.pInfo( _('Automatically setting state to %s') % 
            MSN.STATES[IDLE_STATE] )
        self.changeState( IDLE_STATE )

    
    def printArt( self, chat, img, refresh= 0 ):
        color= 'W'
        i= 0
        while i < len(img):
            piece= ''
            while i < len(img) and img[i] != 'º':
                piece= piece + str(img[i])
                i= i + 1

            self.pWin( chat, piece, IMG_COLORS[upper(color)] ) 

            if i < len(img):
                color= img[i+1]
                i= i + 2

        self.pWin( chat, '\n', refresh= 0 )    
        if refresh:
            self.refreshWin( chat )
            self.Text.refresh()
        
    def showChatUsers( self, chat ):    
        self.printUsers( chat, chat.users, _('=| USERS ON ACTIVE CHAT |='), 0 )

    def oneStep( self ):
        try:
            st= self.Text.getLine( self.handleInput, self.step )
            if st:
                self.addHistory( st )

            self.lastStartedTyping= time.time()
            self.firstKeyPress= 1
            self.typingCommand= 0
            self.needToSendTypingNotif= 1
            self.Text.erase()

            self.refreshWin()
            self.processInput( st )
        except MSN.SocketError, e:
            if e.errno() == errno.EINTR: # Avoid error 'Interrupted system call'
                pass
            else:
                type= self.socketType( e.socket )
                if type == MSN.SOCK_MAIN:
		    # Added to Fix offline status
		    if self.state != 'FLN':
                        self.pError( _('Socket error on main server connection (%s)') % e.errorMsg() )
                        self.pError( _('Trying to reconnect...') )
                        self.reconnect()
                elif type == MSN.SOCK_RECV:
                    fRecv= self.recvFile[e.socket] 
                    self.pError( _('Socket error receiving \'%s\' (%s)') 
                        % (fRecv.fileName, e.errorMsg()), fRecv.chat )
                    self.removeFileRecv( fRecv )
                    self.showChatBar()
                elif type == MSN.SOCK_SEND:
                    fSend= self.sendFile[e.socket] 
                    self.pError( _('Socket error sending \'%s\' (%s)') 
                        % (fSend.fileName, e.errorMsg()), fSend.chat )
                    self.removeFileSend( fSend )
                    self.showChatBar()
                elif type == MSN.SOCK_CHAT:
                    # To hide errors when server closes idle chat
                    if not self.chats[e.socket].hideSocketError:
                        self.pError( _('Socket error (%s)') % e.errorMsg(), 
                            self.chats[e.socket] )
                else:
                    self.pError( _('Unhandled socket error (%s)') 
                        % e.errorMsg() )
                    
        
        self.Text.refresh()

    def replaceVars( self, action, params ):
        #self.pInfo( str(params) )
        #self.pInfo( action )
        #return ''
        try:
            for var in re.findall( r'(?<=[^\\])\$\w+', action ):
                var= lower( var )
                self.pDebug( 'var=%s, params[var]=%s' % (var, str(params[var]))  )
                action= re.sub( '\\' + var, str(params[var]), action )
        except KeyError, e:        
            
            self.pError( _('Variable \'%s\' not found in action \'%s\'') 
                        % (e, action) )
            return ''
        
        return action
            
    def subExec( self, command ):
        if os.fork() == 0:
            os.system( command )
            os._exit( 0 )

    def execAction( self, action, params ):
        self.subExec( self.replaceVars( action, params ) )

    def loopInput( self ):
        while 1:
            self.oneStep()

    def getAutoListChat( self ):
        if AUTO_LIST_ON_0:
            chat= self.sortedChats[0]
            if AUTO_LIST_ON_0_CLEAR:
                self.Main.erase( chat )
        else:   
            chat= self.activeChat

        return chat

    def drawLastMove( self, chat, msg ):
        allUsers= self.usersList.keys()
        allUsers.sort()
        number= allUsers.index( msg.fields['passport'] )

        self.pWin( chat, '%s' % number ,'LIST_NUM', 0 )
        self.pWin( chat, '- ', 'DEFAULT', 0 )


        self.pWin( chat, '%s ' % msg.fields['passport']
            , 'LIST_PASS', 0 )
        self.pWin( chat, _('changed state to '), 'DEFAULT', 0 )
        self.pWin( chat
            , '\'%s\'\n' %  MSN.STATES[msg.fields['state']]
            , 'LIST_STATE', 1 )
            

    def userChangesState( self, msg ):

        self.updateUsersList()

        if AUTO_LIST:
            chat = self.getAutoListChat()
            self.printUsers( chat ) 

            if AUTO_LIST_LAST_MOVE:
                self.drawLastMove( chat, msg )

        if msg.fields['state'] == 'NLN' and EXEC_ON_PAL_STATE_NLN:
            passport= msg.fields['passport']
            escName= re.escape( self.aliasOrName(passport) )
            self.execAction( EXEC_ON_PAL_STATE_NLN, {'$passport':passport
                , '$user':escName} )
        elif EXEC_ON_PAL_STATE_CHANGE:
            passport= msg.fields['passport']
            escName= re.escape( self.aliasOrName(passport) )
            state= msg.fields['state']
            self.execAction( EXEC_ON_PAL_STATE_CHANGE, {'$passport':passport
                , '$user':escName, '$state':state} )


    def userConnected( self, msg ):
        self.updateUsersList()

        if AUTO_LIST:
            chat = self.getAutoListChat()
            self.printUsers( chat ) 

            if AUTO_LIST_LAST_MOVE:
                self.drawLastMove( chat, msg )

        if EXEC_ON_PAL_CONNECTED:
            passport= msg.fields['passport']
            escName= re.escape( self.aliasOrName(passport) )
            state= msg.fields['state']
            self.execAction( EXEC_ON_PAL_CONNECTED, {'$passport':passport
                , '$user':escName, '$state':state} )

    def userOn( self, passport ):
        self.updateUsersList()

        # Lets reshow user list on init when an ILN arrives after it wasn't expected 
        if self.listShown:
            chat = self.getAutoListChat()
            self.printUsers( chat ) 

    def userOff( self, passport ):
        self.updateUsersList()

        if AUTO_LIST:
            chat = self.getAutoListChat()
            self.printUsers( chat ) 

            if AUTO_LIST_LAST_MOVE:
                self.pWin( chat, '%s ' % passport, 'LIST_PASS', 0 )
                self.pWin( chat, _('disconnected\n'), 'DEFAULT', 1 )

        if EXEC_ON_PAL_DISCONNECTED:
            escName= re.escape( self.aliasOrName(passport) )
            self.execAction( EXEC_ON_PAL_DISCONNECTED, {'$passport':passport
                , '$user':escName} )

    def otherLogin( self ):
        #self.fatalError( _('Detected other login to this account') )
  	    self.pError( _('You have been signed in from another computer. Type \\q to exit or \\s nln to reconnect.') )
	    self.state = 'FLN'
	    self.pError( _('You are currently OFFLINE.') )
	
    
    def userAdded( self, passport, list ):
        list= upper( list )
        self.pInfo( _('%s added to %s') % (passport, MSN.LISTS[list]) )

        if AUTO_ADD_TO_ALLOWED and list == 'RL':
            self.addUser( passport, 'FL' )
            self.addUser( passport, 'AL' )
    
    def userRemoved( self, passport, list ):
        list= upper( list )
        self.pInfo( _('%s removed from %s') % (passport, MSN.LISTS[list]) )
    
    def selfStateChanged( self, state ):
        self.pInfo( _('state: %s') % MSN.STATES[state] )

        # This is for firing alarms after login
        self.alarmExecAndProgramNext()
    
    def invitedBy( self, msg ):
        self.acceptChat( msg )

    def msgFrom( self, chat, passport, msg ):
        # Make chat visible on first msg received
        self.pDebug(passport)
        
        if chat not in self.sortedChats:
            self.pInfo( _('%s is talking to you') % passport )
            if BEEP_ON_NEW_CHAT:
                curses.beep()    
            if EXEC_ON_NEW_CHAT:    
                escName= re.escape( self.aliasOrName(passport) )
                escMsg= re.escape( msg )
                self.execAction( EXEC_ON_NEW_CHAT, {'$passport':passport
                    , '$user':escName, '$msg':escMsg} )

            if SHOW_INVITED:
                self.sortedChats.append( chat )
                self.Main.newWin( chat )

        # Avoid problems displaying \r
        msg= replace( msg, '\r', '' )
        self.pDebug( 'MSG===' + msg )
        if SHOW_INVITED:
            self.pChat( chat, passport, msg )
        else:    
            self.pChat( self.activeChat, passport, msg )

        self.showChatBar()
        self.showStateBar()

    def gotMail( self, numUnread ):
        self.pInfo( _('You\'ve got mail: %s unread.') % numUnread )

    def incomingMail( self, mailFrom ):
        self.pInfo( _('Incoming mail from: %s') % mailFrom )

    def userAcceptChat( self, chat, passport ):
        if not self.Main.hasWin( chat ):
            self.Main.newWin( chat )
        self.pInfo( _('%s joined the chat') % passport, chat )
        chat.logText( _('%s >> %s joined the chat <<') % 
                        (time.strftime('[%H:%M:%S]'), passport) )
        self.showStateBar()
        self.showChatBar()

    def nameChanged( self, name ):
        self.name= name
        self.pInfo( _('Name changed to: %s') % name )

    def protoError( self, code ):
        self.pError( _('Protocol error: %s') % MSN.ERRORS[code] )
        
    def fatalProtoError( self, code ):       
        self.fatalError( _('Protocol error (%s)') % MSN.ERRORS[code] )

    def addUserError( self, passport, list ):
        self.pError( _('Failed to add \'%s\' to %s') % 
                    (passport, MSN.LISTS[list]) )

    def delUserError( self, passport, list ):
        self.pError( 
            _('Failed to remove \'%s\' from %s (user wasn\'t on this list)') % 
             (passport, MSN.LISTS[list]) )

    def userOnListError( self, passport, list ):
        self.pError( _('User \'%s\' already on %s') % 
                    (passport, MSN.LISTS[list]) )

    def userLeavesChat( self, chat, user ):
        self.pInfo( _('%s left the chat') % user, chat )
        chat.logText( _('%s >> %s left the chat <<') % 
                        (time.strftime('[%H:%M:%S]'), user) )
        if len(chat.users) == 0:
            if self.usersOnline.has_key(user):
                chat.deactivateUser( user )
                #chat.invite( user )
            #else:
                #self.sortedChats.remove( chat )
                #if self.activeChat== chat:
                    #self.activeChat= self.nextChat()

        self.showStateBar()
        self.showChatBar()

    def msgNotReceived( self, chat ):
        self.pError( _('Message was not received'), chat )

    def typing( self, chat, user ):
        chat.typingUser( user )
        self.showChatBar()
        self.needToResetTypingNotif= 1
        signal.alarm( 5 )

    def chatConnectError( self, chat ):
        self.pError( _('Can\'t connect to chat server'), chat )

    def sendFileAccepted( self, fSend ):
        self.pInfo( _('Request for sending \'%s\' accepted') % fSend.fileName
            , fSend.chat )

    def sendFileRejected( self, fSend ):
        self.pInfo( _('Request for sending \'%s\' rejected') % fSend.fileName
            , fSend.chat )

    def sendFileCancelled( self, fSend ):
        self.pInfo( _('Sending \'%s\' cancelled by peer') % fSend.fileName
            , fSend.chat )
        self.showChatBar()

    def sendFileFinished( self, fSend ):
        self.pInfo( _('\'%s\' successfully sent') % fSend.fileName, fSend.chat )
        self.showChatBar()

    def recvFileRequest( self, fRecv ):
        passport= fRecv.user
        name= self.aliasOrName( passport )
        
        msg= _('Request for receiving file \'%s\' (%s bytes) from %s') % \
            (fRecv.fileName, fRecv.size, fRecv.user)

        # Make chat visible on first msg received
        if fRecv.chat not in self.sortedChats:
            self.pInfo( _('File send request received') )
            if BEEP_ON_NEW_CHAT:
                curses.beep()    
            if EXEC_ON_NEW_CHAT:    
                escName= re.escape( name )
                escMsg= re.escape( msg )
                self.execAction( EXEC_ON_NEW_CHAT, {'$passport':passport
                    , '$user':escName, '$msg':escMsg} )
            if SHOW_INVITED:
                self.sortedChats.append( fRecv.chat )
                self.Main.newWin( fRecv.chat )

        fRecv.chat.written= 1
        self.pInfo( msg, fRecv.chat )

        if EXEC_ON_FILE_RECV_BEGIN:
            escName= re.escape( name )
            escFile= re.escape( fRecv.fileName )
            self.execAction( EXEC_ON_FILE_RECV_BEGIN, {'$passport':passport
                , '$user':escName, '$file':escFile, '$size':fRecv.size } )

        self.showChatBar()
        self.showStateBar()

    def chunkSent( self, fSend ):
        if fSend.chat == self.activeChat:
            self.showChatBar()

    def chunkReceived( self, fRecv ):
        if fRecv.chat == self.activeChat:
            self.showChatBar()

    def recvFileFinished( self, fRecv ):
        self.pInfo( _('\'%s\' successfully received') % fRecv.fileName, fRecv.chat )
        if EXEC_ON_FILE_RECV_END:
            passport= fRecv.user
            escName= re.escape( self.aliasOrName(passport) )
            escFile= re.escape( fRecv.fileName )
            self.execAction( EXEC_ON_FILE_RECV_END, {'$passport':passport
                , '$user':escName, '$file':escFile, '$size':fRecv.size } )
        self.showChatBar()

    def recvFileCancelled( self, fRecv ):
        self.pInfo( _('Receiving \'%s\' cancelled by peer') % fRecv.fileName
            , fRecv.chat )
        self.showChatBar()

    def listUpdated( self, list, type ):
        self.printUsers( self.activeChat
                        , list, '=| %s |=' % (upper(MSN.LISTS[type])) 
                        , numbers=0, state=0, name=0  )

    def palListComplete( self ):
        self.printUsers()

    #def qngReceived( self ):
        #self.pInfo( 'QNG received' )

    def userAddedYou( self, passport ):
        self.pInfo( _('User %s added you to his/her list') % passport );

        if AUTO_ADD_TO_ALLOWED:
            self.addUser( passport, 'FL' )
            self.addUser( passport, 'AL' )

def gogogo( stdscr ):
    global PASSWD

    curses.noecho()
    stdscr.keypad(1)
    #try:
    con= TextMSN( stdscr )
    con.readConfig( CONFIG_FILE )
    con.parseArgs()
    PASSWD= con.getPassword()   

    con.initBarColors()
    con.refreshAll()

    # Create dir for chat logs under MAIN_LOG_DIR
    MSN.LOG_DIR= MAIN_LOG_DIR + USER + '/'
    if MSN.LOG_CHATS and not os.access( MSN.LOG_DIR, os.F_OK ):
        os.mkdir( MSN.LOG_DIR )

    # Print logo 
    try:
        f= open( LOGO_FILE )
        LOGO= f.read()
        con.printArt( con.activeChat, LOGO )
    except IOError:
        pass
           
    con.pActive( VER_MSG )

    if MSN.MY_IP == 'auto':
        MSN.MY_IP= con.getPublicIp()

    con.connect( USER, PASSWD, INIT_STATE )
    
    # Just begin with the alarms
    con.loopInput()
    #except (KeyboardInterrupt):
        #curses.endwin()


def replaceOldConfig( oldConfig ):
    print
    print _('Old config file found. I\'m going to move this config file to %s.') % CONFIG_FILE
    raw_input( _('Press Enter to continue...') )
    print
    print _('Moving %s to %s ...') % (oldConfig, CONFIG_FILE)
    os.rename( oldConfig, CONFIG_FILE )
    print 
    raw_input( _('All done, press Enter to continue...') )

def main():

    if not os.access( CONFIG_DIR, os.F_OK ):
        os.mkdir( CONFIG_DIR )
    if not os.access( MAIN_LOG_DIR, os.F_OK ):
        os.mkdir( MAIN_LOG_DIR )

    os.chmod( CONFIG_DIR, 0700 )

    oldConfig= os.getenv('HOME') + '/.pebrotrc'
    if os.access( oldConfig, os.F_OK ):
        replaceOldConfig( oldConfig )
    else:    
        if not os.access( CONFIG_FILE, os.R_OK ):
            print _('I can\'t find %s, so I will copy the supplied pebrotrc there.') \
                % CONFIG_FILE
            raw_input( _('Press Enter to continue...') )
            shutil.copyfile( sys.prefix + '/share/doc/pebrot/pebrotrc', CONFIG_FILE )
            #os.chmod( CONFIG_FILE, 0600 )
            print _('Please edit %s and specify your login and password.') \
                % CONFIG_FILE
            sys.exit()
        
    curses.wrapper( gogogo )

if __name__ == '__main__':
    sys.stderr.write( _('You should execute pebrot, not pebrot.py :)\n') )

