#!/usr/bin/env python

##############################################################
#    smscon_daemon                                           #
##############################################################

BUILD   = '1'
BRANCH  = '0.8'
VERSION = '%s.%s' % (BRANCH, BUILD)
NAME    = 'smscon'
DATE    = '13/6/2011'

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

# 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                         = True                              # if set, show time/date in smscon_log file

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

Path                         = '/opt/%s' % NAME                  # 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
ScriptFile                   = '%s_script' % NAME                # name of custom script file
AudioFile                    = 'alarm.wav'                       # name of alarm audio 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

CheckHost                    = None
CheckHostPrev                = 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',
                                'COM_CUSTOM',
                                'COM_SHELL',
                                'COM_ALARM',
                                'COM_RESTART',
                                'ENABLECHECKHOST',
                                'CHECKHOST',
                                'CHECKTIME',
                                'USER',         
                                'PASSWORD',        
                                'EMAILFROM',       
                                'MAILSERVER',      
                                'MAILPORT',
                                'REMOTEHOST',            
                                'REMOTEPORT',           
                                'REMOTEUSER',       
                                'REMOTEPASSWORD',
                                'MESSAGESEND',
                                'ENABLERESEND',
                                'RESENDTIME',
                                'COMMANDREPLY',
                                'KEYBOARDDETECT',
                                'AUTODEVICELOCK',
                                'SIMUNLOCK',
                                'AUTOBATREPORT',
                                'SILENCEDEVICE',
                                'GPSTIMEOUT',
                                'GPSPOLLING',
                                'GPSINTERVAL',
                                'GPSSEND']

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

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

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

if TIME == True:
    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')
elif TIME == False:
    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 SMSreceive(PDUmessage, MessageCenter, SomeString, Number):
    """
    Receive SMS command and execute command.
    """

    n = int(PDUmessage[1])
    n = n // 2 + n % 2 + 13

    MessageArray = PDUmessage[n:len(PDUmessage)]

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

    # process SMS command
    ProcessCommand(Message, Number)

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

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

    MessageMax = 160 # max. number of characters in SMS message
    
    # get total message length
    MessageLength = 0
    for Item in MessageList:
        MessageLength += len(Item)

    # message can be send
    if MessageLength <= MessageMax:

        # convert list to string
        Message = ''
        for Item in MessageList:
            Message += Item

        if ENABLERESEND == 'yes':
            # if sending fails
            if SendToGSMmodem(Number, Message, Log) == True:

                # continiously retry sending after <RESENDTIME> minutes
                gobject.timeout_add(RESENDTIME * 60000,
                                    SendToGSMmodem,
                                    Number,
                                    Message,
                                    Log)
                logging.warning('continuously trying to resend SMS message after %s minutes' % RESENDTIME)
            
        elif ENABLERESEND == 'no':
            SendToGSMmodem(Number, Message, Log)

    # message must be split
    else:
        logging.warning('SMS message is too long (%s chars); splitting message' % MessageLength)

        n = 1
        TextList = ['-#%s-\n' % n]

        i = 0
        while i < len(MessageList):
            AddText = MessageList[i]

            TextListLength = 0
            for j in TextList:
                TextListLength += len(j)
            
            TotalChars = TextListLength + len(AddText)
          
            if TotalChars <= MessageMax:
                TextList.extend( [AddText] )

                if i == len(MessageList) - 1:
                    SMSsend(Number,
                            TextList,
                            'send splitted SMS message to')    

            elif TotalChars > MessageMax:
                SMSsend(Number,
                        TextList,
                        'send splitted SMS message to') 

                n += 1
                TextList = ['-#%s-\n' % n]
                i -= 1
                
            i += 1

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

def SendToGSMmodem(Number, Message, Log):
    """
    Send message to GSM modem.
    """

    Delay    = 2 # in sec.
    MaxRetry = 2 # number of retries to re-activate GSMmodem when the GSM modem control sequence has failed.

    Loop = True
    Retry = 0 
    while Loop:
        Retry += 1
        if Retry == MaxRetry:
            Loop = False

        modem = pexpect.spawn('pnatd', [], timeout = 2) # start pnatd
        #modem.logfile = sys.stdout

        try:
            time.sleep(1)
            modem.send('AT\r') # GSM modem returns '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 returns '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 returns '> ' 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)
        time.sleep(Delay) 
        return True
    else:
        logging.info(Log + ' "%s"' % Number)
        time.sleep(Delay) 
        return False

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

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])

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

def EMAILsend(EmailTo,
              Subject,
              Text,
              Log,
              Attachment=None):
    """
    Send email; retry after <RESENDTIME> minutes if send failure.
    """

    if ENABLERESEND == 'yes':
        # if sending fails
        if SendToSMTPserver(EmailTo,
                            Subject,
                            Text,
                            Log,
                            Attachment) == True:

            # continiously retry sending after <RESENDTIME> minutes until SendToSMTPserver returns 'False'
            gobject.timeout_add(RESENDTIME * 60000,
                                SendToSMTPserver,
                                EmailTo,
                                Subject,
                                Text,
                                Log,
                                Attachment)
            logging.warning('continuously trying to resend email message after %s minutes' % RESENDTIME)

    elif ENABLERESEND == 'no':
        # send only once
        SendToSMTPserver(EmailTo,
                         Subject,
                         Text,
                         Log,
                         Attachment)

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

def SendToSMTPserver(EmailTo,
                     Subject,
                     Text,
                     Log,
                     Attachment):
    """
    Send email to SMTP server.
    """

    # convert string list to string
    if type(Text).__name__ == 'list':

        Temp = ''
        for i in Text:
            Temp += i
        Text = Temp

    # convert text to html text
    Text = Text.replace('\n', '<br>')

    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

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

    # email: 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 True
        else:
            Image.add_header( 'Content-ID', '<%s%s>' % (AttachmentDescription, RandomNumber) )
            Message.attach(Image)

    try:
        logging.info( '(EMAIL) trying to connect to "%s:%s" (%s)' % ( MAILSERVER, MAILPORT, GetTime() ) )

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

        # if smtp server requires user/password authentication
        if USER != '' and PASSWORD != '':
            try:
                logging.info('(EMAIL) trying to login to "%s"' % MAILSERVER)

                # set username & pasword
                Server.login(USER, PASSWORD) 
            except smtplib.SMTPAuthenticationError:
                logging.error('(EMAIL) wrong username/password')
                Server.quit()
                return True
        try:
            logging.info('(EMAIL) trying to send email to "%s"' % MAILSERVER)

            # send email
            Server.sendmail( EMAILFROM,
                             EmailTo,
                             Message.as_string() ) 
        except smtplib.SMTPException, e:
            logging.error('(EMAIL) [%s]' % e)
            Server.quit()
            logging.error('(EMAIL) mailserver connection closed')
            return True
        else:
            logging.info(Log)
            logging.info('(EMAIL) mailserver connection closed successful')
            Server.quit()
            return False

############################################################################################################################
#   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 command 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,
                   COM_CUSTOM,
                   COM_SHELL,
                   COM_ALARM,
                   COM_RESTART]

    #print CommandList

    # 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)

        # make phone silent
        if SILENCEDEVICE == 'yes':
            EnableDeviceSilence()
        elif SILENCEDEVICE == 'no':
            logging.info('phone silencing disabled by user setting')

        # battery level detection
        global EnableBatteryDetect
        if AUTOBATREPORT == 'yes':
            EnableBatteryDetect = True
        elif AUTOBATREPORT == 'no':
            logging.info('battery status reporting disabled by user setting')

        # keyboard detection
        global EnableKeyboardDetect
        if KEYBOARDDETECT == 'yes':
            EnableKeyboardDetect = True
        elif KEYBOARDDETECT == 'no':
            logging.info('keyboard activity reporting disabled by user setting') 

        # auto device locking
        if AUTODEVICELOCK == 'yes' and Command != COM_UNLOCK:
            EnableDeviceLock()

        # command reply
        if COMMANDREPLY == 'yes':

            if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
                SMSsend(SENDERNUMBER,
                        ['NOKIA N900\n',
                         '%s\n' % GetTime(),
                         '-reply-\n',
                         'command (%s) accepted.\n' % Command],
                         'send SMS message with command reply to')
            if MESSAGESEND == 'email' or MESSAGESEND == 'both':
                EMAILsend(EMAILADDRESS,
                          'Email from Nokia N900',
                          'SMSCON command (%s) accepted.' % Command,
                          'send reply email message to "%s"' % EMAILADDRESS)
            elif MESSAGESEND == 'none':
                logging.info('message sending disabled by user setting')
                
        elif COMMANDREPLY == 'no':
            logging.info('acknowledge command disabled by user setting')

        # command execution
        if Command == COM_CHECK:
            if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
                ShowSMScommands('sms')
            if MESSAGESEND == 'email' or MESSAGESEND == 'both':
                ShowSMScommands('email')
            if MESSAGESEND == 'none':
                logging.info('message sending disabled by user setting')

        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:
            if REMOTEUSER == '' and REMOTEPASSWORD == '':
                logging.warning('no user/password entered for enabling SSH connection')
            else:
                EnableConnection() # enable GPRS data network
                EnableConnectBehaviour('GPRS') # disable switching to other network
                EnableSSHcontrol()
                
        elif Command == COM_REMOTEOFF:
            if REMOTEUSER == '' and REMOTEPASSWORD == '':
                logging.warning('no user/password entered for disabling SSH connection')
            else:
                DisableSSHcontrol()
                
        elif Command == COM_CAMERA:
            EnableConnection() # enable GPRS data network
            EnableConnectBehaviour('GPRS') # disable switching to other network
            EnableCamera()

        elif Command == COM_CALL:
            EnablePhonecall()

        elif Command == COM_LOCK:
            EnableDeviceLock()

        elif Command == COM_UNLOCK:
            DisableDeviceLock()

        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)

        elif Command == COM_CUSTOM:
            EnableShellScript()

        elif Command == COM_SHELL:
            EnableDirectShellCommand(Command)

        elif Command == COM_ALARM:
            EnableAlarm()

        elif Command == COM_RESTART:
            RestartSmscon()

        else:
            logging.debug('Unknown command: [%s]' % Command)

############################################################################################################################
#   restart smscon_daemon
############################################################################################################################

def RestartSmscon():
    """
    Restart smscon_daemon.
    """ 

    Delay = 10

    logging.warning('%s will be restarted!' % DaemonFile)
    try:
        time.sleep(Delay)
        os.system( '%s/%s -restart' % (Path, NAME) )
    except:
        logging.error('(RESTART) failed to kill %s' % DaemonFile)

############################################################################################################################
#   silence phone
############################################################################################################################

def EnableAlarm():
    """
    Make phone play sound file for alarm.
    """ 

    # get current active phone profile
    Output = os.popen('dbus-send --print-reply \
                      --session --dest=com.nokia.profiled \
                      --type=method_call \
                      /com/nokia/profiled \
                      com.nokia.profiled.get_profile').read()
    E = re.compile('string "(\S+)"')

    try:
        CurrentProfile = E.findall(Output)[0]
    except:
        logging.error('(ALARM) could not get current phone profile')
        return
    else:
        if CurrentProfile != 'general':
            # set "General" profile
            os.system('dbus-send --type=method_call \
                      --dest=com.nokia.profiled \
                      /com/nokia/profiled \
                      com.nokia.profiled.set_profile string:"general"')
            logging.info('(ALARM) set phone profile to "General"')
        else:
            logging.info('(ALARM) current profile is "%s"' % CurrentProfile)

        # set volume to maximum
        Volume = 100
        os.system('dbus-send --type=method_call \
                   --dest=com.nokia.mafw.renderer.Mafw-Gst-Renderer-Plugin.gstrenderer \
                   /com/nokia/mafw/renderer/gstrenderer \
                   com.nokia.mafw.extension.set_extension_property \
                   string:volume variant:uint32:%s' % Volume)
        
        # play audio
        PlayAudio(AudioFile)
        #gobject.timeout_add( PlayAudio(AudioFile) )

        # loop until aplay quits
        while os.popen('ps ax | grep -v grep | grep aplay').read() != '':
            time.sleep(1)

        # set previous profile
        os.system('dbus-send --type=method_call \
                  --dest=com.nokia.profiled \
                  /com/nokia/profiled \
                  com.nokia.profiled.set_profile string:"%s"' % CurrentProfile)

        if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
            SMSsend(SENDERNUMBER,
                    ['NOKIA N900\n',
                     '%s\n' % GetTime(),
                     '-alarm-\n',
                     'alarm sound activated.'],
                    'send SMS message alarm to') 
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      'Alarm sound activated.',
                      'send alarm executed email message to "%s"' % EMAILADDRESS)
        if MESSAGESEND == 'none':
            logging.info('message sending disabled by user setting')

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

def PlayAudio(File):
    """
    Play audio file.
    """ 

    # play audio file in background
    try:
        os.system( '/usr/bin/aplay -q %s/%s &' % (Path, File) )
    except:
        logging.info('(ALARM) failed; no audio file found')
    else:
        logging.info('(ALARM) activated; audio file playing')

    #return True

############################################################################################################################
#   silence phone
############################################################################################################################

def EnableDeviceSilence():
    """
    Make the phone silent for email, instand-messaging, phone ringing & SMS.
    """    

    KeyList = [('email.alert.volume'  , '0'),
               ('im.alert.volume'     , '0'),
               ('ringing.alert.volume', '0'),
               ('sms.alert.volume'    , '0')]

    # get current active phone profile
    Output = os.popen('dbus-send --print-reply \
                      --session --dest=com.nokia.profiled \
                      --type=method_call \
                      /com/nokia/profiled \
                      com.nokia.profiled.get_profile').read()
    E = re.compile('string "(\S+)"')

    if E:
        #logging.debug( 'CurrentProfile = %s' % E.findall(Output) )
        try:
            CurrentProfile = E.findall(Output)[0]
        except:
            logging.error('(SILENCE) could not get current phone profile')
            return
        else:
            logging.info('(SILENCE) current profile is "%s"' % CurrentProfile)
    else:
        logging.error('(SILENCE) could not get current phone profile')
        return

    # set items in current profile
    for Key, Value in KeyList:
        try:
            #logging.debug( 'Setting %s = [%s]' % (Key, Value) )
            os.system( 'dbus-send --session --dest=com.nokia.profiled \
                        --type=method_call \
                        /com/nokia/profiled \
                        com.nokia.profiled.set_value string:"%s" string:"%s" string:"%s"' % (CurrentProfile, Key, Value) )
        except:
            logging.error( '(SILENCE) could not set: %s = "%s"' % (Key, Value) )
            return
        
    logging.info('(SILENCE) phone is silenced in current "%s" profile' % CurrentProfile)

############################################################################################################################
#   run custom scripts
############################################################################################################################

def EnableShellScript():
    """
    Run user editable script.
    """

    if CheckFile(Path, ScriptFile) == True:
        # run script file
        os.system( '%s/%s &' % (Path, ScriptFile) )
        logging.info('script file (%s) executed' % ScriptFile)
        if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
            SMSsend(SENDERNUMBER,
                    ['NOKIA N900\n',
                    '%s\n' % GetTime(),
                    '-script-\n',
                    'user script executed.'],
                    'send SMS message script to') 
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      'User shell script executed.',
                      'send script executed email message to "%s"' % EMAILADDRESS)
        if MESSAGESEND == 'none':
            logging.info('message sending disabled by user setting')
            
    else:
        logging.error('no execution of %s file; not found' % ScriptFile)
        if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
            SMSsend(SENDERNUMBER,
                    ['NOKIA N900\n',
                    '%s\n' % GetTime(),
                    '-script-\n',
                    'user script not found!'],
                    'send SMS message script to')    
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      'ERROR: User shell script not found.',
                      'send script not found email message to "%s"' % EMAILADDRESS)
        if MESSAGESEND == 'none':
            logging.info('message sending disabled by user setting')

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

def EnableDirectShellCommand(Command):
    """
    Run direct shell command.
    """

    try:
        os.system(Command)
    except:
        logging.info('failed to run shell command (%s)' % Command)
        if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
            SMSsend(SENDERNUMBER,
                    ['NOKIA N900\n',
                    '%s\n' % GetTime(),
                    '-shell-\n',
                    'shell command failed.'],
                    'send SMS message shell command to') 
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      'Shell command [%s] failed.' % Command,
                      'send shell command failed email message to "%s"' % EMAILADDRESS)
        if MESSAGESEND == 'none':
            logging.info('message sending disabled by user setting')

    else:
        logging.info('shell command (%s) executed' % Command)
        if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
            SMSsend(SENDERNUMBER,
                    ['NOKIA N900\n',
                    '%s\n' % GetTime(),
                    '-shell-\n',
                    'shell command executed.'],
                    'send SMS message shell command to')
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      'Shell command [%s] executed.' % Command,
                      'send shell command executed email message to "%s"' % EMAILADDRESS)
        if MESSAGESEND == 'none':
            logging.info('message sending disabled by user setting')            

############################################################################################################################
#   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.extend(AddText)

                if i == len(ConfigVars) - 1:
                    SMSsend(SENDERNUMBER,
                            Text,
                            'send SMS message with SMSCON 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

    # create email
    elif Type == 'email':
        Text = 'Available SMS commands for SMSCON are:\n\n'
    
        for UserVar in ConfigVars:
            Value = eval(UserVar)
            Text += '%s= [%s]\n' % (UserVar.ljust(TabMax, ' '), Value)

        EMAILsend(EMAILADDRESS,
                  'Email from Nokia N900',
                  Text,
                  'send SMS commands email message to "%s"' % EMAILADDRESS)

    # create sms & email
    elif Type == 'both':
        
        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.extend(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
            
        Text = 'Available SMS commands for SMSCON are:\n\n'
    
        for UserVar in ConfigVars:
            Value = eval(UserVar)
            Text += '%s= [%s]\n' % (UserVar.ljust(TabMax, ' '), Value)

        EMAILsend(EMAILADDRESS,
                  'Email from Nokia N900',
                  Text,
                  'send SMS commands email message to "%s"' % EMAILADDRESS)

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

def EnableDeviceLock():
    """
    Lock phone after receiving valid SMS command.
    """

    # lock device
    os.system ('run-standalone.sh \
               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('phone is now locked')

    if COMMANDREPLY == 'yes':
        if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
            SMSsend(SENDERNUMBER,
                    ['NOKIA N900\n',
                     '%s\n' % GetTime(),
                     '-lock-\n',
                     'phone has been locked.'],
                     'send SMS message phone locked to')
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      'Nokia N900 phone is locked!',
                      'send phone locked email message to "%s"' % EMAILADDRESS)
        if MESSAGESEND == 'none':
            logging.info('message sending disabled by user setting')  

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

def DisableDeviceLock():
    """
    Unlock phone after receiving valid SMS command.
    """

    # unlock device
    os.system ('run-standalone.sh \
               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('phone is now unlocked')

    if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
        SMSsend(SENDERNUMBER,
                ['NOKIA N900\n',
                 '%s\n' % GetTime(),
                 '-unlock-\n',
                 'phone has been unlocked.'],
                'send SMS message phone unlocked to')
    if MESSAGESEND == 'email' or MESSAGESEND == 'both':
        EMAILsend(EMAILADDRESS,
                  'Email from Nokia N900',
                  'Nokia N900 phone is unlocked.',
                  'send phone unlocked email message to "%s"' % EMAILADDRESS)
    if MESSAGESEND == 'none':
        logging.info('message sending disabled by user setting') 

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

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

    # delay before actual reboot
    Delay = 30

    # force disable resending of messages if set
    global ENABLERESEND
    if ENABLERESEND == 'yes':
        ENABLERESEND = 'no' 

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

    if MESSAGESEND == 'sms' or MESSAGESEND == 'both': 
        SMSsend(SENDERNUMBER,
                ['NOKIA N900\n',
                 '%s\n' % GetTime(),
                 '-reboot-\n',
                 'phone is rebooting.'],
                 'send SMS message phone rebooting to')
    if MESSAGESEND == 'email' or MESSAGESEND == 'both':
        EMAILsend(EMAILADDRESS,
                  'Email from Nokia N900',
                  'Nokia N900 phone is rebooting.',
                  'send phone rebooting email message to "%s"' % EMAILADDRESS)       
    if MESSAGESEND == 'none':
        logging.info('message sending disabled by user setting')
    
    time.sleep(Delay)
    os.system('reboot')

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

def EnablePoweroff():
    """
    Shutdown phone (switch off).
    """

    # delay before actual shutdown
    Delay = 30 

    # force disable resending of messages if set
    global ENABLERESEND
    if ENABLERESEND == 'yes':
        ENABLERESEND = 'no' 

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

    if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
        SMSsend(SENDERNUMBER,
                ['NOKIA N900\n',
                 '%s\n' % GetTime(),
                 '-shutdown-\n',
                 'phone is switched off!!!'],
                 'send SMS message phone shutdown to')
    if MESSAGESEND == 'email' or MESSAGESEND == 'both':
        EMAILsend(EMAILADDRESS,
                  'Email from Nokia N900',
                  'Nokia N900 phone is switched off!!!',
                  'send phone shutdown email message to "%s"' % EMAILADDRESS)
    if MESSAGESEND == 'none':
        logging.info('message sending disabled by user setting')
    
    time.sleep(Delay)
    os.system('poweroff')

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

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

    if TakeCamPicture() == True:
        EMAILsend( EMAILADDRESS,
                   'From "%s" command / Nokia N900' % COM_CAMERA,
                   ['Frontcam picture from Nokia N900 phone:\n',
                    '%s\n' % GetTime()],
                   'send email camera picture send to "%s"' % EMAILADDRESS,
                   '%s/%s' % (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/%s' \
                 % (CamName,
                    CamRes[0],
                    CamRes[1],
                    CamEncoding,
                    LogPath,
                    PhotoName))
    except:
        logging.error('camera picture failed (image capture error)')
        return False
    else:
        time.sleep(Delay)

        if os.path.isfile( '%s/%s' % (LogPath, PhotoName) ) == True:
            logging.info('camera picture acquired')
            return True
        else:
            logging.error('camera picture failed (image 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 connecting
        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 successful' % (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 successful' % (Type, Name) )
            return True

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

def DisableConnection(): 
    """
    Disconnect any current data connection.
    """

    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 phone.
    """

    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) 

    DiscardList = ['wap', 'mms']

    NetworkList = []

    for IAP in IAPlist:
        # get connection type
        Type = os.popen('gconftool -g /system/osso/connectivity/IAP/%s/type' % IAP).read().replace('_INFRA', '').strip('\n')

        if Type == 'GPRS':
            # get gprs_accespointname
            NameAP = os.popen('gconftool -g /system/osso/connectivity/IAP/%s/gprs_accesspointname' % IAP).read().strip('\n')

            # discard unwanted connections
            Found = False
            for Discard in DiscardList:
                if NameAP.find(Discard) >= 0:
                    Found = True

            if Found == False:
                # get connection name & it's IMSI number
                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) ) )

                # add GPRS connection to list
                #logging.debug( '%s, %s, %s, %s, %s' % (Type, IAP, Name, Imsi, NameAP) )
                NetworkList.append( (Type, IAP, Name, Imsi, NameAP) )
            
        elif Type == 'WLAN':
            Name = os.popen('gconftool -g /system/osso/connectivity/IAP/%s/name' % IAP).read().strip('\n')
            # add WLAN connection to list
            #logging.debug( '%s, %s, %s' % (Type, IAP, Name) )
            NetworkList.append( (Type, IAP, Name) )
        
    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

    if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
        SMSsend(SENDERNUMBER,
                ['NOKIA N900\n',
                 '%s\n' % GetTime(),
                 '-battery-\n',
                 'charge = %s%%' % ChargePerc],
                 'send SMS message battery charge to')
    if MESSAGESEND == 'email' or MESSAGESEND == 'both':
        EMAILsend(EMAILADDRESS,
                  'Email from Nokia N900',
                  'Battery charge is at %s%%. (%s)' % ( ChargePerc, GetTime() ),
                  'send battery charge email message to "%s"' % EMAILADDRESS)
    if MESSAGESEND == 'none':
        logging.info('message sending disabled by user setting')

############################################################################################################################
#    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)

        if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
            SMSsend(SENDERNUMBER,
                    ['NOKIA N900\n',
                     '%s\n' % GetTime(),
                     '-ssh-\n',
                     'connection established.'],
                     'send SMS message SSH connection established')
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      'SSH connection established. (%s)' % GetTime(),
                      'send SSH connection established email message to "%s"' % EMAILADDRESS)
        if MESSAGESEND == 'none':
            logging.info('message sending disabled by user setting')

    else:
        logging.error( 'ssh connection failed to "%s" (%s)' % (REMOTEHOST, Output) )
        if StatusSSH() == True:
            StopSSH()

        if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
            SMSsend(SENDERNUMBER,
                    ['NOKIA N900\n',
                     '%s\n' % GetTime(),
                     '-ssh-\n',
                     'connection failed.'],
                     'send SMS message SSH connection failed to')
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      'SSH connection failed. (%s)' % GetTime(),
                      'send SSH connection failed email message to "%s"' % EMAILADDRESS)
        if MESSAGESEND == 'none':
            logging.info('message sending disabled by user setting')
            
###############################

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' or GPSSEND == 'both':
                    SMSsend(SENDERNUMBER,
                            ['NOKIA N900\n',
                             '%s\n' % GetTime(),
                             '-GPS-\n',
                             'failure\n',
                             'Search time = %.1f sec.' % PastTime],
                             'send SMS message GPS timeout failure to')
                        
                if GPSSEND == 'email' or GPSSEND == 'both':
                    EMAILsend(EMAILADDRESS,
                              'From "%s" command / Nokia N900' % COM_TRACKON,
                              'timeout of GPS device after %.1f sec (%s)' % ( PastTime, GetTime() ),
                              'send email timeout of GPS device to "%s"' % EMAILADDRESS)
                    
                if GPSSEND == 'none':
                    logging.info('message sending disabled by user setting')
                    
                # 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 (#%s) acquired (%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',
                                   '%s\n' % GetTime(), 
                                   '-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' or GPSSEND == 'both':
                            GpsText.extend(LinkText)
                            SMSsend(SENDERNUMBER,
                                    GpsText,
                                    'send SMS message GPS coordinate to')

                        if GPSSEND == 'email' or GPSSEND == 'both':
                            if CreateGoogleMap(LatBest, LongBest):
                                EMAILsend( EMAILADDRESS,
                                           'From "%s" command / Nokia N900' % COM_LOCATION,
                                           GpsText,
                                           'send email GPS coordinate map to "%s"' % EMAILADDRESS,
                                           '%s/%s' % (LogPath, MapName) )
                            else:
                                GpsText.extend( ['<br>Failed to create GoogleMap image.<br>'] )
                                EMAILsend(EMAILADDRESS,
                                          'From "%s" command / Nokia N900' % COM_LOCATION,
                                          GpsText,
                                          'send email GPS coordinate to "%s"' % EMAILADDRESS)
                        if GPSSEND == 'none':
                            logging.info('message sending disabled by user setting')

                        # 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',
                               '%s\n' % GetTime(),
                               '-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' or GPSSEND == 'both':
                        GpsText.extend(LinkText)
                        SMSsend(SENDERNUMBER,
                                GpsText,
                                'send SMS message GPS coordinate to')

                    if GPSSEND == 'email' or GPSSEND == 'both':
                        if CreateGoogleMap(Latitude, Longitude):
                            EMAILsend( EMAILADDRESS,
                                       'From "%s" command / Nokia N900' % COM_TRACKON,
                                       GpsText,
                                       'send email GPS coordinate map to "%s"' % EMAILADDRESS,
                                       '%s/%s' % (LogPath, MapName) )
                        else:
                            GpsText.extend( ['<br>Failed to create GoogleMap image.<br>'] )
                            EMAILsend( EMAILADDRESS,
                                       'From "%s" command / Nokia N900' % COM_TRACKON,
                                       GpsText,
                                       'send email GPS coordinate to "%s"' % EMAILADDRESS)
                    if GPSSEND == 'none':
                        logging.info('message sending disabled by user setting')
        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, '%s/%s' % (LogPath, MapName) )
    except:
        logging.error('failed to create GPS coordinate map (retrieve error)')
        return False
    else:
        if os.path.isfile( '%s/%s' % (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.info('no SIM card present; %s will quit!' % DaemonFile)
        sys.exit(1)

    # compare current IMSI code with saved one or more in code file.
    elif CurrentIMSI in 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)    

        # auto unlock phone after new SIM is inserted
        if SIMUNLOCK == 'yes':
            DisableDeviceLock()                 

##        # send reply SMS message (will discard MESSAGESEND user setting!) 
##        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')
        
        if MESSAGESEND == 'sms' or MESSAGESEND == 'both' or MESSAGESEND == 'none':
            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')
        if MESSAGESEND == 'email' or MESSAGESEND == 'both':
            Text = 'WARNING&nbsp&nbspWARNING&nbsp&nbspWARNING<br>' + \
                   'New unknown SIM card has been inserted in phone (%s).\n' % GetTime() + \
                   'New IMSI number = %s\n' % CurrentIMSI + \
                   'IMEI number = %s\n' % GetCode('IMEI') + \
                   'Telecom operator = "%s"' % GetOperatorName()
            EMAILsend(EMAILADDRESS,
                      'Email from Nokia N900',
                      Text,
                      'send SMS message SIM number/IMSI code to "%s"' % EMAILADDRESS)

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

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

    if CheckFile(Path, CodeFile) == True:
        try:
            # read code file
            List = []
            f = open('%s/%s' % (Path, CodeFile), 'r+')
            List = f.readlines()
            f.close()

            # create list of found IMSI codes
            for n, Code in enumerate(List):
                List[n] = Code.strip('\n').strip(' ') 

            CodeList = [] 
            for L in List:
                if L != '' and L != '\n':
                    CodeList.append(L) # = ['XXXXXXXXXXXXX', 'XXXXXXXXXXXXX', etc.]
            #logging.debug('CodeList = %s' % CodeList)
                
        except:
            logging.error('failed to read valid IMSI code from "%s" file' % CodeFile)
            return ['000000000000000']
        
        else:
            NumOfCodes = len(CodeList)
            if NumOfCodes == 1:
                logging.info('reading 1 valid IMSI code from "%s" file' % CodeFile)
            else:
                logging.info( 'reading %s IMSI codes from "%s" file' % (NumOfCodes, CodeFile) )

            return CodeList 
    
    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('%s\n' % 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' % CodeFile)

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

        logging.info( 'new IMSI code (%s) saved to "%s" file' % (Code, CodeFile) )
    else:
        
        f = open('%s/%s' % (Path, CodeFile), 'a')
        f.writelines('%s\n' % Code)
        f.close()

        logging.info( 'added IMSI code (%s) to "%s" file' % (Code, CodeFile) )

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

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('could not 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('could not 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 phone'],
                         'send SMS message test to')
                ExitDaemon()

            elif Func == 'sms2': # test mode for sms
                SMSsend(SENDERNUMBER,
                        ['Test SMS from Nokia N900 phone'],
                         '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 / Nokia N900',
                              'Email sending from Nokia N900 phone is successful!',
                              'send test email message to "%s"' % EMAILADDRESS)
                elif Func == 'email2': # test mode2 for email + frontcam image
                    if TakeCamPicture() == True:
                        EMAILsend( EMAILADDRESS,
                                   'Test email / Nokia N900',
                                   'Frontcam picture from Nokia N900 phone:<br>' + \
                                   '%s<br>' % GetTime(),
                                   'send email camera picture send to "%s"' % EMAILADDRESS,
                                   '%s/%s' % (LogPath, PhotoName) )

                RestoreNetwork()
                EnableConnectBehaviour(ConnectionBehaviour)
                ExitDaemon()      

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

            elif Func == 'script': # test mode for script
                EnableShellScript()
                ExitDaemon()

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

        # /* ONLY FOR DEVELOPMENT USAGE */
        elif Mode == '-comtest':
            Command = sys.argv[2]
            #logging.debug('Command = [%s]' % Command)
            
            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,
                           COM_CUSTOM,
                           COM_SHELL,
                           COM_ALARM,
                           COM_RESTART]

            logging.warning('simulating received SMS message with "%s" command' % Command)
            
            if Command in CommandList:
                ProcessCommand(Command, SENDERNUMBER)
                return
            else:
                logging.error( '"%s" was not a valid %s command' % (Command, NAME.upper()) )
                ExitDaemon()

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

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

def CheckAutoloader():
    """
    Check if smscon auto-loads at system boot.
    """

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

############################################################################################################################
#   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)

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

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':
            if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
                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')     
            if MESSAGESEND == 'email' or MESSAGESEND == 'both':
                EMAILsend(EMAILADDRESS,
                          'Email from Nokia N900',
                          'Phone keyboard is being used\n' + 'Keyboard is %s' % KeyState,
                          'send keyboardslider used email message to "%s"' % EMAILADDRESS)
            if MESSAGESEND == 'none':
                logging.info('message sending disabled by user setting')

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

def BATTERYstatus(BarLevel, BarLevelMax):
    """
    Get the battery charge level (in percentage). 
    """

    if EnableBatteryDetect == True:
        ChargePercMin = 10

        ChargePerc  = dev_obj.GetProperty('battery.charge_level.percentage')  # charge amount of battery in percentage
        ChargeState = dev_obj.GetProperty('battery.rechargeable.is_charging') # charging: 1 / discharging: 0
        
        logging.warning('phone battery is now at %s%% charge level' % ChargePerc)

        # only when battery is discharging
        if ChargeState == 0: 
            Text = ['NOKIA N900\n',
                    '%s\n' % GetTime(),
                    '-battery warning-\n',
                    'Phone battery is now\n']

            if ChargePerc <= ChargePercMin:
                Text.extend('almost empty! (%s%%).' % ChargePerc)
            else:
                Text.extend('at %s%% charge level.' % ChargePerc)

            if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
                SMSsend(SENDERNUMBER,
                        Text,   
                        'send SMS message battery level warning to')
            if MESSAGESEND == 'email' or MESSAGESEND == 'both':
                EMAILsend(EMAILADDRESS,
                          'Email from Nokia N900',
                          Text,
                          'send battery level warning email message to "%s"' % EMAILADDRESS)
            if MESSAGESEND == 'none':
                logging.info('message sending disabled by user setting')

############################################################################################################################
#   remote host file checking (remote smscon trigger)
############################################################################################################################

def EnableCheckHost(CHECKHOST):
    """
    Remote host file checking for triggering smscon
    """

    global CheckHost
    global CheckHostPrev

    CommandList = {'01' : COM_CHECK,
                   '02' : COM_REBOOT,
                   '03' : COM_POWEROFF,
                   '04' : COM_POWER,
                   '05' : COM_LOCATION,
                   '06' : COM_REMOTEON,
                   '07' : COM_REMOTEOFF,
                   '08' : COM_CAMERA,
                   '09' : COM_CALL,
                   '10' : COM_LOCK,
                   '11' : COM_UNLOCK,
                   '12' : COM_TRACKON,
                   '13' : COM_TRACKOFF,
                   '14' : COM_CUSTOM,
                   '15' : COM_SHELL,
                   '16' : COM_ALARM,
                   '17' : COM_RESTART}  

    #logging.debug('(CHECKHOST) opening remote host file')
    try:
        Url = urllib.urlopen('http://%s' % CHECKHOST)
        Command = Url.read(2)
        Url.close()
    except:
        logging.error('(CHECKHOST) failed to open remote file at "%s"' % CHECKHOST)
        CheckHost = False
    else:
        #logging.debug('(CHECKHOST) Command = [%s]' % Command)
        if Command in CommandList:
            if CheckHost == False or CheckHost == None:
                CheckHost = True
                CheckHostPrev = CheckHost

                logging.info( '(CHECKHOST) %s is activated (command "%s") by remote host file' % (DaemonFile, CommandList[Command]) )
                ProcessCommand(CommandList[Command], SENDERNUMBER)

        elif Command == '00':
            CheckHost = False
            if CheckHostPrev == True:                
                logging.info( '(CHECKHOST) %s is deactivated (%s) by remote host file' % (DaemonFile, Command) )
            CheckHostPrev = CheckHost

        elif Command == '-1':
            if CheckHost == False:
                CheckHost = True
                CheckHostPrev = CheckHost

                logging.info( '(CHECKHOST) %s will restart (%s) by remote host file' % (DaemonFile, Command) )
                try:
                    os.system( '%s/%s -restart' % (Path, NAME) )
                except:
                    logging.error('(CHECKHOST) failed to kill %s' % DaemonFile)

    return True # run it continiously

############################################################################################################################
#    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:
        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 line (%s = %s) of "%s" file' % (Variable.rstrip(' '), Value, ConfigFile) )
                        f.close()
                        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) )
                                    f.close()
                                    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 line (%s = %s) of "%s" file' % (Variable.rstrip(' '), Value, ConfigFile) )
                            f.close()
                            ExitDaemon()

        logging.info('successfully loaded "%s" file' % ConfigFile)
        #ShowSMScommands('log')
        
    f.close()
else:
    logging.warning("%s doesn't exist" % 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:
        logging.warning('(GPS) unknown interval setting (%s sec.); set to default 60 sec.' % GPSINTERVAL)
        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')

    if KEYBOARDDETECT == 'yes':
        # 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')

    if AUTOBATREPORT == 'yes':
        # 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()

    if ENABLECHECKHOST == 'yes':
        EnableCheckHost(CHECKHOST)
        gobject.timeout_add(CHECKTIME * 60000,
                            EnableCheckHost,
                            CHECKHOST)

    gobject.MainLoop().run()

except Exception, e:
    logging.critical('<<< SMSCON FATAL ERROR:\n%s >>>' % e)
    #print 'SMSCON FATAL ERROR:\n%s' % e
    
    if MESSAGESEND == 'sms' or MESSAGESEND == 'both':
        SMSsend(SENDERNUMBER,
                ['NOKIA N900\n',
                '%s\n' % GetTime(),
                '-fatal error-\n',
                'smscon_daemon has crashed!'],
                'send SMS message fatal error to')
    if MESSAGESEND == 'email' or MESSAGESEND == 'both':
        EMAILsend(EMAILADDRESS,
                  'Email from Nokia N900',
                  'Smscon_daemon has crashed on Nokia N900 phone!\n' + 'Error:\n' + '%s' % e,
                  'send fatal error email message to "%s"' % EMAILADDRESS)
    if MESSAGESEND == 'none':
        logging.info('message sending disabled by user setting')
        
    ExitDaemon()

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

"""

 0.8.1-1
 FIX: shutdown & reboot routine (EnablePoweroff & EnableReboot) give internal error.
 NEW: added remote activation of smscon_daemon; content of file CHECKHOST (hostname+filename)
      on server will be checked every CHECKTIME minutes. If other then 0 it will execute the corresponding command.

 0.8.0-1
 NEW/CHANGE: expanded user setting MESSAGESEND ('sms', 'email', 'both' & 'none').
             removed DISABLESMS user setting. This is superseded by MESSAGESEND user setting!
             SIM card change SMS always will be send regardless of MESSAGESEND setting.
 NEW: added smscon command (RESTART); user can restart smscon_daemon by SMS command.
 NEW: added smscon command (COM_ALARM); phone will play user configurable sound file (only WAV!) when command is received. 
 NEW: smscon_daemon sends SMS/email message if smscon_daemon crashes.
 CHANGE: changed user setting ENABLERESEND ('yes', 'no'). Resending of messages will again be experimental feature!!!
 CHANGE: removed MAXRESEND user setting (didn't work properly).
 CHANGE: some log text changed/added.
 CHANGE: removed updatechecker during installation of smscon (failed to work previously).
 CHANGE: some changes in GPS message sending code.
 CHANGE: AUTODEVICELOCK message is only send if COMMANDREPLY is enabled.
 FIX: small bug in SMSsend().
 FIX: bug in GPSINTERVAL handeling; GPSINTERVAL was always set at 60 sec. regardless of user setting.

 0.7-2
 FIX: bug in RestoreNetwork(); (None must be 'NONE').
 FIX: bug in heckArguments(); EnableScript() must be EnableShellScript().
 EXPERIMENTAL: updatechecker to save "smscon_config" file when updating to new version.
 
 0.7-1
 NEW: multiple IMSI codes are allowed in smscon_code file; use new option "smscon -add imsi" to store current IMSI code in smscon_code file.
 NEW: added new user option (DISABLESMS); WARNING: disables ALL SMS sending in smscon program.
 NEW: added new smscon command (COM_SHELL); run directly shell commands. (no check & feedback from command!).
 NEW: added new user option (SIMUNLOCK); auto unlock phone after new SIM is inserted (to fool a possible thief of your phone).
 NEW: added new user option (SILENCEDEVICE); disable all phone sounds (calendar alarm, clock, email alert, IM alert, ringing alert & SMS alert) if smscon is active.
 NEW/CHANGE: new user option (AUTOBATREPORT); user can enable/disable sending automatically a SMS message when smscon is activated & device battery is depleting.
 CHANGE: when initializing a new smscon_config file all user settings wil be set default to 'no'.
 REFACTORING: some parts in both scripts.
 FIX: better number handeling in SMSrecieve() routine (thanks to maemo.org user!).
 FIX: small bug in GSMmodem() in smscon_daemon script.
 FIX: small text bug in smscon script

 EXPERIMENTAL/CHANGE: when smscon fails to send SMS message (no cell reception) it will retry after 900 seconds.
 EXPERIMENTAL/FIX: change in getting the right GPRS connections; MMS & WAP will be discarded in available GPRS network list.
    
 0.5-9
 NEW: added new smscon command: COM_CUSTOM to run a user configurable shell script (/opt/smscon/smscon_script)
 NEW: if user setting REMOTEUSER, REMOTEPASSWORD is empty, "Remoteon" & "Remoteoff" function is disabled in smscon. 
 NEW: smscon will send automatically a SMS message when smscon is activated & device battery is depleting.
 CHANGE: added new options in smscon menu.
 CHANGE: added time notation in GPS email & SMS messages.
 CHANGE: improved the way smscon reads user settings from smscon_config file.
 EXPERIMENTAL/CHANGE: when smscon fails to send SMS message (no cell reception) it will retry after 900 seconds.
 EXPERIMENTAL/FIX: change in getting the right GPRS connections; MMS & WAP will be discarded in available GPRS network list.
 FIX: picture of frontcam & GoogleMap image are named wrong & placed therefore in wrong directory.
 FIX: smscon fails to send SMS message when message is too long (> 160 chars.)
 FIX: "smscon -test2" test function would fail if a valid smscon command has spaces in it. /*Developer usage only*/

 0.5-8 /* UNRELEASED VERSION */
 NEW: smscon will send automatically a SMS message when smscon is activated & device battery is depleting.
 CHANGE: added time notation in GPS email & SMS messages.
 EXPERIMENTAL/FIX: change in getting the right GPRS connections; MMS will be discarded in available network list. 
 FIX: smscon fails to send SMS message when message is too long.
 FIX: "smscon -test2" test function would fail if a valid smscon command has spaces in it.

 0.5-7
 FIX: activation of the current active/available GPRS connection could fail in specific cases (bug in acquiering the syntax correct 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)
"""

