#!/usr/local/bin/python
# $Id: GoSMS.py,v 1.3 2005/06/02 09:16:39 cvs Exp $

import socket
import md5
import time
import re

# prepare regular expression
statusmatch = r'^[0-9]{3}.*'
statusparser = re.compile(statusmatch)
challengematch = r'<(.+)>'
challengeparser = re.compile(challengematch)
challenge = ''

verbouse = 0



# Establishes communication
def connect() :

  out = ''
  out += '### CONNECT\n'

  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
  s.connect(('137.208.7.72',14289))
  s.setblocking(1)
  s.settimeout(30.0)

  t = hear(s)
  out += t

  global challenge
  challenge = challengeparser.split(t)[1] 

  return out, s



# Reads from the server
def hear(s, repeat=1) :

  out = ''
  
  if repeat <= 0 :
    return ''

  c = ''
  l = ''
  t = ''

  while 1 : # collect lines until status 
    l = ''  
    while 1 : # collect characters of a line
      c = s.recv(1)
#      print c,
      l = l + c
      if c == '\n' :
        break
  
    out += '>>> %s' % (l)  
    t = t + l
  
    status = statusparser.match(l)
    if status :
      out += '---\n'
      break
      
#  return t + hear(s, repeat - 1)
  return out + hear(s, repeat - 1)



# Writes to the server
def say(s, t) :

  out = ''
  out += '<<< SAY: %s\n' % (repr(t))
  t += '\n'
  n = s.send(t)
  if n != len(t) :
    out += 'WARNING: data dropped while sending.\n'
    raise "Data dropped"
  return out



# Login procedure
def authenticate(s, app, level, area, secr1, secr2, challenge):

  out = ''
  out += '### AUTHENTICATE\n'
  out += '#   Using challenge: [%s]\n' % (challenge)

  t1 = '%s:%s:%s:%s:%s' % (app, level, area, secr1, challenge)
  t2 = '%s:%s:%s:%s:%s' % (app, level, area, secr2, challenge)
  key1 = md5.new(t1).hexdigest()
  key2 = md5.new(t2).hexdigest()

  t = 'app %s %s %s %s %s' % (app, level, area, key1, key2)

  out += say(s, t)
  out += hear(s,5)

  return out



# shell mode
def interactive() :

  out = ''

  # prepare socket connection
  res, s = connect()
  out += res

  # authenticate 
  authenticate(s, 'pass', '1', 'smsgw', 'challenge', 'challenge', challenge)
  
  out += """
      client.py Interactive mode
      ---------
      Status:   Z61
      SMS send: SMS [username] [infochannel] [message text]
      Quit:     exit
  \n"""
  
  while 1 :
    print out
    out = ''
    cmd = raw_input("Command:")
    out += 'Command: %s\n' % (cmd)
    out += say(s, cmd)
    if cmd == 'exit' :
      break
    out += hear(s)
  
  s.close()
  return out



# Clear message
def clearSM(message) :
  message=message.replace('\r', ' ')	# My ipaq h6340 phone interprets that as 'space' character, not as return.
  message=message.replace('\n', ' ')	# Newline (0x0a) terminates commands for the server. We have to ommit that (or change the protocol).
  message=message.replace("'", '?')	# Server would die on single quotes. @@@ SQL Server could handle this better.
  return message



# SMS Multipart send
def sendSMS(user, channel, message, maxnum=9, sender='') :

  out = ''

  message = clearSM(message)
  msglst = []

  mlen=140
  hlen=16
  l=mlen-hlen

  seq=range(0, len(message), l)

  # Limit number of messages that can be sent, 9 is hard limit!
  if (len(seq) > maxnum) or (len(seq) > 9) :
    seq=seq[0:maxnum]

  num = 1
  for i in seq :

    # Format: 
    #         1111111
    # 123456790123456 (hlen=16)
    # UUUUUUUUUU#C/N:
    # U = user / C = count / N = number 

    if sender != '' :
      senderstring = '%-11.11s#%d/%d:' % (sender[0:11], num, len(seq))
    else :
      senderstring = '#%d/%d:' % (num, len(seq))

    msglst.append(senderstring + message[i:i+l])
    num = num + 1

  out += 'Sending %d messages to user %s on channel %s.\n' % (len(seq), user, channel)
  res = ''

  msglst.reverse()

  for msg in msglst :
    res += sendSMS160(user, channel, msg)    
 
  out += res

  return out 



# Encapsulated SMS send
def sendSMS160(user, channel, message) :

  out = ''

  message = clearSM(message)

  # prepare socket connection
  res, s = connect()
  out += res

  # authenticate
  out += authenticate(s, 'pass', '1', 'smsgw', 'challenge', 'challenge', challenge)

  cmd = "SMS %s %s %s" % (user, channel, message)
  out += say(s, cmd)
  out += hear(s)

  cmd = "Z61"
  out += say(s, cmd)
  out += hear(s)
 
  cmd = 'exit'
  out += say(s, cmd)

  return out



if __name__ == '__main__' :
  verbouse = 1
  interactive()

