+++ /dev/null
-"""SMS Transports by phd, corvin, r_sabitov"""
-# -*- coding: koi8-r -*-
-
-
-import socket
-
-from m_lib.opstring import koi2win
-from m_lib.rus.rus2lat import rus2lat
-
-
-debug_level = 0
-use_syslog = 0
-
-def debug(level, message):
- if level <= debug_level:
- if use_syslog:
- import syslog
- syslog.syslog(message)
- import sys
- sys.stderr.write("%s\n" % message)
-
-
-smsSize = 160
-
-#
-# Base transport classes
-#
-
-class Transport(object):
- def __init__(self, phone, message):
- self.phone = phone
- self.message = message
-
- def calcText(self, maxsize=smsSize, transFunction=None):
- "Recode text on demand and truncate the result"
-
- text = self.message
- if transFunction:
- text = transFunction(text)
- text = text[:maxsize - 1]
- return text
-
- def calcExpiration(self):
- from mx import DateTime
- return DateTime.now() + DateTime.DateTimeDelta(0, 4)
-
-class CP1251(object):
- "Mixin that converts input text from koi8 to cp1251"
-
- def calcText(self, maxsize=smsSize):
- text = super(CP1251, self).calcText(maxsize=maxsize)
- return koi2win(text)
-
-#
-# HTTP transport
-#
-
-class HTTPTransport(Transport):
- "Base class for HTTP transport"
-
- HTTPHeaders = {
- 'User-Agent': 'Mozilla/5.0 (X11; U; Linux 2.2.19 i686; en-US; rv:0.9) Gecko/20010507',
- 'Accept': '*/*'
- }
-
- referer = ''
-
- def post(self, dict):
- import urllib, urlparse
- _none, host, uri, _none, _none, _none = urlparse.urlparse(self.url)
- postdata = urllib.urlencode(dict)
-
- methodFunc = getattr(self, self.method)
-
- try:
- reply = methodFunc(postdata, host, uri)
- except socket.error:
- ret = 0
- else:
- html = reply[2].fp.read()
- debug(2, html)
-
- if html.find(self.ok_match) >= 0:
- ret = 1
- else:
- ret = 0
-
-
- _debug = 'msg to %s via %s (%s)' % \
- (self.phone, self.__class__.__name__, ret)
- debug(1, _debug)
-
- return ret
-
- def GET(self, postdata, host, uri, port=80, proxy_host=None, proxy_port=None):
- "HTTP method GET"
-
- if postdata:
- uri = uri + "?" + postdata
-
- import httplib
- if proxy_host:
- http = httplib.HTTP(proxy_host, proxy_port)
- http.set_debuglevel(debug_level)
- http.putrequest("GET", 'http://%s%s' % (host, uri))
- else:
- http = httplib.HTTP(host, port)
- http.set_debuglevel(debug_level)
- http.putrequest("GET", uri)
-
- http.putheader("Host", host)
- if self.referer:
- http.putheader("Referer", self.referer)
-
- for name, val in self.HTTPHeaders.items():
- http.putheader(name, val)
-
- http.endheaders()
-
- reply = http.getreply()
- reply[2].fp = http.getfile()
- return reply
-
- def POST(self, postdata, host, uri, port=80, proxy_host=None, proxy_port=None):
- "HTTP method POST"
-
- import httplib
- if proxy_host:
- http = httplib.HTTP(proxy_host, proxy_port)
- http.set_debuglevel(debug_level)
- http.putrequest("POST", 'http://%s%s' % (host, uri))
- else:
- http = httplib.HTTP(host, port)
- http.set_debuglevel(debug_level)
- http.putrequest("POST", uri)
-
- http.putheader("Host", host)
- if self.referer:
- http.putheader("Referer", self.referer)
- http.putheader('Content-Type', 'application/x-www-form-urlencoded')
- http.putheader('Content-Length', str(len(postdata)))
-
- for name, val in self.HTTPHeaders.items():
- http.putheader(name, val)
-
- http.endheaders()
- http.send(postdata)
-
- reply = http.getreply()
- reply[2].fp = http.getfile()
- return reply
-
-class CP1251HTTPTransport(CP1251, HTTPTransport):
- pass
-
-#
-# SNPP transport
-#
-
-class SNPPTransport(Transport):
- "Base class for SNPP transport"
-
- host = 'localhost'
- port = 444
- ok_match = '250 Message Sent Successfully'
-
- def post(self, dict):
- # raw snpp hack w/o error checking
- try:
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.connect(('www.extel-gsm.com', 4444))
- sock_file = sock.makefile('r')
-
- # 220 Extel Mobile Communications. SNPP Gateway v.0.2.15; Wed May 30 00:43:56 2001
- # 220 SNPP Gateway Ready
- debug(2, sock_file.readline())
- debug(2, sock_file.readline())
-
- sock.send('PAGE %s\r\n' % dict['phone'])
- # 250 Pager ID Accepted
- debug(2, sock_file.readline())
-
- sock.send('DATA\r\n')
- # 354 Begin Input; End with <CRLF>'.'<CRLF>
- debug(2, sock_file.readline())
-
- sock.send('%s\r\n' % dict['message'])
- sock.send('.\r\n')
- # 250 Message OK
- debug(2, sock_file.readline())
-
- sock.send('SEND\r\n')
- # 250 Message Sent Successfully. Msg ID = 692274
- reply = sock_file.readline()
- debug(2, reply)
-
- sock.send('QUIT\r\n')
- sock.close()
- except:
- ret = 0
- else:
- if reply.find(self.ok_match) >= 0:
- ret = 1
- else:
- ret = 0
-
- _debug = 'msg to %s via %s (%s)' % \
- (self.phone, self.__class__.__name__, ret)
- debug(1, _debug)
- return ret
-
-#
-# E-mail trnsport
-#
-
-class EMailTransport(Transport):
- def __init__(self, phone, message, mail_from=None, gate=None):
- Transport.__init__(self, phone, message)
-
- if not mail_from:
- import getpass
- try:
- realuser = getpass.getuser()
- except AttributeError:
- # Not all systems have os.environ or getpw...
- realuser = 'nobody'
- thishost = socket.getfqdn()
- mail_from = "%s@%s" % (realuser, thishost)
- debug(1, mail_from)
- self.mail_from = mail_from
-
- self.gate = gate
-
- def send(self):
- message = """\
-From: %s
-To: %s@%s
-Subject: SMS
-
-%s
-""" % (self.mail_from, self.phone, self.gate, self.message)
- self.post(message)
- debug(2, message)
- return 1
-
-class SendmailTransport(EMailTransport):
- def __init__(self, phone, message, mail_from=None, gate=None):
- EMailTransport.__init__(self, phone, message, mail_from, gate)
-
- sendmail = self.find_sendmail()
- if not sendmail:
- raise ValueError("cannot find sendmail binary")
- self.sendmail = sendmail
-
- def find_sendmail(self):
- import os
- for sendmail in ("/usr/lib/sendmail", "/usr/sbin/sendmail"):
- if os.path.exists(sendmail):
- return sendmail
- return None
-
- def post(self, message):
- cmd = "%s -oem -oi '%s@%s'" % (self.sendmail, self.phone, self.gate)
- debug(1, cmd)
- import os
- sendmail = os.popen(cmd, 'w')
- sendmail.write(message)
- sendmail.close()
-
-class SMTPTransport(EMailTransport):
- def __init__(self, phone, message, mail_from=None, gate=None, mail_relay="localhost"):
- EMailTransport.__init__(self, phone, message, mail_from, gate)
- self.mail_relay = mail_relay
-
- def post(self, message):
- debug(1, self.mail_relay)
- import smtplib
- smtp = smtplib.SMTP(self.mail_relay)
- smtp.sendmail(self.mail_from, "%s@%s" % (self.phone, self.gate), message)
- smtp.close()
-
-
-#
-# Real transports
-#
-
-class MTSru(HTTPTransport):
- "Russian provider MTS.ru"
-
- method = "POST"
- url = "http://www.mts.ru:5051/cgi-bin/cgi.exe"
- referer = ""
- ok_match = 'Ваше сообщение отправлено'
-
- def send(self):
- dict = {}
- text = self.calcText()
- time = self.calcExpiration()
-
- dict['function'] = 'sms_send'
- #dict['MMObjectType'] = '0'
- #dict['MMObjectID'] = ''
- phone = self.phone
- if phone[:4] == "8902":
- phone = "8916%s" % phone[4:]
- dict['To'] = phone
- dict['Msg'] = text
- dict['Hour'] = time.strftime('%k').strip()
- dict['Min'] = time.strftime('%M')
- dict['Day'] = time.strftime('%e').strip()
- dict['Mon'] = str(time.month)
- dict['Year'] = time.strftime('%Y')
- dict['count'] = str(len(text))
- dict['Lang'] = '2'
-
- return self.post(dict)
-
- def POST(self, postdata, host, uri, port=80, proxy_host=None, proxy_port=None):
- postdata = "MMObjectType=0&MMObjectID=&" + postdata
- return HTTPTransport.POST(self, postdata, host, uri, port, proxy_host, proxy_port)
-
-
-class MTSGSMcom(HTTPTransport):
- url = "http://www.mtsgsm.com/sms/sent.html"
- referer = "http://www.mtsgsm.com/sms/"
- method = "GET"
- ok_match = 'Ваше сообщение отправлено'
-
- def send(self):
- dict = {}
- text = self.calcText()
- time = self.calcExpiration()
-
- dict['Posted'] = '1'
- dict['To'] = self.phone
- dict['Msg'] = text
- dict['count'] = str(len(text))
- dict['SMSHour'] = time.strftime('%k').strip()
- dict['SMSMinute'] = time.strftime('%M')
- dict['SMSDay'] = time.strftime('%e').strip()
- dict['SMSMonth'] = str(time.month - 1)
- dict['SMSYear'] = time.strftime('%Y')
-
- return self.post(dict)
-
-
-class BeeOnLine(CP1251HTTPTransport):
- "Russian provider BeeOnLine.ru"
-
- method = "POST"
- url = "http://www.beeonline.ru/portal/comm/send_sms/simple_send_sms.sms"
- referer = "http://www.beeonline.ru/portal/comm/send_sms/simple_send_sms.sms"
- crap = 'BOL '
- ok_match = koi2win('Ваше сообщение отправлено')
-
- def __init__(self, phone, message,
- mode="GSM", # mode can be either GSM or DAMPS
- transliterate=1, # turn transliteration of/off
- reply_to=''): # send reply to this e-mail
- Transport.__init__(self, phone, message)
- if mode not in ('GSM', 'DAMPS'):
- raise ValueError("mode (%s) must be either 'GSM' or 'DAMPS'" % mode)
- self.mode = mode
- self.transliterate = transliterate
- self.reply_to = reply_to
-
- def send(self):
- dict = {}
- text = self.calcText(smsSize - len(self.crap))
-
- # hidden
- #dict['deferto'] = ''
- #dict['adv_year'] = ''
- dict['send'] = 'send'
- dict['destination_number_from'] = 'ordinary' # number, not a BEEpost
-
- prf = self.phone[:4]
- if self.mode == "GSM":
- dict['network_code'] = '3'
- elif prf == '7095':
- dict['network_code'] = '2'
- elif prf == '7901':
- dict['network_code'] = '1'
- else:
- raise RuntimeError("incorrect combination of mode (%s) and prefix (%s)" % (self.mode, prf))
-
- dict['phone'] = self.phone[4:]
- dict['message'] = text
- dict['mlength'] = str(smsSize - len(self.crap) - len(text))
-
- if self.mode == "GSM" and not self.transliterate:
- dict['translit'] = '1' # turn transliteration OFF! :)
-
- if self.reply_to:
- dict['send_email'] = '1'
- dict['reply_addr'] = self.reply_to
-
- return self.post(dict)
-
-class BeeOnLineSMS(BeeOnLine):
- "Russian provider BeeOnLine.ru"
-
- url = "http://www.beeonline.ru/servlet/send/sms/"
-
- def send(self):
- dict = {}
- text = self.calcText(smsSize - len(self.crap))
-
- dict['prf'] = self.phone[:4]
- dict['phone'] = self.phone[4:]
- dict['termtype'] = self.mode[0]
- dict['message'] = text
-
- if self.mode == "GSM" and not self.transliterate:
- dict['translit'] = '1' # turn transliteration OFF! :)
-
- dict['number_sms'] = "number_sms_send"
- return self.post(dict)
-
-
-class Pcom(HTTPTransport):
- "Russian provider PCOM.ru"
-
- method = "POST"
- url = "http://www.pcom.ru/online.phtml"
- referer = "http://www.pcom.ru/online.phtml"
- crap = ''
- ok_match = koi2win('поставлено в очередь')
-
- def calcText(self, maxsize=smsSize):
- "force translitertaion"
-
- return HTTPTransport.calcText(self, maxsize=maxsize,
- transFunction=rus2lat)
-
- def send(self):
- dict = {}
- text = self.calcText(120 - len(self.crap))
- expiration = self.calcExpiration()
- from mx import DateTime
- now = DateTime.now()
-
- dict['ACTION'] = 'SENDSMS'
-
- dict['SMS_START_HOUR'] = now.strftime('%H')
- dict['SMS_START_MINUTE'] = now.strftime('%M')
- dict['SMS_START_DAY'] = now.strftime('%d')
- dict['SMS_START_MONTH'] = now.strftime('%m')
- dict['SMS_START_YEAR'] = now.strftime('%Y')
-
- dict['SMS_STOP_HOUR'] = expiration.strftime('%H')
- dict['SMS_STOP_MINUTE'] = expiration.strftime('%M')
- dict['SMS_STOP_DAY'] = expiration.strftime('%d')
- dict['SMS_STOP_MONTH'] = expiration.strftime('%m')
- dict['SMS_STOP_YEAR'] = expiration.strftime('%Y')
-
- dict['prefix'] = self.phone[1:4]
- dict['DN'] = self.phone[4:]
- dict['MSG'] = text
-
- return self.post(dict)
-
-
-class ExtelGsmCom(SNPPTransport):
- "Russian provider Extel-GSM.com"
-
- host = 'www.extel-gsm.com'
- port = 4444
- ok_match = '250 Message Sent Successfully'
- crap = ''
-
- prefix = '0119'
-
- def calcText(self, maxsize=smsSize):
- "force translitertaion"
-
- return SNPPTransport.calcText(self, maxsize=maxsize,
- transFunction=rus2lat)
-
- def send(self):
- dict = {}
- text = self.calcText(smsSize - len(self.crap))
- phone = self.phone[5:]
-
- # 0112 (city code) must be replaced with (0119)
- dict['phone'] = '+7%s%s' % (self.prefix, phone)
- dict['message'] = text
-
- return self.post(dict)
-
-
-class MegafoneMoscow(CP1251HTTPTransport):
- "Rissian provider megafonmoscow.ru"
-
- method = "POST"
- url = "http://www.megafonmoscow.ru/rus/sms.xpml"
- referer = "http://www.megafonmoscow.ru/rus/sms.xpml"
- ok_match = koi2win('Сообщение успешно отправлено')
-
- def send(self):
- dict = {}
- text = self.calcText()
-
- dict['prefix'] = self.phone[:4]
- dict['addr'] = self.phone[4:]
- dict['message'] = text
- dict['send'] = " Send "
-
- if hasattr(self, 'transliterate') and self.transliterate:
- dict['transliterate'] = '1'
-
- return self.post(dict)
-
-
-class SkyLinkMsk(CP1251HTTPTransport):
- "Russian provider SkyLink.Msk.ru"
-
- method = "POST"
- url = "http://skylink.msk.ru/inc/sendsms.php"
- ok_match = koi2win('Сообщение было отправлено')
-
- def send(self):
- dict = {}
- text = self.calcText()
-
- phone = self.phone
- if phone[0] == '7':
- phone = '8' + phone[1:]
- dict['dest'] = phone
- dict['smsbody'] = text
-
- return self.post(dict)
-
-
-BEELINE = BeeOnLineSMS
-#BEELINE = Whizdiary
-MTS = MTSru
-#MTS = MTSGSMcom
-#MTS = Whizdiary
-SONET = Pcom
-EXTEL = ExtelGsmCom
-Megafone = MegafoneMoscow
-SkyLink = SkyLinkMsk
-
-
-Prefix2Provider = {
- "903": BEELINE,
- "905": BEELINE,
- "906": BEELINE,
-
- "910": MTS,
- "916": MTS,
-
- "926": Megafone
-}
-
-
-class SMSError(Exception): pass
-
-def Phone2Provider(phone):
- prefix = phone[1:4]
-
- if prefix in ("095", "901"): # 901 is being used by Beeline and SkyLink
- raise SMSError("unknown provider for phone %s" % phone)
-
- if Prefix2Provider.has_key(prefix):
- return Prefix2Provider[prefix]
-
- raise SMSError("bad prefix for phone %s" % phone)