DIY PBX: Unexpected benefits

My partner recently asked if I could receive a FAX for her. Sadly, I had to tell her that, while I can send faxes, I couldn't easily receive them. I do have a multifunction printer with FAX capability, but I have not designed my "Receptionist" dialplan to handle incoming faxes. And so, admitting that failing, I skulked back to my cave, and did some research.

When I installed Asterisk, I had included one of it's optional prerequisite packages: SpanDSP. "SpandDSP is a library which provides many of the DSP functions needed for telephony. spandsp is also a complete software FAX machine, with soft modems. The spandsp package is designed to be independent of the telephony platform itself." With that, and a feature that I had not enabled in chan_dahdi.conf, there was a good chance that I could configure Asterisk itself to act as a fax machine to receive, store and deliver inbound faxes.

So, I played a bit, changing chan_dahdi.conf, and extensions.conf, adding a simple res_fax.conf and writing a small AGI script to handle the fax distribution. Fortunately for me, I had not disabled either or in my modules.conf, so the fax functionality, while unused, was present in my active Asterisk installation.

In extensions.conf, I built a new "fax" extension under my "Receptionist" context, using as an example, the fax extension documented in Chapter 19 of "Asterisk: The Definitive Guide". I modified the sample extension to invoke an AGI script that would handle the backend distribution of the fax file. I next wrote, tested, and installed that AGI script, so that it would generate a PDF from the received fax, and email that PDF file to me. AGI scripts aren't the most complex of tools, but they aren't simple either, so this took a bit of work to get right. The initial version did nothing more than invoke ImageMagick's convert(1) tool to generate the PDF, and mailx(1) to mail it as an attachment.

In chan_dahdi.conf, I enabled fax reception for my FXO port (connected to the PSTN), ensuring that the fax detection was active only during that time that the caller would interact with the robot receptionist. This fax detection, working only on the inbound connections, would hear the caller's fax handshake, and redirect the call to the Receptionist context fax extension, which (as I already indicated) would receive the fax and email it onwards to me.

With all the changes coded, I refreshed the dahdi module (dahdi restart) and reloaded my dialplan (dialplan reload). Nothing broke, and I could still make and receive phone calls.

Now, for the moment of truth: I sent a fax to a "fax back" test phone number, and waited for their fax reply. Five minutes later, I had an email in my in-basket, with a PDF attachment of the fax that the "fax back" service had sent to me. Whooo-hooo! It worked!

I no longer have to slink around, embarrassed about my lack of FAX. Asterisk came through for me, and I can add another tickmark to my "wanted features" list. Not bad for a couple of hours of research and ten minutes of coding.

System Configuration: 
System Management: 


Here's what I added to my chan_dahdi.conf

;2022-06-20 Enable incoming fax handling for PSTN

channel => 1

Here is the basic "fax" extension I added to the "Receptionist" context invoked by DAHDI on an incoming call.

exten => fax,1,NoOp(Incoming fax)
exten => fax,n,Set(FAXDEST=/var/spool/asterisk/fax)
exten => fax,n,Set(tempfax=${STRFTIME(,,%C%y%m%d%H%M)})
exten => fax,n,ReceiveFax(${FAXDEST}/${tempfax}.tiff)
exten => fax,n,NoOp(Fax receipt completed with status: ${FAXSTATUS})
exten => fax,n,AGI(,${FAXDEST}/${tempfax}.tiff)    ; convert the fax to pdf and email it
exten => fax,n,Hangup

I wrote a relatively simple script to convert the received fax TIFF file into a PDF, then email that PDF to me.
The AGI script (invoked from the "fax" extension) looks like:

# AGI script to distribute received faxes by email

# Populate shell global variables with supplied channel vars #
while read VAR && [ -n "${VAR}" ] 
  CVAL=$(echo "${VAR#*:}" | sed -e 's/^ *//g' -e 's/ *$//g')
  eval "${CVAR}='${CVAL}'"

# Gather requisite information
#  agi_callerid 	to derive a well-formed numeric CallerID
#  agi_arg_1		to identify the received fax file
#  date&time		to identify the date and time of reception

CIDNUMBER=$(echo "${agi_callerid}" | tr -c -d '0-9')
FAXWHEN=$(date "+%F @ %R")

# Convert the received TIFF file into a PDF, and email it
if /usr/bin/convert "$FAXTIFF" "$FAXPDF"
  # convert worked, so email the PDF, then delete both files
  echo "FAX received from $CIDNUMBER on $FAXWHEN"		|
  /usr/bin/mailx -s "FAX from $CIDNUMBER"			\
		     -a "$FAXPDF"				\
		     -r "switchboard"				\

  rm -f "$FAXTIFF" "$FAXPDF"
  # Report that we cannot convert the fax file to PDF
  # don't attach original TIFF file, because it may not exist
  echo "FAX $FAXTIFF received from $CIDNUMBER on $FAXWHEN"	|
  /usr/bin/mailx -s "FAX from $CIDNUMBER"			\
		     -r "switchboard"				\


exit 0