FreeSwitch send receive Fax

Receiving fax in FreeSwitch is quite simple with mod_spandsp, but managing these faxes can be complex.

To help you, i decided to put here all the informations i have about my FreeSwitch configuration to receive faxes. Once you’ll be done with this tutorial, you sould have a nice system which will receive faxes and manage them easily.

STEP 1

Configuration of mod_spandsp
${CONF}/autoload_configs/spandsp.conf.xml You shouldn’t have to modify this file!
${CONF}/autoload_configs/fax.conf.xml

<configuration name="fax.conf" description="FAX application configuration">
    <settings>
    <param name="use-ecm"       value="true"/>
    <param name="verbose"       value="false"/> <!-- For debugging -->
    <param name="disable-v17"   value="false"/>
    <param name="ident"         value="My FAX IDENT"/> <!-- Fax IDENT -->
    <param name="header"        value="My FAX HEADER"/> <!-- Fax HEADER -->
    <param name="spool-dir"     value="/tmp"/> <!-- Répertoire de travail -->
    <param name="file-prefix"   value="faxrx"/> <!-- Sera ajouté au début du nom du fichier -->
    </settings>
</configuration>

Dialplan configuration

<extension name="inbound_t1_fax">
    <condition field="destination_number" expression="^0000$"> <!-- Change for your own DID -->
       	<action application="set" data="session_in_hangup_hook=true"/> <!-- Pass fax headers at hangup -->
        <action application="set" data="api_hangup_hook=system ${base_dir}/scripts/process-fax.sh '\\\${fax_remote_station_id}' '\\\${fax_local_station_id}' '\\\${fax_document_transferred_pages}' '\\\${fax_document_total_pages}' '\\\${fax_result_text}' '${caller_id_number}' '${uuid}'"/> <!-- Script who receive fax headers at hangup-->
       	<action application="python" data="process-rxfax"/> <!-- Fax reception management script -->
       	<action application="hangup"/>
    </condition>
</extension>

process-rxfax.py
Based on steffler.info

#
# Fax reception Python
# 2011-06-30
# David Laperle @ RSS
#

import sys                            # import the sys module for argv
import os                              # import the os modules
import time
import smtplib, email, datetime
import glob
import math
import string
from email.Encoders import encode_base64
from email.MIMEMultipart import MIMEMultipart
from email.MIMEAudio import MIMEAudio
from email.Header import Header
from email.MIMEText import MIMEText
from datetime import *
from freeswitch import *

## EDIT THESE VARIABLES TO MATCH YOUR NEEDS:

tiff2pscmd = '/usr/bin/tiff2ps'      # location of tiff2ps
tiff2pdfcmd = '/usr/bin/tiff2pdf'      # location of tiff2pdf
ncftpputcmd = '/usr/local/bin/ncftpput' # location of ncftpput
lprcmd = '/usr/bin/lpr'              # location of lpr
convertcmd = '/usr/bin/convert'      # location of convert
incomingfaxes = '/tmp/'              # change this to where your .tiffs are saved by mod_fax, trailing slash required
ftphost = 'host'
ftpuser = 'user'
ftppass = 'pass'
today = date.today().strftime("%Y%m%d")
tiff2psoptions = '-a -O '              # command line options go here 
logfile = "/usr/local/freeswitch/log/rxfax_" + today + ".log"

def sendErrorToMail(subject,reason):
    email_from = "from@domain.com"
    email_rcpt = "to@domain.com"
    msg = MIMEMultipart('alternative')
    msg.set_charset('utf-8')
    sub = Header(subject,'utf-8')
    msg['Subject'] = sub
    msg['From'] = email_from
    msg['To'] = email_rcpt
    textv = reason
    part1 = MIMEText(textv,'plain','utf-8')
    msg.attach(part1)
    mailer = smtplib.SMTP("server.smtp.com",25)
    mailer.sendmail(email_from, email_rcpt, msg.as_string())
    mailer.quit()

def writeToLog(session,msg):
    global logfile
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    uuid = session.getVariable("uuid")
    towrite = now + " " + uuid + " " + msg + "\n"
    fh = open(logfile,"a")
    fh.write(towrite)
    fh.close()

def handler(session, args):

    #get required variables
    the_uuid = session.getVariable("uuid")
    the_recipient = session.getVariable("recipient")
    the_caller = session.getVariable("caller_id_number")
    the_dest = session.getVariable("destination_number")

    writeToLog(session,"Incoming Fax from " + the_caller)
    #test if FTP works before proceeding
    error = os.system(ncftpputcmd + " -m -u " + ftpuser + " -p " + ftppass + " " + ftphost + " / /usr/local/freeswitch/scripts/testfile.txt ")
    if error == 0:
        writeToLog(session,"Fax reception in progress ...")
        #answer the phone, save the fax file in .tiff format.    
        session.answer()
        session.execute("playback", "silence_stream://2000")
        session.execute("rxfax", incomingfaxes + "rxfax-" + the_uuid + ".tiff")
        writeToLog(session,"Fax reception done: " + incomingfaxes + "rxfax-" + the_uuid + ".tiff")
        # We split the TIFF so we can have different page size within the same fax
        writeToLog(session,"Splitting TIFF")
        os.system(convertcmd + " " + incomingfaxes + "rxfax-" + the_uuid + ".tiff " + incomingfaxes + "rxfax-" + the_uuid + "_%04d.tiff")
        # We send the TIFF to ps directly to the printer
        writeToLog(session,"Sending Fax to printer")
        pages = glob.glob(incomingfaxes + "rxfax-" + the_uuid + "_*.tiff")
        pages.sort()
        for page in pages:
            # For each page we want to be able to determine the height, but depending on the y-resolution, it can change a little, so we do a little math, to always put the height on the same resolution
            val = 0
            p = os.popen('/usr/bin/identify -format \"%h\" ' + page)
            height = p.readline()
            p.close()
            r = os.popen('/usr/bin/identify -format \"%y\" ' + page)
            resolution = r.readline()
            r.close()
            ardpi = string.split(resolution)
            dpi = int(ardpi[0])
            if dpi != 196:
                if dpi > 196:
                    val = (dpi/196)*int(height)
                else:
                    val = (196/dpi)*int(height)
            else:
                val = int(height)
            if int(val) > 2300:
                writeToLog(session,page + " is LEGAL!")
                os.system(tiff2pscmd + " -h14 " + page + " | " + lprcmd + " -P myprinter -o media=legal -o fitplot=true")
            else:
                writeToLog(session,page + " is LETTER!")
                os.system(tiff2pscmd + " -h11 " + page + " | " + lprcmd + " -P myprinter -o media=letter -o fitplot=true")
        writeToLog(session,"PDF creation in progress...")
        error = os.system(tiff2pdfcmd + " " + incomingfaxes + "rxfax-" + the_uuid + ".tiff -o " + incomingfaxes + "rxfax-" + the_uuid + ".pdf")
        if error == 0:
            # Generating JPG preview
            writeToLog(session,"Generating JPG preview file (continue on failure)...")
            os.system(convertcmd + " -scale 500 " + incomingfaxes + "rxfax-" + the_uuid + ".tiff[0] " + incomingfaxes + "rxfax-" + the_uuid + ".jpg")
            writeToLog(session,"Sending PDF to FTP server...")
            # We send the PDF on remote server
            error = os.system(ncftpputcmd + " -m -u " + ftpuser + " -p " + ftppass + " " + ftphost + " / " + incomingfaxes + "rxfax-" + the_uuid + ".pdf ")
            os.system(ncftpputcmd + " -m -u " + ftpuser + " -p " + ftppass + " " + ftphost + " / " + incomingfaxes + "rxfax-" + the_uuid + ".jpg ")
            if error == 0:
                # Everything went fine, party time!
                writeToLog(session, "Everything went fine, cleaning up tmp files...")
                os.system('rm -f ' + incomingfaxes + "rxfax-" + the_uuid + ".pdf " + incomingfaxes + "rxfax-" + the_uuid + ".tiff " + incomingfaxes + "rxfax-" + the_uuid + "_*.tiff " + incomingfaxes + "rxfax-" + the_uuid + ".jpg")
            else:
                # FTP failed, we're fucked!
                writeToLog(session,"FTP upload failed, hanging up")
                session.hangup()
        else:
            # PDF couldn't be created, we're fucked!
            #writeToLog(session,"PDF creation FAILED, hanging up")
            mysub = "Fax en provenance de " + the_caller + " à ÉCHOUÉ"
            mytxt = "Raison: Création du fichier PDF à échoué! Fichier à récupérer: " + incomingfaxes + "rxfax-" + the_uuid + ".tiff"
            writeToLog(session,mytxt)
            sendErrorToEmail(mysub,mytxt)
            session.hangup()
    else:
    # FTP host is not working, we cancel right now!
        mysub = "Fax en provenance de " + the_caller + " à ÉCHOUÉ"
        mytxt = "Raison: FTP sur " + ftphost + " est hors service! Erreur Fatal, impossible de récupérer!"
        writeToLog(session,mytxt)
        sendErrorToMail(mysub,mytxt)
        session.hangup()

process-fax.sh

# Réception des arguments
REMOTE_STATION=$1
LOCAL_STATION=$2
PAGES=$3
TOTAL_PAGES=$4
CODE=$5
CALLER=$6
UUID=$7

# On passe les variables à un script python qui fera le reste du travail
/usr/bin/python /usr/local/freeswitch/scripts/process-rxfax-mysql.py "$REMOTE_STATION" "$LOCAL_STATION" "$PAGES" "$TOTAL_PAGES" "$CODE" "$CALLER" "$UUID"

process-rxfax-mysql.py

import sys
import MySQLdb 

# Réception des paramètres
remote_station = sys.argv[1]
local_station = sys.argv[2]
pages = sys.argv[3]
total_pages = sys.argv[4]
code = sys.argv[5]
caller = sys.argv[6]
uuid = sys.argv[7]

#Variables MySQL
myHost = "mysqlhost"
myUser = "mysqluser"
myPass = "mysqlpass"
myDb = "mysqldb"

conn = MySQLdb.connect (host = myHost,
                           user = myUser,
                           passwd = myPass,
                           db = myDb)
cursor = conn.cursor()
cursor.execute("""
    INSERT INTO rss_faxes(uuid,caller_id,remote_station,local_station,pages,pages_total,code,date_received)
    VALUES
    (%s,%s,%s,%s,%s,%s,%s,NOW())
""", (uuid,caller,remote_station,local_station,pages,total_pages,code))

Structure de la DB MySQL

-- ----------------------------
-- Table structure for `rss_faxes`
-- ----------------------------
DROP TABLE IF EXISTS `rss_faxes`;
CREATE TABLE `rss_faxes` (
  `id_fax` INT(8) NOT NULL AUTO_INCREMENT,
  `uuid` VARCHAR(255) NOT NULL,
  `caller_id` VARCHAR(255) NOT NULL,
  `remote_station` VARCHAR(255) NOT NULL,
  `local_station` VARCHAR(255) NOT NULL,
  `pages` VARCHAR(3) NOT NULL,
  `pages_total` VARCHAR(3) NOT NULL,
  `code` VARCHAR(255) NOT NULL,
  `date_received` datetime NOT NULL,
  PRIMARY KEY  (`id_fax`)
) ENGINE=MyISAM AUTO_INCREMENT=95 DEFAULT CHARSET=latin1;

After that, all you have to do, is make a little interface to show these MySQL entries.