The concept of facsimile transmission has been around for over 100 years, but it was not until the 1980s that the use of fax machines became essential in business. This lasted for perhaps two decades. Then the Internet came along, and very shortly after that, the fax quickly became almost irrelevant.
A fax machine allows a facsimile (copy) of a document to be transmitted across a telephone line. In the Internet age, this sort of functionality seems useless; however, prior to ubiquitous Internet access, this was a very useful thing indeed. Fax machines scan a document into a digital format, transmit the digital information in a manner similar to that used by an analog modem, and then convert and print the received information on the other end.
Asterisk offers the ability to both send and receive faxes, but it should be noted that all Asterisk is doing is the basics of fax transport. This means that providing a complete experience to your users will require external programs and resources beyond what Asterisk delivers.
Receiving is relatively simple, since the format of the document is determined at the sending end, and thus all Asterisk needs to do is store the document.
Transmitting is somewhat more complex, since the transmitting end is responsible for ensuring that the document to be sent is in the correct format for faxing. This typically places a burden on the user to understand how to create a properly formatted document, or requires complex client or server software to handle the formatting (for example, through a print driver installed on the local PC) and placement of the fax job in a location where the server can grab it and transmit it.
Initially, the only way to handle faxing in Asterisk was
through the spandsp
library. spandsp
provides a multitude of Digital Signal Processing (DSP) capabilities,
but in this context all we are interested in is its fax
functionality.
Asterisk has the hooks built in to make use of spandsp, but due to incompatible licenses, the spandsp libraries must be downloaded and compiled separately from Asterisk. Also, since spandsp was not written only for Asterisk, it will not assume that it is being installed for Asterisk. This means that a few extra steps will be required to ensure Asterisk can use spandsp.
As of this writing, the current version of spandsp is 0.0.6.
Download and extract the spandsp source code as follows:
$
mkdir ~/src/asterisk-complete/thirdparty
$
cd ~/src/asterisk-complete/thirdparty
$
wget http://www.soft-switch.org/downloads/spandsp/spandsp-0.0.6pre17.tgz
$
tar zxvf spandsp-0.0.6pre17.tgz
$
cd spandsp-0.0.6
The spandsp software should compile and install with the following commands:
$
./configure
$
make
$
sudo make install
This will install the library in the /usr/local/lib/ folder. On many Linux systems this folder is not automatically part of the library path (libpath), so it will need to be added manually.
In order to make the spandsp
library visible to all applications
on the system, the folder where it is located must be added to the
libpath for the system. This is
typically done by editing files in the /etc/ld.so.conf.d/ directory. You simply
need to ensure that one of the files in that directory has /usr/local/lib
listed. If not, the following
command will create a suitable file for you:
$
sudo cat >> /etc/ld.so.conf.d/usrlocallib.conf /usr/local/lib
Press Ctrl+D to save the file, then run the ldconfig command to refresh the library paths:
$
sudo ldconfig
You are now ready to recompile Asterisk for spandsp support.
Since the spandsp
library was probably not installed on the system when Asterisk was
first compiled, you will need to do a quick recompile of Asterisk in
order to have the spandsp support added:
$
cd ~/src/asterisk-complete/asterisk/1.8/
$
./configure
$
make menuselect
Ensure that under the Resource Modules heading, the section for spandsp looks like this:
[*] res_fax_spandsp
XXX res_fax_spandsp
It means that Asterisk was not able to find
the spandsp
library.
Once you have verified that Asterisk can see spandsp, you are ready to recompile. Save and exit from menuselect, and run the following:
$
make
$
make install
You can verify that spandsp is working with Asterisk by issuing the following command from the Asterisk CLI:
*CLI>
module show like res_fax_spandsp.so
At
this point the SendFAX()
and
ReceiveFAX()
dialplan applications
will be available to you.
The spandsp
library
and the Digium fax library, discussed in the next section, are
mutually exclusive. If you want to try out the Digium fax product, you
will need to ensure that spandsp does not load. To disable spandsp in
Asterisk, simply edit your /etc/asterisk/modules.conf file as
follows:
noload => res_fax_spandsp.so
Save the changes and restart Asterisk.
Digium Fax For Asterisk (FFA) was developed out of a strong desire from the Asterisk community to have a Digium-supported fax mechanism in Asterisk. Free for single-channel use, this product can also be licensed from Digium to handle more than one simultaneous fax channel.
Digium FFA can be obtained from the Digium website (http://www.digium.com). The process of downloading, installing, and registering this library is thoroughly documented in the Fax For Asterisk Administrator Manual, which you can also download from Digium. You will need to register at the Digium website in order to obtain your free single-channel fax license key and download the admin manual. There is also a comprehensive README file included with the software, which details the steps necessary to get Digium FFA going on your system.
Received faxes are commonly encoded in Tagged Image File Format (TIFF). This graphics file format, while not as well known as JPEG or GIF, is not as obscure as one might think. In fact, we suspect your computer (whether you’re running Windows, Linux, or MacOS) will already have the ability to interpret TIFF files built in. While it has become popular to offer PDF as a delivery format for received faxes, we’re not sure this is strictly required, since TIFF is so ubiquitous.
Received faxes will be stored by Asterisk as files. Where those files are stored will depend on several factors, including:
In the dialplan, you will need to build in
enough intelligence to name faxes in such a way that they will be
distinct from each other. There are many channel variables and functions
that can be used for this purpose, such as the STRFTIME()
function. Asterisk can easily
handle capturing the fax to a file, but you will need to make sense out
of what happens to that file once it is stored on the system.
The Tagged Image File Format is not very well known, but it is actually more common than you might realize, and since it is natively supported on Windows, MacOS, and Linux, TIFF files can be viewed on pretty much any computer with the most basic graphics viewer. A subset of the TIFF file format has for a long time been the de facto file format used for faxes.
Since Asterisk will receive and store faxes in TIFF format, there is no post-processing required. Once the incoming fax call has been completed, the resulting TIFF file can be opened directly from the folder where it was stored (or perhaps emailed to the intended user).
Once Asterisk has received a fax, the resulting TIFF file needs a way to get to its final destination: a person.
The key consideration is that unless the information that Asterisk knows about the fax is sufficiently detailed, it may not be possible to deduce the intended recipient without having someone actually read the fax (it is common for a fax to have a cover page with the recipient’s information written on it, which even the most capable text recognition software would have a difficult time making sense of). In other words, unless you dedicate a DID to each user who might receive a fax, Asterisk isn’t going to be able to do much more than send all faxes to a single email address. You could code something in the dialplan to handle this, though, or have an external cron job or other daemon handle distributing the received faxes.
A simple dialplan to handle fax to email might look something like this (you will need the mail program mutt installed on your system):
exten => fax,1,Verbose(3,Incoming fax) ; folder where your incoming faxes will initially be stored same => n,Set(FAXDEST=/tmp) ; put a timestamp on this call so the resulting file is unique same => n,Set(tempfax=${STRFTIME(,,%C%y%m%d%H%M)}) same => n,ReceiveFax(${FAXDEST}/${tempfax}.tif) same => n,Verbose(3,- Fax receipt completed with status: ${FAXSTATUS}) ; *** This line should not have any line breaks same => n,System(echo | mutt -a ${FAXDEST}/${tempfax} -s "received fax" [email protected])
Obviously, this sample would not be suitable for production (for example, it does not handle fax failure); however, it would be enough to start prototyping a more fully featured incoming fax handler.
You may have a dedicated phone number for receiving
faxes. However, with Asterisk, that is not a requirement. Asterisk has
the ability to detect that an incoming call is a fax and can handle it
differently in the dialplan. Fax detection is available for both DAHDI
and SIP channels. To enable it for DAHDI, set the faxdetect
option in /etc/asterisk/chan_dahdi.conf. In most
cases, you should set this option to incoming
. Table 19-1 lists the possible values for
the faxdetect
option in chan_dahdi.conf.
Table 19-1. Possible values for the faxdetect option in chan_dahdi.conf
Value | Description |
---|---|
incoming | Enables fax detection on inbound calls. When a fax is
detected, applies the faxbuffers option if it has been set
and redirects the call to the fax extension in the dialplan. For
more information on the faxbuffers option, see Using Fax Buffers in chan_dahdi.conf. |
outgoing | Enables fax detection on outbound calls. The dialplan
is not executing on an outbound channel. If a fax is detected,
the faxbuffers option will
be applied and the channel will be redirected and start
executing the dialplan at the fax extension. |
both | Enables fax detection for both incoming and outgoing calls. |
no | Disables fax detection. This is the default. |
To enable fax detection for SIP calls, you
must set the faxdetect
option in
/etc/asterisk/sip.conf. This
option may be set in the [general]
section, or for a specific peer. Table 19-2 covers the possible values for the
faxdetect
option in sip.conf.
Table 19-2. Possible values for the faxdetect option in sip.conf
Transmitting faxes from Asterisk is somewhat more difficult than receiving them. The reason for this is simply due to the fact that the preparation of the fax prior to transmission involves more work. There isn’t anything particularly complex about fax transmittal, but you will need to make some design decisions about things like:
To transmit a fax from Asterisk, you must have a TIFF
file. How you generate this TIFF is important, and may involve many
steps. However, from Asterisk’s perspective the sending of a fax is
fairly straightforward. You simply run the SendFAX()
dialplan
application, passing it the path to a valid TIFF file:
exten => faxthis,1,SendFAX(/path/to/fax/file,d)
In practice, you will normally want to set some parameters prior to transmission, so a complete extension for sending a fax using Digium’s Fax For Asterisk might look something like this:
exten => faxthis,1,Verbose(2,Set options and transmit fax) ; some folder where your outgoing faxes will be found same => n,Set(faxlocation=/tmp) ; In production you would probably not want to hardcode the filename same => n,Set(faxfile=faxfile.tif) same => n,Set(FAXOPT(headerinfo)=Fax from ShiftEight.org) same => n,Set(FAXOPT(localstationid=4169671111) same => n,SendFax(${faxlocation}/${faxfile})
The real trick of sending a fax is having a source file that is in a format that the fax engine can handle. At a basic level, these files are known as TIFF files; however, the TIFF spec allows for all sorts of parameters, not all of which are compatible with fax and not many of which are documented in any useful way. Additionally, the types of TIFF formats that spandsp can handle are different from those Digium FFA will handle.
In the absence of one simple, clear specification of what TIFF file format will work for sending faxes from Asterisk, we will instead document what we know to work, and leave it up to the reader to perform any experimentation required to find other ways to generate the TIFF.[165]
Digium’s Fax For Asterisk Administration Manual documents a process for converting a PDF file into a TIFF using commonly available Linux command-line tools. While kludgy, this method should allow you to build Linux scripts to handle the file conversion, and your users will be able to submit PDFs as fax jobs.
You will need the ghostscript PDF interpreter, which can be installed in CentOS by the command:
$
sudo yum -y install ghostscript
$
sudo apt-get install ghostscript
Once installed, ghostscript can convert the PDF into an Asterisk-compatible TIFF file with the following command:
$
gs -q -dNOPAUSE -dBATCH -sDEVICE=tiffg4 -sPAPERSIZE=letter -sOutputFile=
<dest>
<src>
Replace
<dest>
with the name of the output
file, and specify the location of your source PDF with
<src>
.
The ghostscript program should create a TIFF
file from your PDF that will be suitable for transmission using
Asterisk SendFax()
.
Many users would like to be able to send emails as fax documents. The primary challenge with this is ensuring that what the users submit is in a format suitable for faxing. This ultimately requires some form of application development, which is outside the scope of this book.
What we have done is provided a simple example of some methods that at least provide a starting point for delivering email to fax capabilities.
One of the first changes that you would need to make in order to handle this is a change to your /etc/aliases file, which will redirect incoming faxes to an application that can handle them. We are not actually aware of any app that can do this, so you’ll have to write one. The change to your /etc/aliases file would look something like this:
fax: "| /path/to/program/that/will/handle/incoming/fax/emails
"
In our case, Russell built a little Python script called fax.py, so our /etc/aliases file would read something like this:
fax: "| /asteriskpbx/fax.py"
We have included a copy of the Python script we developed for your reference in Example 19-1. Note that this file is not suitable for production, but merely serves as an example of how a very basic kind of email to fax functionality might be implemented.
Example 19-1. Proof of concept email to fax gateway, fax.py
#!/usr/bin/env python """Poor Man's Email to Fax Gateway. This is a proof of concept email to fax gateway. There are multiple aspects that would have to be improved for it to be used in a production environment. Copyright (C) 2010 - Russell Bryant, Leif Madsen, Jim Van Meggelen Asterisk: The Definitive Guide """ import sys import os import email import base64 import shutil import socket AMI_HOST = "localhost" AMI_PORT = 5038 AMI_USER = "hello" AMI_PASS = "world" # This script will pull a TIFF out of an email and save it off to disk to allow # the SendFax() application in Asterisk to send it. This is the location on # disk where the TIFF will be stored. TIFF_LOCATION = "/tmp/loremipsum.tif" # Read an email from stdin and parse it. msg = email.message_from_file(sys.stdin) # For testing purposes, if you wanted to read an email from a file, you could # do this, instead. #try: # f = open("email.txt", "r") # msg = email.message_from_file(f) # f.close() #except IOError: # print "Failed to open email input file." # sys.exit(1) # This next part pulls out a TIFF file attachment from the email and saves it # off to disk in a format that can be used by the SendFax() application. This # part of the script is incredibly non-flexible. It assumes that the TIFF file # will be in a specific location in the structure of the message (the second # part of the payload, after the main body). Further, it assumes that the # encoding of the TIFF attachment is base64. This was the case for the test # email that we were using that we generated with mutt. Emails sent by users' # desktop email clients will vary in _many_ ways. To be used with user- # generated emails, this section would have to be much more flexible. try: f2 = open(TIFF_LOCATION, "w") f2.write(base64.b64decode(msg.get_payload()[1].get_payload().replace(" ", ""))) f2.close() except IOError: print "Failed to open file for saving off TIFF attachment." sys.exit(1) # Now that we have a TIFF file to fax, connect to the Asterisk Manager Interface # to originate a call. ami_commands = """Action: Login Username: %s Secret: %s Action: Originate Channel: Local/s@sendfax/n Context: receivefax Extension: s Priority: 1 SetVar: SUBJECT=%s Action: Logoff """ % (AMI_USER, AMI_PASS, msg['subject']) print ami_commands def my_send(s, data): """Ensure that we send out the whole data buffer. """ sent = 0 while sent < len(data): res = s.send(data[sent:]) if res == 0: break sent = sent + res def my_recv(s): """Read input until there is nothing else to read. """ while True: res = s.recv(4096) if len(res) == 0: break print res s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((AMI_HOST, AMI_PORT)) my_send(s, ami_commands) my_recv(s) s.shutdown(socket.SHUT_RDWR) s.close()
We tested this out at a very rudimentary level, and proved the basic concept. If you want to put email to fax into production, you need to understand that you will have more work to do on the application development side in order to deliver something that will actually be robust enough to turn over to an average group of users.
In theory, it should be possible to connect a traditional fax machine to an FXS port of some sort and then pass incoming faxes to that device (see Figure 19-1). This concept is attractive for a few reasons:
Unfortunately, fax pass-through is not the home run we would like it to be. The analog carrier signal that two fax machines use to communicate is a delicate thing, and any corruption of that signal will often cause a transmission failure. In an Asterisk system performing pass-through, internal timing issues, coupled with signal attenuation, can create an environment that is unstable for fax use, especially for larger (multipage) faxes.
If you are using fax on a casual basis (mostly noncritical, one-page faxes), this sort of setup can work well. If faxing is critical to your business, or you are often expecting multipage faxes, we must reluctantly recommend that you connect your fax machines directly to the PSTN and leave Asterisk out of it.
Many of the problems with fax pass-through are caused by inconsistent timing. Since faxes are more tolerant of latency than voice calls (a fax has to be able to travel halfway around the world, which takes a few dozen milliseconds), the introduction of a buffer in DAHDI (which is strictly used for faxes) has reportedly corrected many of the problems that have plagued fax pass-through.
As of this writing, this is a fairly new configuration option. The currently preferred setting is as follows:
faxbuffers => 12,half
This would be placed in your /etc/asterisk/chan_dahdi.conf file and would cause chan_dahdi to create a 96 ms buffer for fax calls and delay start of transmission until the buffer was half full.
You would also need to set faxdetect
, since the fax buffers are part of
the faxdetect
functionality:
faxdetect = both
We have not extensively tested this capability yet, but anecdotal evidence suggests that this should greatly improve the performance of fax pass-through in Asterisk.
Fax is a technology whose days are behind it. Having said that, it remains popular. Asterisk has some interesting technology built into it that allows you some level of creativity in how you handle faxes. With careful planning and system design, and a patient prototyping and debugging phase, you can use your Asterisk system to handle faxing in creative ways.
[165] One format we tried was using the Microsoft Office Document Image Writer, which offers “TIFF-monochrome fax” as an output format. This seemed too good to be true, which is exactly what it turned out to be (neither spandsp nor Digium FFA could handle the resulting file). It would have been ideal to have found something common to Windows PCs that could be used by users to “print” an Asterisk-compatible TIFF file.