#!/usr/bin/python # coding=UTF-8 import urllib,urllib2 import socket import time from optparse import OptionParser from lxml import etree parser = etree.XMLParser(remove_blank_text=True) class OptionParser(OptionParser): def check_required (self, opt): option = self.get_option(opt) # Assumes the option's 'default' is set to None! if getattr(self.values, option.dest) is None: self.error("%s option not supplied" % option) cmd_parser = OptionParser() cmd_parser.add_option("--host", help="address of this small server (typically, it's the ip of this computer)") cmd_parser.add_option("--publication", help="address of the publication (e.g. http://localhost:9080/erp5Serv)") cmd_parser.add_option("-p", "--port", type="int", help="port used by this server (default is 1234)", default=1234) (options, args) = cmd_parser.parse_args() cmd_parser.check_required("--publication") cmd_parser.check_required("--host") #CONFIGURATION SECTION #address of this small server : #Host = '192.168.242.247' Host = options.host #address of the publication : #publication_url = 'http://localhost:9080/erp5Serv' publication_url = options.publication #address use to transmit the message received from the external client : to_url = publication_url+"/portal_synchronizations/readResponse" #port of this server : #Port = 1234 Port = options.port #address of the this server : syncml_server_url = 'http://%s:%s' % (Host, Port) #socket : sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # reuse the same socket # if already open #END CONFIGURATION SECTION CRLF = "\015\012" #in unix, it's the same as \r\n, and on windows, it's the same as \n (\r on mac) #this octal constant just increase a little this application portability def nodeToString(node): """ return an xml string corresponding to the node """ return etree.tostring(node, encoding='utf-8') def xml2wbxml(xml): """ convert xml string to wbxml using a temporary file """ import os # XXX we must check at the begining if xml2wbxml is installed # it seems that now there is a python biding for this : pywbxml f = open('/tmp/xml2wbxml', 'w') f.write(xml) f.close() os.system('/usr/bin/xml2wbxml -o /tmp/xml2wbxml /tmp/xml2wbxml') f = open('/tmp/xml2wbxml', 'r') wbxml = f.read() f.close() return wbxml def wbxml2xml(wbxml): """ convert wbxml string to xml using a temporary file """ import os f = open('/tmp/wbxml2xml', 'w') f.write(wbxml) f.close() os.system('/usr/bin/wbxml2xml -o /tmp/wbxml2xml /tmp/wbxml2xml') f = open('/tmp/wbxml2xml', 'r') xml = f.read() f.close() return xml def hexdump(raw=''): """ print raw in readable format without broke the terminal output ! """ buf = "" line = "" start = 0 done = False while not done: end = start + 16 max = len(raw) if end > max: end = max done = True chunk = raw[start:end] for i in xrange(len(chunk)): if i > 0: spacing = " " else: spacing = "" buf += "%s%02x" % (spacing, ord(chunk[i])) if done: for i in xrange(16 - (end % 16)): buf += " " buf += " " for c in chunk: val = ord(c) if val >= 33 and val <= 126: buf += c else: buf += "." buf += "\n" start += 16 return buf def getClientUrl(text): """ find the client url in the text and return it """ document = etree.XML(text, parser=parser) # XXX this xpath expression have to be rewrited in a generic way to handle # namspace client_url = '%s' % document.xpath('string(//SyncHdr/Source/LocURI)') # client_url = '%s' % document.xpath('string(//syncml:SyncHdr/syncml:Source/syncml:LocURI)', namespaces={'syncml':'SYNCML:SYNCML1.2'}) return client_url def sendResponse(text, to_url, client_url): """ send the message receive from the external client to erp5 server """ result = None opener = urllib2.build_opener() urllib2.install_opener(opener) to_encode = {} print '\nsendResponse...' text = wbxml2xml(text) text = text.replace(syncml_server_url, publication_url) text = text.replace(client_url, syncml_server_url) print "text = ",text to_encode['text'] = text to_encode['sync_id'] = 'Person' headers = {'Content-type': 'application/vnd.syncml+xml'} encoded = urllib.urlencode(to_encode) data=encoded request = urllib2.Request(url=to_url, data=data) try: result = urllib2.urlopen(request).read() except socket.error, msg: print 'error, url:%s ,data : %s'%(to_url, data) except urllib2.URLError, msg: print "sendResponse, can't open url : %s" % to_url return result def main(): sock.bind((Host,Port)) # we just listen to one and unique connection sock.listen(1) text = '' # the script stop here until a client connect to him print 'wait for a client connection...' client, address = sock.accept() print "the host ",address," is connected." while 1: print('\n\nwait for message ...') msg = client.recv(1024) # we receive 1024 caracter max if not msg: # if we receive nothing break elif not msg.startswith('POST'): text = text + msg if text.endswith('\x01\x01'): client_url = getClientUrl(wbxml2xml(text)) response = sendResponse(text=text, to_url=to_url, client_url=client_url) if response: response = response.replace(syncml_server_url, client_url) response = response.replace(publication_url, syncml_server_url) print "\nresponse = \n",response response = xml2wbxml(response) print "response send to the phone :\n", hexdump(response) date_to_print = time.strftime("%a, %d %b %Y %H:%M:%S GMT") head = CRLF.join(( "HTTP/1.1 200 OK", "Date: %s GMT" % date_to_print, "Server: myPythonServer", "Content-Length: %s" % len(response), "Content-Type: application/vnd.syncml+wbxml", )) message = "%s%s%s%s" % (head, CRLF, CRLF, response) #here it's necessary to have 2 CRLF, for more details #see http://www.w3.org/Protocols/rfc2616/rfc2616.html client.send(message) text='' else: print "this message is a POST header." sock.close() if __name__ == "__main__": main()