Revize 622
Přidáno uživatelem harryhk před více než 14 roky(ů)
freenetis/trunk/kohana/media/download/smsbackend.py | ||
---|---|---|
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
#
|
||
# AWEG stands for AnnyWay Enterprise SMS Gateway, the main end-user oriented SMS connectivity solution by MATERNA Communication a.s.
|
||
# Smsbackend is used as common back-end executable program for easy integration of AWEG OpenInterface to the b-SMS, e-SMS, or other 3rd party applications.
|
||
#
|
||
# Authors:
|
||
# Emanuel Petr <epetr@fincom.cz> 2005-2006
|
||
# Lucie Leistnerova <lucie.leistnerova@fincom.cz> 2007
|
||
# Vojtech Pithart <vojtech.pithart@maternacz.com> 2008+
|
||
# (c) MATERNA Communications 2005-2008
|
||
#
|
||
# $Id: smsbackend.py 565 2008-10-22 09:16:33Z vojta $
|
||
#
|
||
|
||
# Version strings: * Please increment version string after major updates
|
||
# * Revision number is incremented by SVN on each commit
|
||
# * Date is updated by SVN on each commit
|
||
version_num= "3.0"
|
||
revision= "$Revision: 565 $"[11:][:-2] # [11:][:-2] removes all except the number (NNNNN)
|
||
date= "$Date: 2008-10-22 11:16:33 +0200 (St, 22 říj 2008) $"[7:][:10] # [7:][:10] removes all except date (YYYY-MM-DD)
|
||
|
||
# Displayed 1) after calling "smsbackend -v"
|
||
# 2) in windows front-end in help/about dialog
|
||
version= "AWEG-smsbackend version " + version_num + " revision " + revision + " (" + date + ")"
|
||
|
||
# Transferred in HTTP and logged on server:
|
||
version_short= "v" + version_num + "/r" + revision + "/" + date
|
||
|
||
import time
|
||
import sys
|
||
import re
|
||
import urllib
|
||
import urllib2
|
||
from urllib2 import Request, urlopen, URLError
|
||
import signal, os
|
||
import socket
|
||
from optparse import OptionParser
|
||
#import DNS # Used in dns_resolve()
|
||
import base64
|
||
#import ezPyCrypto
|
||
import httplib
|
||
import encodings, encodings.idna, encodings.ascii, encodings.punycode
|
||
#import pprint # pro Dumpovani promennych
|
||
#pp = pprint.PrettyPrinter(indent=4) # pro Dumpovani promennych
|
||
|
||
sent_sms = 0
|
||
rest_sms = 0
|
||
we_flag = 0 # warning, error status
|
||
returned_status_text = "" #status_text at STATUS line
|
||
body_parts = 0
|
||
#bulk_id = 0
|
||
# for progress bar, sms counting
|
||
sms_id = 0
|
||
# stop handler
|
||
sig_stop_generic_handler_flag = 0
|
||
# proxy
|
||
proxy_user = ""
|
||
proxy_password = ""
|
||
proxy_ip = ""
|
||
proxy_port = ""
|
||
proxy_default_port = "3128"
|
||
proxy_use = 0
|
||
# delivery reports
|
||
parts_count = 0
|
||
dr_file_name = ""
|
||
dr_file_write_flag = 0
|
||
# rsa encryption
|
||
#public_key = "mykey.pub"
|
||
|
||
# For -M parameter: 4kb spaces + \n + 4 kb spaces
|
||
big_space = ((4*1024) * " " ) + "\n" + ((4*1024) * " " ) + "\n"
|
||
|
||
|
||
# Timeout for HTTP request (sending AO-MT messages)
|
||
http_timeout = 15
|
||
# maximum retry count (on HTTP error)
|
||
max_retry_count = 25
|
||
# retry pause (seconds) incremented by:
|
||
retry_pause = 2
|
||
|
||
|
||
retry_counter = 0
|
||
# ports
|
||
default_ports = { 'http' : 80, 'https' : 443}
|
||
default_host_port = 443
|
||
host_port = default_host_port
|
||
http_conn = None
|
||
version_sent = 0
|
||
config = ""
|
||
DR_FILE_LIST = []
|
||
|
||
class ConnectionError(Exception):
|
||
def __init__(self, value):
|
||
self.value = value
|
||
def __str__(self):
|
||
return repr(self.value)
|
||
|
||
def read_lines(ser):
|
||
lines = ser.readlines()
|
||
ser.flushInput
|
||
ser.flushOutput
|
||
if options.verbose: print "read_lines: ", lines
|
||
return lines
|
||
|
||
def read_line(ser):
|
||
line = ser.readline()
|
||
if options.verbose: print "read_line (%d): %s" % ( len(line), line )
|
||
lines = [line, ""] #make list
|
||
if len(line): status_parser(lines)
|
||
return len(line)
|
||
#vojta#if len(line): status_parser(line)
|
||
#vojta# if len(line): status_parser(line)
|
||
#vojta#return line
|
||
|
||
def status_action(status,status_text):
|
||
global rest_sms
|
||
global we_flag
|
||
global returned_status_text
|
||
global config
|
||
#status codes are print without -q
|
||
if options.verbose: print "status action", (status, status_text)
|
||
# 3xx code?
|
||
if status[0] == "3" and status != "315":
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:%d:%d:2: %s" % (sent_sms, rest_sms, status_text)
|
||
leave(local_text)
|
||
#if status == "315":
|
||
# we_flag = 1
|
||
# returned_status_text = status_text
|
||
if status == "200":
|
||
we_flag = 0
|
||
returned_status_text = "OK "
|
||
if status == "201" or status == "202" or status == "315":
|
||
we_flag = 1
|
||
returned_status_text = status_text
|
||
if status == "102":
|
||
pr = re.compile('^\s*:(\d+)')
|
||
mr = pr.match(status_text)
|
||
if mr:
|
||
if options.verbose: print "rest:", mr.group(1)
|
||
rest_sms = int( mr.group(1) )
|
||
if status == "103":
|
||
pr = re.compile('^\s*:(.*)')
|
||
mr = pr.match(status_text)
|
||
if mr:
|
||
if options.verbose: print "config:", mr.group(1)
|
||
config += mr.group(1) + ";"
|
||
|
||
def status_parser(lines,checkfor=""):
|
||
ret_status = 0 #return value for status_parser ... is modified later ... 1 if checkfor was found at status lines
|
||
global body_parts
|
||
global sent_sms
|
||
global msg_ids
|
||
#global bulk_id
|
||
|
||
# null values
|
||
msg_ids = []
|
||
#bulk_id = 0
|
||
|
||
p = re.compile('^\s*(\d{3})\s*(.*)', re.IGNORECASE)
|
||
# parsing for how many sms was sent, their msg_id and bulk_id
|
||
# 200 [2] bodypart, accepted as [0002433e,0002433x] bulk_id [12345]
|
||
# p_bp = re.compile('^.*\[(\d+)\]\s*bodypart.*\[(.*)\].*\[(.*)\]', re.IGNORECASE) # s bulk_id
|
||
p_bp = re.compile('^.*\[(\d+)\]\s*bodypart.*\[(.*)\].*', re.IGNORECASE)
|
||
|
||
if options.verbose: print "status_parser (%d): \"%s\"" % ( len(lines), lines )
|
||
for i in range( len(lines) ):
|
||
#match for NO CARRIER
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
if lines[i].find("NO CARRIER") != -1 :
|
||
local_text = local_text + "STATUS:%d:%d:2: NO CARRIER" % (sent_sms, rest_sms)
|
||
leave(local_text)
|
||
#match for DELAYED
|
||
if lines[i].find("DELAYED") != -1 :
|
||
local_text = local_text + "STATUS:%d:%d:2: DELAYED" % (sent_sms, rest_sms)
|
||
leave(local_text)
|
||
#match for NO DIALTONE
|
||
if lines[i].find("NO DIALTONE") != -1 :
|
||
local_text = local_text + "STATUS:%d:%d:2: NO DIALTONE" % (sent_sms, rest_sms)
|
||
leave(local_text)
|
||
#match for status numbers
|
||
m = p.match( lines[i] )
|
||
if m:
|
||
status = m.group(1)
|
||
status_text = m.group(2)
|
||
status_action(status,status_text)
|
||
if status.find(checkfor) != -1 : ret_status = 1
|
||
#match for bodyparts #200 [2] bodyparts
|
||
#lines_test = "200 [3] bodypart, accepted as [0002433e 0002433x] bulk_id [12345]" # s bulk_id
|
||
#lines_test = "200 [2] bodypart, accepted as [0002433e 0002433x]"
|
||
#m_bp = p_bp.match( lines_test )
|
||
m_bp = p_bp.match( lines[i] )
|
||
if m_bp:
|
||
body_parts = int( m_bp.group(1) )
|
||
msg_ids = m_bp.group(2)
|
||
#bulk_id = m_bp.group(3) # s bulk_id
|
||
if options.verbose: print "body parts:|%d| sent_sms{%d}" % (body_parts,sent_sms)
|
||
if body_parts >= 2: sent_sms += body_parts - 1 #increase sent_sms count
|
||
return ret_status
|
||
|
||
|
||
def parse_destination():
|
||
#destination_list = options.destination.split(",")
|
||
temp_list = options.destination.split(",")
|
||
pr = re.compile('^(.*\d.*\d.*\d.*)') #3 digits anyware
|
||
destination_list = []
|
||
|
||
for destination in temp_list:
|
||
if options.verbose:
|
||
print "parse_destination: ", destination
|
||
mr = pr.match(destination) #pr na cely radek
|
||
if mr:
|
||
if options.verbose: print 'Match found: ', mr.group()
|
||
destination_list.append( mr.group(1) )
|
||
return destination_list
|
||
|
||
|
||
def generate_random_number():
|
||
#from random import randint
|
||
random_number = time.strftime( "%d%H%M%S", time.localtime() )
|
||
if options.verbose: print "random number ", random_number
|
||
return random_number
|
||
|
||
def get_protocol(url):
|
||
# get protocol of the url - HTTP, HTTPS
|
||
pr = re.compile('^([^\/]*):\/\/(.*)')
|
||
mr = pr.match(url)
|
||
if mr:
|
||
return mr.group(1)
|
||
else:
|
||
return 'https'
|
||
|
||
def get_domain(url):
|
||
# get domain of the url - www.something.com
|
||
pr = re.compile('^(.*):\/\/([^\/]*)\/(.*)')
|
||
mr = pr.match(url)
|
||
if mr:
|
||
return mr.group(2)
|
||
else:
|
||
return url
|
||
|
||
def get_rest(url):
|
||
# get rest of the url after http://domain/
|
||
pr = re.compile('^[^\/]*:\/\/[^\/]*\/(.*)')
|
||
mr = pr.match(url)
|
||
if mr:
|
||
return mr.group(1)
|
||
else:
|
||
return ''
|
||
|
||
def dns_resolve(url):
|
||
#http://server:port/uri
|
||
#http://aweg.fincom.cz
|
||
|
||
# automatically load nameserver(s) from /etc/resolv.conf
|
||
# (works on unix - on others, YMMV)
|
||
global retry_counter
|
||
try:
|
||
DNS.ParseResolvConf()
|
||
except:
|
||
if options.verbose: print "We are probably on windows system, no /etc/resolv.conf found"
|
||
#return unchanged or https://* (have not --allow-http)
|
||
pr = re.compile('.*:\/\/(.*)') #rip the name only
|
||
mr = pr.match(url)
|
||
if get_protocol(url) != 'https' and not options.allow_non_ssl:
|
||
return "https://" + mr.group(1)
|
||
else:
|
||
return url
|
||
|
||
while (1):
|
||
try:
|
||
pr = re.compile('(.*):\/\/([\d+\.]+\/?i.*)') #is name or IP, if match it is iP
|
||
mr = pr.match(url)
|
||
if mr:
|
||
# it is ip, return unchanged
|
||
# set https://* in case --allow-non-ssl is not used
|
||
if get_protocol(url) != 'https' and not options.allow_non_ssl:
|
||
return "https://" + mr.group(2)
|
||
else:
|
||
return url
|
||
|
||
# it is name, we need IP, so do DNS querry
|
||
pr = re.compile('(.*)(:\/\/)([^\/]+)(\/?.*)') #rip the name only
|
||
mr = pr.match(url)
|
||
if mr:
|
||
query_name = mr.group(3)
|
||
if options.verbose: print "DNS request for: ", query_name
|
||
# it is name, so translate to IP
|
||
r = DNS.DnsRequest(name=query_name, qtype='A', timeout=30)
|
||
# do the request
|
||
a=r.req()
|
||
answer = a.answers
|
||
if options.verbose: print answer
|
||
# set https://* in case --allow-non-ssl is not used
|
||
if get_protocol(url) != 'https' and not options.allow_non_ssl:
|
||
return "https" + mr.group(2) + answer[0]['data'] + mr.group(4)
|
||
else:
|
||
return mr.group(1) + mr.group(2) + answer[0]['data'] + mr.group(4)
|
||
else:
|
||
#return unchanged or https://* (have not --allow-non-ssl)
|
||
pr = re.compile('.*:\/\/(.*)') #rip the name only
|
||
mr = pr.match(url)
|
||
if get_protocol(url) != 'https' and not options.allow_non_ssl:
|
||
return "https://" + mr.group(1)
|
||
else:
|
||
return url
|
||
except:
|
||
if options.verbose: print "dns_resolve failed!"
|
||
if retry_counter < max_retry_count:
|
||
retry_counter+=1
|
||
new_retry_pause = retry_counter * retry_pause
|
||
if options.spaces:
|
||
spaces = big_space
|
||
else:
|
||
spaces = ""
|
||
print "INFO: Can't do dns resolve. Retry after %d seconds (%d/%d)%s" % ( new_retry_pause, retry_counter, max_retry_count, spaces )
|
||
time.sleep( new_retry_pause )
|
||
continue
|
||
else:
|
||
# after max_retry_count, we can't do dns resolve, so stop processing and quit program
|
||
leave("STATUS:0::2: Can't send URL request. Timeout reached.")
|
||
retry_counter = 0
|
||
|
||
|
||
def process_http(csv_lines = ""):
|
||
#only for http, bulk identification
|
||
#if only one sms, send bulk=random_number_end else bulk=random_number, and last sms with bulk=random_number_end
|
||
global bulk_number
|
||
global parts_count
|
||
global DR_FILE
|
||
global dr_file_name
|
||
global dr_file_write_flag
|
||
global config
|
||
global http_conn
|
||
global last_http_error
|
||
|
||
bulk_number = generate_random_number()
|
||
sms_iter = 0
|
||
|
||
# open file for delivery report writing
|
||
if options.dr_file:
|
||
# try to assign bulk_id for %s at dr_file param, if false, leave it without substitution
|
||
try:
|
||
dr_file_name = options.dr_file % bulk_number
|
||
except:
|
||
dr_file_name = options.dr_file
|
||
|
||
# try to open file, if false continue without writing delivery reports (tak si preje vojta)
|
||
try:
|
||
DR_FILE = open(dr_file_name,"w") # if file exists will overwrite
|
||
dr_file_write_flag = 1 # for detect if we can save to file and close file at leave() function
|
||
except:
|
||
if options.verbose: print "Can't open file %s for writing" % dr_file_name
|
||
print "Can't open file %s for writing. Delivery reports will be not written.\n" % dr_file_name
|
||
#local_text = "STATUS:0::2: Can't open file \"%s\" for writing" % dr_file_name
|
||
#leave(local_text)
|
||
|
||
# HTTP address of AWEG server
|
||
ip_name = options.addr
|
||
|
||
# Create HTTP connection object - Proxy mode
|
||
if options.proxy_string: # http://[<username>[:<password>]@]<hostname>[:port]
|
||
p_proxy = re.compile('^\w*://(.*)@(.*)$', re.IGNORECASE)
|
||
m_proxy = p_proxy.match( options.proxy_string )
|
||
if m_proxy:
|
||
parse_proxy_user_password( m_proxy.group(1) )
|
||
parse_proxy_ip_port( m_proxy.group(2) )
|
||
else:
|
||
p_proxy = re.compile('^\w*://(.*)$', re.IGNORECASE)
|
||
m_proxy = p_proxy.match( options.proxy_string )
|
||
if m_proxy:
|
||
parse_proxy_ip_port( m_proxy.group(1) )
|
||
else:
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
leave(local_text + "STATUS:0::2: Proxy mismatch parametr.")
|
||
|
||
if options.verbose: print "Using proxy: proxy_ip=%s, proxy_port=%s, proxy_user=%s, proxy_password=%s" % (proxy_ip, proxy_port, proxy_user, proxy_password)
|
||
|
||
# set parameters for proxy_connect
|
||
pr = re.compile('^(\w*)://(.*)')
|
||
mr = pr.match(options.addr)
|
||
if mr:
|
||
if options.allow_non_ssl:
|
||
host_port = default_ports[mr.group(1)]
|
||
else:
|
||
host_port = default_host_port
|
||
proxy_host = get_domain(options.addr)
|
||
else:
|
||
host_port = default_host_port
|
||
proxy_host = options.addr
|
||
# set proxy connection
|
||
while (not http_conn):
|
||
try:
|
||
http_conn = proxy_connect(proxy_ip, proxy_user, proxy_password, proxy_host, host_port, int(proxy_port))
|
||
except ConnectionError, c_err:
|
||
# retry routine, max_retry_count , retry_pause
|
||
if retry_counter < max_retry_count:
|
||
retry_counter+=1
|
||
new_retry_pause = retry_counter * retry_pause
|
||
if options.spaces:
|
||
spaces = big_space
|
||
else:
|
||
spaces = ""
|
||
print "INFO: %s. Retry after %d seconds (%d/%d)%s" % ( c_err.value, new_retry_pause, retry_counter, max_retry_count, spaces )
|
||
time.sleep( new_retry_pause )
|
||
else:
|
||
# after max_retry_count, we can't connect, so stop processing and quit program
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:0:0:2: error - %s" % c_err.value
|
||
leave(local_text)
|
||
retry_counter = 0
|
||
# Create HTTP connection object - direct non-proxy mode
|
||
else:
|
||
if ( options.addr_is_https ):
|
||
http_conn = httplib.HTTPSConnection(get_domain(options.addr))
|
||
else:
|
||
http_conn = httplib.HTTPConnection(get_domain(options.addr))
|
||
|
||
# Sending messages from CSV file
|
||
if csv_lines:
|
||
if options.verbose: print "process_http: csv_lines mode"
|
||
# print header to file with name of csv_file, it is used by frontend
|
||
if options.dr_file and dr_file_write_flag:
|
||
DR_FILE.write("# " + options.csvfilename + "\n")
|
||
# get count of lines/sms (it means last sms no)
|
||
last_sms_no = len(csv_lines) #bulk
|
||
|
||
#for destination,text in csv_lines.items():
|
||
destination_list = csv_lines.keys()
|
||
destination_list.sort()
|
||
# for each destination number from csv
|
||
for destination in destination_list:
|
||
text = csv_lines[destination]
|
||
config = "" # the only last "103:" is converted to "CONFIG:" output
|
||
destination = destination[8:] # remove id from destination number
|
||
sms_iter+=1 #bulk
|
||
if last_sms_no == sms_iter: bulk_number = str(bulk_number) + "end" #bulk
|
||
|
||
if options.verbose: print "Sending text \"%s\" to destination %s" % (text, destination)
|
||
if send_http(destination, text, bulk_number, ip_name):
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:%d:%d:%d: %s" % (sent_sms, rest_sms, we_flag, returned_status_text)
|
||
else:
|
||
#some problem,we dont receive 20x code
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:%d:%d:2: %s " % (sent_sms, rest_sms, last_http_error)
|
||
leave(local_text)
|
||
|
||
# exit with local_text message set above
|
||
leave(local_text)
|
||
# Sending messages from command line (one text, multiple destination from -d)
|
||
else:
|
||
if options.verbose: print "process_http: destination_list mode"
|
||
# print header to file, it is used by frontend
|
||
if options.dr_file and dr_file_write_flag:
|
||
DR_FILE.write("# destination list\n")
|
||
# multidestination receivers are parsed and created destination_list structure
|
||
destination_list = parse_destination() #parse options.destination parameter
|
||
# get sms count for frontedns progress bar
|
||
parts_count = parts_count + ( get_bodyparts( smstext ) * len(destination_list) )# how many bodyparts * how many receivers
|
||
print "\nPARTS:%d\n" % (parts_count)
|
||
|
||
if options.verbose: print "Parse destination list: %s\nParts count: %s\n" % (destination_list, parts_count)
|
||
|
||
# get count of receivers from destination_list (it means last sms no)
|
||
last_sms_no = len(destination_list)
|
||
|
||
for destination in destination_list:
|
||
sms_iter+=1 #bulk
|
||
if last_sms_no == sms_iter: bulk_number = str(bulk_number) + "end" #bulk
|
||
|
||
if options.verbose:
|
||
print "Sending \"%s\" to %s" % ( smstext, destination )
|
||
|
||
last_http_error= "--placeholder--"
|
||
if send_http(destination, smstext, bulk_number, ip_name):
|
||
#send ok + print status line
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:%d:%d:%d: %s" % (sent_sms, rest_sms, we_flag, returned_status_text)
|
||
else:
|
||
#some problem,we dont receive 20x code
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:%d:%d:2: %s " % (sent_sms, rest_sms, last_http_error)
|
||
leave(local_text)
|
||
# exit with local_text message set above
|
||
leave(local_text)
|
||
|
||
|
||
def send_http(destination, message, bulk_number, ip_name):
|
||
""" send HTTP request to specified URL """
|
||
|
||
global big_space
|
||
global sent_sms
|
||
global sms_id
|
||
global retry_counter
|
||
global proxy_ip
|
||
global proxy_port
|
||
global proxy_host
|
||
global proxy_user
|
||
global proxy_password
|
||
global host_port
|
||
global version_sent
|
||
global config
|
||
global DR_FILE_LIST
|
||
global http_conn
|
||
global last_http_error
|
||
|
||
#encode message
|
||
login = options.login
|
||
message = urllib.quote(message)
|
||
login = urllib.quote_plus(login)
|
||
destination_unqoted = destination # for dr_file
|
||
destination = urllib.quote_plus(destination)
|
||
|
||
#create url_string
|
||
while 1: # infinite loop
|
||
try:
|
||
url_params = "auth=" + login + "&receiver=" + destination + "&bulk=" + bulk_number + "&smstext=" + message
|
||
except:
|
||
leave("STATUS:0::2: Missing URL parameters")
|
||
if options.true160:
|
||
url_params = url_params + "&true160=1"
|
||
if options.dr_file:
|
||
url_params += "&report=1"
|
||
if options.email_reply:
|
||
url_params += "&emailre=1"
|
||
if options.use_anumber:
|
||
url_params += "&use_anumber=1"
|
||
if options.force_tts:
|
||
url_params += "&forcetts=1"
|
||
if not version_sent:
|
||
version_sent = 1
|
||
be_version_escaped = urllib.quote_plus(version_short)
|
||
fe_version_escaped = urllib.quote_plus(options.frontend_name)
|
||
url_params = url_params + "&be=" + be_version_escaped + "&fe=" + fe_version_escaped
|
||
|
||
#make url_string
|
||
url_string = ip_name + "?" + url_params
|
||
url_string_proxy = "/" + get_rest(options.addr) + "?" + url_params
|
||
|
||
if options.verbose:
|
||
print "url_string (exact): ", url_string
|
||
print "url_string_proxy (exact): ", url_string_proxy
|
||
|
||
# send it and read ouput
|
||
try:
|
||
if options.proxy_string: # is proxy connection
|
||
# make http request
|
||
if options.verbose:
|
||
http_conn.set_debuglevel(3)
|
||
print "------ sending request"
|
||
http_conn.putrequest('GET', url_string_proxy)
|
||
http_conn.endheaders()
|
||
if options.verbose: print "------ getting response"
|
||
response = http_conn.getresponse()
|
||
data = response.read()
|
||
if options.verbose: print data
|
||
else:
|
||
# elif (get_protocol(url_string) == 'https') or (not options.allow_non_ssl): # is https connection
|
||
# make https connection
|
||
if options.verbose:
|
||
print " http_conn.putrequest: ", url_string_proxy
|
||
http_conn.putrequest('GET', url_string_proxy)
|
||
http_conn.endheaders()
|
||
response = http_conn.getresponse()
|
||
data = response.read()
|
||
# VojtaP 15.5.2008: Nechapu proc je zde ruzny postup pro HTTP a HTTPS. Kdo se pak ma vyznat v exceptions?
|
||
# else:
|
||
# # make http connection
|
||
# if options.verbose:
|
||
# print " url_GET http: ", url_string
|
||
# http_request = urllib2.Request( url_string )
|
||
# socket.setdefaulttimeout( http_timeout ) # in seconds
|
||
# opener = urllib2.build_opener()
|
||
# urllib2.install_opener(opener)
|
||
# # make http request
|
||
# pagehandle = urllib2.urlopen(http_request)
|
||
# data = pagehandle.read()
|
||
except Exception, inst:
|
||
#print("%s: %s"%(repr(inst), str(dir(inst))))
|
||
last_http_error= "Ex[%s]" % ( inst )
|
||
if hasattr(inst, 'reason'):
|
||
#print 'We failed to reach a server.'
|
||
#print 'Reason: ', inst.reason
|
||
last_http_error= "Failed to reach server: %s" % ( inst.reason )
|
||
elif hasattr(inst, 'code'):
|
||
#print 'The server couldn\'t fulfill the request.'
|
||
#print 'Error code: ', inst.code
|
||
last_http_error= "Server couldn\'t fulfill the request: HTTP %d" % ( inst.code )
|
||
elif hasattr(inst, 'args'):
|
||
#print 'Message: ', inst.args
|
||
last_http_error= "Exception message: %s" % ( str(inst.args) )
|
||
last_http_error= str(inst)
|
||
if options.verbose:
|
||
print "Exception:",
|
||
print inst
|
||
if sig_stop_generic_handler_flag:
|
||
leave("") # we return nothing because it is handled with sig_stop_generic_handler
|
||
else:
|
||
# retry routine, max_retry_count , retry_pause
|
||
if retry_counter < max_retry_count:
|
||
retry_counter+=1
|
||
new_retry_pause = retry_counter * retry_pause
|
||
if options.spaces:
|
||
spaces = big_space
|
||
else:
|
||
spaces = ""
|
||
print "INFO: HTTP request failed. Retry after %d seconds (%d/%d)%s" % ( new_retry_pause, retry_counter, max_retry_count, spaces ) +":",
|
||
print inst
|
||
if options.proxy_string:
|
||
print "INFO: Try to connect again to proxy %s:%s." % (proxy_ip, proxy_port)
|
||
http_conn.close()
|
||
try:
|
||
http_conn = proxy_connect(proxy_ip, proxy_user, proxy_password, proxy_host, host_port, int(proxy_port))
|
||
except:
|
||
pass
|
||
if get_protocol(ip_name) == 'https' or (not options.allow_non_ssl):
|
||
http_conn.close()
|
||
if ( options.addr_is_https ):
|
||
http_conn = httplib.HTTPSConnection(get_domain(options.addr))
|
||
else:
|
||
http_conn = httplib.HTTPConnection(get_domain(options.addr))
|
||
|
||
time.sleep( new_retry_pause )
|
||
version_sent = 0
|
||
continue
|
||
else:
|
||
# after max_retry_count, we can't send request, so stop processing and quit program
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:%d:%d:2: HTTP request failed [%s]" % (sent_sms, rest_sms, last_http_error)
|
||
leave(local_text)
|
||
break # no exception, so we can continue to process response
|
||
# end while
|
||
# null retry_counter
|
||
retry_counter = 0
|
||
|
||
if options.verbose: print "Response from server: ", data
|
||
data = data.split("\n")
|
||
|
||
# check HTTP status (200 OK or 404 not found etc.)
|
||
if ( response.status != 200 ):
|
||
last_http_error= "Server HTTP response: " + str(response.status) + " " + response.reason + " (" + ip_name + ")"
|
||
|
||
# print to stdout (only id if sent failed)
|
||
print "ID:%s %s" % (sms_id, last_http_error)
|
||
return 0
|
||
|
||
# check for 200-209 code (it not present => msg was not sent)
|
||
if status_parser(data,"20"):
|
||
if options.verbose: print "message sent"
|
||
sent_sms+=1
|
||
# print to stdout for delivery report processing by frontends
|
||
# if ok ... idzpravy,bulkid,timestamp,receiver
|
||
# if no ... idzpravy
|
||
timestamp = time.strftime( "%Y%m%d%H%M%S", time.localtime() )
|
||
# msg_ids is content of [], msg_ids was set from status_parser procedure, so we split them
|
||
if not (len(msg_ids) == 0):
|
||
msg_id_list = msg_ids.split(" ")
|
||
for msg_id in msg_id_list:
|
||
#print msg_id, bulk_id, timestamp, destination # s bulk_id
|
||
# substitue "end" to " " at bulk_id string for last message
|
||
p = re.compile('end')
|
||
bulk_number = p.sub( '', bulk_number)
|
||
if options.spaces:
|
||
spaces = big_space
|
||
else:
|
||
spaces = ""
|
||
# incrementsms_id
|
||
sms_id+=1
|
||
# message_id;submit_timestamp;receiver\n
|
||
# prepare string for print to stdout
|
||
delivery_line_stdout = "ID:%s,%s,%s,%s,%s,%s%s" % (sms_id, msg_id, bulk_number, timestamp, destination_unqoted, message, spaces)
|
||
# print to stdout
|
||
print delivery_line_stdout
|
||
|
||
# print to file if requested
|
||
if options.dr_file and dr_file_write_flag:
|
||
# prepare string for print to drfile and add it to drfile_list
|
||
delivery_line_drfile = "%s;%s;%s\n" % (msg_id, timestamp, destination_unqoted)
|
||
DR_FILE_LIST.append(delivery_line_drfile)
|
||
#DR_FILE.write(delivery_line_drfile)
|
||
|
||
return 1
|
||
# sms not sent (No matching route found), but continue in processing
|
||
elif status_parser(data,"315"):
|
||
sms_id+=1
|
||
# print to stdout (only id if sent failed)
|
||
print "ID:%s %s" % (sms_id, last_http_error)
|
||
return 1
|
||
else:
|
||
# In case of receiving 200 OK, but unparseable HTML document
|
||
# if no ... idzpravy
|
||
|
||
# incrementsms_id
|
||
sms_id+=1
|
||
|
||
if ( len(data[0]) ):
|
||
last_http_error= "Invalid response from server (" + ip_name + "): " + data[0][:-1]
|
||
if ( len(data[1]) ):
|
||
last_http_error= last_http_error + "..."
|
||
else:
|
||
last_http_error= "Empty response from server (" + ip_name + ")"
|
||
|
||
# print to stdout (only id if sent failed)
|
||
print "ID:%s %s" % (sms_id, last_http_error)
|
||
return 0
|
||
|
||
|
||
# when lines[1] is \n or \r or \r\n, we substite for lines[2]
|
||
def drop_unuse(lines):
|
||
pr = re.compile('^\\r\\n|\\r|\\n$',re.IGNORECASE)
|
||
mr = pr.match(lines[1])
|
||
if mr:
|
||
lines[1] = lines[2]
|
||
# remove \r\n or \r \n ( min_line_size is then exact )
|
||
lines[1] = chomp( lines[1] )
|
||
if options.verbose: print "lines: ", lines
|
||
return lines
|
||
|
||
|
||
def delivery_reports():
|
||
"""
|
||
-R <last_timestamp>[,bulk_id]
|
||
$url is HTTP submit URL (-a)
|
||
$auth is login information (-l)
|
||
$lastts is last timestamp (-R before ,)
|
||
$bulkid is bulk_id (-R after ,)
|
||
Construct URL this way: "$url/report?auth=$auth&since=$lastts&bulkid"
|
||
|
||
Normally, smsbackend returns the http body as-is, with no modifications.
|
||
REPORT:<message_id>,<bulk_id>,<status>,<timestamp_end>
|
||
REPORT:<message_id>,<bulk_id>,<status>,<timestamp_end>
|
||
REPORT:<message_id>,<bulk_id>,<status>,<timestamp_end>
|
||
...
|
||
<status> 0 - not-delivered
|
||
1 - delivered
|
||
"""
|
||
global config
|
||
global options
|
||
|
||
# Create HTTP connection object
|
||
if ( options.addr_is_https ):
|
||
http_conn = httplib.HTTPSConnection(get_domain(options.addr))
|
||
else:
|
||
http_conn = httplib.HTTPConnection(get_domain(options.addr))
|
||
|
||
|
||
#parse delivery_reports string
|
||
temp_array = options.delivery_reports.split(",")
|
||
try:
|
||
last_timestamp = temp_array[0]
|
||
bulk_id = temp_array[1]
|
||
except:
|
||
bulk_id = ''
|
||
print bulk_id
|
||
|
||
login = options.login
|
||
#encode message
|
||
last_timestamp = urllib.quote_plus(last_timestamp)
|
||
bulk_id = urllib.quote_plus(bulk_id)
|
||
login = urllib.quote_plus(login)
|
||
|
||
# translate name to ip with timeout, we use for this special DNS module
|
||
#ip_name = dns_resolve(options.addr)
|
||
ip_name= options.addr
|
||
|
||
#create url_string
|
||
if options.verbose: print "\ndelivery reports url_string (pre): %s, auth: %s, last_timestamp: %s, bulk_id: %s" % (ip_name, login, last_timestamp, bulk_id)
|
||
|
||
try:
|
||
#$url/report?auth=$auth&since=$lastts&bulkid
|
||
url_string = ip_name + "/report?auth=" + login + "&since=" + last_timestamp + "&bulkid=" + bulk_id
|
||
url_string_proxy = "/" + (get_rest(options.addr)) + "/report?auth=" + login + "&since=" + last_timestamp + "&bulkid=" + bulk_id
|
||
except:
|
||
leave("STATUS:0::2: Missing URL parameters")
|
||
|
||
if options.verbose:
|
||
print "url_string (exact): ", url_string
|
||
print "url_string_proxy (exact): ", url_string_proxy
|
||
|
||
# send it and read ouput
|
||
try:
|
||
#use proxy setting if defined as parametr
|
||
if options.proxy_string:
|
||
# make http request
|
||
print "http_conn.request " + url_string_proxy
|
||
http_conn.request('GET', url_string_proxy)
|
||
data = http_conn.getresponse().read()
|
||
else:
|
||
# elif get_protocol(url_string) == 'https':
|
||
# set timeout
|
||
if ( options.verbose ):
|
||
print "http_conn.putrequest " + url_string_proxy
|
||
http_conn.putrequest('GET', url_string_proxy)
|
||
http_conn.endheaders()
|
||
response = http_conn.getresponse()
|
||
data = response.read()
|
||
|
||
# else: # is http
|
||
# print "urllib2 " + url_string
|
||
# http_request = urllib2.Request( url_string )
|
||
# socket.setdefaulttimeout( http_timeout ) # in seconds
|
||
# opener = urllib2.build_opener()
|
||
# urllib2.install_opener(opener)
|
||
# # make http request
|
||
# pagehandle = urllib2.urlopen(http_request)
|
||
# #read the output from previous http request
|
||
# data = pagehandle.read()
|
||
except:
|
||
if sig_stop_generic_handler_flag:
|
||
leave("") # we return nothing because it is handled with sig_stop_generic_handler
|
||
else:
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
leave(local_text + "STATUS:0::2: Can't send URL request")
|
||
|
||
# print output from server
|
||
if options.verbose: print "Response from server: ", data
|
||
if options.spaces:
|
||
spaces = big_space
|
||
else:
|
||
spaces = ""
|
||
|
||
# check HTTP status (200 OK or 404 not found etc.)
|
||
if ( response.status != 200 ):
|
||
print "STATUS:0:0:2: Server HTTP response: " + str(response.status) + " " + response.reason + " (" + ip_name + ")"
|
||
return 0
|
||
|
||
data = data.split("\n")
|
||
for data_line in data:
|
||
print data_line + spaces
|
||
# print "\n" vpithart: data_line already contains \n from server
|
||
print "END"
|
||
|
||
|
||
def parse_csvfile():
|
||
"""
|
||
open file, parse lines and valid lines are saved to list "csv_lines"
|
||
computes number of bodyparts (from length(s) of text
|
||
save final count of sms to "parts_count" variable, which is printed at startup for frontends progress bar
|
||
"""
|
||
global csv_lines
|
||
global parts_count
|
||
global config
|
||
csv_lines = dict()
|
||
try:
|
||
f=open(options.csvfilename, 'r')
|
||
csv_lines_mass = f.readlines()
|
||
f.close()
|
||
except:
|
||
if options.verbose: print "Cant open file %s for reading" % options.csvfilename
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:0::2: Can't open file \"%s\" for reading" % options.csvfilename
|
||
leave(local_text)
|
||
|
||
local_iter = 500000 # start at 500 000, because we need fix length of number
|
||
#check for valid rows, 3 digits ? reason is PSMS
|
||
pr = re.compile('^([^;]*\d[^;]*\d[^;]*\d[^;]*);(.+)') #3 digits anywhere ; text
|
||
for line in csv_lines_mass:
|
||
if options.verbose: print "l:", line
|
||
mr = pr.match(line) #pr na cely radek
|
||
|
||
#get number and text form line
|
||
if mr:
|
||
if options.verbose: print 'Match found: ', mr.groups()
|
||
key = "id" + str(local_iter) + str( mr.group(1) ) # id5xxxxxx it is used for sorting by keys (we need to process cvs at the same order)
|
||
csv_lines[ key ] = mr.group(2)
|
||
local_iter+=1
|
||
# get bodyparts
|
||
parts_count = parts_count + get_bodyparts( mr.group(2) )
|
||
if local_iter == 1000000: # 5000 000 + 5000 000 :)
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:0::2: CSV file \"%s\"too big (it contatins more than 500 000 lines). Please split it." % (options.csvfilename)
|
||
leave(local_text)
|
||
#return csv_lines #500 000 sms via csv file is limit
|
||
return csv_lines
|
||
|
||
|
||
def parse_proxy_ip_port(proxy_string):
|
||
global proxy_ip, proxy_port
|
||
p_proxy = re.compile('^(.*):(.*)$', re.IGNORECASE)
|
||
m_proxy = p_proxy.match( proxy_string )
|
||
if m_proxy:
|
||
proxy_ip = m_proxy.group(1)
|
||
proxy_port = m_proxy.group(2)
|
||
else:
|
||
p_proxy = re.compile('^(.*)$', re.IGNORECASE)
|
||
m_proxy = p_proxy.match( proxy_string )
|
||
if m_proxy:
|
||
proxy_ip = m_proxy.group(1)
|
||
proxy_port = proxy_default_port
|
||
|
||
|
||
def parse_proxy_user_password(user_password):
|
||
global proxy_user, proxy_password
|
||
p_proxy = re.compile('^(.*):(.*)$', re.IGNORECASE)
|
||
m_proxy = p_proxy.match( user_password )
|
||
if m_proxy:
|
||
proxy_user = m_proxy.group(1)
|
||
proxy_password = m_proxy.group(2)
|
||
|
||
def proxy_connect(p_ip, p_user, p_pass, p_host, p_host_port, p_port=proxy_default_port):
|
||
'''
|
||
connect to proxy with or without authorization
|
||
p_ip - proxy ip
|
||
p_port - proxy port, default proxy_default_port
|
||
p_user - proxy login (can be empty string)
|
||
p_pass - proxy password (can be empty string)
|
||
p_host - host ip or name to what should the proxy connect to
|
||
p_host_port - host port to what should the proxy connect to
|
||
'''
|
||
global proxy_use
|
||
|
||
base64string = base64.encodestring('%s:%s' % (p_user, p_pass))[:-1]
|
||
authheader = "Basic %s" % base64string
|
||
#setup basic authentication
|
||
if p_user and p_pass:
|
||
proxy_authorization = 'Proxy-authorization: ' + authheader + '\r\n'
|
||
else:
|
||
proxy_authorization = ''
|
||
proxy_conn = 'CONNECT %s:%s HTTP/1.0\r\n' % (p_host, p_host_port)
|
||
user_agent = 'User-Agent: python\r\n'
|
||
proxy_pieces = proxy_conn + proxy_authorization + user_agent + '\r\n'
|
||
|
||
#now connect, very simple recv and error checking
|
||
proxy = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
# proxy.settimeout(http_timeout)
|
||
# proxy.setdefaulttimeout( http_timeout ) # in seconds
|
||
try:
|
||
proxy.connect((p_ip, p_port))
|
||
proxy.sendall(proxy_pieces)
|
||
response = proxy.recv(8192)
|
||
if options.verbose:
|
||
print "Response from proxy:"
|
||
print response
|
||
status = response.split()[1]
|
||
except:
|
||
status = 0
|
||
if status != str(200):
|
||
raise ConnectionError('Could not connect to proxy, status: ' + str(status))
|
||
else:
|
||
if p_host_port == 443:
|
||
#trivial setup for ssl socket
|
||
ssl = socket.ssl(proxy, None, None)
|
||
sock = httplib.FakeSocket(proxy, ssl)
|
||
#initalize httplib and replace with your socket
|
||
connection = httplib.HTTPConnection('localhost')
|
||
connection.sock = sock
|
||
else:
|
||
connection = httplib.HTTPConnection('localhost')
|
||
connection.sock = proxy
|
||
proxy_use = 1
|
||
if options.verbose: print "connected to proxy: %s(%s)" % (p_ip, p_port)
|
||
return connection
|
||
|
||
def process_http_longtime( ack ):
|
||
global config
|
||
|
||
# HTTP address of AWEG server
|
||
ip_name = options.addr
|
||
|
||
# Unless disabled by --allow-non-ssl, force HTTPS even if HTTP is given in '-a'
|
||
if not options.allow_non_ssl:
|
||
pr = re.compile('.*:\/\/(.*)') # strip 'proto://' out
|
||
mr = pr.match(ip_name)
|
||
if mr:
|
||
ip_name= "https://" + mr.group(1)
|
||
|
||
# This is for --spaces option
|
||
if options.spaces:
|
||
spaces = big_space
|
||
else:
|
||
spaces = ""
|
||
|
||
# Create HTTP connection object - Proxy mode
|
||
if options.proxy_string: # http://[<username>[:<password>]@]<hostname>[:port]
|
||
p_proxy = re.compile('^\w*://(.*)@(.*)$', re.IGNORECASE)
|
||
m_proxy = p_proxy.match( options.proxy_string )
|
||
if m_proxy:
|
||
parse_proxy_user_password( m_proxy.group(1) )
|
||
parse_proxy_ip_port( m_proxy.group(2) )
|
||
else:
|
||
p_proxy = re.compile('^\w*://(.*)$', re.IGNORECASE)
|
||
m_proxy = p_proxy.match( options.proxy_string )
|
||
if m_proxy:
|
||
parse_proxy_ip_port( m_proxy.group(1) )
|
||
else:
|
||
leave( time.strftime("%H:%M:%S ") + "STATUS:0::2: Proxy mismatch parametr.")
|
||
|
||
if options.verbose: print "Debug: Using proxy: proxy_ip=%s, proxy_port=%s, proxy_user=%s, proxy_password=%s" % (proxy_ip, proxy_port, proxy_user, proxy_password)
|
||
|
||
# set parameters for proxy_connect
|
||
pr = re.compile('^(\w*)://(.*)')
|
||
mr = pr.match(options.addr)
|
||
if mr:
|
||
if options.allow_non_ssl:
|
||
host_port = default_ports[mr.group(1)]
|
||
else:
|
||
host_port = default_host_port
|
||
proxy_host = get_domain(options.addr)
|
||
else:
|
||
host_port = default_host_port
|
||
proxy_host = options.addr
|
||
# set proxy connection
|
||
while (not http_conn):
|
||
try:
|
||
http_conn = proxy_connect(proxy_ip, proxy_user, proxy_password, proxy_host, host_port, int(proxy_port))
|
||
except ConnectionError, c_err:
|
||
# retry routine, max_retry_count , retry_pause
|
||
if retry_counter < max_retry_count:
|
||
retry_counter+=1
|
||
new_retry_pause = retry_counter * retry_pause
|
||
if options.spaces:
|
||
spaces = big_space
|
||
else:
|
||
spaces = ""
|
||
print "Debug: %s. Retry after %d seconds (%d/%d)%s" % ( c_err.value, new_retry_pause, retry_counter, max_retry_count, spaces )
|
||
time.sleep( new_retry_pause )
|
||
else:
|
||
# after max_retry_count, we can't connect, so stop processing and quit program
|
||
local_text = time.strftime("%H:%M:%S ") + "STATUS:0:0:2: error - %s" % c_err.value
|
||
leave(local_text)
|
||
retry_counter = 0
|
||
# Create HTTP connection object - direct non-proxy mode
|
||
else:
|
||
http_conn = httplib.HTTPSConnection(get_domain(options.addr))
|
||
|
||
request_sleep_first= 0; # The first request should return CONF: and set intervals
|
||
|
||
request_sleep= 290; # Default for request sleep time (interval A) - it is overriden by server by "CONF:" push at runtime
|
||
sleep_interval= 5; # Default for Sleep interval B - it is overriden by server by "CONF:" push at runtime
|
||
sleep_interval_error= 30; # Default for sleep interval C - it is overriden by server by "CONF:" push at runtime
|
||
connected= 0;
|
||
|
||
print time.strftime("%H:%M:%S ") + "TRYING:" + ip_name + spaces;
|
||
|
||
first_connect_time= time.strftime( "%Y%m%d%H%M%S", time.localtime() )
|
||
|
||
# Infinite loop that makes "smsbackend -G" to not exit (polling)
|
||
# Loop that run only once (if sending ack)
|
||
while 1:
|
||
# (a) create URL arguments
|
||
url_params = "auth=" + options.login
|
||
# For the first time, do 'fast' get
|
||
if ( request_sleep_first == 0 ):
|
||
request_sleep= 0
|
||
request_sleep_first= -1;
|
||
|
||
if ack:
|
||
url_params = url_params + "&limit=0&sleep=0&ack=" + str(ack)
|
||
else:
|
||
url_params = url_params + "&limit=1000&sleep=" + str(request_sleep)
|
||
|
||
be_version_escaped = urllib.quote_plus(version_short)
|
||
fe_version_escaped = urllib.quote_plus(options.frontend_name)
|
||
url_params = url_params + "&online_since=" + first_connect_time
|
||
url_params = url_params + "&be=" + be_version_escaped + "&fe=" + fe_version_escaped
|
||
|
||
url_string = ip_name + "/longtime?" + url_params
|
||
url_string_proxy = "/" + get_rest(options.addr) + "/longtime?" + url_params
|
||
|
||
if (options.verbose):
|
||
print "INFO: intervals: A=" + str(request_sleep) + " B=" + str(sleep_interval) + " C="+ str(sleep_interval_error) + " url: [" + url_string +"]"
|
||
|
||
# (b) send the request and read ouput
|
||
try:
|
||
if options.proxy_string: # is proxy connection
|
||
if options.verbose:
|
||
print "url_string_proxy (exact): ", url_string_proxy
|
||
# make http request
|
||
if options.verbose:
|
||
http_conn.set_debuglevel(3)
|
||
print "------ sending request"
|
||
http_conn.putrequest('GET', url_string_proxy)
|
||
http_conn.endheaders()
|
||
if options.verbose: print "------ getting response"
|
||
response = http_conn.getresponse()
|
||
data = response.read()
|
||
if options.verbose: print data
|
||
else:
|
||
# make http connection
|
||
http_request = urllib2.Request( url_string )
|
||
#socket.setdefaulttimeout( int(request_sleep)+30 ) # in seconds
|
||
opener = urllib2.build_opener()
|
||
urllib2.install_opener(opener)
|
||
# make http request
|
||
pagehandle = urllib2.urlopen(http_request)
|
||
data = pagehandle.read()
|
||
except Exception, inst:
|
||
#print("%s: %s"%(repr(inst), str(dir(inst))))
|
||
last_http_error= "Ex[%s]" % ( inst )
|
||
if hasattr(inst, 'reason'):
|
||
#print 'We failed to reach a server.'
|
||
#print 'Reason: ', inst.reason
|
||
last_http_error= "Failed to reach server: %s" % ( inst.reason )
|
||
elif hasattr(inst, 'code'):
|
||
#print 'The server couldn\'t fulfill the request.'
|
||
#print 'Error code: ', inst.code
|
||
last_http_error= "Server couldn\'t fulfill the request: HTTP %d" % ( inst.code )
|
||
elif hasattr(inst, 'args'):
|
||
#print 'Message: ', inst.args
|
||
last_http_error= "Exception message: %s" % ( str(inst.args) )
|
||
last_http_error= str(inst)
|
||
if options.verbose:
|
||
print "Exception:",
|
||
print inst
|
||
if sig_stop_generic_handler_flag:
|
||
leave("") # we return nothing because it is handled with sig_stop_generic_handler
|
||
connected= 0;
|
||
print time.strftime("%H:%M:%S ") + "CONNECTED:"+ str(connected) +":" + last_http_error + spaces
|
||
request_sleep= 0 # Next request after failure will be "fast"
|
||
if ( options.verbose ):
|
||
print "Sleep: interval C (" + str(sleep_interval_error) +"s) after failed HTTP request"
|
||
time.sleep(sleep_interval_error) # Sleep interval C - after failed HTTP request
|
||
continue
|
||
|
||
# (c) Now, HTTP request is successfully finished:
|
||
connected= 1;
|
||
|
||
# (c2) parse HTTP result - first line
|
||
if options.verbose: print "Response from server: [" + data + "]"
|
||
data = data.split("\n")
|
||
|
||
result_200ok= 0
|
||
first_line= data[0] # First line should contain numberic result from server
|
||
p = re.compile('^\s*(\d{3})\s*(.*)', re.IGNORECASE) # Regex for "200 OK" or "305 error message"
|
||
m = p.match( first_line )
|
||
if m:
|
||
status = m.group(1) # 200 or 304 or 305 etc.
|
||
status_text = m.group(2) # any message after the number
|
||
if ( status == "200" ):
|
||
result_200ok= 1
|
||
|
||
if ( result_200ok ):
|
||
connected= 1
|
||
connected_msg= ""
|
||
else:
|
||
connected= 0
|
||
connected_msg= first_line
|
||
|
||
# (c3) parse HTTP result - second line (may contain CONF:U_anumber)
|
||
if ( data[1][:15] == "CONF:U_anumber=" ): # CONF:U_anumber=420495412012
|
||
connected_msg= data[1][5:] # U_anumber=420495412012
|
||
|
||
if ( not ack ):
|
||
print time.strftime("%H:%M:%S ") + "CONNECTED:"+ str(connected) +":" + connected_msg + spaces
|
||
|
||
# (c4) parse HTTP result - other lines
|
||
for line in data:
|
||
if line == "":
|
||
continue
|
||
if ( line[:2] == "SM" ):
|
||
print time.strftime("%H:%M:%S ") + line + spaces
|
||
if ( line[:6] == "REPORT" ):
|
||
print time.strftime("%H:%M:%S ") + line + spaces
|
||
if ( line[:4] == "CONF" ):
|
||
# CONF:something=foo bar
|
||
dvojtecka= line.find(":") # --^ ^
|
||
rovnitko= line.find("=") # ------------+
|
||
conf_variable= line[dvojtecka+1:rovnitko]
|
||
conf_value= line[rovnitko+1:]
|
||
#print " Conf [" + conf_variable + "] [" + conf_value + "]"
|
||
|
||
# Known Config-push values:
|
||
if ( conf_variable == "intervalA" ): # "sleep" argument for next request
|
||
request_sleep= int(conf_value);
|
||
if ( conf_variable == "intervalB" ): # Sleep interval beetween HTTP requests (after success)
|
||
sleep_interval= int(conf_value);
|
||
if ( sleep_interval < 1 ): sleep_interval=1 # Safety
|
||
if ( conf_variable == "intervalC" ): # Sleep interval beetween HTTP requests (after failed request)
|
||
sleep_interval_error= int(conf_value);
|
||
if ( line[:4] == "INFO" ):
|
||
print time.strftime("%H:%M:%S ") + line + spaces
|
||
|
||
if ( ack ):
|
||
break
|
||
|
||
# Sleep interval C - after failed HTTP request
|
||
# (even if HTTP-level request was ok, but response body does not contain "200 OK", we consider request as failure)
|
||
if ( not result_200ok ):
|
||
if ( options.verbose ):
|
||
print "Sleep: interval C (" + str(sleep_interval_error) +"s) after failed HTTP request"
|
||
time.sleep(sleep_interval_error)
|
||
continue
|
||
|
||
# Sleep interval B - after successfull HTTP request
|
||
if ( options.verbose ):
|
||
print "Sleep: interval B (" + str(sleep_interval) + "s) after successfull HTTP request"
|
||
time.sleep(sleep_interval)
|
||
# end while 1
|
||
|
||
|
||
def leave(text):
|
||
global DR_FILE;
|
||
print text
|
||
if options.dr_file and dr_file_write_flag:
|
||
if sig_stop_generic_handler_flag: # must open DR_FILE again
|
||
dr_file_name = options.dr_file
|
||
try:
|
||
dr_file_name = options.dr_file % bulk_number
|
||
except:
|
||
dr_file_name = options.dr_file
|
||
DR_FILE = open(dr_file_name,"w") # if file exists will overwrite
|
||
if DR_FILE_LIST:
|
||
DR_FILE_LIST.sort()
|
||
for record in DR_FILE_LIST:
|
||
DR_FILE.write(record)
|
||
DR_FILE.close()
|
||
sys.exit(1)
|
||
|
||
|
||
#def encrypt(text):
|
||
# """
|
||
# Import a public key, and encrypt some data
|
||
# return ecrypted base64 encoded text
|
||
# """
|
||
# global public_key
|
||
#
|
||
# if options.verbose: print "ecrypting this string: ", text
|
||
# # Create a key object
|
||
# k = ezPyCrypto.key()
|
||
#
|
||
# # Read in a public key
|
||
# try:
|
||
# fd = open(public_key, "rb")
|
||
# pubkey = fd.read()
|
||
# fd.close()
|
||
# except:
|
||
# print "Can't open public key: ", public_key
|
||
#
|
||
# # import this public key
|
||
# k.importKey(pubkey)
|
||
#
|
||
# # Now encrypt some text against this public key
|
||
# enc = k.encStringToAscii(text)
|
||
#
|
||
# # we muset remove those strings to make enc usefull
|
||
# #del <StartPycryptoMessage>
|
||
# p = re.compile('<StartPycryptoMessage>')
|
||
# enc = p.sub( '', enc)
|
||
#
|
||
# #del <EndPycryptoMessage>
|
||
# p = re.compile('<EndPycryptoMessage>')
|
||
# enc = p.sub( '', enc)
|
||
#
|
||
# #del \n
|
||
# p = re.compile('\n')
|
||
# enc = p.sub( '', enc)
|
||
#
|
||
# if options.verbose: print "Encrypt output:\n\n%s\n\n" % enc
|
||
#
|
||
# return enc
|
||
|
||
|
||
def chomp(s):
|
||
if s[-2:] == '\r\n':
|
||
return s[:-2]
|
||
if s[-1:] == '\r' or s[-1:] == '\n':
|
||
return s[:-1]
|
||
return s
|
||
|
||
|
||
def get_bodyparts(text_line):
|
||
char_counts = len(text_line)
|
||
|
||
# it can't be, because null text is not allowed and if it go here with null text if it't fault and we send sms, so return 1 as 1 sms
|
||
if not char_counts:
|
||
parts = 1 # ! see comment above
|
||
return parts
|
||
|
||
if options.true160:
|
||
parts = 1+(char_counts-1)/160
|
||
elif char_counts > 160:
|
||
parts = 1+(char_counts-1)/156
|
||
else:
|
||
parts = 1
|
||
|
||
return parts
|
||
|
||
|
||
def sig_stop_generic_handler(signum, frame):
|
||
global config
|
||
if options.verbose: print 'Signal handler called with signal', signum
|
||
global sig_stop_generic_handler_flag
|
||
sig_stop_generic_handler_flag = 1
|
||
if not (config == ""):
|
||
local_text = "CONFIG:%s\n" % (config)
|
||
else:
|
||
local_text = ""
|
||
local_text = local_text + "STATUS:%d:%d:2:Program terminated (signal %d received)." % (sent_sms, rest_sms, signum)
|
||
leave(local_text)
|
||
|
||
|
||
def main():
|
||
if options.verbose: print "\nmain_procedure"
|
||
if options.verbose: print options
|
||
if options.verbose: print sys.argv
|
||
|
||
if __name__=='__main__':
|
||
#parse options & arguments
|
||
usage = "usage: %prog [options] \"message text up to 800 characters\""
|
||
parser = OptionParser(usage=usage)
|
||
parser.add_option("-v", "--version", action="store_true", dest="version", default=False, help = "print program version")
|
||
parser.add_option("-V", "--verbose", action="store_true", dest="verbose", default=False, help="don't print status messages to stdout")
|
||
parser.add_option("-s", "--detectport", action="store_true", dest="scan", default=False, help="Ignored (backwards compatibility)")
|
||
parser.add_option("-m", "--modemport", dest="modemport", default=0, help="Ignored (backwards compatibility)", metavar=" <number>")
|
||
parser.add_option("-p", "--protocol", dest="protocol", default=False, help="Obsolete, do not use, ignored", metavar=" http")
|
||
parser.add_option("-a", "--addr", dest="addr", help="URL of AWEG server (example: https://aweg.maternacz.com/)", metavar=" <server URL>")
|
||
parser.add_option("-i", "--initstring", dest="initstring", help="only for -p modem ... init sting like \"ATX1L2M0\"", metavar=" <initstring>")
|
||
parser.add_option("-d", "--destination", dest="destination", help="destination number(s), separated by comma (no space!) e.g. 123456789,00420223344559", metavar=" <destination>")
|
||
parser.add_option("-f", "--file", dest="csvfilename", help="File with messages(s) to read in form: #comment\t\t\t\t\t\t\tdestination1;text1\t\t\t\t\t\t\t\t\tdestinaton2;text2", metavar=" <filename>")
|
||
parser.add_option("-t", "--true160", action="store_true", dest="true160", default=False, help = "do not insert \"m/n \" in to the beggining of the split messages (longer than 160 characters) e.g. 1/2 ... 2/2 ")
|
||
parser.add_option("-T", "--force-tts", action="store_true", dest="force_tts", default=False, help = "Message will be converted to the voice via TTS and delivered as voice, even if the receiving terminal is capable of text delivery. This feature depends on AWEG server configuration.")
|
||
parser.add_option("-l", "--login", dest="login", help="for -p http, username and password", metavar=" <login:password>")
|
||
parser.add_option("-x", "--hex", action="store_true", dest="hexformat", default=False, help = "use this parameter if smstext is given at hexadecimal format")
|
||
parser.add_option("-u", "--uri", action="store_true", dest="uriformat", default=False, help = "use this parameter if smstext is given at URI escaped format")
|
||
parser.add_option("-P", "--proxy", dest="proxy_string", default=False, help = "URL for http proxy: http://[<username>[:<password>]@]<hostname>[:port]", metavar=" <proxy_string>")
|
||
parser.add_option("-M", "--spaces", action="store_true", dest="spaces", default=False, help = "Add 4kB of spaces to end of every STDOUT line")
|
||
parser.add_option("-D", "--delivery_file", dest="dr_file", default=False, help = "Delivery report template file will be written to <delivery_file>.", metavar=" <delivery_file>")
|
||
parser.add_option("-R", "--delivery_reports", dest="delivery_reports", default=False, help = "Print delivery reports.", metavar=" <last_timestamp>[,bulk_id]")
|
||
parser.add_option("-N", "--frontend_name", dest="frontend_name", default="-", help = "Frontend name and version, transferred via HTTP and logged on server.", metavar=" <name,version>")
|
||
parser.add_option("-A", "--user-anumber", action="store_true", dest="use_anumber", default=False, help = "Use customer's A-number as sender.")
|
||
parser.add_option("-G", "--get-messages", action="store_true", dest="get_messages", default=False, help = "Receiving MO SMS via HTTP. Messages or reports received in -G mode are returned as STDOUT, one per line.")
|
||
parser.add_option("--Ga", "--get-ack", dest="get_ack", default=False, help = "List of unique ID of messages (M) or delivery reports (R) previously received by client; such a message is considered as delivered (to client) and deleted by AWEG server. Comma-separated list of 10-digit snippets (M: or R: and 8-digit hexadecimal numbers). Maximum 256 IDs at a time. Prefix M: or R: can be omitted in second and subsequent snippets; in such a case, the same prefix as in previuos snippet is assumed.", metavar = "<M:id1[,M:id2,R:id3,...]>" )
|
||
parser.add_option("--allow-non-ssl", action="store_true", dest="allow_non_ssl", default=False, help = "SMSbackend will use HTTP protocol with no SSL encryption (if it is given in -a parameter) instead of HTTPS. As a default, HTTPS is forced even if http:// is given in -a parameter.")
|
||
parser.add_option("--email-reply", action="store_true", dest="email_reply", default=False, help = "Reply message to e-mail available.")
|
||
|
||
(options, args) = parser.parse_args()
|
||
|
||
# print version info
|
||
if options.version:
|
||
print version
|
||
sys.exit(1)
|
||
|
||
# -l --login is mandatory option in any case
|
||
if len(args) != 1 and not options.login:
|
||
msg= "Required option (--login) is missing"
|
||
print "STATUS:0::2: " + msg + "\n"
|
||
parser.error(msg + ". Please use -h for help.")
|
||
|
||
# -a --addr is mandatory option in any case
|
||
if len(args) != 1 and not options.addr:
|
||
msg= "Required option (--addr) is missing"
|
||
print "STATUS:0::2: " + msg + "\n"
|
||
parser.error(msg + ". Please use -h for help.")
|
||
|
||
# if no smstext is given and no csv or version or delivery_reports is requested, it means, that required opt are missing
|
||
if len(args) != 1 and not options.csvfilename and not options.delivery_reports and not options.get_messages and not options.get_ack:
|
||
#print "STATUS:0::2: No text specified.\n"
|
||
#parser.error("No text specified!")
|
||
print "STATUS:0::2: Required option is missing.\n"
|
||
parser.error("Required option is missing")
|
||
|
||
# we parse smstext only if no version or delivery_reports is requested
|
||
if not options.csvfilename and not options.delivery_reports and not options.get_messages and not options.get_ack:
|
||
global smstext
|
||
smstext = args[0]
|
||
#print "smstext: ", smstext
|
||
|
||
# signal handling
|
||
signal.signal( signal.SIGTERM, sig_stop_generic_handler )
|
||
signal.signal( signal.SIGINT, sig_stop_generic_handler )
|
||
#signal.signal( signal.SIGKILL, sig_stop_generic_handler ) # windows doesn't support this signal
|
||
|
||
options.addr_is_https= 0
|
||
if (options.addr):
|
||
|
||
# Put slash at the end of URL:
|
||
if ( options.addr[-1:] != "/" ):
|
||
options.addr= options.addr + "/"
|
||
|
||
|
||
# Unless disabled by --allow-non-ssl, force HTTPS even if HTTP is given in '-a'
|
||
options.addr_is_https= 0
|
||
if not options.allow_non_ssl:
|
||
pr = re.compile('^http:\/\/(.*)') # strip 'proto://' out
|
||
mr = pr.match( options.addr )
|
||
if mr:
|
||
options.addr= "https://" + mr.group(1)
|
||
|
||
if ( options.addr[:5] == "https" ):
|
||
options.addr_is_https= 1
|
||
|
||
|
||
|
||
# sms to destination
|
||
if options.destination and smstext:
|
||
if options.verbose: print "RunMode: sending single SMS to destinations(s) from commandline via [" + options.addr + "]"
|
||
#detect if hexformat and decode to ascii
|
||
if options.hexformat:
|
||
from binascii import b2a_hex, a2b_hex
|
||
smstext = a2b_hex(smstext)
|
||
if options.verbose: print "Hex decoded text: ", smstext
|
||
|
||
# try to decode smstext before another processing, only if -u is given
|
||
if options.uriformat: smstext = urllib.unquote_plus(smstext)
|
||
|
||
# HTTP request with retrying:
|
||
process_http()
|
||
|
||
# csv file
|
||
elif options.addr and options.csvfilename:
|
||
if options.verbose: print "RunMode: sending Bulk SMS from CSV file (" + options.csvfilename + ") via [" + options.addr + "]"
|
||
csv_lines = parse_csvfile()
|
||
# print how many parts we will send
|
||
print "\nPARTS:%d\n" % (parts_count)
|
||
#V if (not (options.allow_non_ssl and get_protocol(options.addr) == 'http')) and (not options.proxy_string):
|
||
#V # set connection to https
|
||
#V if options.verbose: print "prepare http(s) connection 2"
|
||
#V http_conn = httplib.HTTPSConnection(get_domain(put_slash(options.addr)))
|
||
process_http(csv_lines)
|
||
# delivery reports
|
||
elif options.delivery_reports:
|
||
#V if (not (options.allow_non_ssl and get_protocol(options.addr) == 'http')) and (not options.proxy_string):
|
||
#V # set connection to https
|
||
#V http_conn = httplib.HTTPSConnection(get_domain(put_slash(options.addr)))
|
||
delivery_reports()
|
||
# get messages (presence)
|
||
elif options.get_messages or options.get_ack:
|
||
process_http_longtime( options.get_ack )
|
||
|
||
# nothing
|
||
else:
|
||
print "STATUS:0::2: Missing one of required options or text argument\n"
|
||
parser.error("Missing one of required options or text argument")
|
||
|
||
main()
|
||
|
||
# ChangeLog
|
||
# 22.10.2008 vpithart
|
||
# Error message from HTTP layer (404 not found etc.) now correctly appears in STATUS: line
|
||
# 9.10.2008 vpithart
|
||
# URL-encoding of message text adjusted: space is now '%20' instead '+'
|
||
# 8.9.2008 vpithart
|
||
# Using new 'connected' message with aNumber
|
||
# CONNECTED:1:U_anumber=420234493002
|
||
# 13.8.2008 vpithart
|
||
# Error codes 32x now parsed
|
||
# 5.8.2008 vpithart
|
||
# --spaces now effective in -G mode
|
||
# 29.7.2008 vpithart
|
||
# bugfix: CONFIG: is not multiplied while sending CSV file (-f mode)
|
||
# 15.5.2008 Vojtech Pithart (build numbers replaced by SVN revision numbers)
|
||
# version 2.0
|
||
# removing all "serial" releated stuff
|
||
# http_conn is initialized in process_http
|
||
# 23.4.2007 Lucie Leistnerova (build 37)
|
||
# if server returns 315 (No matching route found), don't cancel
|
||
# sending and proceed
|
||
# 21.6.2006 lucie leistnerova (build 36)
|
||
# fixed bug with multiple config lines
|
||
# 5.4.2006 lucie leistnerova (build 35)
|
||
# argument --use-anumber added
|
||
# 5.4.2006 lucie leistnerova (build 34)
|
||
# argument --email-reply added
|
||
# 30.3.2006 lucie leistnerova (build 33)
|
||
# sorted drfile created
|
||
# 10.3.2006 lucie leistnerova (build 32)
|
||
# report=1 added to url when drfile is given
|
||
# backslash added to the end of url if only domain is given
|
Také k dispozici: Unified diff
Pridany skripty pro zasilani sms.