Link: How to Install Asterisk 11 in RHEL/CentOS/Fedora and Ubuntu/Debian/Linux Mint

Asterisk is an Open Source software PBX (Private Branch Exchange), developed by Mark Specer of Digium. It allows you to make calls to one another which may have connected to other PSTN (Public Switched Telephone Network) and Voice over Internet Protocol (VoIP). Asterisk runs on Linux, BSD, MacOSX and others. Having built-in features like voicemail, conferencing, IVR, queuing etc.

In this article, we are going to see a basic installation and working of Asterisk 11 in RHEL 6.4/6.3/6.2/6.1/6.0, CentOS 6.4/6.3/6.2/6.1/6.0, Fedora 18,17,16, Ubuntu 12.10/12.04/11.10, Linux Mint 14/13 and Debian Linux Operating Systems.

How to Install Asterisk 11 in RHEL/CentOS/Fedora and Ubuntu/Debian/Linux Mint (TecMint.com)

Link + notes: Configuration of APC UPS in Ubuntu

The APC PowerChute utility will only run in Windows, but if you are running Ubuntu Linux (or some variation thereof) you can have pretty much the same thing by following the instructions in this article:

Configuration of APC UPS in Ubuntu (Tech-Niche)

The article suggests you install gapcmon but you probably don’t need to do that, because Ubuntu already includes a Power Statistics utility that works well enough for most users.

If you have problems getting it to work, make sure the USB cable is plugged into the correct jack on the UPS (with some models it’s easy to plug it into a network cable jack by mistake). From the command prompt issue the command lsusb and it will tell you what is connected to your USB ports — you should see an entry for “American Power Conversion Uninterruptible Power Supply” if the cable is correctly connected.

Nomachine NX: An alternative to VNC for using a remote Linux desktop

 

Important
This article includes a small bit of text that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging.  It is reposted with his permission.

If you have ever tried to access a Linux system using VNC, and your host computer wasn’t particularly fast, you may have noticed that things slow down considerably – you definitely know you’re accessing the computer remotely, even if the access is via a local network link. And even if you don’t have that issue, you might want to know about this alternative.

You can install the NX Free Edition server and client from Nomachine and it works great!  Note that NX does not work in precisely the same way as VNC – while VNC lets you take control of the current desktop on the target machine, NX lets each login have its own session and desktop. So you could be using your Linux box and let another family member come into it via NX, and each of you would have your own desktop and session.

The best reasonably current instructions I have found are in this article:
Remote Linux Desktops with NoMachine NX

Those instructions are somewhat geared toward users of Debian-based distributions such a Ubuntu, Linux Mint, etc. but there are also packages available for systems that utilize RPM or compressed TAR packages rather than DEB. Most experienced Linux users should have no problem figuring out how to adjust the instructions for their particular distribution.

Installing Linux in place of OS X on an older Mac

Former title: Installing Ubuntu Linux (or Mythbuntu) in place of OS X on a Mac Mini (changed because this article is now less specific to Ubuntu or the Mac Mini).

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

All computers have their day in the sun, and then something new comes along that makes them less desirable. I have a Mac Mini that I acquired sometime around the time OS X Leopard came out, probably in 2009. As time went by it became slower and slower and I finally replaced it. However, it seemed a bit of a shame to have it just sitting in a closet doing nothing, so I decided to see if I could put Ubuntu Linux 12.04 (at that time the most recent LTS version) on it. I started out by trying to follow the instructions on this page (the Single Boot/MBR option) but ultimately it turned out to be not nearly that complicated. NOTE THAT THIS WILL NOT GIVE YOU A DUAL BOOT SYSTEM; THIS IS REPLACING OS X WITH UBUNTU LINUX, AND ALL YOUR EXISTING DATA ON THE HARD DRIVE WILL BE ERASED!!! YOU HAVE BEEN WARNED!!!

Also: THIS IS FOR EXPERIMENTAL AND INFORMATIONAL PURPOSES ONLY. This is what worked for ME. This MAY or MAY NOT work for you. It may even brick your system (if that should happen, try doing a PRAM reset before you totally panic). It did not do that to me, and I have never heard of anyone having that problem, but your hardware may be a bit different than mine, so I make NO guarantees!!! Here’s all I wound up having to do:

  • Connected a USB keyboard and mouse.
  • Before proceeding I made sure I had the latest firmware upgrade – that is very important since you can’t do it once you have installed Ubuntu, and the rest of this might not work if you have old firmware. Another thing you can’t do is disable the startup chime so if you don’t want that after Linux is installed, find a way to turn it off before proceeding (I wanted it, and it’s used in the instructions that follow, so I didn’t search for a way to disable it, and I don’t recommend you do either).
  • Inserted my old OS X Leopard installation CD (any OS X installation CD that works with the Mac should be fine for this procedure)
  • Rebooted while holding down the “C” key
  • Accepted the default language (English) and pressed Enter
  • From the top menu bar, I selected Utilities | Disk Utility
  • In Disk Utility I selected the internal hard drive in the left hand side panel. You have to select the drive itself (it shows the brand name of your drive), not the partition
  • Clicked the Partition tab, selected 1 Partition under the Volume Scheme (instead of Current)
  • Clicked the Options button and selected “Master Boot Record”
  • Clicked the Continue button (this erases the hard drive)
  • When the above finished, I quit Disk Utility, then quit the installer
  • The Mac rebooted — at that point I clicked and held the leftmost mouse button until the CD ejected
  • Inserted the Ubuntu Install CD, then power cycled the Mac Mini
  • At the startup chime I pressed the “C” key to boot from the CD
  • When the Ubuntu CD boots, you are given the option to install Ubuntu directly, or to try Ubuntu (which takes you to the Ubuntu desktop). As it turns out, I could have simply ran the installer. Instead, I did it from the desktop because I was following some instructions that said you have to type in some things during the install, and you need to have access to a terminal window to do that. But when I tried to type those things in, Ubuntu rejected them with errors, so I just let it complete the install (answering the questions it asked during the install). I did NOT select any special partitioning, etc. – I pretty much did a plain vanilla installation, generally accepting the defaults except where they weren’t appropriate or weren’t what I wanted.
Important
NOTE: If, when you boot into the Ubuntu installation CD, you are greeted with a screen that asks you to “Select CD Rom boot type” but you cannot select anything because neither the keyboard nor the mouse are functional, or if you get a black screen at some point early in the install process, then you may need a special build of Linux intended for use with 64-bit Macs that use a 32-bit EFI. Such models were mostly manufactured in the last half of the 2000’s. In that case you can try the solution offered here, if you can understand the instructions, or you can see if you can find a suitable ready-made .iso file at Matt Gadient’s site (note that even though it says “late 2006 models” in the title, those builds should work with late 2006 and later 64-bit Macs that use a 32-bit EFI).

When the install was finished (after quite some time), I rebooted. The only unusual thing was that after the reboot, the screen remains white for about 20-30 seconds right at the start of the reboot. Then after that delay, it finally decides it will load Ubuntu. That still happens and while it makes the startup take a bit longer, I’ve seen worse. The reason for this (and a possible fix) can be found here: Reducing the 30 second delay when starting 64-bit Ubuntu in BIOS mode on the old 32-bit EFI Macs.

Oh, and the sound was muted by default for some reason, and the volume slider set all the way down. After I unmuted it and turned up the volume slider, the sound worked normally.

The wired network connection worked fine. I don’t use WiFi here so I did not test that. If you have issues with those, or any other issues see GNU/Linux Debian on a Macbook Pro 11-1, which contains some additional hints that may make things run more smoothly (or work at all) after installation. I would use any hints on that page only if you have one of the specific issues that the author had. Another page with possibly useful additional hacks to make this work is Mac book pro 11.3 Linux customization (2): grub and hardware, which may be helpful if you have issues with an unrecognized Intel Iris GPU or Thunderbolt, but again I would only use these if you really need them, since this article is from 2014 and such hacks may no longer be necessary (by the way, this blog has several other pages of rather technical hacks for using Linux on a Mac Book Pro, and while many users will not need any of them, you can search that site for the phrase “Mac book pro 11.3 Linux customization” to find the other articles).

The only real hitch I found was that it wouldn’t boot if I didn’t have a video display connected! I have no idea why that was the case but there is a hardware workaround described on this page. Hope you didn’t misplace your Mac Mini’s DVI to VGA adapter! I had a 100 Ω resistor handy and that seems to work fine (it’s probably about 40 or 50 years old but what the heck, carbon resistors don’t change value that much just sitting in a junk box). If you can’t find the DVI to VGA adapter, I have read that at least one user placed a resistor directly into the DVI port between pins C2 and C5; you can use this pinout diagram to find those pins. However, I have not tried this, so I make no guarantees. Either way, the point is that the resistor goes between the analog green signal output and the analog ground return. If you can’t find the adapter, can’t get this to work, or if you just don’t want to mess around with a resistor, you can try a DVI Emulator/Dummy Plug that includes an EDID chip, that may be available from Amazon or eBay. A DVI 1920×1080 or 1920×1200 model should be sufficient. Such a device fools the Mac Mini into thinking a display is connected.

After I had this running for a year or two I read that Ubuntu doesn’t control the system fan properly. You can do this in some newer versions of Ubuntu (and possibly other Linux builds) to get fan control to work. First, try using your chosen Linux distribution’s Software Center or package manager to search for and install the macfanctld package (after you check for updates). If that doesn’t work, try this (assuming you’re using a Ubuntu or Debian based distribution):

sudo apt install -y macfanctld

On my system at least, it appeared that this did cause the fan to run a bit faster but not enough that I really noticed it. Once it is installed, you can type man macfanctld at a Linux command prompt to get configuration instructions. Changes to temperature limits and minimum fan speed are made in the file /etc/macfanctl.conf.

Also, although I have not personally done this, I have read on a couple of pages that you can enable automatic reboot after a power interruption by adding one line to /etc/rc.local (this may be Ubuntu-specific, but you can always remove it if it doesn’t work):

setpci -s 0:1f.0 0xa4.b=0

If you have smb or samba installed (it is installed by default in many distributions) and find that your samba/smb log files fill up with complaints such as “Unable to connect to CUPS server localhost:631 – Connection refused”, here is a workaround. In the [global] section of /etc/samba/smb.conf add these lines:

printing = bsd
printcap name = /dev/null

Then from a command prompt restart samba:

sudo service smbd restart

Long ago I read a post on Reddit where the author said, “After months of headaches and tinkering I’ve finally found the stupid way to let Linux work properly on Macs. Just add acpi_osi=Darwin as boot parameter.” Unfortunately I did not save a link to that post so I have no idea if still valid advice, and therefore I would only try it if something (particularly Thunderbolt) isn’t working right and you can’t resolve it any other way. If you understand Linux kernel stuff see the final paragraphs of this page for a discussion of this setting, also see Section 2 on this page. If you don’t use the Thunderbolt port then you probably don’t need this, and you may not anyway.

By the way, I had intended to try Linux Mint but they no longer offer a version that fits on a CD, and I still have about a gazillion blank CDs (plus I already had Ubuntu 12.04 burned to CD) and since the Mac Mini doesn’t have a BIOS in the traditional sense, there is no way to install from a USB stick, so Ubuntu it was. For what I plan to do with this, having Mint would be no advantage. Many older Macs have issues booting from a USB memory stick, although one site I read said that they might be able to if you use UNetbootin to transfer the Linux image file to the USB stick. I did not attempt that, so I cannot say whether that works. I got it to work using a CD, but I do realize that CD and DVD burners are becoming somewhat of a scarce item these days.

Note that nowhere above did I mention a program called rEFIt, nor did I mention BootCamp. I didn’t need either of those.

Had this not worked I could have always reinstalled Leopard, but again, for what I was doing with this system (running a Tvheadend backend, for one thing) Linux was probably a better choice, but I still wanted to be able to use this as a backup regular desktop computer, should the need ever arise.

One final note: This is the xorg.conf I used with my “headless” system. It came from this blog post and I did have to save it as /etc/X11/xorg.conf and not the filename shown in the post (in newer versions of Linux I believe this location may have moved yet again, so you may need to search to find the correct location for the xorg configuration for your distro). Note this is only something you might consider using if you have installed a version of Linux that includes a desktop; you don’t need this if you have a server-only version of Linux that has no desktop installed (one where you do everything from a Linux command prompt):

Section "Monitor"
  Identifier "Monitor0"
  Modeline "1920x1080_60.00"  172.80  1920 2040 2248 2576  1080 1081 1084 1118  -HSync +Vsync
  Modeline "1024x768_60.00"  64.11  1024 1080 1184 1344  768 769 772 795  -HSync +Vsync
EndSection
Section "Screen"
  Identifier "Screen0"
  Device "VGA1"
  Monitor "Monitor0"
  DefaultDepth 24
  SubSection "Display"
    Depth 24
    Modes "1920x1080_60.00" "1024x768_60.00"
  EndSubSection
EndSection

Asterisk/FreePBX: How to get the DID of a SIP trunk when the provider doesn’t send it (and why some incoming SIP calls fail)

The symptom: On a SIP trunk, you can’t get an inbound route to work – it just doesn’t seem to recognize the number. You might be able to get the call to come in on your any DID/any CID route, or maybe the call doesn’t get answered at all. When you type sip debug from the CLI, you can see (when you scroll back to the point where the call came in) that a sip INVITE packet arrived, and perhaps it contained the DID number in the sip To: header (in the form To: <sip:NUMBER@IP ADDRESS>), but you also see that the FROM_DID was set to s. In other words, you see a line that looks like this:

  -- Executing Set("SIP/9995552368-09876543", "FROM_DID=s") in new stack

EDIT: The most common reasons this happens are one or both of two things. Either you are not specifying the number of your DID in the format your provider is sending it (for example, they are sending 11 digits including a leading “1”, but you are only using ten digits in your inbound route), or they are sending the number in the SIP “To:” header. In the latter case, FreePBX has a much easier way to deal with this now. Before you do anything else, try changing the trunk context from from-trunk or from-pstn to from-pstn-toheader. This works for both SIP and PJSIP trunks, but only if the provider really is sending the number in the SIP “To:” header.

Only continue with this article if you have tried the above and it doesn’t work, as much of what is below simply shows an older and less intuitive way of doing the same thing. If the above did not work, don’t forget to change your trunk context back to whatever it was previously. (End of edit.)

Before you attempt anything else, you may want to try this suggestion by Dan Swartz: Check the registration string (in the trunk settings for the provider), and if it’s not already there, try putting the DID at the end of the registration string, prefixed by a ‘/’. It may (or may not) require a leading ‘1’ too. e.g. ‘/18005551212’ (or your country code if you are not in the U.S./Canada, etc.). So, your registration string would take this format:

accountid:password@your.provider/yourDIDnumber

Remember to try your DID both with and without the country code prefix. If this doesn’t work, it’s time to try a workaround (however, you may want to read the addendum at the bottom of this article first!). Perhaps you can see the DID number in the sip INVITE packet’s To: header, but the CLI reveals that Asterisk isn’t picking it up, and therefore it goes to your default inbound route.

(Oh, and for anyone who’s still trying to figure out how to turn off sip debugging, the CLI command is sip no debug)

Fortunately this isn’t a hard thing to work around, as long as the DID number really is in the sip To: header.

NOTE: In the following examples, we now use the s extension rather than _. – this is considered better practice (and safer) but the disadvantage is that the context will fail if the provider is sending any type of DID, even if it’s incorrect or incomplete. If the code doesn’t seem to work, try replacing the s extension with _X! (the extension is to the right of the => and space characters).

First, create a context in extensions_custom.conf that looks like this:

EDIT: Really, don’t do this next part, unless you are running an old version of FreePBX! Just change your trunk context to from-pstn-toheader and be done with it! (End of edit.)

[custom-get-did-from-sip]
exten => s,1,Noop(Fixing DID using information from SIP TO header)
exten => s,n,Set(pseudodid=${SIP_HEADER(To)})
exten => s,n,Set(pseudodid=${CUT(pseudodid,@,1)})
exten => s,n,Set(pseudodid=${CUT(pseudodid,:,2)})
exten => s,n,Goto(from-trunk,${pseudodid},1)

Or, thanks to naftali5, you can cut the above down to one line of code that does the same thing, but is a bit less obvious to the casual reader:

[custom-get-did-from-sip]
exten => s,1,Goto(from-trunk,${CUT(CUT(SIP_HEADER(To),@,1),:,2)},1)

(And speaking of naftali5, if you are using his Dialplan Injection module – which may not work with the most recent versions of FreePBX, so don’t run out and get it if you aren’t already using it unless you are sure it has been updated to work with the version of FreePBX that you are using – and want to put the above line in the Destination section, then you will need to use a slightly different syntax, changing the commas to bar characters, so it looks like this:

* Custom App: from-trunk,${CUT(CUT(SIP_HEADER(To)|@|1)|:|2)},1

The part after Custom App: is what you paste into the text box. This ONLY applies to Dialplan Injection users)

Then, in the trunk associated with the provider, change the trunk context statement (which should read context=from-trunk) to:

context=custom-get-did-from-sip

(Or for Dialplan Injection users, just use

context=injection-n

but replace n with the actual injection number, which will appear next to the injection name in the right-hand column menu of injections.)

And note that with such providers, you may have to move that context statement from the USER details to the PEER details section. This is why calls from some SIP providers sometimes fail to come in at all – they effectively never "see" the User context and details, therefore they don’t see the context statement there and have nowhere to go. It’s also why you sometimes see instructions for sip providers that leave the User context and User details sections totally blank, but include a context statement in the peer details – in most such cases it’s because the provider is treating the customer as an end user (like someone using a softphone or a VoiP adapter) rather than as a peer, and they aren’t sending DID information.

The above instructions may also solve the problem where you have two (or more) trunks from the same provider, but Asterisk always treats it as if all calls are coming in on one of the two trunks, therefore again not allowing you to set up separate inbound routes for each trunk. As long as the provider sends the number in the sip To header, the above code should set the DID properly.

If the first part of the To: statement is something other than a DID number (a user name, for example), then you may have to add a line just before the final Goto statement. For example, let’s say the provider is sending To: <sip:Fred@IP ADDRESS> and your DID number (or at least, the number you want to use to denote your inbound route) is really 5551212. You’d then use code similar to this:

[custom-get-did-from-sip]
exten => s,1,Noop(Fixing DID using information from SIP TO header)
exten => s,n,Set(pseudodid=${CUT(CUT(SIP_HEADER(To),@,1),:,2)})
exten => s,n,Set(pseudodid=${IF($["${pseudodid}"="Fred"]?5551212:${pseudodid})})
exten => s,n,Goto(from-trunk,${pseudodid},1)

Or, as long as you only have ONE trunk from that provider, you could always just cheat a little and hardcode the desired DID in a separate custom context, like this:

[custom-stupid-provider]
exten => s,1,Noop(Fixing DID to 5551212)
exten => s,n,Goto(from-trunk,5551212,1)

And use the name of this context in the trunk settings. I hear you asking, why not just do it this way on all trunks with this issue? Well, because if you add a second trunk from the same provider, this won’t work correctly for both trunks, and if you ever change your number and then forget what you’ve done and just try to set your inbound route to the new number, it won’t work. And besides all that, if you have more than one SIP provider that doesn’t send proper DID, you’d have to create a separate custom context for each of them, instead of having one custom context that works for all of them.

One final note for Free World Dialup users, you may find that sip calls will still not come in until you put the following statement in sip.conf:

insecure=invite

I have no idea why that works, but it seems to make a difference.

What if the provider doesn’t send the number in the sip To: header?

There is at least one provider that actually sends a s character instead of a number in the sip To: header. What can you do with a provider like that? Well, all may not be lost. If you only have a single trunk from that provider, you can just use the "cheat" shown above, since it doesn’t rely on the contents of the sip headers. If, however, you have TWO or more trunks from the same provider, you can do a sip debug from the CLI and watch as calls come in on each trunk and note whether there are any consistent differences.

For example, if you have two lines on the same account, the provider will often assume that you are using a VoIP adapter (such as a Sipura or Linksys) and will use port 5060 for line 1, and port 5061 for line 2. That difference might show up in the headers of the sip INVITE packet, for example:

Via: SIP/2.0/UDP 111.222.333.444:5060;branch=z9hQ4bK67sc0a8e;rport

In this case, you see that there is a colon (:) before the port number and a semicolon following, and that there are actually TWO colons on the line before the port number, so maybe this would work:

[custom-really-stupid-provider]
exten => s,1,Noop(Fixing DID using port from SIP VIA header)
exten => s,n,Set(pseudodid=${CUT(CUT(SIP_HEADER(Via),;,1),:,3)})
exten => s,n,Set(pseudodid=${IF($["${pseudodid}"="5060"]?5551111:${pseudodid})})
exten => s,n,Set(pseudodid=${IF($["${pseudodid}"="5061"]?5552222:${pseudodid})})
exten => s,n,Goto(from-trunk,${pseudodid},1)

Or, if you only have two trunks from this provider, you probably could just condense the two test lines into one, by testing for one port number and assuming the other if the conditional test fails, like this:

exten => s,n,Set(pseudodid=${IF($["${pseudodid}"="5060"]?5551111:5552222)})

Note that the code in this section is untested, it’s just to give you some ideas about how to possibly handle the really oddball situation were you have two (or more) lines from the same provider, and cannot find any other way to differentiate them. And, don’t automatically assume you have a bigger problem than you actually have – for example, it may well be that having different port numbers on the different trunks would allow Asterisk to distinguish them enough that the simple "cheat" method would work (you’d have to make one for each trunk, of course).

Again, if a particular piece of code doesn’t seem to work at all, try replacing
exten => s, . . . . .
with
exten => _X!, . . . . .
in all lines of the context (just in case the provider is sending some unknown number). But if that works, you should consider using whatever the provider is actually sending as the DID for your Inbound Route, which would eliminate the need for this extra code altogether.

Addendum

There are a few other reasons that incoming calls may fail that have nothing to do with the main topic of this How-To, but are common enough that they should be mentioned anyway. The first is that in every trunk configuration, there must be a statement that reads:
context=from-trunk
Some providers will tell you to set the context statement to something else – don’t do it (unless you have a valid reason, such as following the instructions above). Providers are generally more familiar with Asterisk than FreePBX, and often don’t realize that you can’t just use any context name you like in FreePBX unless you also create that context somewhere (usually extensions_custom.conf).

Also the placement of that context= statement can be important. If a provider is treating you as an extension (which would likely be the case if most of their customers use VoIP adapters and/or you are a “Bring Your Own Device” customer) then in most cases you will not need to have a USER context or USER details at all in your trunk, but you still need to have a context=from-trunk statement (or another context= statement if you are following instructions elsewhere on this page) and in this case it will need to be in your PEER details, not your user details. So, if incoming calls aren’t working, try putting context=from-trunk in your trunk PEER details, and if that works, then totally remove the USER context and USER settings from your trunk configuration, since they aren’t doing any good anyway.

To further complicate matters, I have noted that with a couple of providers, nothing seems to work until you do the following. The symptom typically is that you have no problem connecting with other providers, and (if you have tried this) you have no problem connecting with the provider in question if you are using an external hardware device (such as an ATA), but no matter what you do you can’t receive calls from the provider. I hate recommending this because I don’t know exactly why it works, but it’s a trick I’ve used for a couple years now to resolve issues with a couple of specific providers. Don’t try this until you’ve first tried adding the DID at the end of the registration string, prefixed by a ‘/’, as shown near the top of this page.

Open the file /etc/asterisk/sip_general_custom.conf in any text editor and check to see if the following lines are in there:

bindport = 5060 ; Port to bind to (SIP is 5060)
bindaddr = 0.0.0.0 ; Address to bind to (all addresses on machine)
insecure=invite
tos=0x68
srvlookup=no

If any of the above lines are missing, add them to the file. In my experience these lines will not cause problems with other providers but they do magically get things working with a select few providers that are more used to serving customers using an ATA than through a trunk into an Asterisk box. If this gets things working you can try removing lines one at a time to find out which are actually doing the trick, or you can just leave them all in place – as I say, in my experience they don’t seem to cause problems with other providers, although obviously your experience might be different.

Yet another issue that you may encounter, especially if you are upgrading from an earlier version of FreePBX, is that if you have disallow= and allow= statements in your trunk configuration (to specify the use of particular codecs), starting with Asterisk 1.4 the disallow= statement(s) (particularly if it’s disallow=all) must be placed above the allow= statements. This is one of many cases where Asterisk upgrades have broken existing functionality for no good reason whatsoever, other than that the Asterisk developers could not be bothered to ensure backward compatibility.

One other note: There is an obscure bug in Asterisk that can cause incoming calls to fail. If Asterisk ever receives a Caller ID NAME that contains only one quotation mark (usually a name with a quotation mark at the start of the string but not at the end) it will not handle the call properly, and may ignore the incoming call completely.

Additional Reference:

Asterisk SIP channels

Notes on using HDHomeRun recorder under Ubuntu for lowest CPU usage when recording from HDHomeRun device

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

Sometimes I write articles for others, but sometimes I use this blog just to post a collection of notes on something I had a need to do.  This is one of the latter type.  You are welcome to look, and if it helps anyone else, great, but it’s not a full, step-by-step, “here’s how to do it” article, and I definitely do not advise you to just blindly emulate what I did, since there are probably more elegant ways to do this. This was one of those “I just want to get the thing working” type projects, and I must say that it works very well, at least for me.

HDHomeRun recorder is a simple Python script that will record specific channels based on a pre-established schedule.  It is less CPU intensive (and in my opinion, much easier to set up) than some of the other ways to do this, such as Myth TV.  But it is more limited — in particular the current script will only record from a single tuner, and there’s no GUI at all.  Everything is done in script and configuration files.

Here’s what I did:

Using  apt-get or Synaptic, install the following:  hdhomerun-config-gui, hdhomerun-config, libhdhomerun1, python-pip:
sudo apt-get install hdhomerun-config-gui hdhomerun-config libhdhomerun1 python-pip

Install apscheduler:
sudo pip install apscheduler

EDIT (for newer version mentioned by Fred C. in the comments): Then go to this page and click the ZIP button to download a ZIP file containing HDHomeRun recorder.  Unzip it into a subdirectory off of your user directory (I named the subdirectory HDHomeRun_Recorder on my system) — you should get several files and a scripts directory, which contains a couple more files. At this point you might want to check the ownership and permissions of the files — in particular make sure the .py files are executable. Do NOT follow the instructions in the README file; they are for an older version of the software. You may want to read the file, particularly if you have a WDTV Live media player, but the INSTALL section needs updating.

You then need to modify a couple of the files.  This is a bit tricky because what might seem the obvious way to do it won’t work (unless you apply my fix mentioned below).  So here is what you need to do:

(Read through this before actually doing anything, it may save you some effort!)

First, modify config-file.  There are three things you need to change here.  First you need to change this line as shown:

hdhomerun_config = /usr/bin/hdhomerun_config

This assumes the hdhomerun_config program is in /usr/bin, which it will be if you installed it using apt-get or Synaptic.  Next, you need to modify the tuners line to show the correct tuner IDs for your HDHomeRun — you can discover these using the HDHomeRun Configuration utility, if you don’t already know them.  And, you need to replace the entries in the channelmap section with your local channels.  You can run the hdhomerun_setup.py program (run it with no arguments and it will tell you what arguments it needs) and it will show you a list of channels (note it takes a few minutes to run, so be patient), and it appears as if it produces an entire config-file for you, but it doesn’t (keep reading before you do anything, there’s a fix for this below that you might want to try).  So look at the list of channels it outputs and then change each line to match the format you see in the included config-file.  For example, let’s say you see:

35.1   = 11   3   WGVU-11
35.2   = 11   4   WGVU-11
35.3   = 11   5   WGVU-11
35.4   = 11   6   WGVU-11
0 = 11 9 (control)
0 = 11 10 (control)

What that needs to be changed to in config-file is:

35.1 = 8vsb:11, 3       ; WGVU-11
35.2 = 8vsb:11, 4       ; WGVU-11
35.3 = 8vsb:11, 5       ; WGVU-11
35.4 = 8vsb:11, 6       ; WGVU-11

I have no clue why the hdhomerun_setup.py program doesn’t produce the correct output, it just doesn’t. I tried fixing it to produce the correct output (despite the fact that Python is a very incoherent language to me), and think I have it, so if you want to try my fix to hdhomerun_setup.py, the code is at the end of this article.

Second, you need to modify schedule-file. This is an example schedule file, and you just modify the entries with programs you like to record.

When you run the python script, I found that you need to actually be in its directory, and also that when you run it, it will seem like nothing is happening. And the command prompt won’t come back either, so you may want to initially run it using screen:

screen ./hdhomerun_recorder.py

You can use Control-A followed by D to detach from the process. To check that it started and everything is working okay, check logfile (in the same directory as the hdhomerun_recorder.py program). I found that the following could be used to kill the process:

pkill -f “python ./hdhomerun_recorder.py”

I made a bash shell script that looks like this:

#!/bin/bash
export USER=yourusername
pkill -f “python ./hdhomerun_recorder.py”
cd HDHomeRun_Recorder
./hdhomerun_recorder.py &

I can run this script to restart the Python script (in case I make a change to the schedule) and I can also add it to my startup items to start the script after a reboot (the pkill is ignored in that case).

Note: If you need a way to determine the channels in your area and don’t want to run hdhomerun_setup.py, see hdhomeruntoolbox, but note that the .strm files it produces are not in the correct format for XBMC (in case you are using XBMC, and if you are and you know anything about Java, maybe you want to grab a Java Decompiler and see if you can fix the output). However, after it runs, the information you will need for each channel in HDHomeRun recorder will be in a file named scan_tuner1.txt in the (hidden) .hdhomerun directory. When you look in that file, you will see sections for each working channel that contain information such as this:

SCANNING: 201000000 (us-bcast:11)
LOCK: 8vsb (ss=100 snq=91 seq=100)
TSID: 0x05E7
PROGRAM 3: 35.1 WGVU-11
PROGRAM 4: 35.2 WGVU-11
PROGRAM 5: 35.3 WGVU-11
PROGRAM 6: 35.4 WGVU-11
PROGRAM 9: 0 (control)
PROGRAM 10: 0 (control)

In config-file, the above would translate to something like this:

35.1 = 8vsb:11, 3 ; WGVU-11
35.2 = 8vsb:11, 4 ; WGVU-11
35.3 = 8vsb:11, 5 ; WGVU-11
35.4 = 8vsb:11, 6 ; WGVU-11

Note that stations will not always have the real channel numbers after the callsign, so the real channel number actually comes from the (us-bcast:11) in this case.

Setting up config-file is probably the hardest (not actually hard, but maybe tedious) part, if you can’t get my modifications to the hdhomerun_setup.py script (below) to work, but remember that you only need to add the channels from which you might want to record programming, not every channel available. Remember to restart the Python script if you change the schedule, but of course you DON’T want to do that while it’s recording programming!

I should add that since HDHomeRun recorder is a Python script, it’s probably possible to make it run under operating systems other than Ubuntu, but I just haven’t attempted to do that.

Here are my changes for hdhomerun_setup.py, that should cause it to print out usable lines that replace the existing lines in config-file (you will have to copy and paste them; it does NOT overwrite config-file). Note that there will only be one tuner shown on the tuners line, so you should add your additional tuners if you want to use them For example, if you have a HDHomeRun Dual, and this script prints out tuners = 10ABCDEF:0 you should add the second tuner so that the line is tuners = 10ABCDEF:0, 10ABCDEF:1 (with comma and space separating the two).

#!/usr/bin/env python

def channel_iter(file):
    for line in file:
        if line.startswith("SCANNING: "):
            channel = line.split()[2].strip('()')
            channel = channel.split(':')[1]
        elif line.startswith("LOCK: "):
            modulation = line.split()[1]
        elif line.startswith("PROGRAM "):
            (PROGRAM, subchannel, vchannel, name) = line.split(None, 3)
            subchannel = subchannel.rstrip(':')
            name = name.strip()     # remove new line
            name = name.replace(' ', '-')
            yield (vchannel, modulation, channel, subchannel, name)

def channel_info(hdhomerun_config, device_id, tuner):
    import subprocess
    import tempfile

    f = tempfile.TemporaryFile()
    cmd = [hdhomerun_config, device_id, "scan", "/tuner%d" % tuner]
    p = subprocess.Popen(cmd, stdout=f)
    p.wait()
    f.seek(0)
    return list(channel_iter(f))

def main():
    import sys, os, os.path

    usage = ("usagde: %s path-to-hdhomerun-config device-id tuner-number"
             % sys.argv[0])
    if len(sys.argv) != 4:
        sys.exit(usage)
    hdhomerun_config = sys.argv[1]
    device_id = sys.argv[2]
    tuner = int(sys.argv[3])
    if not os.path.exists(hdhomerun_config):
        sys.exit("%s doesn't exist, aborting!")
    if not os.path.isfile(hdhomerun_config):
        sys.exit("%s is not a regular file, aborting!")
    if not os.access(hdhomerun_config, os.X_OK):
        sys.exit("%s doesn't have execute permission set, aborting!")

    chan_info = channel_info(hdhomerun_config, device_id, tuner)
    if not len(chan_info):
        sys.exit("couldn't find any channels, quitting!")

    print("[global]")
    print("logfile = logfile")
    print("media_dir = media")
    print("schedule_file = schedule-file")
    print("hdhomerun_config = %s" % hdhomerun_config)
    print("tuners = %s:%s" % (device_id, tuner))
    print("[channelmap]")
    print("# virtual-channel = physical-channel program-number name-of-program")
    for (vchannel, modulation, channel, subchannel, name) in chan_info:
        line = "%s = %s:%s, %s t; %s" % (vchannel, modulation, channel, subchannel, name)
        if vchannel != '0':
            print(line)

if __name__ == '__main__':
    main()

Why Asterisk-based GUI software should make better use of regular expressions

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

Having recently discovered that Asterisk supports regular expressions (with some limitations), I got to wondering why the FreePBX-based GUI’s don’t make better use of them.  For example, in FreePBX, Trixbox, Elastix, etc., the Outbound Route pages all support entering patterns that are then directly used in Asterisk dialplans, in a most inefficient manner.  Let me give you an example.  Let’s say you have created an Outbound Route for toll-free calls, and in the “Dial Patterns that will use this Route” section you have entered these patterns:

1800NXXXXXX
1844NXXXXXX
1855NXXXXXX
1866NXXXXXX
1877NXXXXXX
1888NXXXXXX
800NXXXXXX
844NXXXXXX
855NXXXXXX
866NXXXXXX
877NXXXXXX
888NXXXXXX

Let’s further say that you have specified three trunks in the “Trunk Sequence for Matched Routes” section (this is actually conservative given that there are a number of ways you can route toll-free calls for free).  Anyway, this is an actual representative context that would be generated in extensions_additional.conf:

[outrt-5] ; TollFree
include => outrt-5-custom
exten => _1800NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1800NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1800NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1800NXXXXXX,n,Set(_NODEST=)
exten => _1800NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(outisbusy,)
exten => _1844NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1844NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1844NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1844NXXXXXX,n,Set(_NODEST=)
exten => _1844NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(outisbusy,)
exten => _1855NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1855NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1855NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1855NXXXXXX,n,Set(_NODEST=)
exten => _1855NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(outisbusy,)
exten => _1866NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1866NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1866NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1866NXXXXXX,n,Set(_NODEST=)
exten => _1866NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(outisbusy,)
exten => _1877NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1877NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1877NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1877NXXXXXX,n,Set(_NODEST=)
exten => _1877NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(outisbusy,)
exten => _1888NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1888NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1888NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1888NXXXXXX,n,Set(_NODEST=)
exten => _1888NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(outisbusy,)
exten => _800NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _800NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _800NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _800NXXXXXX,n,Set(_NODEST=)
exten => _800NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _800NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _800NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _800NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _800NXXXXXX,n,Macro(outisbusy,)
exten => _844NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _844NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _844NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _844NXXXXXX,n,Set(_NODEST=)
exten => _844NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _844NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _844NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _844NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _844NXXXXXX,n,Macro(outisbusy,)
exten => _855NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _855NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _855NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _855NXXXXXX,n,Set(_NODEST=)
exten => _855NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _855NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _855NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _855NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _855NXXXXXX,n,Macro(outisbusy,)
exten => _866NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _866NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _866NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _866NXXXXXX,n,Set(_NODEST=)
exten => _866NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _866NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _866NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _866NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _866NXXXXXX,n,Macro(outisbusy,)
exten => _877NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _877NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _877NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _877NXXXXXX,n,Set(_NODEST=)
exten => _877NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _877NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _877NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _877NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _877NXXXXXX,n,Macro(outisbusy,)
exten => _888NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _888NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _888NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _888NXXXXXX,n,Set(_NODEST=)
exten => _888NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _888NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _888NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _888NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _888NXXXXXX,n,Macro(outisbusy,)

; end of [outrt-5]

That is 112 lines of dialplan generated by only 12 lines of pattern in the Outbound Route and three trunk selections.  If you have more patterns and/or more trunks, the generated dialplan can grow MUCH larger.  At one point I had a dialplan so large that whenever I did an “orange bar” reload, it took two to three minutes to finish reloading.  Clearly, there is something wrong here.

Now, let’s suppose that instead of, or better yet, in addition to the current Outbound Route pages, we had pages called “Outbound Route – Regular Expression”.  It would eliminate the “Dial Patterns that will use this Route” section, replacing it with a “Regular Expression to select this Route” text box, and it would also have a text box that says “Extensions allowed to use this route (separated by the | character – RegEx patterns may be used).

Let’s say that the contents of the first text box are ^1?8(00|44|55|66|77|88)[2-9][0-9]{6}$ and are stored in variable string1. And let’s say that the contents of the second text box are 10[0-4]X|106[5-8] (specifying that extensions 1000-1049 and 1065-1068 may use this route) and are stored in string 2. Assuming that the part of the dialplan that processes this were in some kind of macro, and the called number is in the variable OUTNUM, you could perhaps do something like this:

exten => s,n,ExecIf($[${REGEX(“${string2}” ${CALLERID(num)})} != 1]?MacroExit())
exten => s,n,ExecIf($[${REGEX(“${string1}” ${OUTNUM})} != 1]?MacroExit())
exten => s,n,Macro(dialout-trunk,7,${OUTNUM},)
exten => s,n,Macro(dialout-trunk,10,${OUTNUM},)
exten => s,n,Macro(dialout-trunk,8,${OUTNUM},)
exten => s,n,Macro(outisbusy,)

The first line bails out of the macro if the Caller ID doesn’t match one of the permitted extensions (this would assume you by default use a regex that matches any extension on your system, for example ^[0-9]{4}$ would match any 4 digit extension number. If you don’t assume this, then you’d need an extra bit of code to skip a null value in string2).

The second line bails if the called number doesn’t match the pattern in string1.

If the called number does match on the regular expression for this route, and the call is from a permitted extension, then the three trunks are tried. Note that now you only need to list each of the three trunks once.

Granted this doesn’t account for every line in the original context, but the other lines could also be transferred into the macro, with a bit of thought. Wouldn’t this be simpler for those who know how to use regular expressions? And, it has the potential to generate MUCH shorter dialplans.

Those who understand the power of regular expressions and who have long or complicated dialplans would appreciate the ability to use regular expressions!  If anyone implements this, don’t be stingy with the maximum string length in the Regular Expression text boxes; I guarantee you that 256 characters is not enough, and neither is 1,024 characters! At a MINIMUM I would say that you need to allow a 4,096 character (4K) regular expression, and while I personally can’t conceive of anyone exceeding that, my bet is that sooner or later someone will.  Remember, you are potentially saving a whole bunch of memory and drive space by allowing regular expressions, so you don’t have to be miserly on the space allotted for the expressions themselves.

How to send various types of notifications on an incoming call in FreePBX

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

PLEASE NOTE: When you look at examples in this article, be aware that WordPress has a nasty habit of changing quotation marks to “prettified” quotes, which WILL NOT WORK. So, unfortunately, if you copy an example line or code block, you may need to go through and change the quotation marks back to the “real” quotes that most software understands. I get SO sick and tired of WordPress changing my articles on me!

Every so often I’ve had someone ask if they could be sent some sort of notification whenever they get an incoming call. It is, although it will send a notification on any incoming call, not just a missed one (if you only want notifications of missed calls, look toward the bottom of this article). The basic technique is as follows:

First, create or edit a Follow-Me for the user’s extension. Let’s say the extension number is 1122. In the Follow-Me, add a line that looks like this:

****1122#

Note you can use about anything in place **** followed by the extension number and # sign — only the trailing # must be there — but I do it that way so I can keep track of what’s going on.  If you are creating the Follow-Me for the first time, be sure to also put your actual extension number in the Follow-Me list (with NO trailing # sign), and check the other Follow-Me options to make sure that they are what you want (for example, that the “Destination if no answer” is your voicemail, if that’s what you want).

Next, in /etc/asterisk/extensions_custom.conf, find the [from-internal-custom] context (it’s right at the top of the file) and add lines similar to the ones shown below to the bottom of that context. Again, we are using 1122 as the extension, but replace it with the actual extension number:

Note that some of these examples depend on certain other software being present. Also, the FIRST line in each sequence must have the line number 1, but if you use more than one of these for an extension, replace the 1 with n in subsequent lines.

To send an e-mail or SMS message (the mail command must function properly from the Linux command prompt for this to work):

exten => ****1122,1,TrySystem(echo "Call from ${CALLERID(name)} at ${CALLERID(number)} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)}" | mail 9995559999@yourcarrier.com)

Replace 9995559999@yourcarrier.com with the e-mail address (which could be an e-mail to SMS gateway – most wireless carriers have one. Just search for “Email to SMS Gateway” and add your carrier’s name to the search, and you should find the format to use).  You can use “System” or “TrySystem” in the line above, the difference being that TrySystem is non-blocking, so the call should not be delayed if the process of sending the e-mail bogs things down.

To send an instant message using the XMPP/Jabber protocol — for this to work /etc/asterisk/xmpp_custom.conf (/etc/asterisk/jabber.conf in older versions of FreePBX) must be configured correctly, and there must NOT be a noload => res_jabber.so statement in modules.conf:

exten => ****1122,1,JabberSend(asterisk,username@gmail.com,Call from ${CALLERID(name)} at ${CALLERID(number)} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})

In the above, asterisk is the account context in xmpp_custom.conf or jabber.conf, and username@gmail.com is the address of the XMPP/Jabber user you want to send the IM to (in this case we assume you and the user are on Google Chat, but you could use any other XMPP/Jabber-based IM service, including a local XMPP/Jabber server set up with Prosody or Openfire or similar software. EDIT: If you are running FreePBX on a Raspberry Pi, see this article and this thread for information on setting up a Prosody server).

If you have Kodi (formerly XBMC) running on a system on your local network, you could use a line such as this to send notifications to it (EDITED to include Kodi Leia, Matrix, and Nexus):

In Eden and other Pre-Frodo versions:

exten => ****1122,1,TrySystem(wget -b -O /dev/null -o /dev/null "http://192.168.0.234:8080/xbmcCmds/xbmcHttp?command=ExecBuiltIn&parameter=XBMC.Notification(Call%20from%20%22${URIENCODE(${CALLERID(name)})}%22%2C${CALLERID(number)}%20calling%20extension%2C15000%2C%2Fhome%2Fusername%2Fphone.png)")

In Frodo through Krypton:

exten => ****1122,1,TrySystem(wget -b -O /dev/null -o /dev/null "http://192.168.0.234:8080/jsonrpc?request={%22jsonrpc%22:%222.0%22,%22method%22:%22GUI.ShowNotification%22,%22params%22:{%22title%22:%22Call%20from%20${URIENCODE(${CALLERID(name)})}%22,%22message%22:%22${CALLERID(number)}%20calling%20extension%22,%22displaytime%22:15000,%22image%22:%22%2Fhome%2Fusername%2Fphone.png%22},%22id%22:1}")

In Leia, Matrix, Nexus and probably later versions, until they decide to change it again:

exten => ****1122,1,TrySystem(curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"GUI.ShowNotification","params":{"title":"Call from ${CALLERID(name)}","message":"${CALLERID(number)} calling extension","displaytime":15000,"image":"/home/username/phone.png"},"id":0}' http://192.168.0.234:8080/jsonrpc)

Note: If you set Kodi to require authentication, then in place of the example http://192.168.0.234:8080/jsonrpc instead use the format http://kodiusername:password@ip-address:port/jsonrpc where kodiusername, password and port are the same values you used when setting up your Settings | Service | Control | Web Server settings in Kodi. Note that “kodiusername” is the user name set up in Kodi’s web server settings, and is probably not the same as “username” in the phone.png icon path (which in that case is the name of the user’s home directory).

Note that each of the above is a single long line, and to see any of those lines in its entirety you will need to copy and paste it to a text editor. The above examples assume that Kodi is configured to allow control via a Web interface, at a fixed IP address and port (192.168.0.234 port 8080 in this example). Phone icon - right click and copy imageIf you happen to still have a Linux version of Boxee, the Eden version line might also work with it, perhaps with a bit of tweaking.  I have no idea if it would work (with appropriate modification) with any other standalone home theater PC equipment.  You will likely need to replace username with the user’s name, extension with the called extension’s number, and change the icon path to wherever you put the phone.png file (which is an icon you need to supply, such as the one at the right). Note that the icon path requires %2F in place of forward slashes in pre-Leia versions (therefore %2Fhome%2Fusername%2Fphone.png really means /home/username/phone.png) and this refers to the icon directory and filename — if you choose not to use an icon then leave out this part, depending on your Kodi version:

Eden and prior: %2C%2Fhome%2Fusername%2Fphone.png
Frodo through Krypton: ,%22image%22:%22%2Fhome%2Fusername%2Fphone.png%22
Leia through Nexus (and beyond?): ,"image":"/home/username/phone.png"

Note the comma at the start; it must be included in the deleted section).  By the way, if you are wondering why it had to be changed again for Kodi Leia, see this thread in the Kodi forum.

If you want to receive Caller ID popup notifications on your computer, see A Perl script to send Caller ID popups from Asterisk to computers running Notify OSD (such as Ubuntu Linux) or any command-line invoked notification system and/or A Perl script to send Caller ID popups from Asterisk to computers running Growl under OS X on a Mac or Growl for Windows.  These articles are a bit dated and may not be compatible with some of the newer notification methods in use (such as the Notification Center used in MacOS that was introduced in OS X 10.8); in such cases it may be easier to just send instant messages to the computer as described above, although it may be possible to ssh into the computer and send a notification from the command line – this article describes how to send a notification from the command line in MacOS.

After using the any of above lines (and, again, remember to change the line number from 1 to n on any lines after the first), you may want to include a statement like this:

exten => ****1122,n,NoCDR()

That should prevent Asterisk from creating a separate, usually unwanted CDR entry for the ****(extension number) call.  There will still be a CDR record created for the actual call. EDIT: Actually, if you are going to use this, you probably should make it line 1, and move it above the other ****(extension number) lines, so that the CDR record is not saved even if the call gets disconnected in the middle of sending the notification. EDIT 2: In recent Asterisk versions NoCDR() has been deprecated and replaced by CDR_PROP(disable) so be sure to make that change if you are getting BUSY lines in the CDR when this runs.

Finally, to end this part of the context, you need to include a statement that ends the call in a “not completed” state, so that any other extensions in the Follow-Me will be called.  A couple of possibilities are:

exten => ****1122,n,Congestion
exten => ****1122,n,Busy

Use one and if it doesn’t work as expected, try the other. On my system it appears either will work. Remember that you must do a configuration reload in FreePBX before any of your changes to extensions_custom.conf will take effect!

One reason you may want to do instant message or SMS notifications is if you are forwarding calls via a trunk that does not preserve the original caller’s Caller ID.  If you were forwarding the call to a smart phone (perhaps one that also has a Jabber client running), you could deliver the original caller’s name and number to the phone via instant message or SMS.  It may or may not arrive in time for you to make a decision on whether to answer the call, but if you chose not to answer you’d at least know within a few seconds to a few minutes who the call was from, and you could decide whether to call them back.

If you have figured out any neat tricks to do other types of notifications, or if you know of a way to send a notification only when a call was not answered, feel free to leave a comment.

EDIT: At one time this page showed a method for sending SMS notifications using Google Voice rather than an e-mail to SMS gateway. Unfortunately it appears that the unofficial API that was used by such applications is no longer functional. If you know of a way to send a SMS message from a Linux command prompt using Google Voice that is currently working, please leave a message in the comments!

EDIT: I did think of a couple of techniques that might work for sending a notification only if the call is “missed” — the first is a bit of a hack, and has a serious limitation in that if the caller hangs up prior to the call going to voicemail, no notification would be sent (which might or might not be what you want). Basically, you would create (or modify) a Follow-Me as described above, but don’t put the ****extension# line in it. Instead of making the “Destination if no answer” your voicemail (for example), you would point it to a Ring Group that you have created, and in that Ring Group you would place the ****extension# line, and make the “Destination if no answer” of the ring group your voicemail. So the Follow-Me would ring your extension(s) for the number of seconds in the “Ring Time” setting, but if that time expires and no one answers, the call would be sent to the Ring Group where the only “extensions” would be your ****, which would do their thing and immediately return a “busy” or “congestion”. Since there are no other extensions to try, the Ring Group should immediately pass the call on to its “Destination if no answer”, which would most likely be your voicemail.

I haven’t tested this but I see no reason why it would not work, and in that case the notification would be sent just as the caller is being transferred to voicemail. If the caller doesn’t stick around that long you don’t get a notification, however, the fact that you do get a notification doesn’t necessarily mean that the caller actually left a voicemail — if they hang up during the voicemail greeting, a notification would still have already been sent.

EDIT: Here is a different technique for sending notifications on ONLY missed calls, and with this one the notification is sent whether the caller sticks around to leave a voicemail or not. You do NOT add anything to your Follow Me list when using this technique, nor do you modify extensions_custom.conf.

Note that this technique probably will not work on extensions that are part of a Ring Group or Follow Me where more than one extension is rung simultaneously (at least not without some modification — it should work if you use a ring strategy that only rings one phone at a time, though, such as firstnotonphone or firstavailable) (2017 EDIT: Some users have reported those two ring strategies are broken in FreePBX 13 and 14). Be aware that this one is a bit riskier since you will need to re-do the first part of the following procedure after any FreePBX upgrade, otherwise things may break!

  • Load the /etc/asterisk/extensions.conf file into a text editor such as nano
  • Search (Control-W in nano) for the string [from-internal] (including the brackets)
  • Copy the entire context (you can omit the comments) to a new open page in a text editor on your local computer
  • Next, search for the string [from-did-direct] (including the brackets)
  • Also copy that entire context (without the comments) to the same open page in your text editor
  • You MAY also need to copy the context [macro-hangupcall] which is found in /etc/asterisk/extensions_additional.conf. You would do that the same way as the previous two. I don’t show it in the examples below, but further down I’ll explain why you might need to do that one.

At this point the open page in your text editor will look something like this, although this may vary depending on your version of FreePBX. It is important that you copy these contexts from YOUR system, and repeat the procedure every time you upgrade FreePBX to a newer version:

[from-internal] include => from-internal-noxfer
include => from-internal-xfer
include => bad-number ; auto-generated

[from-did-direct] include => ext-findmefollow
include => ext-local

Now add these two lines to the bottom of EACH of the above two contexts (or all three if you also added [macro-hangupcall]):

exten => h,1,Macro(missed-call)
exten => h,n,hangup

The above is the part you will need to redo if you upgrade FreePBX. Next, add a new macro that looks something like this (this is just an example, don’t copy it verbatim!):

[macro-missed-call]
exten => s,1,NoOp(Checking to see if we need to send a missed call notification)
exten => s,n,GotoIf($["${CT_EXTEN}" > "1121" & "${CT_EXTEN}" < "1125" & "${CT_EXTEN}" != "" & "${ANSWEREDTIME}" = ""]?notify1)
exten => s,n,MacroExit()
exten => s,n(notify1),NoOp(Sending notification of missed call on extension ${CT_EXTEN})
exten => s,n,ExecIf($["${CALLERID(num)}" > "1121" & "${CALLERID(num)}" < "1125"]?MacroExit())
exten => s,n,ExecIf($["${CALLERID(num)}" = "8005551212"]?MacroExit())
exten => s,n,JabberSend(asterisk,username@gmail.com,Missed call from ${CALLERID(name)} at ${CALLERID(num)} for extension ${CT_EXTEN} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,MacroExit()

Here’s an explanation of what’s happening in the above Macro, so you can modify it for your specific needs:

  • Line 1 is just a NoOp that tells what the macro is doing.
  • Line 2 says that if the call was for extensions 1122 through 1124 (greater than 1121 and less than 1125), that we didn’t get a null value for the extension, and the call was not answered, go to the label notify1. If we only wanted to test for a single extension such as 1122, instead of “${CT_EXTEN}” > “1121” & “${CT_EXTEN}” < “1125” just use “${CT_EXTEN}” = “1122”. You can add as many lines as you need for various extensions, just be sure to jump to different labels.
  • Line 3 says that if none of the above conditions match, exit the macro.
  • Line 4 is just a NoOp that serves as a placeholder for the label and prepares to send the notification.
  • Line 5 is optional and shows how to exclude notifications for calls if the calling number is within a range, in this case 1122-1124 (so, in this example no notifications would be made on calls from one of your own extensions to another in the 1122-1124 range). You can use as many of these lines as you need to exclude more ranges, or none at all if you don’t want to exclude any ranges. Make sure you get the quotation marks right (and again, if you copy lines from this blog, beware of the quotation marks that WordPress has “prettified” – you’ll need to fix them before the lines will work!).
  • Line 6 is optional and shows how to exclude a specific number from triggering the notification. In this case, if the call comes from 8005551212, no notification would be sent. Note that you could use the ${CALLERID(name) variable as a trigger also, and exclude calls from certain names from triggering a notification. Note you can place multiple tests in the same expression by using the bar character (logical OR in Asterisk) to connect them, for example, “${CALLERID(num)}” = “8005551212” | “${CALLERID(name)}” = “INFORMATION” would match if either condition were true. This line can be omitted completely if you don’t want to exclude calls from any specific numbers.
  • Line 7 is where we actually send the notification. Basically any of the methods mentioned in the previous part of this article can be used here, just replace ****1122,1, in the example lines with s,n,
  • Line 8 is where we gracefully exit the macro. Of course, you can repeat lines 4-7 as many times as needed if you are doing notifications for more than one extension.

Now that you have the three (or four) macros in your text file and edited as they should be, copy the whole mess and paste it into the existing file /etc/asterisk/extensions_override_freepbx.conf — do not just overwrite the existing file! Instead, open it with nano or another text editor and paste in the lines you’ve been working on. Also, do a quick scan to make sure you haven’t duplicated a context that’s already in there (the likelihood of that is pretty remote unless you have done something like this in the past). Note that after you do a FreePBX reload, the contexts in this file will replace the equivalents in the files generated by FreePBX, so that’s why you have to check that the originals haven’t changed each time you upgrade FreePBX.

If you want to know what variables are available for you to trigger on, instead of a line sending a notification you can use a line like this:

exten => s,n,DumpChan

This will show you a lot of information, including how several of the channel variables are set.

One final note, the added macro call under [from-internal] is the one that would be taken after a failed call from another extension, while the one under [from-did-direct] is the one used by failed calls coming in from outside the system. Note that most answered calls (not including ones that go to voicemail) will wind up in [macro-hangupcall] and not go through either of those, BUT in certain circumstances unanswered calls might also end via [macro-hangupcall], such as if they go through a ring group. That is why I mentioned above that you might need to include [macro-hangupcall] along with the other contexts copied into /etc/asterisk/extensions_override_freepbx.conf. However, I have not tested every possible condition, and I do know that some things won’t work — such as if multiple extensions are rung simultaneously, no extension number shows up in the ${CT_EXTEN} variable, so those tests in [macro-missed-call] that depend on that variable being set will fail. You may be able to modify the macro so that it will work in such circumstances by using one or more variables you can find by using DumpChan, but that’s a piece of dialplan I’m not prepared to write, since I personally have no need for it (and have already spent way too much time on this)!

I probably should also include this note about the use of extensions_override_freepbx.conf as taken from this page:

If extensions.conf (or extensions_additional.conf) has a context or macro that you NEED to modify, you place that code here as asterisk will only execute the first occurrences of that code and ignores other occurrences. This file will not be overwritten. Be very careful as replacing an existing piece of code this way is the fastest possible way to break your system. If you are doing this you should probably think about filing for a feature request or bug fix to get it addressed properly.

Of course, my experience is that filing a feature request with FreePBX is a great way to either be ignored, or lectured by some a**h*le on why you don’t need that feature, so I wouldn’t bother. Just be sure that if you upgrade FreePBX, or even just the Core or Framework modules, you check to make sure that the contexts you’ve copied into extensions_override_freepbx.conf haven’t been changed (or if they have, make sure you copy over the changes).

How to use the FreePBX [macro-dialout-trunk-predial-hook] macro and regular expressions to blacklist or whitelist outgoing calls on all trunks

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission.

DISCLAIMER: Just because I do something a certain way doesn’t mean YOU should, at least not without extensive testing to make sure that it will do what you want. I do not warrant that anything shown here will in any way save you from getting a high phone bill. If that should happen, consider it a happy coincidence, but if it doesn’t work as you expected, don’t come crying to me. I only offer what I think might work, it’s up to YOU to test it on YOUR system.

This is a followup to my article, Did you know that Asterisk has the ability to evaluate Regular Expressions, though not in the same way as Perl or FreeSWITCH? If you have not read that article, you may want to do that first. Both that article and this one are subject to future revision, if I discover more on this subject.

The problem I have always had on the two systems I help administer is that we don’t allow international calls that cost money. In the past I had resorted to putting a complete list of valid area code patterns in each user’s Outbound Route, and since I had to include patterns for both 11 digit and 10 digit calls, it grew to be a pretty long list. Fortunately, when adding a new user, it was easy to copy the list from an existing user’s outbound account, bring it into a text editor to edit the /extension suffix, and paste it into the new user’s Outbound Route. That worked in FreePBX 2.7, and in versions 2.8 and 2.9 if you use the Swiss Army Knife module, but that module won’t work with FreePBX 2.10 and above.

Unfortunately, as you are very well aware if you have read many of my previous posts, starting in FreePBX 2.8 the FreePBX developers changed the entry method for Outbound Route dial patterns, changing it from an incredibly easy process to enter a list of patterns into a painful, totally stupid method involving individual text boxes for each individual part of each line of each Outbound Route dial pattern. That one change was enough to make me hate FreePBX. The FreePBX developers have been totally unresponsive to those who don’t care for the new method (they pretty much try to pretend such users don’t even exist).

One thing I am NOT going to EVER do is enter a long list of dial patterns using their input method! Someone once said of me that I would spend weeks looking for a way around having to re-type something, when it might only take me a few hours to re-type it, and that’s true. I have even abandoned whole articles because my system crashed and there was no backup, and there was no damn way I was retyping what I had already typed. So in that spirit, I started looking for ways to get around having to enter all those dial patterns for each Outbound Route. I’ve used various stopgap measures including weird things like custom trunks, but that was an ugly solution and leaves extra entries in the Call Detail Records. When I found out that Asterisk could use Regular Expressions, I figured out this method, which is actually much faster, and gives me another way to bypass the limitations of FreePBX.

This uses the [macro-dialout-trunk-predial-hook] macro which is already present, but commented out, in /etc/asterisk/extensions_custom.conf. The thing you need to understand about this macro is that it is called AFTER most of the dial plan associated with both the outbound route and the trunk have been applied. So if you change the called number in either the Outbound Route Dial Patterns or the Trunk Dialed Number Manipulation Rules, those transformations will already have been applied by the time this macro is called. Also, if the calling number has been changed (by setting the Outbound Caller ID in the Trunk, for example), that change have been made by the time this macro is called. And finally, this macro is called when a call is about to go out on ANY trunk in your system (at least I think it does, although I haven’t tested it with every possible trunk type. I’m reasonably sure it is called whenever a call is destined for a SIP trunk, but I am not sure that it gets called if you use one of the other available types of trunks. It does NOT seem to get called when a call is destined for an ENUM trunk). If you don’t want it to act on calls from a certain trunk, you need to test for that and bail out of the macro (I show an example of that below).

The interesting thing about this is that because this macro is invoked so late in the progress of an outgoing call, you can use it as a last chance to look for “undesirable things” or to send notifications of certain happenings. For example, although I don’t show exactly how to do it below, you could have it send an instant message or an e-mail notification when someone calls a specific number, such as 911.

What I am showing here is what I have set up on one of my systems. This assumes that the outbound routes only contain the following Dial Patterns (not all Outbound Routes will contain all of these, and I assume that each user has their own outbound route, or that groups of users may share outbound routes):

1NXXNXXXXXX/ext
NXXNXXXXXX/ext
1aaa+NXXXXXX/ext

…where aaa is the user’s local area code for 7 digit dialing , and ext is the user’s extension (which could be a pattern to allow a group of extensions). In addition, if the user’s service allows the *67 code to be used to block outgoing Caller ID, then these patterns might also be used (with the same substitutions):

*671NXXNXXXXXX/ext
*67NXXNXXXXXX/ext
*671aaa+*67|NXXXXXX/ext

The way I have set it up also ignores any number under 10 digits (allowing it to go through) so if you have dial patterns that allow users to dial provider service codes, or extensions on another connected server, or some such thing, those calls won’t be affected — only calls of 10 digits or longer are checked (this is one reason I prepend the area code to 7 digit calls in the Outbound Routes). Note that there are long lines in the following so if you want to see them in their entirety, you need to cut and paste this into a text editor:

[macro-dialout-trunk-predial-hook]
; this macro intentially left blank so it may be safely overwritten for any custom
; requirements that an installation may have.
;
; MACRO RETURN CODE: ${PREDIAL_HOOK_RET}
;                    if set to "BYPASS" then this trunk will be skipped
;
exten => s,1,NoOp(Trunk ${OUT_${DIAL_TRUNK}} selected)
exten => s,n,ExecIf($[${LEN(${OUTNUM})}<10]?MacroExit())
exten => s,n,ExecIf($["${OUT_${DIAL_TRUNK}}"="SIP/TollFree-Future9"]?MacroExit())
exten => s,n,Set(regx=^([*]67)?1?900[0-9]{7}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?premium)
exten => s,n,Set(regx=^([*]67)?1?8(00|22|33|44|55|66|77|88)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?tollfree)
exten => s,n,Set(regx=^([*]67)?1?[2-9][0-9]{2}976[0-9]{4}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?premium)
exten => s,n,Set(regx=^([*]67)?1?(201|202|203|205|206|207|208|209|210|212|213|214|215|216|217|218|219|220|223|224|225|228|229|231|234|239|240|248|251|252|253|254|256|260|262|267|269|270|272|274|276|279|281|301|302|303|304|305|307|308|309|310|312|313|314|315|316|317|318|319|320|321|323|325|327|330|331|332|334|336|337|339|346|347|351|352|360|361|364|380|385|386|401|402|404|405|406|407|408|409|410|412|413|414|415|417|419|423|424|425|430|432|434|435|440|442|443|445|458|463|469|470|475|478|479|480|484|501|502|503|504|505|507|508|509|510|512|513|515|516|517|518|520|530|534|539|540|541|551|559|561|562|563|564|567|570|571|573|574|575|580|585|586|601|602|603|605|606|607|608|609|610|612|614|615|616|617|618|619|620|623|626|628|629|630|631|636|641|646|650|651|657|660|661|662|667|669|678|680|681|682|701|702|703|704|706|707|708|712|713|714|715|716|717|718|719|720|724|725|726|727|731|732|734|737|740|743|747|754|757|760|762|763|765|769|770|772|773|774|775|779|781|785|786|801|802|803|804|805|806|808|810|812|813|814|815|816|817|818|828|830|831|832|838|843|845|847|848|850|854|856|857|858|859|860|862|863|864|865|870|872|878|901|903|904|906|907|908|909|910|912|913|914|915|916|917|918|919|920|925|928|929|930|931|934|936|937|938|940|941|947|949|951|952|954|956|959|970|971|972|973|978|979|980|984|985|986|989)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?usa)
exten => s,n,Set(regx=^([*]67)?1?(204|226|236|249|250|289|306|343|365|367|403|416|418|431|437|438|450|506|514|519|548|579|581|587|604|613|639|647|705|709|778|780|782|807|819|825|867|873|879|902|905)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?canada)
exten => s,n,Set(regx=^0118835100[0-9]{8}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?inum)
exten => s,n,NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a NOT a permitted call)
exten => s,n,Jabbersend(asterisk,system_administrator_address@gmail.com,${CALLERID(num)} attempted to call ${OUTNUM} at ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,Playback(custom/sorry-outside-us-canada)
exten => s,n,Macro(hangupcall,)
exten => s,n,Set(PREDIAL_HOOK_RET=BYPASS)
exten => s,n,MacroExit()
exten => s,n(premium),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a premium number call - NOT perrmitted)
exten => s,n,Jabbersend(asterisk,system_administrator_address@gmail.com,${CALLERID(num)} attempted to call a premium number ${OUTNUM} at ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,Playback(silence/1&sorry-cant-let-you-do-that3)
exten => s,n,Macro(hangupcall,)
exten => s,n,Set(PREDIAL_HOOK_RET=BYPASS)
exten => s,n,MacroExit()
exten => s,n(usa),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a USA call)
exten => s,n,MacroExit()
exten => s,n(canada),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a Canada call)
exten => s,n,MacroExit()
exten => s,n(tollfree),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a toll free call)
exten => s,n,MacroExit()
exten => s,n(inum),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is an inum call)
exten => s,n,MacroExit()
exten => h,1,Macro(hangupcall,)

Now here’s a breakdown of the above, in which I try to explain what I have done. I will note at the outset that some may think I am using two lines where one will do, by putting the regular expression into a variable when I could just evaluate it directly in the REGEX function in the following line. However, when I was researching the use of regular expressions in Asterisk, I read on several pages that at least some versions of Asterisk have issues parsing a regular expression if it contains certain characters that also have a meaning to Asterisk, unless you put the regex into a string variable first and then use that in the REGEX function. I don’t know if that’s always true but I figured better safe than sorry, and besides, it’s much less confusing to me when I do it that way. But if you want to try combining the two-line pairs into one, feel free — it’s your system, and your headache if it doesn’t work. Also, remember that Asterisk doesn’t parse regular expressions in the same way that some other software (such as Perl) does, so if you try to simplify my regex patterns you might run into trouble. So with that said, let’s begin our tour.

This part of the macro is already present in extensions_custom.conf. You only need to uncomment the lines that are not obviously comments:

[macro-dialout-trunk-predial-hook]
; this macro intentially left blank so it may be safely overwritten for any custom
; requirements that an installation may have.
;
; MACRO RETURN CODE: ${PREDIAL_HOOK_RET}
;                    if set to "BYPASS" then this trunk will be skipped
;
exten => s,1,NoOp(Trunk ${OUT_${DIAL_TRUNK}} selected)

The next line is the first of many places we will use MacroExit() to bail out of the macro. This checks to see in the number called was 9 digits or less. We don’t want to block 911 calls, intra-company calls to other extensions, calls to another network that uses extensions of 9 digits or less, etc.:

exten => s,n,ExecIf($[${LEN(${OUTNUM})}<10]?MacroExit())

This bails out if the call is going out a specific trunk. In this case we don’t want to mess with toll-free calls going to Future9 because they are sent using a rather unusual pattern that would not be recognized by the regular expressions below.

exten => s,n,ExecIf($["${OUT_${DIAL_TRUNK}}"="SIP/TollFree-Future9"]?MacroExit())

Here’s an example of blocking a call that conforms to a specific pattern, in this case 1-900-XXX-XXXX or just 900-XXX-XXXX. It will block calls with or without the *67 (privacy) prefix, and with or without the leading “1”. If you don’t allow *67 calling then just remove the [*]67)? from the start of the expression. If it matches that pattern, it is transferred to the label “premium”, which we’ll get to in a bit:

exten => s,n,Set(regx=^([*]67)?1?900[0-9]{7}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?premium)

Here’s an example of allowing a call that matches a specific pattern, in this case a NANP toll-free call, again with or without the *67 (privacy) prefix (as is the case with most of these regular expressions — note that your outbound route(s) would also need to allow the *67 pattern). These calls are transferred to the label “tollfree”, which we’ll also talk about later:

exten => s,n,Set(regx=^([*]67)?1?8(00|22|33|44|55|66|77|88)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?tollfree)

Here we block calls that match the pattern 1-[2-9]XX-976-XXXX or just [2-9]XX-976-XXXX (with or without a *67 prepend) and send them to “premium”:

exten => s,n,Set(regx=^([*]67)?1?[2-9][0-9]{2}976[0-9]{4}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?premium)

Here we test to see if the call is going to any USA area code. This is a really long regex but it works. If the pattern matches, it transfers to the “usa” label

exten => s,n,Set(regx=^([*]67)?1?(201|202|203|205|206|207|208|209|210|212|213|214|215|216|217|218|219|220|223|224|225|228|229|231|234|239|240|248|251|252|253|254|256|260|262|267|269|270|272|274|276|279|281|301|302|303|304|305|307|308|309|310|312|313|314|315|316|317|318|319|320|321|323|325|327|330|331|332|334|336|337|339|346|347|351|352|360|361|364|380|385|386|401|402|404|405|406|407|408|409|410|412|413|414|415|417|419|423|424|425|430|432|434|435|440|442|443|445|458|463|469|470|475|478|479|480|484|501|502|503|504|505|507|508|509|510|512|513|515|516|517|518|520|530|534|539|540|541|551|559|561|562|563|564|567|570|571|573|574|575|580|585|586|601|602|603|605|606|607|608|609|610|612|614|615|616|617|618|619|620|623|626|628|629|630|631|636|641|646|650|651|657|660|661|662|667|669|678|680|681|682|701|702|703|704|706|707|708|712|713|714|715|716|717|718|719|720|724|725|726|727|731|732|734|737|740|743|747|754|757|760|762|763|765|769|770|772|773|774|775|779|781|785|786|801|802|803|804|805|806|808|810|812|813|814|815|816|817|818|828|830|831|832|838|843|845|847|848|850|854|856|857|858|859|860|862|863|864|865|870|872|878|901|903|904|906|907|908|909|910|912|913|914|915|916|917|918|919|920|925|928|929|930|931|934|936|937|938|940|941|947|949|951|952|954|956|959|970|971|972|973|978|979|980|984|985|986|989)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?usa)

Same as the previous except now we’re testing for Canadian area codes, and if successful transferring to the “canada” label:

exten => s,n,Set(regx=^([*]67)?1?(204|226|236|249|250|289|306|343|365|367|403|416|418|431|437|438|450|506|514|519|548|579|581|587|604|613|639|647|705|709|778|780|782|807|819|825|867|873|879|902|905)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?canada)

(NOTE: It should be noted that a few new area codes are added every year. You can see a list of the most recently added ones here: NPAs Introduced over the Last 10 Years. You should check this periodically and add the new USA and Canadian area codes to the appropriate sections above.)

Google Voice used to complete calls to iNum numbers for free, so this test allowed for those. Now I don’t use this section, but leave it here as an example. Back when these calls were free, this was the only “international” format call that was permitted on our system:

exten => s,n,Set(regx=^0118835100[0-9]{8}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?inum)

If we get here, the call failed all the tests that would indicate it might be a permitted call. I play a custom recorded message that tells the caller that their call is not allowed, possibly because it’s not to a destination in the United States or Canada. Just prior to playing the message, I use the Jabbersend function to send myself an instant message showing that a call failed. I mainly do this so I can determine whether the caller really tried to dial a number that’s not permitted, or if there’s an error in my logic. For example, someone could be trying to place calls to a valid area code that somehow got omitted from the regular expression. Note that Jabbersend will only work if you have jabber.conf configured correctly AND the res_jabber.so module is loading when Asterisk starts up. If you have ever configured your server to place Google Voice calls using Asterisk’s jabber/gtalk support, those things are probably true, but it still may be a bit tricky to get working, however I’m not going to discuss that in this article — just search for Asterisk Jabbersend in a search engine if you need help:

exten => s,n,NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a NOT a permitted call)
exten => s,n,Jabbersend(asterisk,system_administrator_address@gmail.com,${CALLERID(num)} attempted to call ${OUTNUM} at ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,Playback(custom/sorry-outside-us-canada)
exten => s,n,Macro(hangupcall,)
exten => s,n,Set(PREDIAL_HOOK_RET=BYPASS)
exten => s,n,MacroExit()

One further note about the last three lines in the above section: Setting the PREDIAL_HOOK_RET variable and the MacroExit() are probably superfluous because the Macro(hangupcall,) will terminate the call, but I left them there for safety. Note that if you were to comment out the Macro(hangupcall,), the last two lines would execute, and then if more than one trunk was specified in the Outbound Route, it would keep retrying the call on each of those trunks (and the caller would hear the error message multiple times, and you’d get multiple IM’s). Although I can’t personally envision such a situation, there might be situations where trying the additional trunks would be desirable.

Next is where the call goes if the caller attempted to dial a premium (900 or 976) number. This is similar to the previous section, except the recording played to the caller is one second of silence followed by a standard system recording. The silence is necessary to keep the first word or two of the recording from being cut off on some clients. Also the IM I send to myself is slightly different. And the same comment applies on the last three lines. I know, I could have done a Goto to the same three lines in the previous section, but that would have only saved me two lines, and since the last two lines are superfluous anyway, if you are that tight on space you can just delete them.

exten => s,n(premium),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a premium number call - NOT perrmitted)
exten => s,n,Jabbersend(asterisk,system_administrator_address@gmail.com,${CALLERID(num)} attempted to call a premium number ${OUTNUM} at ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,Playback(silence/1&sorry-cant-let-you-do-that3)
exten => s,n,Macro(hangupcall,)
exten => s,n,Set(PREDIAL_HOOK_RET=BYPASS)
exten => s,n,MacroExit()

The next sections contain the usa, canada, tollfree, and inum labels. All they do is print an appropriate message to the CLI and exit. Rather than jump to these labels, I could have simply used statements of the form ExecIf($[${REGEX(“${regx}” ${OUTNUM})} = 1]?MacroExit()) and left out this entire next section, but I like the idea of being able to see which regex allowed the call in the CLI, and this also allows for the possibility of additional code. For example, I could insert code that keeps a running tally of calls broken down by their destination. I have no need to do anything like that, but maybe you do:

exten => s,n(usa),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a USA call)
exten => s,n,MacroExit()
exten => s,n(canada),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a Canada call)
exten => s,n,MacroExit()
exten => s,n(tollfree),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a toll free call)
exten => s,n,MacroExit()
exten => s,n(inum),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is an inum call)
exten => s,n,MacroExit()

This last line is important and was omitted from the default macro. It handles the case where the caller hangs up while this code is executing. Although that window is only a small fraction of second, bad things could happen if the caller does manage to hang up within that window and this statement isn’t present:

exten => h,1,Macro(hangupcall,)

Well, that’s how I’m doing it right now. It may not be how I do it two months from now, but this seems better than any method I have used thus far.

I will point out, for whatever it’s worth, that you COULD use an Asterisk dial command right in this macro. So, for example, let’s say you had a trunk that was real expensive at a certain time of the day but much cheaper during another. You COULD test to see if the call is being made within the “cheap” time and if so just exit, but if not you could execute a dial command that looks like the one normally executed, which is…

exten => s,n,Dial(${OUT_${DIAL_TRUNK}}/${OUTNUM},300,${DIAL_TRUNK_OPTIONS})

…except that in place of ${OUT_${DIAL_TRUNK}} you could specify a different trunk, for example SIP/OtherTrunk. And of course you could use this macro to change the dial trunk options for specific trunks, or to play a recording to callers if their call is going out over an “expensive” trunk. In fact, I can see where this macro could be used to get to get around some of the FreePBX syntax checking that has plagued me in the past.

It should probably be noted that if you watch these macros execute in the Asterisk CLI, every time one of them is executed you may see a WARNING message such as this:

[2012-11-22 11:48:12] WARNING[30588][C-00000055]: pbx.c:1585 pbx_exec: The application delimiter is now the comma, not the pipe. Did you forget to convert your dialplan? (Set(regx=^([\*]{2}1)?([\*]67)?W?1?8(00|22|33|44|55|66|77|88)[2-9][0-9]{6}$))

This warning doesn’t apply in this situation because the pipe IS the correct character to use in a regular expression, when you are matching a single regular expression out of several possible regular expressions. See this page on Alternation with The Vertical Bar or Pipe Symbol for more information, with the caveat that Asterisk’s expression engine is a bit different from that used in other software, and therefore may not recognize syntax that would be perfectly valid in another application. Therefore, not all examples shown on various regular expression tutorials or reference pages will work. If you wish to try a particular syntax, be sure to do a test run or two and make sure it works. But, Asterisk’s regular expression engine DOES recognize the pipe character when used as shown in the above examples, but spits out these warning messages anyway, even though the pipe characters are part of the regular expression, and are not being used as application delimiters. You can safely ignore the warnings – they are a nuisance, but are meaningless in this particular situation. If you really don’t want to see those warnings, use a text editor to go to /etc/asterisk/asterisk.conf and uncomment this line:

;dontwarn = yes                 ; Disable some warnings.

This will disable some non-essential warnings such as this one. If it were me, I would have named this option “stopwhining” rather than “dontwarn”, because Asterisk does tend to complain a lot about things that don’t matter and that you can’t do anything about. 🙂

Once again, I will mention that articles in this blog (and especially one such as this) are subject to future editing as I discover more about the subject. If you find any neat uses for the [macro-dialout-trunk-predial-hook] macro (ESPECIALLY if they allow you to do something that FreePBX tries to stop you from doing, but that’s perfectly allowable in Asterisk), feel free to leave a comment!

Recent Posts

Recent Comments

Archives

Categories

Meta

GiottoPress by Enrique Chavez