Home » 2005 » December » 25

Daily Archives: Sunday, December 25, 2005

The Most Versatile VoIP Provider: FREE PORTING

The Last Frontier: Handling Incoming Faxes with Asterisk

We've covered lots of Asterisk® territory these past few months, but one of the real stumbling blocks has been reliable incoming fax support. Yes, Asterisk has some fax support built into the product; however, to use it you have to invest in Digium® hardware. Not that we're cheap or anything (we are actually!), but we wanted a fax solution that didn't rely on hardware. Stated another way, we wanted a pure software solution to faxing just like Asterisk provides a pure software solution to PBX telephony for those that want to use it. And we wanted reliable delivery of faxes using VoIP lines including IAX and SIP trunks, not just Ma Bell's PSTN lines. Well, we've finally found a solution, and it works reliably with Asterisk@Home 1.5 as well as Asterisk@Home 2.x. For those not using Asterisk@Home, the solution we're covering works reliably with Asterisk 1.09 as well as Asterisk 1.2 and 1.2.1. This tutorial assumes you're using Asterisk@Home, but the concepts will work equally well with "pure Asterisk" assuming you know what you're doing.

2008 Update: This article has been updated to support Asterisk 1.4. Click here for the latest article.

2011 Update: This article has been updated to support Asterisk 1.8 using HylaFax, AvantFax, and IAXmodem. Click here for the latest article.

The way this works is pretty straightforward. A fax machine calls your regular Asterisk phone number, no dedicated fax line required. When Asterisk answers the call, it listens for a fax tone. If it hears one, it reroutes the incoming call to a context which then processes the incoming fax. The fax is saved as a TIFF image, converted to a PDF document, and emailed to the address specified in your Asterisk@Home setup: AMP->Setup->General Settings. If a fax tone isn't detected, the call is processed as an incoming voice call according to the rules you've set up for incoming calls.

Prerequisites. As previously noted, you won't need any special hardware to make our incoming fax solution work with Asterisk. All you need is Asterisk@Home and Newman Telecom's NVfaxdetect, which is FREE for the downloading. You'll also need the Fax-to-PDF conversion tools which are included with Asterisk@Home. Your system also must be configured to send emails reliably. If you followed our tutorial for setting up your Asterisk@Home system, you should be all set. Otherwise, start there in the Activating Email Delivery of VoiceMail Messages section. Finally, if you're using an IAX or SIP trunk, you'll need a line from a VoIP provider with an uncompressed codec (such as ulaw), and you need an ITSP that knows what it's doing and supports incoming faxes. HINT: TelaSIP lines work. BroadVoice lines usually don't.


An equally important requirement for this project is following along carefully with this tutorial. We're going to be recompiling a modified Asterisk as part of this project and, if you screw it up by not paying careful attention to the details, not only will your system not accept faxes, it probably won't accept phone calls either when we're finished. Having said that, there is nothing hard about this project. You just need to type carefully and not skip any steps. So let's get started.

Installing Fax-to-PDF Conversion Tools. Let's begin with the easy stuff. First, you'll need to install the Fax-To-PDF conversion tools that are already scripted in Asterisk@Home. Log in to your server as root. At the command prompt, type install-pdf. Your system will whir away for a couple minutes after which you'll get a completed message that looks something like this:

Running Transaction
Installing: xorg-x11-font-utils ######################### [1/8]
Installing: ttmkfdir ######################### [2/8]
Installing: xorg-x11-xfs ######################### [3/8]
Installing: chkfontpath ######################### [4/8]
Installing: urw-fonts ######################### [5/8]
Installing: VFlib2 ######################### [6/8]
Installing: ghostscript-fonts ######################### [7/8]
Installing: ghostscript ######################### [8/8]

Installed: ghostscript.i386 0:7.07-33
Dependency Installed: VFlib2.i386 0:2.25.6-25 chkfontpath.i386 0:1.10.0-2 ghostscript-fonts.noarch 0:5.50-13 ttmkfdir.i386 0:3.0.9-14.1.EL urw-fonts.noarch 0:2.2-6.1 xorg-x11-font-utils.i386 0:6.8.2-1.EL.13.20 xorg-x11-xfs.i386 0:6.8.2-1.EL.13.20
Complete!

For those running Asterisk with flavors of Linux other than CentOS, the install command you'll need above is yum -y install ghostscript.

Routing Your Incoming Faxes. To specify where you want incoming faxes delivered, start up the Asterisk Management Panel (AMP) by pointing a web browser to the IP address of your Asterisk server. Then choose AMP->Setup->General Settings. In the Fax Machine section, leave the extension of fax machine set to System. For the email fax delivery address, enter a valid email address where you want the PDF copies of faxes to be sent. Click the Submit Changes button and then click the red bar to reload Asterisk.


Downloading NVfaxdetect. Log in to your Asterisk server as root, stop Asterisk, and download NVfaxdetect using the following commands:

amportal stop
cd /usr/src/asterisk/apps
wget http://www.newmantelecom.com/download/asterisk/faxdetect/1.0.6/app_nv_faxdetect.c

NOTE: Some intermittent problems have been reported downloading the source code from Newman Telecom. If you have a problem, you can download the patched source code directly from Nerd Vittles. Here's the link for Asterisk@Home 1.5 systems:

wget http://nerdvittles.com/aah15/app_nv_faxdetect.c

And here's the link for Asterisk@Home 2.x systems:

wget http://nerdvittles.com/aah2/app_nv_faxdetect.c

Modifying the NVfaxdetect Source Code. Skip this section if you downloaded the source for your version of Asterisk@Home directly from Nerd Vittles. Otherwise, a change needs to be made in the NVfaxdetect source code whether you are running Asterisk@Home 1.5 or Asterisk@Home 2.x. Edit the source code by issuing the following command: nano -w app_nv_faxdetect.c.

If you're using Asterisk@Home 1.5, search for the word CALLERID in the source file: Ctrl-W, CALLERID. When it finds the match, you'll be in the middle of three lines of code that look like this:

// Use the second one for recent Asterisk releases
#define CALLERID_FIELD cid.cid_num
//#define CALLERID_FIELD callerid

Change these three lines so that the first define line is commented out and the second one is uncommented. Your code should look like the following when you're finished:

// Use the second one for recent Asterisk releases
//#define CALLERID_FIELD cid.cid_num
#define CALLERID_FIELD callerid

Once you complete the change, save the file: Ctrl-X, Y, then press Enter.

If you're using Asterisk@Home 2.x, we need to make a different change in the source code. Search for #include with the command: Ctrl-W, #include. When the match is found, move the cursor up one line, press the Enter key, and insert the following code:

  • Ignore the two dots in the right column above. WordPress quirk! Once you make this addition, save the file: Ctrl-X, Y, then press Enter.

    Adjusting Makefile. Regardless of your version of Asterisk@Home, we need to modify Makefile to include this code in the Asterisk executable: nano -w Makefile. With AAH 2.x, search for the word "experimental": Ctrl-W, experimental. Move down one line and press Enter. With AAH 1.5, search for the expression "#APPS+": Ctrl-W,#APPS+. Move up one line and press Enter. Now, with either version, add the following making sure there is no pound sign (#) at the beginning of the line:

    APPS+=app_nv_faxdetect.so

    Once you make this addition, save the file: Ctrl-X, Y, then press Enter.


    Recompiling Asterisk. Now we're ready to recompile and reinstall the Asterisk application with your new NVfaxdetect code. Issue the following commands:

    cd /usr/src/asterisk
    make
    make install

    You'll get some warning messages when the install completes. You can safely ignore them. When the install is finished, you can restart Asterisk and use the Command Line Interface (CLI) to make certain that NVfaxdetect installed properly:

    amportal start
    asterisk -r
    set verbose 10
    show application nvfaxdetect

    The following CLI message should be displayed:

    This application listens for fax tones (on IAX and SIP channels too)
    for waitdur seconds of time. In addition, it can be interrupted by digits,
    or non-silence. Audio is only monitored in the receive direction. If
    digits interrupt, they must be the start of a valid extension unless the
    option is included to ignore. If fax is detected, it will jump to the
    'fax' extension. If a period of non-silence greater than 'mindur' ms,
    yet less than 'maxdur' ms is followed by silence at least 'sildur' ms
    then the app is aborted and processing jumps to the 'talk' extension.
    If all undetected, control will continue at the next priority.
    waitdur: Maximum number of seconds to wait (default=4)
    options:
    'n': Attempt on-hook if unanswered (default=no)
    'x': DTMF digits terminate without extension (default=no)
    'd': Ignore DTMF digit detection (default=no)
    'f': Ignore fax detection (default=no)
    't': Ignore talk detection (default=no)
    sildur: Silence ms after mindur/maxdur before aborting (default=1000)
    mindur: Minimum non-silence ms needed (default=100)
    maxdur: Maximum non-silence ms allowed (default=0/forever)
    Returns -1 on hangup, and 0 on successful completion with no exit conditions.

    For questions or comments, please e-mail support@newmantelecom.com.

    Adjusting Your Dialplans to Implement Fax Detection. The final step in this process is to adjust the Asterisk dialplans to implement fax detection and process incoming faxes. Several different methods are shown below. Choose the one that applies to your deployment of Asterisk.


    For those using a standard Asterisk 1.5 or Asterisk 2.x install with no Stealth AutoAttendant, you'll need to make the following changes to both the [from-pstn-reghours] and [from-pstn-afthours] contexts in the extensions.conf config file.

    To modify [from-pstn-reghours], use the Asterisk Management Portal (AMP) to access AMP->Maintenance->Config Edit->extensions.conf->from-pstn-reghours. Delete all of the existing code:

    exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-reghours-nofax,s,1:2) ; if fax detection is disabled, then jump to from-pstn-nofax - else continue
    exten => s,2,Answer
    exten => s,3,Wait(1)
    exten => s,4,SetVar(intype=${INCOMING})
    exten => s,5,Cut(intype=intype,-,1)
    exten => s,6,GotoIf($[${intype} = EXT]?7:9) ; If INCOMING starts with EXT, then assume its an extension
    exten => s,7,Wait(3) ;wait 3 more second to make sure this isn't a fax before dialing someone
    exten => s,8,Goto(ext-local,${INCOMING:4},1)
    exten => s,9,GotoIf($[${intype} = GRP]?10:12) ; If INCOMING starts with GRP, then assume its a ring group
    exten => s,10,Wait(3)
    exten => s,11,Goto(ext-group,${INCOMING:4},1)
    exten => s,12,GotoIf($[${intype} = QUE]?13:15)
    exten => s,13,Wait(3)
    exten => s,14,Goto(ext-queues,${INCOMING:4},1)
    exten => s,15,Goto(${INCOMING},s,1) ; not EXT or GR1 - it's an auto attendant
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => h,1,Hangup

    Then insert the following new code in the [from-pstn-reghours] context and click the Update button:

    exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-reghours-nofax,s,1:2); if fax detection is disabled, then jump to from-pstn-nofax - else continue
    exten => s,2,Answer
    exten => s,3,Playtones(ring) ; play fake ring so caller doesn't wonder whats going on
    exten => s,4,NVFaxDetect(10) ; while playing ring sound, detect faxes for 2 rings (goes to "fax" extension if detected)
    exten => s,5,SetVar(intype=${INCOMING})
    exten => s,6,Cut(intype=intype,-,1)
    exten => s,7,GotoIf($[${intype} = EXT]?8:9) ; If INCOMING starts with EXT, then assume its an extension
    exten => s,8,Goto(ext-local,${INCOMING:4},1)
    exten => s,9,GotoIf($[${intype} = GRP]?10:11) ; If INCOMING starts with GRP, then assume its a ring group
    exten => s,10,Goto(ext-group,${INCOMING:4},1)
    exten => s,11,GotoIf($[${intype} = QUE]?13:14)
    exten => s,13,Goto(ext-queues,${INCOMING:4},1)
    exten => s,14,Goto(${INCOMING},s,1) ; not EXT or GRP - it's an auto attendant
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => h,1,Hangup

    Now we need to make a similar change in the [from-pstn-afthours] context. Replace the following code:

    exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-afthours-nofax,s,1:2) ; if fax detection is disabled, then jump to from-pstn-nofax - else continue
    exten => s,2,Answer
    exten => s,3,Wait(1)
    exten => s,4,SetVar(intype=${AFTER_INCOMING})
    exten => s,5,Cut(intype=intype,-,1)
    exten => s,6,GotoIf($[${intype} = EXT]?7:9) ; If INCOMING starts with EXT, then assume its an extension
    exten => s,7,Wait(3) ;wait 3 more second to make sure this isn't a fax before dialing someone
    exten => s,8,Goto(ext-local,${AFTER_INCOMING:4},1)
    exten => s,9,GotoIf($[${intype} = GRP]?10:12) ; If INCOMING starts with GRP, then assume its a ring group
    exten => s,10,Wait(3)
    exten => s,11,Goto(ext-group,${AFTER_INCOMING:4},1)
    exten => s,12,GotoIf($[${intype} = QUE]?13:15)
    exten => s,13,Wait(3)
    exten => s,14,Goto(ext-queues,${AFTER_INCOMING:4},1)
    exten => s,15,Goto(${AFTER_INCOMING},s,1) ; not EXT or GR1 - it's an auto attendant
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => h,1,Hangup

    Replace the above code with the following new code and then click the Update button:

    exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-afthours-nofax,s,1:2); if fax detection is disabled, then jump to from-pstn-nofax - else continue
    exten => s,2,Answer
    exten => s,3,Playtones(ring) ; play fake ring so caller doesn't wonder whats going on
    exten => s,4,NVFaxDetect(10) ; while playing ring sound, detect faxes for 2 rings (goes to "fax" extension if detected)
    exten => s,5,SetVar(intype=${AFTER_INCOMING})
    exten => s,6,Cut(intype=intype,-,1)
    exten => s,7,GotoIf($[${intype} = EXT]?8:9) ; If INCOMING starts with EXT, then assume its an extension
    exten => s,8,Goto(ext-local,${AFTER_INCOMING:4},1)
    exten => s,9,GotoIf($[${intype} = GRP]?10:11) ; If INCOMING starts with GRP, then assume its a ring group
    exten => s,10,Goto(ext-group,${AFTER_INCOMING:4},1)
    exten => s,11,GotoIf($[${intype} = QUE]?13:14)
    exten => s,13,Goto(ext-queues,${AFTER_INCOMING:4},1)
    exten => s,14,Goto(${AFTER_INCOMING},s,1) ; not EXT or GRP - it's an auto attendant
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => h,1,Hangup


    For those using the Nerd Vittles' Stealth AutoAttendant to process incoming calls, you'll need to adjust the extension 111 script in your extensions_custom.conf file. Here's the NVfaxdetect setup that we use which also includes the BlackList, CallerID, and Bluetooth Proximity Detection systems we've previously built. So, yes, we do eat our own dog food. Special note: We don't accept faxes on our Southern Bell PSTN line (111,6,Goto(9)). If you'd prefer to accept them, then change Goto(9) to Goto(7).

    exten => 111,1,LookupBlacklist ; If CID blacklisted, goto 102 ; Check for creeps
    exten => 111,2,Answer
    exten => 111,3,GotoIf($["${CALLERIDNUM:0:2}" = "00"]?4:7) ; Special Handling for SPA-3000
    exten => 111,4,SetCIDNum(${CALLERIDNUM:2})
    exten => 111,5,LookupBlacklist ; If CID blacklisted, goto 106 ; Gotta do it again after adjusting CallerID
    exten => 111,6,Goto(9)
    exten => 111,7,Playtones(ring) ; play fake ring so caller doesn't wonder whats going on
    exten => 111,8,NVFaxDetect(10) ; while playing ring sound, detect faxes for seconds (goes to "fax" extension if detected)
    exten => 111,9,SetMusicOnHold(default)
    exten => 111,10,Wait(1)
    exten => 111,11,GotoIf($["${CALLERIDNUM}" = ""]?who-r-u,s,1)
    exten => 111,12,GotoIf($["foo${CALLERIDNUM}" = "foo"]?who-r-u,s,1)
    exten => 111,13,GotoIf($["${CALLERIDNAME:0:9}" = "Anonymous"]?who-r-u,s,1)
    exten => 111,14,GotoIf($["${CALLERIDNAME:0:7}" = "Unknown"]?who-r-u,s,1)
    exten => 111,15,GotoIf($["${CALLERIDNUM:0:7}" = "Private"]?who-r-u,s,1)
    exten => 111,16,GotoIf($["${CALLERIDNAME:0:7}" = "Private"]?who-r-u,s,1)
    exten => 111,17,GotoIf($["${CALLERIDNUM:0:10}" = "Restricted"]?who-r-u,s,1)
    exten => 111,18,GotoIf($["${CALLERIDNAME:0:11}" = "OUT OF AREA"]?who-r-u,s,1)
    exten => 111,19,GotoIf($["${CALLERIDNUM:0:4}" = "PSTN"]?who-r-u,s,1)
    exten => 111,20,GotoIf($["${CALLERIDNUM:0:3}" = "199"]?who-r-u,s,1)
    exten => 111,21,GotoIfTime(${REGTIME}|${REGDAYS}|*|*?25)
    exten => 111,22,Background(silence/2)
    exten => 111,23,VoiceMail(204@default)
    exten => 111,24,Hangup
    exten => 111,25,AGI(calleridname.agi)
    exten => 111,26,Background(custom/welcome)
    exten => 111,27,DigitTimeout,2
    exten => 111,28,ResponseTimeout,2
    exten => 111,102,Goto(custom-blacklisted,s,1)
    exten => 111,106,Goto(custom-blacklisted,s,1)
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => t,1,Background(pls-hold-while-try)
    exten => t,2,AGI(homecheck.agi)
    exten => t,3,GotoIf($["${WARD:0:2}" = "OU"]?custom-outhouse,s,1:4)
    exten => t,4,Dial(local/222@from-internal,20,m)
    exten => t,5,VoiceMail(204@default)
    exten => t,6,Hangup
    exten => t,105,Voicemail(204@default)
    exten => t,106,Hangup
    exten => i,1,Playback(wrong-try-again-smarty)
    exten => i,2,Goto(111,26)
    exten => o,1,Macro(rg-group,20,,200-201-204-210)
    exten => o,2,VoiceMail(204@default)
    exten => o,3,Hangup
    exten => h,1,Hangup

    After you finish up the remainder of this tutorial and try a test fax, you may need to adjust the number in the NVfaxdetect lines (s,4 or 111,8) of the above contexts. The original tutorial we read on this topic had 4 for the value which was supposed to mean 4 seconds; however, Asterisk read it as 4 milliseconds. 10 works on both of our Asterisk systems, one version 1.5 and one version 2.2. YMMV. Others have had luck deleting the number altogether leaving just the open and closed parentheses. Try different approaches until it works reliably on your system. What you're striving for is just enough time to detect a fax without putting regular callers to sleep waiting on your fax detection mechanism to complete its task.


    Whether you're using a standard Asterisk@Home 1.5 or 2.x or the Stealth AutoAttendant, you'll also need to modify the incoming fax context. Just choose the code below that matches your version of Asterisk and replace the existing content in [ext-fax]: AMP->Maintenance->Config Edit->extensions.conf->ext-fax. Don't forget to restart Asterisk after making these changes. From a command prompt while logged in as root, the following command will do it in Asterisk@Home 2.2: amportal restart. For Asterisk@Home 1.5 users, it takes two commands: amportal stop then amportal start. Then you'll be ready to start receiving junk faxes just like we do. Enjoy!

    For Asterisk@Home 1.5 systems whether you're using the Stealth AutoAttendant or not, replace the existing contents of [ext-fax] with the following code. If you cut-and-paste, be sure to replace the typographic quote marks in line h,2 with regular quotation marks or expect smoke.

    exten => s,1,Answer
    exten => s,2,Goto(in_fax,1)
    exten => in_fax,1,StopPlaytones ; you must do this or it will play ring sounds over your fax
    exten => in_fax,2,GotoIf($[${FAX_RX} = system]?3:analog_fax,1)
    exten => in_fax,3,Macro(faxreceive)
    exten => in_fax,7,Hangup
    exten => analog_fax,1,GotoIf($[${FAX_RX} = disabled]?3:2) ;if fax is disabled, just hang up
    exten => analog_fax,2,Dial(${FAX_RX},20,d)
    exten => analog_fax,3,Hangup
    exten => out_fax,1,txfax(${TXFAX_NAME}|caller)
    exten => out_fax,2,Hangup
    exten => h,1,system(tiff2ps -2eaz -w 8.5 -h 11 ${FAXFILE} | ps2pdf - ${FAXFILE}.pdf)
    exten => h,2,system(mime-construct --to ${EMAILADDR} --subject "Fax from ${CALLERIDNUM} ${CALLERIDNAME}" --attachment ${CALLERIDNUM}.pdf --type application/pdf --file ${FAXFILE}.pdf)
    exten => h,3,system(rm ${FAXFILE} ${FAXFILE}.pdf)
    exten => h,4,Hangup()

    For Asterisk@Home 2.x systems whether you're using the Stealth AutoAttendant or not, replace the existing contents of [ext-fax] with the following code. If you cut-and-paste, be sure to replace the typographic quote marks in line h,2 with regular quotation marks or expect smoke.

    exten => s,1,Answer
    exten => s,2,Goto(in_fax,1)
    exten => in_fax,1,StopPlaytones ; you must do this or it will play ring sounds over your fax
    exten => in_fax,2,GotoIf($[${FAX_RX} = system]?3:analog_fax,1)
    exten => in_fax,3,Macro(faxreceive)
    exten => in_fax,7,Hangup
    exten => analog_fax,1,GotoIf($[${FAX_RX} = disabled]?3:2) ;if fax is disabled, just hang up
    exten => analog_fax,2,DBGet(DIAL=DEVICE/${FAX_RX}/dial);
    exten => analog_fax,3,Dial(${DIAL},20,d)
    exten => analog_fax,4,Hangup
    exten => out_fax,1,txfax(${TXFAX_NAME}|caller)
    exten => out_fax,2,Hangup
    exten => h,1,system(tiff2ps -2eaz ${FAXFILE} | ps2pdf - ${FAXFILE}.pdf)
    exten => h,2,system(mime-construct --to ${EMAILADDR} --subject "Fax from ${CALLERIDNUM} ${CALLERIDNAME}" --attachment ${CALLERIDNUM}.pdf --type application/pdf --file ${FAXFILE}.pdf)
    exten => h,3,system(rm ${FAXFILE} ${FAXFILE}.pdf)
    exten => h,4,Hangup()

    R.I.P. WordPress Cut-And-Paste Nightmare. For long-time readers of this column, you know what a royal pain cutting-and-pasting has been at Nerd Vittles thanks to the WordPress blog's proclivity for changing quotation marks (used in many Asterisk commands) to 'smart quotes'. The original version of this column introduced an entirely new problem when WordPress replaced double-hyphens (used in many Linux commands) with 'long hyphens.' The end result of these quirks has been that, while the code worked great on our development systems, it blew up when you used cut-and-paste to move it to your Asterisk server. This all came to a head this past week when this article on faxing hit the street and blew up on every reader's system because of the double-hyphens (h,2 above), a problem that neither we nor anyone else in the WordPress blog community had ever documented.

    Just like everything else that's great about the Open Source community, there is always someone smart enough not only to recognize a problem but also to fix it. So our hat is forever tipped to Alex King, one of WordPress's most ardent supporters. He wrote about the smart quotes problem and then he single-handedly fixed it with his WP Unformatted Plugin. As luck would have it, the plugin also resolves the double-hyphen problem. It just took us a while to discover the plugin. But there's nothing like an extra scoop of egg on your face to make you scratch a little harder for a solution. We were either going to solve the formatting problems or give WordPress the heave-ho. Now that we've implemented the plugin, we'll be using it regularly and, as quickly as we can, we'll go back and rework all of our previous articles just as we have this one. Hopefully the problem is history both for you and for us. Should you see any Nerd Vittles code that still looks like it has quotation marks pointing in two different directions, please let us know. And ... our apologies for the mess.


    Some Recent Nerd Vittles Articles of Interest...