Producing Content-Length 0 Responses in Rails

Returning a blank response in Ruby on Rails is trickier than we first imagined. For instance, we thought that either one of these two methods would suffice:

render text: nil
render nothing: true

But they don’t. Both of these methods result in a response with a Content-Length value of 1. What’s the content? A single hex character of 20, which represents a single whitespace character.

We played around with a few other options and found that this is the most straightforward method to provide a truly blank response:

render text: ''

Configuring a Cisco 9951 Phone for Asterisk

Keep in Mind

The Cisco Unified IP Phone 8961, 9951, and 9971 phones were not designed to work with any phone system other than Cisco Unified Communications Manager. So, this effort is essentially a hack. If you are OK with certain features not working, like the Contacts button or aspects of Voicemail indication/calling, proceed. If you are looking for tight integration between your endpoints and your Asterisk PBX, you’ll want to consider a phone that supports Asterisk, such as a Polycom, Snom, Grandstream, Aastera, or Linksys device.

What are the Tricky Parts?

Cisco phones have never been terribly straightforward to configure. The 9951 introduces a few more curveballs, which I will mention here. Other peculiar aspects, which the 9951 shares with the previous 79XX series of phones have been left out as there are ample resources available elsewhere.

The 9951 brought these additional challenges:

  • SIP over TCP support only (no traditional SIP over UDP allowed)
  • A particular bug that can cause troubleshooting confusion
  • Seemingly different XML configuration requirements

Configuring Asterisk for SIP over TCP

We are using Asterisk 1.8, so adding TCP support is simply a matter of configuration. I believe this is a similar case for 1.6. If you are using Asterisk 1.4, you will need to determine how to add TCP support as it is not native.

There are two steps to configuring SIP over TCP. The first is to enable it at the global level in Asterisk. Add the following to your SIP configuration. If you’re using vanilla Asterisk, you can put this in your sip.conf file below [general]. If you’re using FreePBX, the most appropriate place is in sip_general_custom.conf.

tcpenable=yes
tcpbindaddr=0.0.0.0

Next, make sure that transport=tcp for each extension that you plan to use the 9951 with.

Once you’ve completed these steps, open the Asterisk CLI and run sip reload to apply the settings.

Self Call Bug

We found that calling ourselves from the 9951 and then answering the call resulted in the phone keeping a “dead” call open on the screen. This seems like nothing more than a visual artifact, but it actually causes loss of additional functionality on the phone. During our troubleshooting, we’d SSH into the device and issue reset soft commands to force the device to grab it’s new configuration. Once we had one of these dead calls, reset soft would no longer do anything, adding to our confusion as we tried to get the phone up and running. Beware!

XML Configuration Files

Yes, the part that most of you have been waiting for. Here is the configuration that ended up working for us. You will want to modify this configuration and save it to a file named SEP[MAC_ADDRESS].cnf.xml on your TFTP server. Replace all of the bracketed sections with your applicable settings.

Note: Make sure that the proxy node under the sipLines/line node remains set to USECALLMANAGER. We were unable to get the device to register with Asterisk when we individually added the hostname or IP of our PBX here.

<device> 
   <deviceProtocol>SIP</deviceProtocol> 
   <sshUserId>admin</sshUserId> 
   <sshPassword>password</sshPassword> 
   <devicePool> 
      <dateTimeSetting> 
         <dateTemplate>M/D/YA</dateTemplate> 
         <timeZone>Pacific Standard/Daylight Time</timeZone> 
         <ntps> 
            <ntp> 
               <name>pool.ntp.org</name> 
               <ntpMode>Unicast</ntpMode> 
            </ntp>         
         </ntps> 
      </dateTimeSetting> 
      <callManagerGroup> 
         <members> 
            <member priority="0"> 
               <callManager> 
                  <ports> 
                     <ethernetPhonePort>2000</ethernetPhonePort> 
                     <sipPort>5060</sipPort> 
                     <securedSipPort>5061</securedSipPort> 
                  </ports> 
                  <processNodeName>[YOUR PBX HOST/IP]</processNodeName> 
               </callManager> 
            </member> 
         </members> 
      </callManagerGroup> 
   </devicePool> 
   <sipProfile> 
      <sipProxies> 
         <backupProxy></backupProxy> 
         <backupProxyPort>5060</backupProxyPort> 
         <emergencyProxy></emergencyProxy> 
         <emergencyProxyPort></emergencyProxyPort> 
         <outboundProxy></outboundProxy> 
         <outboundProxyPort></outboundProxyPort> 
         <registerWithProxy>true</registerWithProxy> 
      </sipProxies> 
      <sipCallFeatures> 
         <cnfJoinEnabled>true</cnfJoinEnabled> 
         <callForwardURI>x-serviceuri-cfwdall</callForwardURI> 
         <callPickupURI>x-cisco-serviceuri-pickup</callPickupURI> 
         <callPickupListURI>x-cisco-serviceuri-opickup</callPickupListURI> 
         <callPickupGroupURI>x-cisco-serviceuri-gpickup</callPickupGroupURI> 
         <meetMeServiceURI>x-cisco-serviceuri-meetme</meetMeServiceURI> 
         <abbreviatedDialURI>x-cisco-serviceuri-abbrdial</abbreviatedDialURI> 
         <rfc2543Hold>false</rfc2543Hold> 
         <callHoldRingback>2</callHoldRingback> 
         <localCfwdEnable>true</localCfwdEnable> 
         <semiAttendedTransfer>true</semiAttendedTransfer> 
         <anonymousCallBlock>2</anonymousCallBlock> 
         <callerIdBlocking>2</callerIdBlocking> 
         <dndControl>0</dndControl> 
         <remoteCcEnable>true</remoteCcEnable> 
      </sipCallFeatures> 
      <sipStack> 
         <sipInviteRetx>6</sipInviteRetx> 
         <sipRetx>10</sipRetx> 
         <timerInviteExpires>180</timerInviteExpires> 
         <timerRegisterExpires>3600</timerRegisterExpires> 
         <timerRegisterDelta>5</timerRegisterDelta> 
         <timerKeepAliveExpires>120</timerKeepAliveExpires> 
         <timerSubscribeExpires>120</timerSubscribeExpires> 
         <timerSubscribeDelta>5</timerSubscribeDelta> 
         <timerT1>500</timerT1> 
         <timerT2>4000</timerT2> 
         <maxRedirects>70</maxRedirects> 
         <remotePartyID>false</remotePartyID> 
         <userInfo>None</userInfo> 
      </sipStack> 
      <autoAnswerTimer>1</autoAnswerTimer> 
      <autoAnswerAltBehavior>false</autoAnswerAltBehavior> 
      <autoAnswerOverride>true</autoAnswerOverride> 
      <transferOnhookEnabled>false</transferOnhookEnabled> 
      <enableVad>false</enableVad> 
      <dtmfAvtPayload>101</dtmfAvtPayload> 
      <dtmfDbLevel>3</dtmfDbLevel> 
      <dtmfOutofBand>avt</dtmfOutofBand> 
      <alwaysUsePrimeLine>false</alwaysUsePrimeLine> 
      <alwaysUsePrimeLineVoiceMail>false</alwaysUsePrimeLineVoiceMail> 
      <kpml>3</kpml> 
      <phoneLabel>[LABEL THAT APPEARS AT TOP OF SCREEN]</phoneLabel> 
      <stutterMsgWaiting>1</stutterMsgWaiting> 
      <callStats>false</callStats> 
      <silentPeriodBetweenCallWaitingBursts>10</silentPeriodBetweenCallWaitingBursts> 
      <disableLocalSpeedDialConfig>false</disableLocalSpeedDialConfig> 
      <sipLines> 
         <line button="1"> 
            <featureID>9</featureID> 
            <featureLabel>[LINE LABEL ON SCREEN]</featureLabel> 
            <proxy>USECALLMANAGER</proxy> 
            <port>5060</port> 
            <name>[SIP EXTENSION]</name> 
            <displayName>[SIP EXTENSION]</displayName> 
            <autoAnswer> 
               <autoAnswerEnabled>2</autoAnswerEnabled> 
            </autoAnswer> 
            <callWaiting>3</callWaiting> 
            <authName>[SIP EXTENSION]</authName> 
            <authPassword>[SIP PASSWORD]</authPassword> 
            <sharedLine>false</sharedLine> 
            <messageWaitingLampPolicy>1</messageWaitingLampPolicy> 
            <messagesNumber>*97</messagesNumber> 
            <ringSettingIdle>4</ringSettingIdle> 
            <ringSettingActive>5</ringSettingActive> 
            <contact>[SIP EXTENSION]</contact> 
            <forwardCallInfoDisplay> 
               <callerName>true</callerName> 
               <callerNumber>false</callerNumber> 
               <redirectedNumber>false</redirectedNumber> 
               <dialedNumber>true</dialedNumber> 
            </forwardCallInfoDisplay> 
         </line>
      </sipLines> 
      <voipControlPort>5060</voipControlPort> 
      <startMediaPort>16348</startMediaPort> 
      <stopMediaPort>20134</stopMediaPort> 
      <dscpForAudio>184</dscpForAudio> 
      <ringSettingBusyStationPolicy>0</ringSettingBusyStationPolicy> 
      <dialTemplate>dialplan.xml</dialTemplate> 
      <softKeyFile></softKeyFile> 
   </sipProfile> 
   <commonProfile> 
      <phonePassword></phonePassword> 
      <backgroundImageAccess>true</backgroundImageAccess> 
      <callLogBlfEnabled>2</callLogBlfEnabled> 
   </commonProfile> 
   <loadInformation>SIP45.8-4-2S</loadInformation> 
   <vendorConfig> 
      <disableSpeaker>false</disableSpeaker> 
      <disableSpeakerAndHeadset>false</disableSpeakerAndHeadset> 
      <pcPort>0</pcPort> 
      <settingsAccess>1</settingsAccess> 
      <garp>0</garp> 
      <voiceVlanAccess>0</voiceVlanAccess> 
      <videoCapability>0</videoCapability> 
      <autoSelectLineEnable>0</autoSelectLineEnable> 
      <webAccess>1</webAccess> 
      <daysDisplayNotActive>1,2,3,4,5,6,7</daysDisplayNotActive> 
      <displayOnTime>00:00</displayOnTime> 
      <displayOnDuration>00:00</displayOnDuration> 
      <displayIdleTimeout>00:00</displayIdleTimeout> 
      <spanToPCPort>1</spanToPCPort> 
      <loggingDisplay>1</loggingDisplay> 
      <loadServer></loadServer> 
   </vendorConfig> 
   <userLocale> 
      <name></name> 
      <uid></uid> 
      <langCode>en_US</langCode> 
      <version>1.0.0.0-1</version> 
      <winCharSet>iso-8859-1</winCharSet> 
   </userLocale> 
   <networkLocale></networkLocale> 
   <networkLocaleInfo> 
      <name></name> 
      <uid></uid> 
      <version>1.0.0.0-1</version> 
   </networkLocaleInfo>    
   <deviceSecurityMode>1</deviceSecurityMode> 
   <authenticationURL></authenticationURL> 
   <directoryURL></directoryURL> 
   <servicesURL></servicesURL> 
   <idleURL></idleURL> 
   <informationURL></informationURL> 
   <messagesURL></messagesURL> 
   <proxyServerURL></proxyServerURL> 
   <dscpForSCCPPhoneConfig>96</dscpForSCCPPhoneConfig> 
   <dscpForSCCPPhoneServices>0</dscpForSCCPPhoneServices> 
   <dscpForCm2Dvce>96</dscpForCm2Dvce> 
   <transportLayerProtocol>4</transportLayerProtocol> 
   <capfAuthMode>0</capfAuthMode> 
   <capfList> 
      <capf> 
         <phonePort>3804</phonePort> 
      </capf> 
   </capfList> 
   <certHash></certHash> 
   <encrConfig>false</encrConfig> 
</device>

We also found that the dialplan.xml file is respected as it was with previous Cisco phones. Ours looks like this, though yours may vary:

<DIALTEMPLATE>
	<TEMPLATE MATCH="91.........." Timeout="0"/>
	<TEMPLATE MATCH="911" Timeout="0"/>
	<TEMPLATE MATCH="\*.." Timeout="0"/>
	<TEMPLATE MATCH="[1-8].." Timeout="1"/>
	<TEMPLATE MATCH="*" Timeout="5"/>
</DIALTEMPLATE>

Voicemail

The Cisco 9951 has a Messages button on the front that presumably integrates with a Cisco Communications Manager to provide some form of visual voicemail. We have yet to find a way to reprogram this button to integrate with Asterisk.

As a workaround, we’ve created a speed dial button that does connect directly to Asterisk Voicemail. We added this config to our SEP[MAC_ADDRESS].cnf.xml file:

<line button="2">
	<featureID>21</featureID>
	<featureLabel>Voicemail</featureLabel>
	<speedDialNumber>*97</speedDialNumber>
</line>

Outstanding Issues

We’d still like to find answers to the following:

  • How can we wire the Messages button to automatically dial *97 (Asterisk Voicemail)?
  • Can we configure the Contacts button to show contacts populated by an XML file? Setting DirectoryURL didn’t seem to do anything.

Control Apple Devices and Apps Remotelysimplemdm.com

SimpleMDM allows you to configure, secure, and track iPads, iPhones, and OS X remotely, via the web. SimpleMDM provides advanced functionality usually exclusive to convoluted enterprise offerings.