- Created by Marc Dumontier, last modified on Feb 24, 2017
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
« Previous Version 2 Current »
The following is a method to access the SRFax API via SOAP
Preface
This is a basic installation of a mail to the SR fax server for use with Oscar.
Document Version History
- v1.0 – initial public release to oscarmanual.org – May 10, 2016
copyright ©2016 by Peter Hutten-Czapski MD under the Creative Commons Attribution-Share Alike 3.0 Unported License
Contents
|
Prerequisites
It is assumed that
- You have installed a Debian based Linux (tested on Ubuntu 14+)
We recommend Ubuntu 16.04 LTS with full disk encryption - You have a basic level of Linux knowledge
- You can open a Linux terminal
- You can cut and paste EXACTLY the following instructions
NOTE: Firefox will copy with Control+C while a Linux terminal requires Shift+Control+V for paste
Installing Server Packages
An Internet Fax Gateway is a commercial subscription service that allows for conversion of email to fax and vice versa, fax to email. The main advantage of this service over the Hylafax method is that the phone line and the modem are provided. The main costs are the need for internet connectivity and the gateway subscription cost. Apply for an account at SR fax and then with those particulars proceed
Install Python2. NOTE in Ubuntu 16.04 you need to use apt-get install pyton2.7 as python installs python3
sudo apt-get install python pip
Now you need to install the python module suds. (NOTE if you want to port the below script to Python 3 you will need suds-py3)
sudo pip install suds
Now Uncoil Your Python Script
Use your favourite text editor and load the following. NOTE for Ubuntu 16.04 use the following hash bang
#!/usr/bin/env python2.7
#!/usr/bin/env python #from srfax import srfax # Copyright 2012 Vingd, Inc. under MIT liscence # https://github.com/vingd/srfax-api-python/blob/master/LICENSE.txt # extended by PHC import sys, getopt # -*- coding: utf-8 -*- '''SRFax (www.srfax.com) python library''' import re import json import os.path import base64 import logging import time import suds URL = 'https://www.srfax.com/SRF_UserFaxWebSrv.php?wsdl' LOGGER = logging.getLogger(__name__) RE_E164 = re.compile(r'^\+\d{7,15}$') RE_NANP = re.compile(r'^\+1') class SRFaxError(Exception): '''SRFax Exception''' def __init__(self, error_code, message, cause=None, retry=False): self.error_code = error_code self.message = message self.cause = cause self.retry = retry super(SRFaxError, self).__init__(error_code, message, cause, retry) LOGGER.exception("%s" % (self)) def get_error_code(self): '''Get exception error code''' return self.error_code def get_cause(self): '''Get exception cause''' return self.cause def get_retry(self): '''Get retry option (should we retry the request?)''' return self.retry class SRFax(object): '''SRFax class''' def __init__(self, access_id, access_pwd, caller_id=None, sender_email=None, account_code=None, url=None): self.access_id = access_id self.access_pwd = access_pwd self.caller_id = caller_id self.sender_email = sender_email self.account_code = account_code self.url = url or URL self.client = suds.client.Client(self.url) def queue_fax(self, to_fax_number, filepath, cover_subject, caller_id=None, sender_email=None, account_code=None): '''Queue fax for sending''' to_fax_number = SRFax.verify_fax_numbers(to_fax_number) fax_type = 'BROADCAST' if len(to_fax_number) > 1 else 'SINGLE' to_fax_number = '|'.join(to_fax_number) if isinstance(filepath, basestring): filepath = [filepath] if not isinstance(filepath, list): raise TypeError('filepath not properly defined') if len(filepath) > 5: raise Exception('More than 5 files defined in filepath') # Display input and output file name passed as the args print ("2fax number : %s and input file: %s with subject: %s" % (fax_no,fax_file,cover_subject) ) params = { 'access_id': self.access_id, 'access_pwd': self.access_pwd, 'sCallerID': caller_id or self.caller_id, 'sSenderEmail': sender_email or self.sender_email, 'sFaxType': fax_type, 'sToFaxNumber': to_fax_number, 'sAccountCode': account_code or self.account_code or '', 'sRetries': '2', 'sCPSubject': cover_subject, 'sFaxFromHeader': 'Haileybury Family Health Team', # 'sRetries': retries or self.retries or '', # 'sCoverPage': cover_page or self.cover_page or '', # 'sFaxFromHeader': fax_from_header or self.fax_from_header or '', # 'sCPFromName': cover_from or self.cover_from or '', # 'sCPToName': cover_to or self.cover_to or '', # 'sCPSubject': cover_subject or self.cover_subject or '', # 'sCPComments': cover_comments or self.cover_comments or '', # 'sQueueFaxDate': fax_date or self.fax_date or '', # 'sQueueFaxTime': fax_time or self.fax_time or '', } SRFax.verify_parameters(params) for i in range(len(filepath)): path = filepath[i] basename = os.path.basename(path) if not isinstance(basename, unicode): basename = basename.decode('utf-8') params['sFileName_%d' % (i + 1)] = basename params['sFileContent_%d' % (i + 1)] = SRFax.get_file_content(path) return self.process_request('Queue_Fax', params) def get_fax_status(self, fax_id): '''Get fax status''' params = { 'access_id': self.access_id, 'access_pwd': self.access_pwd, 'sFaxDetailID': fax_id, } SRFax.verify_parameters(params) response = self.process_request('Get_FaxStatus', params) if len(response) == 1: response = response[0] return response def get_fax_inbox(self, period='ALL'): '''Get fax inbox''' params = { 'access_id': self.access_id, 'access_pwd': self.access_pwd, 'sPeriod': period, } SRFax.verify_parameters(params) return self.process_request('Get_Fax_Inbox', params) def get_fax_outbox(self, period='ALL'): '''Get fax outbox''' params = { 'access_id': self.access_id, 'access_pwd': self.access_pwd, 'sPeriod': period, } SRFax.verify_parameters(params) return self.process_request('Get_Fax_Outbox', params) def retrieve_fax(self, fax_filename, folder): '''Retrieve fax content in Base64 format''' params = { 'access_id': self.access_id, 'access_pwd': self.access_pwd, 'sFaxFileName': fax_filename, 'sDirection': folder, } SRFax.verify_parameters(params) response = self.process_request('Retrieve_Fax', params) if len(response) == 1: response = response[0] return response def delete_fax(self, fax_filename, folder): '''Delete fax files from server''' if isinstance(fax_filename, str): fax_filename = [fax_filename] if not isinstance(fax_filename, list): raise TypeError('fax_filename not properly defined') if len(fax_filename) > 5: raise Exception('More than 5 files defined in fax_filename') params = { 'access_id': self.access_id, 'access_pwd': self.access_pwd, 'sDirection': folder, } SRFax.verify_parameters(params) for i in range(len(fax_filename)): params['sFileName_%d' % (i + 1)] = fax_filename[i] return self.process_request('Delete_Fax', params) def process_request(self, method, params): '''Process SRFax SOAP request''' method = getattr(self.client.service, method) try: response = method(**params) # pylint: disable-msg=W0142 except Exception as exc: raise SRFaxError('REQUESTFAILED', 'SOAP request failed', cause=exc, retry=True) return SRFax.process_response(response) @staticmethod def process_response(response): '''Process SRFax SOAP response''' if not response: raise SRFaxError('INVALIDRESPONSE', 'Empty response', retry=True) if 'Status' not in response or 'Result' not in response: raise SRFaxError('INVALIDRESPONSE', 'Status and/or Result not in response: %s' % (response), retry=True) result = response['Result'] try: if isinstance(result, list): for i in range(len(result)): if not result[i]: continue if isinstance(result[i], suds.sax.text.Text): result[i] = str(result[i]) else: result[i] = json.loads(json.dumps(dict(result[i]))) elif isinstance(result, suds.sax.text.Text): result = str(result) except Exception as exc: raise SRFaxError('INVALIDRESPONSE', 'Error converting SOAP response', cause=exc, retry=True) LOGGER.debug('Result: %s' % (result)) if response['Status'] != 'Success': errmsg = result if (isinstance(errmsg, list) and len(errmsg) == 1 and 'ErrorCode' in errmsg[0]): errmsg = errmsg[0]['ErrorCode'] raise SRFaxError('REQUESTFAILED', errmsg) if result is None: result = True return result @staticmethod def verify_parameters(params): '''Verify that dict values are set''' for key in params.keys(): print ("key : %s and value : %s " % (key, params[key]) ) if params[key] is None: raise TypeError('%s not set' % (key)) @staticmethod def is_e164_number(number): '''Simple check if number is in E.164 format''' if isinstance(number, str) and RE_E164.match(number): return True return False @staticmethod def is_nanp_number(number): '''Simple check if number is inside North American Numbering Plan''' if isinstance(number, str) and RE_NANP.match(number): return True return False @staticmethod def verify_fax_numbers(to_fax_number): '''Verify and prepare fax numbers for use at SRFax''' if isinstance(to_fax_number, basestring): to_fax_number = [to_fax_number] if not isinstance(to_fax_number, list): raise TypeError('to_fax_number not properly defined') for i in range(len(to_fax_number)): number = str(to_fax_number[i]) if not SRFax.is_e164_number(number): raise TypeError('Number not in E.164 format: %s' % (number)) if SRFax.is_nanp_number(number): to_fax_number[i] = number[1:] else: to_fax_number[i] = '011' + number[1:] return to_fax_number @staticmethod def get_file_content(filepath): '''Read and return file content Base64 encoded''' if not os.path.exists(filepath): raise Exception('File does not exists: %s' % (filepath)) if not os.path.isfile(filepath): raise Exception('Not a file: %s' % (filepath)) content = None try: fdp = open(filepath, 'rb') except IOError: raise else: content = fdp.read() fdp.close() if not content: raise Exception('Error reading file or file empty: %s' % (filepath)) return base64.b64encode(content) # Store input and output file names fax_no='' fax_file='' cover_subject='' # Read command line args myopts, args = getopt.getopt(sys.argv[1:],"n:f:s:") ############################### # o == option # a == argument passed to the o ############################### for o, a in myopts: if o == '-n': fax_no=a elif o == '-f': fax_file=a elif o == '-s': cover_subject=a else: print("Usage: %s -n faxnumber -f file -s subject" % sys.argv[0]) sys.exit # Display input and output file name passed as the args print ("fax number : %s and input file: %s with subject: %s" % (fax_no,fax_file,cover_subject) ) srfax_client = SRFax(122222, "password", caller_id=8662441234, sender_email="ddd@hhhh.org") fax_id = srfax_client.queue_fax(fax_no, fax_file, cover_subject) time.sleep(30) status = srfax_client.get_fax_status(fax_id) print ("fax status : %s" % (status) ) #outbox=srfax_client.get_fax_outbox() #print ("foutbox : %s" % (outbox) )
Of course you will need to alter the srfax_client line just above with your id, password, caller id, and account email
Save as srfax.py and make it executable
Now make it executable
chmod 777 srfax.py
Now a cron job
With the deb installation Oscar drops faxes into /tmp/tomcat6-tmp as matching txt files with the fax number and pdf for the image to be faxed. These files can be parsed by a script that runs regularly, and assembled into an email that is sent to the email to fax gateway provider.
Save the following as gateway.cron. (note that you should check the path for the tmp file, it can be /tmp/tomcat6-tomcat6-tmp/)
#!/bin/bash # # Fax Gateway cron # Picks up the files dropped by OSCAR for faxing # For Prestofax you need to replace <security code> with the number associated with your account # Make sure you adjust the paths and mutt switches appropriately # if test -n "$(find /tmp/tomcat7-tomcat7-tmp -maxdepth 1 -name '*.txt' -print -quit)"; then for f in `ls /tmp/tomcat7-tomcat7-tmp/*.txt`; do t=`echo $f | sed -e s"/\/tmp\/tomcat7-tomcat7-tmp\///" -e s"/prescription_/Rx-/" -e s"/[0-9]\{13\}\.txt//"` ./srfax.py -s "Oscar Fax $t" -n +1`sed s"/ *//g" $f|tr -d "\n"` -f `echo $f | sed s"/txt/pdf/"` < /dev/null rm $f; rm `echo $f | sed s"/txt/pdf/"`; done fi
Now make it executable
chmod 777 gateway.cron
And set it up as a cron job (you will need to run it as root or as tomcat6 user to open the files that are dropped by Oscar into the tmp directory.) The following example has the root user sending the email to the fax gateway
sudo crontab -e
And add an entry like the following that executes every 3 minutes
*/3 * * * * /home/peter/gateway.cron
- No labels