From 61d918678911cdc4ce81af68a0840932bbc47a44 Mon Sep 17 00:00:00 2001 From: Paul Lecuq Date: Mon, 14 Mar 2016 16:34:11 +0100 Subject: [PATCH] Multi-lists support Code refactor and clean --- .gitignore | 1 + config.py.sample | 18 ++- mailbox.csv.sample | 3 + smsgateway.py | 348 ++++++++++++++++++++++++++++++--------------- 4 files changed, 245 insertions(+), 125 deletions(-) create mode 100644 mailbox.csv.sample diff --git a/.gitignore b/.gitignore index 3ab6154..0a3db34 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ target/ # Config files config.py +mailbox.csv \ No newline at end of file diff --git a/config.py.sample b/config.py.sample index 38ea29d..9db6772 100644 --- a/config.py.sample +++ b/config.py.sample @@ -1,12 +1,10 @@ -smshost="192.168.1.100" -smsusername="admin" -smspassword="admin" -smsrecipients=["0660066006","0620022002"] -smssize=160 -smstemplate="Nouveau mail de %s. Object %s. Message ..." +smshost = "192.168.1.100" +smsusername = "admin" +smspassword = "admin" +smssize = 160 +smstemplate = "Nouveau mail de %s. Object %s. Message ..." +smsformat = "ascii" -mailboxserver="smtp.domain.net" -mailboxlogin="charlie@domain.net" -mailboxpassword="charlieroot" +mailboxes = "/home/paul/PycharmProjects/smsgateway/mailbox.csv" -pidfile='/run/lock/smsgateway.pid' \ No newline at end of file +pidfile = "/run/lock/smsgateway.pid" diff --git a/mailbox.csv.sample b/mailbox.csv.sample new file mode 100644 index 0000000..2ec8d8e --- /dev/null +++ b/mailbox.csv.sample @@ -0,0 +1,3 @@ +# mail, password, number1, number2, number3, ... +mailhost.test.com,test1@test.com,passwordtest1,0606060606,0602020202 +mailhost.test.com,test2@test.com,passwordtest2,0604040404,0603030303 \ No newline at end of file diff --git a/smsgateway.py b/smsgateway.py index 306cb5d..4b0f100 100755 --- a/smsgateway.py +++ b/smsgateway.py @@ -8,138 +8,256 @@ import telnetlib import config import fcntl import sys -import unicodedata from email.header import decode_header from messaging.sms import SmsSubmit -def fetch_unread_mails(): - """Fetch unread emails on specific mailbox, and returns some fields""" - mail = imaplib.IMAP4_SSL(config.mailboxserver) - mail.login(config.mailboxlogin,config.mailboxpassword) - mail.list() - status,messages = mail.select("INBOX") - mails=[] +def csv_config_parser(mailboxes): + params = [] + with open(mailboxes) as f: + for line in f: + if "#" in line: + pass + else: + tmp_str = line.strip("\n").split(",") + params.append(tmp_str) + return params - n=0 - retcode, messages = mail.search(None, '(UNSEEN)') - if retcode == 'OK': - for num in messages[0].split(): - n=n+1 - typ,data = mail.fetch(num,'RFC822') - for response_part in data: - if isinstance(response_part,tuple): - original = email.message_from_string(response_part[1]) - mailfrom = original['From'] - if "<" in mailfrom: - mailfrom = mailfrom.split('<')[1].rstrip('>') - mailsubject = original['Subject'] - mailsubject = decode_header(mailsubject) - default_charset = 'ASCII' - mailsubject=''.join([ unicode(t[0], t[1] or default_charset) for t in mailsubject ]) - mails.append([mailfrom,mailsubject]) - return mails -def clearallsms(): - """Clears all stored SMS on Portech like gateways""" - try: - count=0 - tn = telnetlib.Telnet(config.smshost,23) - tn.read_until("username: ") - tn.write(config.smsusername + "\r\n") - tn.write(config.smspassword + "\r\n") - tn.read_until("user level = admin.") - tn.write("module1\r\n") - tn.read_until("got!! press 'ctrl-x' to release module 1.") - while count<100: - tn.write("AT+CMGD="+str(count)+"\r\n") - count=count+1 - tn.read_until("\r\n") - tn.close() - except: - print("Unexpected error:", sys.exc_info()[0]) - raise +def fetch_unread_mails(mailboxserver, mailboxlogin, mailboxpassword): + """ + Fetch unread emails on specific mailbox, and returns some fields + :param mailboxserver: + :param mailboxlogin: + :param mailboxpassword: + :return str: + """ + mail = imaplib.IMAP4_SSL(mailboxserver) + mail.login(mailboxlogin, mailboxpassword) + mail.list() + mail.select("INBOX") -def formatsms(message): - """Strip SMS if longer than config.smssize""" - if len(message) > config.smssize: - message = message[:config.smssize] - return message + mails = [] -def imap2sms(sender,subject): - """Uses a template to make a short message from email fields""" - sms=config.smstemplate % (sender,subject) - return sms + n = 0 + returncode, messages = mail.search(None, '(UNSEEN)') + if returncode == 'OK': + for num in messages[0].split(): + n += 1 + typ, data = mail.fetch(num, 'RFC822') + for response_part in data: + if isinstance(response_part, tuple): + original = email.message_from_string(response_part[1]) + mailfrom = original['From'] + if "<" in mailfrom: + mailfrom = mailfrom.split('<')[1].rstrip('>') + mailsubject = original['Subject'] + mailsubject = decode_header(mailsubject) + default_charset = 'ASCII' + mailsubject = ''.join([unicode(t[0], t[1] or default_charset) for t in mailsubject]) + mails.append([mailfrom, mailsubject]) + return mails -def pduformat(phonenumber,message): - """Formats SMS using pdu encoding""" - sms = SmsSubmit(phonenumber, message) - pdu = sms.to_pdu()[0] - pdustring=pdu.pdu - pdulength=pdu.length - # debug output - #print(phonenumber, message) - #print(pdu.length, pdu.pdu) - return pdustring,pdulength -def sendsms(pdustring,pdulenght): - """Send SMS using telnetlib, returns exception when issues with telnet communication""" - try: - time.sleep(2) - tn = telnetlib.Telnet(config.smshost,23) - tn.read_until("username: ") - tn.write(config.smsusername + "\r\n") - tn.write(config.smspassword + "\r\n") - tn.read_until("user level = admin.") - tn.write("state1\r\n") - tn.read_until("module 1: free.\r\n]") - tn.write("module1\r\n") - tn.read_until("got!! press 'ctrl-x' to release module 1.") - tn.write("AT+CMGF=0\r\n") - tn.read_until("0\r\n") - tn.write('AT+CMGS=%s\r\n' % pdulength) - tn.read_until("> ") - tn.write("%s\x1A" % pdustring) - tn.read_until("+CMGS") - tn.close() - except: - print("Unexpected error:", sys.exc_info()[0]) - raise +def clear_all_sms(): + """ + Clears all stored SMS on Portech like gateways + :return: None + """ + try: + count = 0 + tn = telnetlib.Telnet(config.smshost, 23) + tn.read_until("username: ") + tn.write(config.smsusername + "\r\n") + tn.write(config.smspassword + "\r\n") + tn.read_until("user level = admin.") + tn.write("module1\r\n") + tn.read_until("got!! press 'ctrl-x' to release module 1.") + while count < 100: + tn.write("AT+CMGD=" + str(count) + "\r\n") + count += 1 + tn.read_until("\r\n") + tn.close() + except: + print("Unexpected error:", sys.exc_info()[0]) + raise + + +def resize_ascii_sms(message): + """ + Strip message if longer than config.smssize + """ + value = config.smssize-3 + if len(message) > value: + message = message[:value] + return message + + +def resize_pdu_sms(message): + """ + Strip SMS if longer than config.smssize + :param message: + :return: str + """ + if len(message) > config.smssize: + message = message[:config.smssize] + return message + + +def sms_template(sender, subject): + """ + Uses a template to make a short message from email fields + :param sender: str + :param subject: str + :return: + """ + text = config.smstemplate % (sender, subject) + return text + + +def imap2sms(conf): + for list in conf: + username = list[0] + password = list[1] + mailserver = list[2] + numbers = [] + i = 3 + while i < len(list): + numbers.append(list[i]) + i += 1 + + mails = fetch_unread_mails(username, password, mailserver) + for number in numbers: + for mail in mails: + sender = mail[0] + subject = mail[1] + if config.smsformat == "pdu": + sms = resize_pdu_sms(sms_template(sender, subject)) + pdustring, pdulength = pduformat(number, sms) + send_pdu_sms(pdustring, pdulength) + elif config.smsformat == "ascii": + sms = resize_ascii_sms(sms_template(sender, subject)) + send_ascii_sms(number, sms) + +def pduformat(phonenumber, message): + """ + Formats SMS using pdu encoding + """ + sms = SmsSubmit(phonenumber, message) + pdu = sms.to_pdu()[0] + pdustring = pdu.pdu + pdulength = pdu.length + # debug output + # print(phonenumber, message) + # print(pdu.length, pdu.pdu) + return pdustring, pdulength + + +def send_ascii_sms(phonenumber, sms): + """ + Send SMS using telnetlib, returns exception when issues with telnet communication + """ + decoded_sms = sms.encode("ascii", "ignore") + try: + time.sleep(2) + tn = telnetlib.Telnet(config.smshost, 23) + tn.read_until("username: ") + tn.write(config.smsusername + "\r\n") + tn.write(config.smspassword + "\r\n") + tn.read_until("user level = admin.") + tn.write("state1\r\n") + tn.read_until("module 1: free.\r\n]") + tn.write("module1\r\n") + tn.read_until("got!! press 'ctrl-x' to release module 1.") + tn.write("AT+CMGF=1\r\n") + tn.read_until("0\r\n") + tn.write('AT+CMGS=%s\r\n' % phonenumber) + tn.read_until("> ") + tn.write("%s\x1A" % decoded_sms) + tn.read_until("+CMGS") + tn.close() + except: + print("Unexpected error :", sys.exc_info()[0]) + raise + + +def send_pdu_sms(pdustring, pdulenght): + """ + Send SMS using telnetlib, returns exception when issues with telnet communication + :param pdustring: is the converted sms to pdu format + :param pdulenght: is the size of the pdustring + """ + try: + time.sleep(2) + tn = telnetlib.Telnet(config.smshost, 23) + tn.read_until("username: ") + tn.write(config.smsusername + "\r\n") + tn.write(config.smspassword + "\r\n") + tn.read_until("user level = admin.") + tn.write("state1\r\n") + tn.read_until("module 1: free.\r\n]") + tn.write("module1\r\n") + tn.read_until("got!! press 'ctrl-x' to release module 1.") + tn.write("AT+CMGF=0\r\n") + tn.read_until("0\r\n") + tn.write('AT+CMGS=%s\r\n' % pdulength) + tn.read_until("> ") + tn.write("%s\r\n\x1A" % pdustring) + tn.read_until("+CMGS") + tn.close() + except: + print("Unexpected error:", sys.exc_info()[0]) + raise + def usage(): - """Prints usage""" - usage="smsgateway.py subcommands : \n\n%s imap2sms\n%s sms \n%s clearallsms\n" % (sys.argv[0],sys.argv[0],sys.argv[0]) - return usage + """ + Prints usage + """ + usagetext = "smsgateway.py subcommands : \n\n%s imap2sms\n%s sms \n%s clearallsms\n" % ( + sys.argv[0], sys.argv[0], sys.argv[0]) + return usagetext + + +def debug(): + """ + Debug Function + :return: + """ + return True + fh = open(config.pidfile, 'w') try: - fcntl.lockf(fh, fcntl.LOCK_EX | fcntl.LOCK_NB) + fcntl.lockf(fh, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: - # another instance is running - print 'Error: Another instance is running...' - sys.exit(0) + # another instance is running + print 'Error: Another instance is running...' + sys.exit(0) if len(sys.argv) > 1: - if sys.argv[1] == "clearallsms": - clearallsms() - elif sys.argv[1] == "sms": - if (len(sys.argv)==4): - phonenumber=sys.argv[2] - sms=sys.argv[3] - pdustring,pdulength=pduformat(phonenumber,sms) - sendsms(pdustring,pdulength) - else: - print(usage()) - elif sys.argv[1] == "imap2sms": - mails = fetch_unread_mails() - for phonenumber in config.smsrecipients: - for mail in mails: - sender=mail[0] - subject=mail[1] - sms=formatsms(imap2sms(sender,subject)) - pdustring,pdulength=pduformat(phonenumber,sms) - sendsms(pdustring,pdulength) + if sys.argv[1] == "clearallsms": + clear_all_sms() + + elif sys.argv[1] == "sms": + if len(sys.argv) == 4: + phonenumber = sys.argv[2] + sms = sys.argv[3] + pdustring, pdulength = pduformat(phonenumber, sms) + if config.smsformat == "pdu": + send_pdu_sms(pdustring, pdulength) + elif config.smsformat == "ascii": + send_ascii_sms(phonenumber, sms) + else: + print(usage()) + + elif sys.argv[1] == "imap2sms": + config_params = csv_config_parser(config.mailboxes) + imap2sms(config_params) + + elif sys.argv[1] == "debug": + print debug() else: - print(usage()) - exit(1) + print(usage()) + exit(1)