#!/usr/bin/env python

##############################################################
#    smscon_daemon - remote control daemon                   #
##############################################################

VERSION = '0.5-7'
NAME    = 'smscon'
DATE    = '11/10/2010'

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

# standard modules
import os
import sys
import time
import string
import logging
import re
import fileinput
import random
import dbus
from dbus.mainloop.glib import DBusGMainLoop

# for GPS control
import location
import gobject
from operator import itemgetter 

# for email sending
import smtplib
import socket
import urllib
import pexpect
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
from email.MIMEMultipart import MIMEMultipart

# for encryption (encoding/decoding)
import base64
from Crypto.Cipher import AES

############################################################################################################################
#    variables
############################################################################################################################

TIME                         = False                             # if set, show time in smscon_log file

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

Path                         = '/opt/smscon'                     # main path of files
BootPath                     = '/etc/event.d'                    # upstart directory

CodeFile                     = '%s_code' % NAME                  # code file name
BootFile                     = '%s_boot' % NAME                  # name of upstart file
ConfigFile                   = '%s_config' % NAME                # name of config file
DaemonFile                   = '%s_daemon' % NAME                # name of daemon file

LocalHost                    = 'localhost'                       # nokia N900 local address for reverse-ssh 
LocalPort                    = 8080                              # nokia N900 local port for reverse-ssh

TimeOut                      = 5                                 # timeout in seconds
PingMax                      = 3                                 # number of ping commands for connection status check
PhotoName                    = 'frontcam.jpg'                    # frontcam filename
MapName                      = 'gpsmap.png'                      # googlemaps filename

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

Gps                          = 0
GpsList                      = []
GpsActive                    = False # true if GPS device is enabled
GpsTest                      = False # true if GPS device is enabled from test mode command
GpsState                     = False # true if GPS device receives valid coordinates
StartTime                    = 0
EndTime                      = 0

SenderNumber                 = ''
EnableKeyboardDetect         = False
EnableBatteryDetect          = False

ConnectionType               = None 
ConnectionIAP                = None
ConnectionName               = None
ConnectionBehaviour          = None

ConfigVars                   = ['SENDERNUMBER',                 
                                'EMAILADDRESS',
                                'COM_CHECK',
                                'COM_REBOOT',
                                'COM_POWEROFF',
                                'COM_POWER',
                                'COM_LOCATION',
                                'COM_REMOTEON',
                                'COM_REMOTEOFF',
                                'COM_CAMERA',
                                'COM_CALL',
                                'COM_LOCK', 
                                'COM_UNLOCK',
                                'COM_TRACKON',
                                'COM_TRACKOFF',
                                'USER',         
                                'PASSWORD',        
                                'EMAILFROM',       
                                'MAILSERVER',      
                                'MAILPORT',
                                'REMOTEHOST',            
                                'REMOTEPORT',           
                                'REMOTEUSER',       
                                'REMOTEPASSWORD',
                                'COMMANDREPLY',
                                'KEYBOARDDETECT',
                                'AUTODEVICELOCK',
                                'GPSTIMEOUT',
                                'GPSPOLLING',
                                'GPSINTERVAL',
                                'GPSSEND']

EncryptedConfigVars          = ['USER',         
                                'PASSWORD',        
                                'REMOTEUSER',       
                                'REMOTEPASSWORD']

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

LogPath                      = '/tmp'                            # path of log file
LogFile                      = '%s.log' % NAME                   # log file name

if TIME:
    logging.basicConfig(filename = '%s/%s' % (LogPath, LogFile),
                        level    = logging.DEBUG,
                        format   = 'DAEMON %(levelname)s: %(message)s (%(asctime)s)',
                        datefmt  = '%d-%m-%Y %H:%M:%S')
else:
    logging.basicConfig(filename = '%s/%s' % (LogPath, LogFile),
                        level    = logging.DEBUG,
                        format   = 'DAEMON %(levelname)s: %(message)s')

############################################################################################################################
#    time
############################################################################################################################

def GetTime():
    """
    Convert time to readable format.
    """

    t = time.localtime( time.time() )

    if t[2] <= 9:
        t2 = '0' + str(t[2])
    else:
        t2 = t[2]

    if t[1] <= 9:
        t1 = '0' + str(t[1])
    else:
        t1 = t[1]

    if t[0] <= 9:
        t0 = '0' + str(t[0])
    else:
        t0 = t[0]

    if t[3] <= 9:
        t3 = '0' + str(t[3])
    else:
        t3 = t[3]

    if t[4] <= 9:
        t4 = '0' + str(t[4])
    else:
        t4 = t[4]

    if t[5] <= 9:
        t5 = '0' + str(t[5])
    else:
        t5 = t[5]
    
    return "%s:%s:%s %s/%s/%s" % (t3, t4, t5, t2, t1, t0)

############################################################################################################################
#    SMS
############################################################################################################################

def DeOctifyMessage(Array):
    """
    Deoctify received message and return text string.
    """
    
    RefBit   = 1
    Doctect  = []    
    ByteNext = 0x00    

    for i in Array:
        ByteCurrent = ((i & (0xff >> RefBit)) << RefBit) >> 1
        ByteCurrent |= ByteNext                                      

        if RefBit != 7:
            Doctect.append(ByteCurrent)
            ByteNext = (i & (0xff << (8 - RefBit)) ) >> 8 - RefBit
            RefBit += 1
            
        else:                                                                  
            Doctect.append(ByteCurrent)                                        
            ByteNext = (i & (0xff << (8 - RefBit)) ) >> 8 - RefBit
            Doctect.append(ByteNext)                                        
            ByteNext = 0x00                                                   
            RefBit = 1                                               

    return ''.join([chr(i) for i in Doctect])

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

def SMSreceive(PDUmessage, MessageCenter, SomeString, Number):
    """
    Receive SMS command and execute command.
    """

    MessageLength = int(PDUmessage[18])
    MessageArray  = PDUmessage[19:len(PDUmessage)]

    Message = DeOctifyMessage(MessageArray) # decode sms message as plain text

    ProcessCommand(Message, Number) # process sms command

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

def SMSsend(Number, Message, Log):
    """
    Send SMS message to number.
    """

    """
    Nokia-N900:~# pnatd
    AT
    OK
    AT+CMGF=1
    OK
    AT+CMGS="+316XXXXXXXX"
    > TEST
    +CMGS: 129

    OK
    """

    Delay    = 4
    MaxRetry = 6
    Retry    = 0
    
    Loop = True
    while Loop:
        Retry += 1
        if Retry == MaxRetry:
            Loop = False
            
        #logging.debug('sending SMS message to "%s"' % Number)
        modem = pexpect.spawn('pnatd', [], timeout = 2) # start pnatd
        #modem.logfile = sys.stdout

        try:
            time.sleep(1)
            modem.send('AT\r') # GSM modem return 'OK' if working
            modem.readline()
            response = modem.readline().strip('\r\n')
        except pexpect.TIMEOUT:
            modem.sendeof()
            modem.kill(9)
            #logging.debug('GSM modem "AT" timeout')
            time.sleep(Delay)
        else:
            if response == 'OK':
                try:
                    time.sleep(1)
                    modem.send('AT+CMGF=1\r') # GSM modem return 'OK' if working
                    modem.readline()
                    response = modem.readline().strip('\r\n')
                except pexpect.TIMEOUT:
                    modem.sendeof()
                    modem.kill(9)
                    #logging.debug('GSM modem "AT+CMGF" timeout')
                    time.sleep(Delay)
                else:
                    if response == 'OK':
                        try:
                            time.sleep(1)
                            modem.send('AT+CMGS="%s"\r' % Number) # GSM modem return '> ' if working
                            modem.readline()
                        except pexpect.TIMEOUT:
                            modem.sendeof()
                            modem.kill(9)
                            #logging.debug('GSM modem "AT+CMGS" timeout')
                            time.sleep(Delay)
                        else:
                            time.sleep(1)
                            modem.send( '%s' % Message + chr(26) ) # set SMS message and Ctrl + Z to send message
                            try:
                                time.sleep(1)
                                modem.readline()
                                response = modem.readline().strip('\r\n')
                            except pexpect.TIMEOUT:
                                modem.sendeof()
                                modem.kill(9)
                                #logging.debug('GSM modem response "+CMGS:" timeout')
                                time.sleep(Delay)
                            else:
                                if response.startswith('+CMGS:'):
                                    modem.readline()
                                    response = modem.readline().strip('\r\n')
                                    if response == 'OK':
                                        modem.sendeof()                       
                                        modem.kill(9)
                                        Loop = False
                                    else:
                                        modem.sendeof()
                                        modem.kill(9)
                                        #logging.debug('GSM modem response "OK" failed (%s)' % response)
                                        time.sleep(Delay) 
                                else:
                                    modem.sendeof()
                                    modem.kill(9)
                                    #logging.debug('GSM modem response "+CMGS:" failed (%s).' % response)
                                    time.sleep(Delay) 
                    else:
                        modem.sendeof()
                        modem.kill(9)
                        #logging.debug('GSM modem did not reply correctly (%s).' % response)
                        time.sleep(Delay) 
            else:
                modem.sendeof()
                modem.kill(9)
                #logging.debug('GSM modem did not reply correctly (%s).' % response)
                time.sleep(Delay)

    if Retry == MaxRetry:
        logging.error('failed to send SMS to "%s"' % Number)
    else:
        logging.info(Log + ' "%s"' % Number)

    time.sleep(2)    

############################################################################################################################
#    email
############################################################################################################################

def EMAILsend(EmailTo, Subject, Text, Log, Attachment=None):
    """
    Send email to owner of the device.
    """

    AttachmentDescription = 'image'
    AltText               = 'image from Nokia N900'
    RandomNumber          = random.randint(1, 100000)
    
    HtmlTextPage = \
""" 
<html> 
<head></head> 
<body> 
<p>
%s<br>
</p>
</body> 
</html>
""" % Text

    HtmlImagePage = \
""" 
<html> 
<head></head> 
<body> 
<p>
%s<br>
""" % Text + \
'<img src="cid:%s%s" alt="%s" /><br>' % (AttachmentDescription, RandomNumber, AltText) + \
"""
</p>
</body> 
</html>
"""

    Message            = MIMEMultipart('related') 
    Message['Subject'] = Subject
    Message['From']    = 'NokiaN900'
    Message['To']      = EmailTo

    # html text only
    if Attachment == None: 
        Message.attach( MIMEText(HtmlTextPage, 'html') )

    # html text & image provided  
    else: 
        Message.attach( MIMEText(HtmlImagePage, 'html') )

        try:
            File = open(Attachment, 'rb')
            Image = MIMEImage( File.read() )
            File.close()
        except:
            logging.error('(EMAIL) attachment not found')
            return
        else:
            Image.add_header( 'Content-ID', '<%s%s>' % (AttachmentDescription, RandomNumber) )
            Message.attach(Image)

    try:
        logging.info('(EMAIL) trying to connect to "%s"' % MAILSERVER)
        server = smtplib.SMTP(MAILSERVER, MAILPORT) # connect to mail server
        #server.set_debuglevel(True)
    except (socket.error, smtplib.SMTPConnectError, smtplib.SMTPException):
        logging.error('(EMAIL) mailserver connection failure')
        return
    else:
        server.ehlo() # identify to server
            
        # if smtp server requires secure authentication
        if server.has_extn('STARTTLS'):
            server.starttls()
            server.ehlo()

        if USER != '' and PASSWORD != '':
            try:
                logging.info('(EMAIL) trying to login to "%s"' % MAILSERVER)
                server.login(USER, PASSWORD) # set username & pasword
            except smtplib.SMTPAuthenticationError:
                logging.error('(EMAIL) wrong username/password')
                server.quit()
                return

        try:
            logging.info('(EMAIL) trying to send email to "%s"' % MAILSERVER)
            server.sendmail( EMAILFROM,
                             EmailTo,
                             Message.as_string() ) # send email
        except smtplib.SMTPException, e:
            logging.error('(EMAIL) [%s]' % e)
        else:
            logging.info(Log)
        finally:
            server.quit()
            logging.info('(EMAIL) mailserver connection closed')


##        try:
##            server.login(USER, PASSWORD) # set username & pasword
##        except smtplib.SMTPAuthenticationError:
##            logging.error('(EMAIL) wrong username/password')
##            server.quit()
##            return
##        else:
##            try:
##                server.sendmail( EMAILFROM,
##                                 EmailTo,
##                                 Message.as_string() ) # send email
##            except smtplib.SMTPException, e:
##                logging.error('(EMAIL) [%s]' % e)
##            else:
##                logging.info(Log)
##            finally:
##                server.quit()
##                logging.info('(EMAIL) mailserver connection closed')

############################################################################################################################
#   save new number
############################################################################################################################

def SaveNumber(SenderNumber):
    """
    Save new SenderNumber as SENDERNUMBER in "smscon_config" file.
    """ 

    # update current used SENDERNUMBER
    global SENDERNUMBER
    SENDERNUMBER = SenderNumber

    UserVar = 'SENDERNUMBER'
    File    = '%s/%s' % (Path, ConfigFile)
    TabMax  = 18

    # save current used SENDERNUMBER
    try:
        for Line in fileinput.FileInput(File, inplace = True):
            if Line.startswith(UserVar):
                Line = "%s= '%s'" % (UserVar.ljust(TabMax,' '), SENDERNUMBER)
            print Line.rstrip()
    except:
        logging.error("couldn't save new SMS number (%s)" % SENDERNUMBER)
    else:
        logging.info( 'new SMS number (%s) saved in %s file' % (SENDERNUMBER, ConfigFile) )

############################################################################################################################
#    command processing
############################################################################################################################

def ProcessCommand(Command, SenderNumber):
    """
    Process the received message.
    """

    CommandList = [COM_CHECK,
                   COM_REBOOT,
                   COM_POWEROFF,
                   COM_POWER,
                   COM_LOCATION,
                   COM_REMOTEON,
                   COM_REMOTEOFF,
                   COM_CAMERA,
                   COM_CALL,
                   COM_LOCK, 
                   COM_UNLOCK,
                   COM_TRACKON,
                   COM_TRACKOFF]

    # Check if SMS message has a specific valid SMSCON command
    if Command in CommandList:
        logging.info( 'received SMSCON command [%s] from "%s"' % (Command, SenderNumber) )

        # if new SenderNumber is different from stored SENDERNUMBER in "smscon_config" file, store new SenderNumber in "smscon_config" file.
        if SenderNumber != SENDERNUMBER:
            SaveNumber(SenderNumber)

        # auto device locking
        if AUTODEVICELOCK == 'yes' and Command != COM_UNLOCK:
            EnableDeviceLock(False)
        elif AUTODEVICELOCK == 'no' and Command != COM_LOCK:
            DisableDeviceLock(False)

        # command reply
        if COMMANDREPLY == 'yes':
            Text = 'NOKIA N900\n' + \
                    '%s\n' % GetTime() + \
                    '-reply-\n' + \
                    'command (%s) accepted.\n' % Command
            
            if AUTODEVICELOCK == 'yes' and Command != COM_UNLOCK:
                SMSsend(SENDERNUMBER,
                        Text + \
                        '-security-\n' + \
                        'phone is locked.\n',
                        'send SMS message with command reply to')
            else:
                SMSsend(SENDERNUMBER,
                        Text,
                        'send SMS message with command reply to')

        # battery level detection
        global EnableBatteryDetect
        EnableBatteryDetect = True

        # keyboard detection
        global EnableKeyboardDetect
        if KEYBOARDDETECT == 'yes':
            EnableKeyboardDetect = True
        elif KEYBOARDDETECT == 'no':
            EnableKeyboardDetect = False      

        # command execution
        if Command == COM_CHECK:
            ShowSMScommands('sms')

        elif Command == COM_REBOOT:
            EnableReboot()

        elif Command == COM_POWEROFF:
            EnablePoweroff()            

        elif Command == COM_POWER:
            EnablePowerStatus()

        elif Command == COM_LOCATION:
            EnableConnection() # enable GPRS data network
            EnableConnectBehaviour('GPRS') # disable switching to other network
            EnableGPStracking('single')

        elif Command == COM_REMOTEON:
            EnableConnection() # enable GPRS data network
            EnableConnectBehaviour('GPRS') # disable switching to other network
            EnableSSHcontrol()

        elif Command == COM_REMOTEOFF:
            DisableSSHcontrol()

        elif Command == COM_CAMERA:
            EnableConnection() # enable GPRS data network
            EnableCamera()

        elif Command == COM_CALL:
            EnablePhonecall()

        elif Command == COM_LOCK:
            EnableDeviceLock(True)

        elif Command == COM_UNLOCK:
            DisableDeviceLock(True)

        elif Command == COM_TRACKON:
            EnableConnection() # enable GPRS data network
            EnableConnectBehaviour('GPRS') # disable switching to other network
            EnableGPStracking('multi')

        elif Command == COM_TRACKOFF:
            DisableGPStracking()
            EnableConnectBehaviour(ConnectionBehaviour)

############################################################################################################################
#   show SMS commands
############################################################################################################################

def ShowSMScommands(Type):
    """
    Log or SMS list of SMS commands.
    """

    ConfigVars = []
    Prefix     = 'COM_'
    MaxChars   = 160
    TabMax     = 18

    # get specific variables
    for Var in globals():
        if Var.startswith(Prefix):
            ConfigVars.append(Var)

    # sort the list
    ConfigVars.sort()

    # create log entry
    if Type == 'log':
        for UserVar in ConfigVars:
            Value = eval(UserVar)
            logging.info( '%s= [%s]' % (UserVar.ljust(TabMax, ' '), Value) )

    # create (multiple) SMS messages
    elif Type == 'sms':

        NumConfigVars = len(ConfigVars)

        n = 1
        Text = 'NOKIA N900\n' + \
               '%s\n' % GetTime() + \
               '-check #%s-\n' % n

        i = 0
        while i < len(ConfigVars):
            Name    = ConfigVars[i].replace('COM_', '')
            Value   = eval(ConfigVars[i])
            AddText = "%s = '%s'\n" % (Name, Value)
            Chars   = len(Text) + len(AddText)
          
            if Chars <= MaxChars:
                Text += AddText

                if i == len(ConfigVars) - 1:
                    SMSsend(SENDERNUMBER,
                            Text,
                            'send SMS message with SMS commands to')    

            elif Chars > MaxChars:
                SMSsend(SENDERNUMBER,
                        Text,
                        'send SMS message with SMS commands to') 

                n += 1
                Text = 'NOKIA N900\n' + \
                       '%s\n' % GetTime() + \
                       '-check #%s-\n' % n
                i -= 1
                
            i += 1

############################################################################################################################
#   device lock
############################################################################################################################

def EnableDeviceLock(SendReply=False):
    """
    Lock device after receiving valid sms command.
    """

    if StatusDeviceLock() == False:
        os.system ('dbus-send --system --type=method_call \
                   --dest=com.nokia.system_ui \
                   /com/nokia/system_ui/request \
                   com.nokia.system_ui.request.devlock_open \
                   string:"com.nokia.mce" \
                   string:"/com/nokia/mce/request" \
                   string:"com.nokia.mce.request" \
                   string:"devlock_callback" uint32:"3"')

        logging.info('device is now locked')

    if SendReply:
        Text = 'phone has been locked.'
        SMSsend(SENDERNUMBER,
                'NOKIA N900\n' + \
                '%s\n' % GetTime() + \
                '-lock-\n' + \
                Text,
                'send SMS message phone locked to')

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

def DisableDeviceLock(SendReply=False):
    """
    Unlock device.
    """

    if StatusDeviceLock() == True:
        os.system ('dbus-send --system --type=method_call \
                   --dest=com.nokia.system_ui \
                   /com/nokia/system_ui/request \
                   com.nokia.system_ui.request.devlock_close \
                   string:"com.nokia.mce" \
                   string:"/com/nokia/mce/request" \
                   string:"com.nokia.mce.request" \
                   string:"devlock_callback" \
                   uint32:"0"')

        logging.info('device is now unlocked')

    if SendReply:
        Text = 'phone has been unlocked.'
        SMSsend(SENDERNUMBER,
                'NOKIA N900\n' + \
                '%s\n' % GetTime() + \
                '-unlock-\n' + \
                Text,
                'send SMS message phone unlocked to')

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

def StatusDeviceLock():
    """
    Get lock status of device.
    """

    Output = os.popen('dbus-send --system --print-reply \
                       --type=method_call \
                       --dest="com.nokia.mce" \
                       "/com/nokia/mce/request" \
                       com.nokia.mce.request.get_devicelock_mode | grep string').read()
    
    E = re.compile('string "(\S+)"')
    R = E.findall(Output)[0]

    if R == 'locked':
        return True
    elif R == 'unlocked':
        return False

############################################################################################################################
#   system
############################################################################################################################

def EnableReboot():
    """
    Reboot the device.
    """

    Delay = 10

    logging.info('executing command "%s"' % COM_REBOOT)

    SMSsend(SENDERNUMBER,
            'NOKIA N900\n' + \
            '%s\n' % GetTime() + \
            '-reboot-\n' + \
            'phone is rebooting...',
            'send SMS message phone rebooting to')
    
    time.sleep(Delay)
    os.system('reboot')
    
    ExitDaemon()

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

def EnablePoweroff():
    """
    Shutdown device.
    """

    Delay = 10

    logging.info('executing command "%s"' % COM_POWEROFF)

    SMSsend(SENDERNUMBER,
            'NOKIA N900\n' + \
            '%s\n' % GetTime() + \
            '-shutdown-\n' + \
            'phone is off!!!',
            'send SMS message phone shutdown to')
    
    time.sleep(Delay)
    os.system('poweroff')
    
    ExitDaemon()

############################################################################################################################
#   camera
############################################################################################################################

def EnableCamera():
    """
    Get picture of front camera and send it to email address.
    """

    if TakeCamPicture() == True:
        EMAILsend(EMAILADDRESS,
                  '%s command / Nokia N900' % COM_CAMERA,
                  'This is a frontcam picture from the Nokia N900 device:\n' + \
                  '%s\n' % GetTime(),
                  'send email camera picture send to "%s"' % EMAILADDRESS,
                  LogPath + PhotoName)

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

def TakeCamPicture():
    """
    Acquire front camera picture.
    """

    CamName     = 'video1'
    CamRes      = (640,480)
    CamEncoding = 'jpegenc'
    Delay       = 5

    try:
        os.popen('gst-launch v4l2camsrc device=/dev/%s num-buffers=10 ! video/x-raw-yuv,width=%s,height=%s ! ffmpegcolorspace ! %s ! filesink location=%s' \
                 % (CamName,
                    CamRes[0],
                    CamRes[1],
                    CamEncoding,
                    LogPath + PhotoName) )
    except:
        logging.error('camera picture failed (capture error)')
        return False
    else:
        time.sleep(Delay)

        if os.path.isfile(LogPath + PhotoName) == True:
            logging.info('camera picture acquired')
            return True
        else:
            logging.error('camera picture failed (file not found)')
            return False

############################################################################################################################
#    phone call
############################################################################################################################

def EnablePhonecall():
    """
    Make phone call to sender number.
    """

    try:
        os.system('run-standalone.sh dbus-send --system --dest=com.nokia.csd.Call \
                   --type=method_call /com/nokia/csd/call com.nokia.csd.Call.CreateWith \
                   string:"%s" \
                   uint32:0' % SENDERNUMBER )
        
        logging.info('making phonecall to "%s"' % SENDERNUMBER)          
    except:
        logging.error('failed to make phonecall to "%s"' % SENDERNUMBER)

############################################################################################################################
#    data connection
############################################################################################################################

def StatusConnection(Log=False): 
    """
    Get info of current data connection; type and status
    """

    Wlan = os.popen('ifconfig | grep "wlan0"').read()
    Gprs = os.popen('ifconfig | grep "gprs0"').read() 

    # wlan data connection active
    if Wlan != '' and Gprs == '':
        Type = 'WLAN'
        if Log == True:
            logging.info('current network is "%s"' % Type)

        return Type

    # gprs data connection active
    elif Wlan == '' and Gprs != '':
        Type = 'GPRS'
        if Log == True:
            logging.info('current network is "%s"' % Type)

        return Type
    
    # no data connection active
    else:
        return 'NONE' 

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

def StatusGPRSconnection():
    """
    Get status of GPRS data network.
    """

    Output = os.popen('dbus-send --print-reply --system --dest=com.nokia.csd.GPRS /com/nokia/csd/gprs com.nokia.csd.GPRS.GetStatus').read()

    E = re.compile(' boolean (\S+)')
    R = E.findall(Output)

    if R:
        if R[0] == 'false':
            return False
        elif R[0] == 'true':
            return True
    else:
        return None

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

def EnableConnection(NewType='GPRS'):
    """
    Connect to GPRS data network.
    """

    Delay = 3  # in sec. between connection status checks
    MaxT  = 30 # in sec. before timeout
    MaxR  = 3  # number of retries

    Type = StatusConnection()

    if Type != 'NONE':
        if Type != NewType:
            DisableConnection()
        elif Type == NewType:
            if Type == 'GPRS':
                Type, IAP, Name = GetGPRSname()
            elif Type == 'WLAN':
                Type, IAP, Name = GetWLANname()

            logging.info( '%s network (%s) already connected' % (Type, Name) )
            return True 

    if NewType == 'GPRS':
        Type, IAP, Name = GetGPRSname()
    elif NewType == 'WLAN':
        Type, IAP, Name = GetWLANname()
    elif NewType == 'NONE':
        Type, IAP, Name = 'NONE', 'NONE', 'NONE'

    if Type == 'NONE':
        # no network available
        logging.error('no available network found')
        return False
    else:
        # initiate network
        logging.info( 'connecting to %s network (%s)' % (Type, Name) )
        os.system( 'dbus-send --system --type=method_call --dest=com.nokia.icd /com/nokia/icd com.nokia.icd.connect string:"%s" uint32:0' % IAP )

        if Type == 'GPRS':
            T = 0
            R = 0
            while StatusGPRSconnection() != True:
                time.sleep(Delay)
                T += Delay

                if R == MaxR:
                    logging.error( "failed to connect to %s network after %s attempts" % (Type, R) )
                    return False

                if T >= MaxT:
                    logging.warning( "no connection to %s network after %s sec." % (Type, R) )
                    R += 1

            Type, IAP, Name = GetGPRSname()
            logging.info( '%s network (%s) connection successfull' % (Type, Name) )
            return True
        else:
            T = 0
            R = 0
            while StatusConnection() != 'WLAN':
                time.sleep(Delay)
                T += Delay

                if R == MaxR:
                    logging.error("failed to connect to WLAN network after %s attempts" % R)
                    return False

                if T >= MaxT:
                    logging.warning("no connection to WLAN network after %s sec." % T)
                    R += 1

            logging.info( '%s network (%s) connection successfull' % (Type, Name) )
            return True

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

def DisableConnection(): 
    """
    Disconnect any current WIFI/GPRS dataconnection.
    """

    Delay = 1

    # disconnect data network
    os.system('dbus-send --system --dest=com.nokia.icd2 /com/nokia/icd2 com.nokia.icd2.disconnect_req uint32:0x8000')
    logging.info('current network disconnected')

    time.sleep(Delay)

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

def GetGPRSname(): 
    """
    Get name of GPRS data connection from current inserted SIM card in Nokia device.
    """

    NetworkList = GetNetworks()

    for Network in NetworkList:
        Type = Network[0]
        IAP  = Network[1]
        Name = Network[2]
        
        if Type == 'GPRS': # format: (Type, IAP, Name, Imsi)
            Imsi = Network[3]
            if Imsi == GetCode('IMSI'):
                #logging.debug('[%s]' % IAP)
                return Type, IAP.replace('@32@', ' '), Name

    return 'NONE', 'NONE', 'NONE'

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

def GetWLANname():
    """
    Get name of active WLAN data connection.
    """

    NetworkList = GetNetworks()

    # get last used data network IAP
    LastUsedNetwork = os.popen(' gconftool -g /system/osso/connectivity/IAP/last_used_network').read().strip('\n')
    #logging.debug('LastUsedNetwork = [%s]' % LastUsedNetwork)

    for Network in NetworkList:
        Type = Network[0]
        IAP  = Network[1]
        Name = Network[2]
        
        if Type == 'WLAN': # format: (Type, IAP, Name)
            if LastUsedNetwork == IAP:
                #logging.debug('WLAN: %s' % Name)
                return Type, IAP, Name

    return 'NONE', 'NONE', 'NONE'

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

def GetNetworks():
    """
    Get list of all available data connections (stored under "Internet connections" in menu).
    """

    Output = os.popen('gconftool -R /system/osso/connectivity/IAP | grep IAP').read()
    E = re.compile('/system/osso/connectivity/IAP/(\S+):')
    IAPlist = E.findall(Output) 

    NetworkList = []

    for IAP in IAPlist:
        Type = os.popen('gconftool -g /system/osso/connectivity/IAP/%s/type' % IAP).read().replace('_INFRA', '').strip('\n')
        if Type == 'GPRS':
            Name = os.popen('gconftool -g /system/osso/connectivity/IAP/%s/name' % IAP).read().strip('\n')
            Imsi = os.popen('gconftool -g /system/osso/connectivity/IAP/%s/sim_imsi' % IAP).read().strip('\n')

            # convert decimal value of ASCII character in IAP string to normal ASCII character.
            E = re.compile('@(\d+)@')
            DecList = E.findall(IAP)

            for Dec in DecList:
                IAP = IAP.replace( '@%s@' % Dec, chr( int(Dec) ) ) 
            
            NetworkList.append( (Type, IAP, Name, Imsi) )
        elif Type == 'WLAN':
            Name = os.popen('gconftool -g /system/osso/connectivity/IAP/%s/name' % IAP).read().strip('\n')
            NetworkList.append( (Type, IAP, Name) )

    #logging.debug('[%s]' % NetworkList)
    return NetworkList

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

def StoreNetwork():
    """
    Save current active data network.
    """

    global ConnectionType
    global ConnectionIAP
    global ConnectionName

    Type = StatusConnection()
    if Type == 'WLAN':
        ConnectionType, ConnectionIAP, ConnectionName = GetWLANname()
    elif Type == 'GPRS':
        ConnectionType, ConnectionIAP, ConnectionName = GetGPRSname()
    else:
        ConnectionType, ConnectionIAP, ConnectionName = 'NONE', 'NONE', 'NONE'

    logging.info( 'stored current %s network (%s)' % (Type, ConnectionName) )

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

def RestoreNetwork():
    """
    Re-enable previous stored data network.
    """

    if ConnectionType != None:
        if ConnectionType == 'GPRS' or ConnectionType == 'WLAN':
            logging.info( 'restoring saved %s network (%s)' % (ConnectionType, ConnectionName) )
            EnableConnection(ConnectionType)
        else:
            DisableConnection()

############################################################################################################################
#    connect behaviour
############################################################################################################################

def EnableConnectBehaviour(Mode):
    """
    Set Nokia phone data connection behavior.
    """    

    if Mode != None:
        Type = StatusConnectBehaviour()
        if Type != Mode: # if new behaviour is different from current

            # store current "Internet connection" behaviour
            global ConnectionBehaviour
            ConnectionBehaviour = Type
            
            SetConnectBehaviour(Mode)

            # check new behaviour setting
            Type = StatusConnectBehaviour()
            if Type == Mode:
                logging.info('internet connection behaviour set to "%s"' % Mode)
            else:
                logging.error('failed to set internet connection behaviour (%s)' % Type)
        else:
            logging.info('internet connection behaviour already set to "%s"' % Mode)
    else:
        logging.info('internet connection behaviour not used')

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

def StatusConnectBehaviour():
    """
    Get current Nokia phone data connection behaviour.
    """ 

    Mode = os.popen('gconftool-2 -g --list-type string /system/osso/connectivity/network_type/auto_connect').read().strip('\n') 

    if Mode == '[]':
        return 'ASK'
    elif Mode == '[*]':
        return 'ANY'
    elif Mode == '[GPRS]':
        return 'GPRS'    
    elif Mode == '[WLAN_INFRA]':
        return 'WLAN'

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

def SetConnectBehaviour(Mode):
    """
    Set new Nokia device data connection behaviour.
    """

    Delay = 2

    # options: [WLAN_INFRA], [GPRS], [*] <=='any', [] <=='ask'
    if Mode == 'ASK':
        String = ''
    elif Mode == 'WLAN':
        String = 'WLAN_INFRA'
    elif Mode == 'GPRS':        
        String = 'GPRS'
    elif Mode == 'ANY':
        String = '*'

    #logging.debug('Mode = [%s]' % Mode)
        
    # set setting "Connect automatically"
    os.system('gconftool-2 -s --type list --list-type string /system/osso/connectivity/network_type/auto_connect [%s]' % String)

    time.sleep(Delay)

############################################################################################################################
#    power status
############################################################################################################################

def EnablePowerStatus():
    """
    Get battery charge and send sms reply.
    """

    ChargePerc = dev_obj.GetProperty('battery.charge_level.percentage') # charge amount of battery in percentage

    SMSsend(SENDERNUMBER,
            'NOKIA N900\n' + \
            '%s\n' % GetTime() + \
            '-battery-\n' + \
            'charge = %s%%' % ChargePerc,
            'send SMS message battery charge to')

############################################################################################################################
#    SSH 
############################################################################################################################

def EnableSSHcontrol():
    """
    Start the ssh routine.
    """

    if StatusSSH() == False:
        if StatusRemoteHost() == False:
            #logging.error('ssh connection failure to "%s"' % REMOTEHOST)
            logging.error('ssh ping failure to "%s", will try to connect anyway.' % REMOTEHOST)
            StartSSH()
            return
        else:
            StartSSH()
            return
    else:
        logging.info('ssh connection is already active')
        return

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

def DisableSSHcontrol():
    """
    Stop the ssh routine.
    """

    if StatusSSH() == True:
        logging.info('trying to disable ssh connection to "%s"' % REMOTEHOST)
        StopSSH()
    else:
        logging.info('ssh connection is already disabled')

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

def StatusRemoteHost():
    """
    Check if remote host is available with a ping command and return
    - false; no or bad ping 
    - true;  good ping (remotehost is available)
    """

    if os.WEXITSTATUS( os.system( 'ping -c %s %s >> /dev/null 2>&1' % (PingMax, REMOTEHOST) ) ) == 0:
        return True
    else:
        return False 
        
###############################

def StartSSH():
    """
    Start reverse-ssh connection to remote host.
    """

    logging.info('connecting to "%s"' % REMOTEHOST)


    (Output, ExitStatus) = pexpect.run( 'ssh -n -N -T -f -p %s -R %s:%s:%s %s@%s &' % (REMOTEPORT, LocalPort, LocalHost, REMOTEPORT, REMOTEUSER, REMOTEHOST),
                                        events = { '(?i)(password|passphrase)':REMOTEPASSWORD + '\n', '(?i)(yes/no) ?':'yes' + '\n' },
                                        withexitstatus = True )
    
    #logging.debug( 'output = %s' % Output.strip('\n') )
    #logging.debug('status = %s' % ExitStatus)

    if ExitStatus == 0:
        logging.info('ssh connection to "%s" established' % REMOTEHOST)

        SMSsend(SENDERNUMBER,
                'NOKIA N900\n' + \
                '%s\n' % GetTime() + \
                '-ssh-\n' + \
                'connection established.',
                'send SMS message SSH connection established')
    else:
        logging.error('ssh connection failed to "%s"' % REMOTEHOST)
        if StatusSSH() == True:
            StopSSH()

        SMSsend(SENDERNUMBER,
                'NOKIA N900\n' + \
                '-ssh-\n' +\
                'connection failed.',
                'send SMS message SSH connection failed to')

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

def StopSSH(Verbose=True):
    """
    Kill reverse-ssh connection to remote host.
    """

    if Verbose == True:
        logging.info('trying to kill ssh')

    PID = os.popen('pidof ssh').read().strip('\n')

    if PID == '':
        if Verbose == True:
            logging.info("ssh wasn't active")
    else:
        os.system('kill $(pidof ssh)')
        if Verbose == True:
            logging.info('ssh is stopped [PID=%s]' % PID)        

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

def StatusSSH():
    """
    Check if ssh is active or not running.
    """

    PID = os.popen('pidof ssh').read().strip('\n')

    if PID == '':
        return False
    else:
        return True

############################################################################################################################
#    GPS
############################################################################################################################

def EnableGPStracking(Mode):
    """
    Start the GPS device mainloop.
    """

    global GpsMode
    GpsMode = Mode
    
    global GpsActive
    if GpsActive == False:
        GpsActive = True

        # enable GPS device control
        gobject.idle_add(GPSstart, GpsControl)
    else:
        logging.warning('GPS device already active')

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

def DisableGPStracking():
    """
    Stop the GPS device mainloop.
    """

    global GpsActive
    if GpsActive == True:
        GpsActive = False

        # stop the GPS device
        GpsData.stop()
        logging.info('stopped acquiering GPS coordinates')

    else:
        logging.info('GPS device already stopped')

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

def GPSstart(GpsData):
    """
    Start the GPS device.
    """
    
    global GpsState
    GpsState = False

    if GpsMode == 'single':
        logging.info('starting GPS device in location mode')
    elif GpsMode == 'multi':
        logging.info('starting GPS device in tracking mode')

    global StartTime
    StartTime = time.time()
    GpsData.start()
    
    return False

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

def GPSstop(GpsControl, GpsData):
    """
    Stop the GPS device.
    """

    global GpsActive
    GpsActive = False

    global GpsTest
    if GpsTest == True:
        GpsTest = False
        RestoreNetwork()
        ExitDaemon()        
    
    logging.info('stopping GPS device')
    GpsData.quit()

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

def GPSchanged(GpsDevice, GpsData):
    """
    Get GPS device data.
    """

    global Gps
    global GpsList
    global GPSPOLLING
    global GpsState
    global StartTime

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

    if GpsActive == True:

        # check GPS device timout
        PastTime = time.time() - StartTime
        if GpsState == False:
            #logging.debug('StartTime = %s' % StartTime)
            if PastTime >= GPSTIMEOUT:
                logging.error('GPS device timeout after %.1f sec' % PastTime)

                if GPSSEND == 'sms':
                    SMSsend(SENDERNUMBER,
                            'NOKIA N900\n' + \
                            '-GPS-\n' + \
                            'failure\n' + \
                            'Search time = %.1f sec.' % PastTime,
                            'send SMS message GPS timeout failure to')
                        
                elif GPSSEND == 'email':
                    EMAILsend(EMAILADDRESS,
                              'From "%s" command / Nokia N900' % COM_TRACKON,
                              'timeout of GPS device after %.1f sec' % PastTime,
                              'send email timeout of GPS device to "%s"' % EMAILADDRESS)
                    
                elif GPSSEND == 'both':
                    SMSsend(SENDERNUMBER,
                            'NOKIA N900\n' + \
                            '-GPS-\n' + \
                            'failure\n' + \
                            'Search time = %.1f sec.' % PastTime,
                            'send SMS message GPS timeout failure to')

                    EMAILsend(EMAILADDRESS,
                              'From "%s" command / Nokia N900' % COM_TRACKON,
                              'timeout of GPS device after %.1f sec' % PastTime,
                              'send email timeout of GPS device to "%s"' % EMAILADDRESS)
                    
                # stop the GPS device
                GpsData.stop()
                return
        else:
            # adjust start time of GPS device
            StartTime = time.time()

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

        #logging.debug('%s' % GpsDevice.status) # LOCATION_GPS_DEVICE_STATUS_NO_FIX  or  LOCATION_GPS_DEVICE_STATUS_FIX

        # GPS device has fix.
        if GpsDevice.fix[1] & location.GPS_DEVICE_LATLONG_SET:
                
            Latitude  = GpsDevice.fix[4]
            Longitude = GpsDevice.fix[5]
            Accuracy  = GpsDevice.fix[6] / 100

            # no valid coordinate acquired by GPS device
            if (str(Latitude) == 'nan') or (str(Longitude) == 'nan') or (str(Accuracy) == 'nan'):
                GpsState = False
                logging.info('waiting for valid GPS coordinate (search time = %.1f sec.)'% PastTime)
                return

            # valid coordinate acquired by GPS device
            else:
                GpsState = True
                # single mode
                if GpsMode == 'single':
                    if Gps == 0:
                        logging.info('start collecting GPS coordinates')

                    # make GPS coordinate list
                    GpsList.append( (Latitude,
                                     Longitude,
                                     Accuracy) )
                    logging.info( 'GPS coordinate acquired (#%s, %f, %f, %.1f m.)' % (Gps + 1,
                                                                                      Latitude,
                                                                                      Longitude,
                                                                                      Accuracy) )
                    # increment GPS coordinate counter
                    Gps += 1
                    
                    if Gps >= GPSPOLLING:
                        Gps = 0
                        if GPSPOLLING != 1:
                            # sort GPS coordinate list on 'Accuracy'
                            GpsList = sorted( GpsList, key=itemgetter(2) )
                            
                            # get most accurate GPS coordinate
                            LatBest, LongBest, AccBest = GpsList[0]
                            logging.info( 'most accurate GPS coordinate (%f, %f, %.1f m.)' % (LatBest,
                                                                                              LongBest,
                                                                                              AccBest) )
                        else:
                            # get GPS coordinate
                            LatBest, LongBest, AccBest = GpsList[0]
                            logging.info( 'GPS coordinate (%f, %f, %.1f m.)' % (LatBest,
                                                                                LongBest,
                                                                                AccBest) )                       
                        GpsText = 'NOKIA N900\n' + \
                                  '-GPS-\n' + \
                                  'Lat = %f\n' % LatBest + \
                                  'Long = %f\n' % LongBest + \
                                  'Pos accuracy = %.1f m.\n' % AccBest + \
                                  'Search time = %.1f sec.\n' % PastTime

                        LinkText = 'GoogleMap:\n' + \
                                   'http://maps.google.com/maps?q=%f,%f\n'% (LatBest, LongBest)
                         
                        if GPSSEND == 'sms':
                            SMSsend(SENDERNUMBER,
                                    GpsText + LinkText,
                                    'send SMS message GPS coordinate to')

                        elif GPSSEND == 'email':
                            if CreateGoogleMap(LatBest, LongBest): 
                                EMAILsend(EMAILADDRESS,
                                          'From "%s" command / Nokia N900' % COM_LOCATION,
                                          GpsText.replace('\n', '<br>'),
                                          'send email GPS coordinate map to "%s"' % EMAILADDRESS,
                                          LogPath + MapName)
                            else:
                                EMAILsend(EMAILADDRESS,
                                          'From "%s" command / Nokia N900' % COM_LOCATION,
                                          GpsText.replace('\n', '<br>'),
                                          'send email GPS coordinate to "%s"' % EMAILADDRESS)
                                
                        elif GPSSEND == 'both':
                            SMSsend(SENDERNUMBER,
                                    GpsText + LinkText,
                                    'send SMS message GPS coordinate to')

                            if CreateGoogleMap(LatBest, LongBest): 
                                EMAILsend(EMAILADDRESS,
                                          'From "%s" command / Nokia N900' % COM_LOCATION,
                                          GpsText.replace('\n', '<br>'),
                                          'send email GPS coordinate map to "%s"' % EMAILADDRESS,
                                          LogPath + MapName)
                            else:
                                EMAILsend(EMAILADDRESS,
                                          'From "%s" command / Nokia N900' % COM_LOCATION,
                                          GpsText.replace('\n', '<br>'),
                                          'send email GPS coordinate to "%s"' % EMAILADDRESS)

                        # erase GPS coordinate list
                        GpsList = []
                        # stop the GPS device
                        GpsData.stop()
                        return
                        
                # tracking mode
                elif GpsMode == 'multi':
                    logging.info( 'GPS coordinate acquired (%f, %f, %.1f m.)' % (Latitude,
                                                                                 Longitude,
                                                                                 Accuracy) )
                    GpsText = 'NOKIA N900\n' + \
                              '-GPS-\n' + \
                              'Lat = %f\n' % Latitude + \
                              'Long = %f\n' % Longitude + \
                              'Pos accuracy = %.1f m.\n' % Accuracy + \
                              'Search time = %.1f sec.\n' % PastTime

                    LinkText = 'GoogleMap:\n' + \
                               'http://maps.google.com/maps?q=%f,%f\n'% (Latitude, Longitude)

                    if GPSSEND == 'sms':
                        SMSsend(SENDERNUMBER,
                                GpsText + LinkText,
                                'send SMS message GPS coordinate to')

                    elif GPSSEND == 'email':
                        if CreateGoogleMap(Latitude, Longitude): 
                            EMAILsend(EMAILADDRESS,
                                      'From "%s" sms command / Nokia N900' % COM_TRACKON,
                                      GpsText.replace('\n', '<br>'),
                                      'send email GPS coordinate map to "%s"' % EMAILADDRESS,
                                      LogPath + MapName)
                        else:
                            EMAILsend( EMAILADDRESS,
                                       'From "%s" sms command / Nokia N900' % COM_TRACKON,
                                       GpsText.replace('\n', '<br>'),
                                       'send email GPS coordinate to "%s"' % EMAILADDRESS)
                    elif GPSSEND == 'both':
                        SMSsend(SENDERNUMBER,
                                GpsText + LinkText,
                                'send SMS message GPS coordinate to')
                        if CreateGoogleMap(Latitude, Longitude): 
                            EMAILsend(EMAILADDRESS,
                                      'From "%s" sms command / Nokia N900' % COM_TRACKON,
                                      GpsText.replace('\n', '<br>'),
                                      'send email GPS coordinate map to "%s"' % EMAILADDRESS,
                                      LogPath + MapName)
                        else:
                            EMAILsend( EMAILADDRESS,
                                       'From "%s" sms command / Nokia N900' % COM_TRACKON,
                                       GpsText.replace('\n', '<br>'),
                                       'send email GPS coordinate to "%s"' % EMAILADDRESS)
        else:
            logging.info('waiting for GPS device satellite fix')

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

def GPSerror(GpsControl, GpsError, GpsData):
    """
    GPS device error detected.
    """

    global GpsActive
    GpsActive = False

    if GpsError == location.ERROR_USER_REJECTED_DIALOG:
        logging.error("GPS device error (User didn't enable requested methods)")
    elif GpsError == location.ERROR_USER_REJECTED_SETTINGS:
        logging.error('GPS device error (User changed settings, which disabled location)')
    elif GpsError == location.ERROR_BT_GPS_NOT_AVAILABLE:
        logging.error('GPS device error (Problems with BT GPS)')
    elif GpsError == location.ERROR_METHOD_NOT_ALLOWED_IN_OFFLINE_MODE:
        logging.error('GPS device error (Requested method is not allowed in offline mode)')
    elif GpsError == location.ERROR_SYSTEM:
        logging.error('GPS device error (System error)')
    else:
        logging.error('GPS device error (Unknown error)')
        
    logging.error('stopping failed GPS device')
    GpsData.quit()

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

def CreateGoogleMap(Latitude, Longitude):
    """
    Create map of current GPS coordinate.
    """

    MapZoom     = 16
    MapSize     = (600,600)
    MarkerColor = 'red'
    MarkerLabel = 'N'

    MapUrl = 'http://maps.google.com/maps/api/staticmap?center=%s,%s&zoom=%s&size=%sx%s&format=png&markers=color:%s|label:%s|%s,%s&sensor=false' \
              % (Latitude,
                 Longitude,
                 MapZoom,
                 MapSize[0],
                 MapSize[1],
                 MarkerColor,
                 MarkerLabel,
                 Latitude,
                 Longitude)

    try:
        urllib.urlretrieve(MapUrl, LogPath + MapName)
    except:
        logging.error('failed to create GPS coordinate map (retrieve error)')
        return False
    else:
        if os.path.isfile(LogPath + MapName) == True:
            logging.info('created GPS coordinate map')
            return True
        else:
            logging.error('failed to create GPS coordinate map (file not found)')
            return False

############################################################################################################################
#    SIM card
############################################################################################################################

def ValidateIMSI():
    """
    Check if current used IMSI code from SIM card is authorized.
    """
    
    CurrentIMSI = GetCode('IMSI')

    # no SIM card found
    if CurrentIMSI == None:
        logging.critical('no SIM card present; %s will quit' % DaemonFile)
        sys.exit(1)

    # compare current IMSI code with saved one in file.
    elif CurrentIMSI == ReadIMSI():
        logging.info('authorized IMSI code found')

    else:
        logging.warning('IMSI code has changed to "%s"' % CurrentIMSI)

        # save new IMSI code to file
        WriteIMSI(CurrentIMSI)    

        # send reply sms
        SMSsend(SENDERNUMBER,
                'NOKIA N900\n' + \
                '%s\n' % GetTime() + \
                '-New SIM card-\n' + \
                'IMSI = %s\n' % CurrentIMSI + \
                'IMEI = %s\n' % GetCode('IMEI') + \
                'Operator = "%s".' % GetOperatorName(),
                'send SMS message SIM number/IMSI code to')

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

def ReadIMSI():
    """
    Load IMSI code from file.
    """

    if CheckFile(Path, CodeFile) == True:
        f = open('%s/%s' % (Path, CodeFile), 'r+')
        Code = f.readline().strip('\n')
        f.close()

        logging.info( 'reading valid IMSI code (%s) from %s file' % (Code, CodeFile) )

        return Code
    else:
        logging.warning('initalizing new %s file with current valid IMSI code' % CodeFile)
    
        CodeValid = GetCode('IMSI')

        # write new file with current valid imsi code
        f = open('%s/%s' % (Path, CodeFile), 'w')
        f.writelines(CodeValid)
        f.close()

        logging.info('current valid IMSI code (%s) saved to file' % CodeValid)

        return CodeValid

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

def WriteIMSI(Code):
    """
    Save new IMSI code to file.
    """

    if CheckFile(Path, CodeFile) == False:
        logging.error('IMSI file not found; creating new %s file' % ConfigFile)

        f = open('%s/%s' % (Path, CodeFile), 'w')
        f.writelines(Code)
        f.close()

        logging.info('new IMSI code (%s) saved to file' % Code)        

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

def GetCode(CodeType):
    """
    Get current SIM IMSI code or device hardware IMEI code.
    """

    if CodeType == 'IMEI':
        Command = 'dbus-send --system --type=method_call --print-reply --dest=com.nokia.phone.SIM /com/nokia/phone/SIM/security Phone.Sim.Security.get_imei'
    elif CodeType == 'IMSI':
        Command = 'dbus-send --system --type=method_call --print-reply --dest=com.nokia.phone.SIM /com/nokia/phone/SIM Phone.Sim.get_imsi'

    try:
        Output = os.popen(Command).read()
        E = re.compile('string "(\S+)"')
        R = E.findall(Output)[0]
    except:
        logging.error("couldn't get %s code" % CodeType)
        return None
               
    return R

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

def GetOperatorName():
    """
    Get current SIM telecom operator name.
    """

    try:
        Output = os.popen('dbus-send --system --print-reply=literal --dest=com.nokia.phone.net /com/nokia/phone/net Phone.Net.get_registration_status').read()
        E = re.compile('uint32 (\d+)')
        R = E.findall(Output)
        Output = os.popen( 'dbus-send --system --print-reply --dest=com.nokia.phone.net /com/nokia/phone/net Phone.Net.get_operator_name byte:0 uint32:"%s" uint32:"%s"' % (R[1], R[2]) ).read()
        E = re.compile('string "(.*)"')
        R = E.findall(Output)[0]
    except:
        logging.error("couldn't get operator name")
        return 'UNKNOWN'        

    return R

############################################################################################################################
#   arguments
############################################################################################################################

def CheckArguments():
    """
    Check arguments for smscon_daemon.
    """    

    # check for arguments
    if sys.argv[1:] != []:
        Mode = sys.argv[1]
        
        if Mode == '-test':
            Func = sys.argv[2]

            if Func == 'gps1' or Func == 'gps2': 
                StoreNetwork()

                if StatusConnection() != 'WLAN':
                    EnableConnection()
                    EnableConnectBehaviour('GPRS') # disable switching to other network

                global GpsTest
                GpsTest = True
                
                if Func == 'gps1': # test mode for gps (location mode)
                    EnableGPStracking('single')
                elif Func == 'gps2': # test mode for gps (tracking mode)
                    EnableGPStracking('multi')

            elif Func == 'ssh': # test mode for ssh connection
                StoreNetwork()

                if StatusConnection() != 'WLAN':
                    EnableConnection()
                    EnableConnectBehaviour('GPRS') # disable switching to other network

                if StatusSSH() == True:
                    StopSSH(False)
                EnableSSHcontrol()

            elif Func == 'cam': # test mode for camera
                StoreNetwork()
                
                if StatusConnection() != 'WLAN':
                    EnableConnection()
                    EnableConnectBehaviour('GPRS') # disable switching to other network

                EnableCamera()
                RestoreNetwork()
                EnableConnectBehaviour(ConnectionBehaviour)
                ExitDaemon()
                
            elif Func == 'sms': # test mode for sms
                SMSsend(SENDERNUMBER,
                        'Test SMS from Nokia N900 device',
                        'send SMS message test to')
                ExitDaemon()

            elif Func == 'email1' or Func == 'email2' : 
                StoreNetwork()
                
                if StatusConnection() != 'WLAN':
                    EnableConnection()
                    EnableConnectBehaviour('GPRS') # disable switching to other network
                
                if Func == 'email1': # test mode1 for email (text only)
                    EMAILsend(EMAILADDRESS,
                              'Test email from Nokia N900 device',
                              'Email sending from Nokia N900 device is successfull.',
                              'send test email message to "%s"' % EMAILADDRESS)
                elif Func == 'email2': # test mode2 for email + attachment (frontcam image)
                    if TakeCamPicture() == True:
                        EMAILsend(EMAILADDRESS,
                                  'Test email from Nokia N900 device',
                                  'This is a frontcam picture from the Nokia N900 device:<br>' + \
                                  '%s<br>' % GetTime(),
                                  'send email camera picture send to "%s"' % EMAILADDRESS,
                                  LogPath + PhotoName)

                RestoreNetwork()
                EnableConnectBehaviour(ConnectionBehaviour)
                ExitDaemon()      

            elif Func == 'call': # test mode for phone call
                EnablePhonecall()
                ExitDaemon()

            else:
                logging.error('test option error (%s)' % Func)
                ExitDaemon()

        # /* ONLY FOR DEVELOPMENT USAGE */
        elif Mode == '-test2':
            Command = sys.argv[2]

            CommandList = [COM_CHECK,
                           COM_REBOOT,
                           COM_POWEROFF,
                           COM_POWER,
                           COM_LOCATION,
                           COM_REMOTEON,
                           COM_REMOTEOFF,
                           COM_CAMERA,
                           COM_CALL,
                           COM_LOCK, 
                           COM_UNLOCK,
                           COM_TRACKON,
                           COM_TRACKOFF]

            logging.info('simulating received SMS message with "%s" command' % Command)
            
            if Command in CommandList: # test mode for all SMSCON commands
                ProcessCommand(Command, SENDERNUMBER)
                return
            else:
                logging.error('"%s" was not a valid SMSCON command' % Command)
                ExitDaemon()

        else:
            logging.error('test option error (%s)' % Mode)
            ExitDaemon()                

############################################################################################################################
#   autoloader
############################################################################################################################

def CheckAutoloader():
    """
    Check if smscon autoloads at system boot.
    """

    if CheckFile(BootPath, BootFile) == True:
        logging.info('smscon auto-loads at boot')
    else:
        logging.warning("smscon does not auto-load at boot")

############################################################################################################################
#   keyboard detect (dbus)
############################################################################################################################

def KEYBOARDslider(Action, Type):
    """
    Check state change of keyboard slider.
    """

    if EnableKeyboardDetect == True:
        # keyboard slider: 'open to closed' state or 'closed to open' state
        KeyState = os.popen('cat /sys/devices/platform/gpio-switch/slide/state').read().strip('\n') 
        #logging.debug(' Keyboard is used (%s)' % KeyState)
        if KeyState == 'closed' or KeyState == 'open':
            SMSsend(SENDERNUMBER,
                    'NOKIA N900\n' + \
                    '%s\n' % GetTime() + \
                    '-Keyboard-\n' + \
                    'Phone is being used:\n' + \
                    'keyboard is set %s.' % KeyState,
                    'send SMS message keyboardslider used to')           

############################################################################################################################
#   battery charge (dbus)
############################################################################################################################

##def BATTERYstatus(Level, LevelMax):
##    """
##    Get the battery bar level.
##    """
##
##    #logging.debug( '%s / %s' % (Level, LevelMax) )
##    if EnableBatteryDetect == True:
##        Percentage = float(Level / LevelMax) * 100
##        logging.warning('device battery is now at %.1f%% charge level' % Percentage)
##        SMSsend(SENDERNUMBER,
##                'NOKIA N900\n' + \
##                '%s\n' % GetTime() + \
##                '-battery warning-\n' + \
##                'Phone battery is now\n' + \
##                'at %.1f%% charge level.' % Percentage,
##                'send SMS message battery level warning to')        

############################################################################################################################
#   check file
############################################################################################################################

def CheckFile(Path, File):
    """
    Check if file exists.
    """

    try:
        f = open('%s/%s' % (Path, File), 'r')
        f.close()
    except:
        return False
    else:
        return True    

############################################################################################################################
#   exit smscon_daemon
############################################################################################################################

def ExitDaemon():
    """
    Exit smscon_daemon.
    """

    logging.info('%s has stopped' % DaemonFile)
    sys.exit(1)

############################################################################################################################
#    MAIN
############################################################################################################################

BlockSize = 16
Padding   = '{'
Pad       = lambda s: s + (BlockSize - len(s) % BlockSize) * Padding

EncodeAES = lambda c, s: base64.b64encode(c.encrypt(Pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(Padding)

Secret = '6983456712936753' 

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

# load user settings from smscon_config file
if CheckFile(Path, ConfigFile) == True:
    f = open('%s/%s' % (Path, ConfigFile), 'r')

    ConfigLines = f.readlines() 
    if ConfigLines == []:
        logging.critical('%s is empty.' % ConfigFile)
        ExitDaemon()
    else:
        try:
            for Line in ConfigLines:
                Line = Line.strip().replace('\r', '').replace('\n', '')
                
                for Var in ConfigVars:
                    # get user variable & it's value from text line
                    if Line.startswith('%s' % Var):
                        try:
                            if Var in EncryptedConfigVars: # encrypted text value
                                Variable, EncryptedValue = Line.split('=', 1)
                            else: # plain text value
                                Variable, Value = Line.split('=', 1)
                        except:
                            logging.critical( 'syntax error in "%s" file (%s)' % (ConfigFile, Line) )
                            ExitDaemon()
                        else:
                            if Var in EncryptedConfigVars: # encrypted text value
                                EncryptedValue = EncryptedValue.lstrip(' ')
                            else: # plain text value
                                Value = Value.lstrip(' ')
                            try:
                                # encrypted text value
                                if Var in EncryptedConfigVars:
                                    if EncryptedValue.startswith("'") == True and EncryptedValue.endswith("'") == True:
                                        EncryptedValue = EncryptedValue.strip("'")
                                        vars()[Var] = DecodeAES(AES.new(Secret), EncryptedValue) # get string
                                        #logging.debug('%s = [%s]' % ( Var, eval(Var) ) )
                                    else:
                                        logging.critical( 'syntax error in "%s" file (%s)' % (ConfigFile, EncryptedValue) )
                                        ExitDaemon()                                        
                                else:
                                    if Value.startswith("'") == True and Value.endswith("'") == True: # plain text value
                                        vars()[Var] = Value.strip("'") # get string
                                        #logging.debug('%s = [%s]' % ( Var, eval(Var) ) )
                                    else: # plain text integer
                                        vars()[Var] = int(Value) # get integer
                                        #logging.debug('%s = [%s]' % ( Var, eval(Var) ) )
                            except:
                                logging.critical( 'syntax error in "%s" file (%s)' % (ConfigFile, Line) )
                                ExitDaemon()
        except:
            logging.critical('error reading "%s" file' % ConfigFile)
            ExitDaemon()
        else:
            logging.info('successfully loaded "%s" file' % ConfigFile)
            
            ShowSMScommands('log')
    f.close()

else:
    logging.warning("%s doesn't excist" % ConfigFile)
    ExitDaemon()

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

ValidateIMSI()
CheckAutoloader()

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

try:            
    DBusGMainLoop(set_as_default = True)

    bus = dbus.SystemBus()

    # set battery charge measurment
    hal_obj = bus.get_object('org.freedesktop.Hal',
                             '/org/freedesktop/Hal/Manager')

    hal = dbus.Interface(hal_obj,
                         'org.freedesktop.Hal.Manager')

    uids = hal.FindDeviceByCapability('battery')
    dev_obj = bus.get_object('org.freedesktop.Hal',
                             uids[0])

    # set GPS device control
    GpsControl = location.GPSDControl.get_default()
    GpsDevice  = location.GPSDevice()

    # set GPS method & interval
    if GPSINTERVAL == '10':
        GpsControl.set_properties(preferred_method   = location.METHOD_GNSS|location.METHOD_AGNSS,
                                  preferred_interval = location.INTERVAL_10S)
    elif GPSINTERVAL == '20':
        GpsControl.set_properties(preferred_method   = location.METHOD_GNSS|location.METHOD_AGNSS,
                                  preferred_interval = location.INTERVAL_20S)
    elif GPSINTERVAL == '30':
        GpsControl.set_properties(preferred_method   = location.METHOD_GNSS|location.METHOD_AGNSS,
                                  preferred_interval = location.INTERVAL_30S)
    elif GPSINTERVAL == '60':
        GpsControl.set_properties(preferred_method   = location.METHOD_GNSS|location.METHOD_AGNSS,
                                  preferred_interval = location.INTERVAL_60S)
    elif GPSINTERVAL == '120':
        GpsControl.set_properties(preferred_method   = location.METHOD_GNSS|location.METHOD_AGNSS,
                                  preferred_interval = location.INTERVAL_120S)
    else:
        GpsControl.set_properties(preferred_method   = location.METHOD_GNSS|location.METHOD_AGNSS,
                                  preferred_interval = location.INTERVAL_60S)       
     
    GpsControl.connect( 'error-verbose', GPSerror, gobject.MainLoop() )
    GpsDevice.connect( 'changed', GPSchanged, GpsControl )
    GpsControl.connect( 'gpsd-stopped', GPSstop, gobject.MainLoop() )

    # set SMS received signal receiver
    bus.add_signal_receiver(SMSreceive,
                            path           = '/com/nokia/phone/SMS',
                            dbus_interface = 'Phone.SMS',
                            signal_name    = 'IncomingSegment')

    # set keyboard slider signal receiver
    bus.add_signal_receiver(KEYBOARDslider,
                            path           = '/org/freedesktop/Hal/devices/platform_slide',
                            dbus_interface = 'org.freedesktop.Hal.Device',
                            signal_name    = 'Condition')

##    # set battery charge signal receiver
##    bus.add_signal_receiver(BATTERYstatus, 
##                            path           = '/com/nokia/bme/signal',
##                            dbus_interface = 'com.nokia.bme.signal',
##                            signal_name    = 'battery_state_changed')

    CheckArguments()

    gobject.MainLoop().run()

except Exception, e:
    
    logging.critical('<<< SMSCON FATAL ERROR:\n%s >>>' % e)
    print 'SMSCON FATAL ERROR:\n%s' % e
    
    ExitDaemon()

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

"""
 0.5-8
 NEW: smscon will send automatically a SMS message when smscon is activated & device battery is depleting.

 0.5-7
 FIX: activation of the current active/available GPRS connection could fail in specific cases (bug in acquiering the name).
 FIX: better logging/behaviour when locking/unlocking of device.

 0.5-6
 NEW: added new command-line option for smscon to set user settings (encrypted & non-encrypted) in smscon_config file.
 NEW: added package icon
 CHANGE: improved user setting reading from smscon_config file.
 CHANGE: cosmetic changes in logging.
 REFACTORING: parts of script code.
 FIX: smscon can send to wrong phone number when activated.
 FIX: added time & date of frontcam picture taken in email.

 0.5-5
 NEW: new test mode for simulating all smscon commands. /*Developer usage only*/
 NEW: in reply SMS from COM_LOCATION command is http link added to GoogleMaps.
 NEW/CHANGE: optification of smscon; new install path of package is now /opt/smscon.
 REFACTORING: improved the way SMS is send (more stable).
 CHANGE: in smscon test modes usage of WLAN connection only if already connected, otherwise usage of GPRS.
 CHANGE: in smscon test modes if usage of GPRS connection, previous connection is saved & re-enabled after GPRS usage.
 CHANGE: if usage of GPRS connection, force Internet connections to GPRS connection (needed for keeping the connection alive/stable).
 CHANGE: new smscon icon (meets requirements of maemo package)
 CHANGE: new package description (meets requirements of maemo Extra-testing package)
 FIX: execption in email sending code failed.
 FIX: internal fixes (better consistency in logging).
 FIX: email attachment (camera picture or Google map) is displayed completely wrong (alt text was wrong/mixed etc.)
 FIX: remove unused variable in ConfigVars list.
 FIX: syntax bug fixed which could crash smscon.

 0.4.4-6
 NEW: GPSSEND has new extra option 'both'. (options are now: 'sms' / 'email' / 'both')

 0.4.4-5
 NEW: COM_CHECK returns all valid SMS commands
 NEW: improved email smtp server handling
 FIX: ssh connection passfrase for auth. keys added
 FIX: "Internet connection" behavior set to initial setting when smscon_daemon stops.
 FIX: email sending works with or without secure authentication (tested with Google gmail)
"""

