more clean up
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@13997 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
d53d493ba7
commit
f22fb723db
|
@ -1,63 +0,0 @@
|
||||||
This directory contains software used for interfacing the
|
|
||||||
FreeSWITCH voicemail application with the AT&T (aka Lucent
|
|
||||||
aka Avaya) System 25 PBX. It's possible that System 75
|
|
||||||
and Definity PBXs may also work.
|
|
||||||
|
|
||||||
s25vmail.js goes into the FreeSWITCH scripts directory.
|
|
||||||
s25vmail_mwi.c should be compiled and the resulting binary
|
|
||||||
put into the FreeSWITCH bin directory. Modify the FreeSWITCH
|
|
||||||
rc.d script to also start / stop s25vmail_mwi.
|
|
||||||
|
|
||||||
Configuration fragments look something like:
|
|
||||||
|
|
||||||
conf/openzap.conf:
|
|
||||||
|
|
||||||
[span zt]
|
|
||||||
; A204DX
|
|
||||||
name => OpenZAP
|
|
||||||
dtmf_hangup = ##99
|
|
||||||
|
|
||||||
number => 551
|
|
||||||
fxo-channel => 49
|
|
||||||
|
|
||||||
number => 552
|
|
||||||
fxo-channel => 50
|
|
||||||
|
|
||||||
number => 553
|
|
||||||
fxo-channel => 51
|
|
||||||
|
|
||||||
number => 554
|
|
||||||
fxo-channel => 52
|
|
||||||
|
|
||||||
conf/dialplan/default.xml
|
|
||||||
|
|
||||||
<extension name="system25_vmail">
|
|
||||||
<condition field="destination_number" expression="^(55[1-4])$">
|
|
||||||
<action application="javascript" data="s25vmail.js"/>
|
|
||||||
</condition>
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
Tested using FreeSWITCH SVN 10428 running on FreeBSD 6.3
|
|
||||||
with a Sangoma A200DX Analog Series w/ Echo Cancellation
|
|
||||||
ports card containing 2 FXO modules.
|
|
||||||
|
|
||||||
Note that the PBX is * very * sensitive to how long it takes
|
|
||||||
for the line to be hung up after it sends the DTMF hangup
|
|
||||||
command. Failure to apply the following patch will cause
|
|
||||||
the PBX to occasionally believe that some vmail lines were
|
|
||||||
off hook for too long and are therfore out of service.
|
|
||||||
|
|
||||||
Index: src/zap_io.c
|
|
||||||
===================================================================
|
|
||||||
--- src/zap_io.c (revision 745)
|
|
||||||
+++ src/zap_io.c (working copy)
|
|
||||||
@@ -1728,7 +1728,8 @@
|
|
||||||
zchan->dtmf_hangup_buf[zchan->span->dtmf_hangup_len - 1] = *p;
|
|
||||||
if (!strcmp(zchan->dtmf_hangup_buf, zchan->span->dtmf_hangup)) {
|
|
||||||
zap_log(ZAP_LOG_DEBUG, "DTMF hangup detected.\n");
|
|
||||||
- zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
|
|
||||||
+ zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CLEARING;
|
|
||||||
+ zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
This directory contains software for configuring FreeSWITCH
|
|
||||||
to provide AT&T (aka Lucent aka Avaya) System 25 PBX compatible
|
|
||||||
park and pickup park functions. Specifically:
|
|
||||||
|
|
||||||
a) Putting a call on hold and then dialing *5 will park the
|
|
||||||
call on your phone.
|
|
||||||
|
|
||||||
b) Dialing *8 followed by an extension will pickup a call parked
|
|
||||||
on that extension.
|
|
||||||
|
|
||||||
as a bonus:
|
|
||||||
|
|
||||||
c) Doing a blind transfer of a call to *5 will park the call
|
|
||||||
on your phone.
|
|
||||||
|
|
||||||
d) Doing a blind transfer of a call to *5 followed by an extension
|
|
||||||
will park the call on that extension.
|
|
||||||
|
|
||||||
e) Dialing *8 without an extension will prompt for an extension.
|
|
||||||
|
|
||||||
s25park.js goes into the FreeSWITCH scripts directory.
|
|
||||||
|
|
||||||
Configuration fragments look something like:
|
|
||||||
|
|
||||||
conf/dialplan/default.xml
|
|
||||||
|
|
||||||
<extension name="system25_park">
|
|
||||||
<condition field="source" expression="mod_sofia"/>
|
|
||||||
<condition field="destination_number" expression="^\*5$"/>
|
|
||||||
<condition field="${sip_h_Referred-By}" expression="^<sip:([0-9]{4})@.*$">
|
|
||||||
<action application="transfer" data="*5$1"/>
|
|
||||||
<anti-action application="javascript" data="s25park.js"/>
|
|
||||||
</condition>
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
<extension name="system25_park_on_extension">
|
|
||||||
<condition field="destination_number" expression="^\*5([0-9]{4})$">
|
|
||||||
<action application="set" data="fifo_music=$${hold_music}"/>
|
|
||||||
<action application="set" data="fifo_orbit_exten=$1:120"/>
|
|
||||||
<action application="fifo" data="$1@$${domain} in"/>
|
|
||||||
</condition>
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
<extension name="system25_pickup">
|
|
||||||
<condition field="destination_number" expression="^\*8$">
|
|
||||||
<action application="answer"/>
|
|
||||||
<action application="sleep" data="1"/>
|
|
||||||
<action application="read" data="3 5 $${base_dir}/sounds/en/us/callie/ivr/8000/ivr-enter_ext.wav ext 1000 #"/>
|
|
||||||
<action application="transfer" data="*8${ext}"/>
|
|
||||||
</condition>
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
<extension name="system25_pickup_from_extension">
|
|
||||||
<condition field="destination_number" expression="^\*8[0-9]{3,4}$"/>
|
|
||||||
<condition field="destination_number" expression="^\*8([0-9]{4})$">
|
|
||||||
<action application="fifo" data="$1@$${domain} out nowait"/>
|
|
||||||
<anti-action application="bridge" data="openzap/5/a/${destination_number}"/>
|
|
||||||
</condition>
|
|
||||||
</extension>
|
|
||||||
|
|
||||||
The system25 park and pickup dialplan patterns are designed
|
|
||||||
to only consider four digit extensions for local parking.
|
|
||||||
"system25_pickup_from_extension" recognizes three digit
|
|
||||||
extensions as being parked on a foreign PBX ... modify
|
|
||||||
as appropriate for your installation.
|
|
||||||
|
|
||||||
Be aware that the default dialplan contains an extension
|
|
||||||
called "group-intercept" which needs to be commented out
|
|
||||||
in order for "system25_pickup" to work since they both
|
|
||||||
match *8.
|
|
||||||
|
|
||||||
Tested using FreeSWITCH SVN 13769 running on FreeBSD 6.4.
|
|
|
@ -1,178 +0,0 @@
|
||||||
/*
|
|
||||||
* File: s25park.js
|
|
||||||
* Purpose: Implement AT&T System 25 PBX style parking.
|
|
||||||
* Machine: OS:
|
|
||||||
* Author: John Wehle Date: June 9, 2009
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2009 Feith Systems and Software, Inc.
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of the original author; nor the names of any contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* RE to sanity check that the caller id is a valid extension */
|
|
||||||
var extRE = /^[0-9]{3,4}$/g;
|
|
||||||
|
|
||||||
|
|
||||||
var dtmf_digits;
|
|
||||||
|
|
||||||
function on_dtmf (session, type, obj, arg)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (type == "dtmf") {
|
|
||||||
dtmf_digits += obj.digit;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function normalize_channel_name (name, direction, ip_addr)
|
|
||||||
{
|
|
||||||
var re = /^sofia\//g;
|
|
||||||
var length = name.search (re);
|
|
||||||
var new_name = name;
|
|
||||||
|
|
||||||
if (length == -1)
|
|
||||||
return new_name;
|
|
||||||
|
|
||||||
if (direction == "inbound") {
|
|
||||||
re = /@.*$/g;
|
|
||||||
|
|
||||||
new_name = name.replace (re, "@" + ip_addr);
|
|
||||||
}
|
|
||||||
else if (direction == "outbound") {
|
|
||||||
re = /\/sip:(.*@[^:]*):.*$/g;
|
|
||||||
|
|
||||||
new_name = name.replace (re, "/$1");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
session.answer ();
|
|
||||||
|
|
||||||
session.execute ("sleep", "1000");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Figure out the normalized form of the requester's channel name.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var requester_channel_name = normalize_channel_name (
|
|
||||||
session.getVariable ("channel_name"), "inbound",
|
|
||||||
session.getVariable ("network_addr"));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find the uuid for a call on the requester's phone.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var channels = apiExecute ("show", "channels as xml");
|
|
||||||
var re = /\s+$/g;
|
|
||||||
var length = channels.search (re);
|
|
||||||
|
|
||||||
if (length == -1)
|
|
||||||
length = channels.length;
|
|
||||||
|
|
||||||
channels = channels.substring (0, length);
|
|
||||||
|
|
||||||
var xchannels = new XML (channels);
|
|
||||||
var our_uuid = session.getVariable ("uuid");
|
|
||||||
var requester_uuid = "";
|
|
||||||
|
|
||||||
for each (var channel in xchannels.row) {
|
|
||||||
if (channel.uuid.toString () == our_uuid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var channel_name = normalize_channel_name (channel.name.toString (),
|
|
||||||
channel.direction.toString (), channel.ip_addr.toString ());
|
|
||||||
|
|
||||||
if (channel_name == requester_channel_name) {
|
|
||||||
requester_uuid = channel.uuid.toString ();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requester_uuid == "") {
|
|
||||||
session.sayPhrase ("voicemail_invalid_extension", "#", "", on_dtmf, "");
|
|
||||||
session.hangup ();
|
|
||||||
exit ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find the peer uuid.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var udump = apiExecute ("uuid_dump", requester_uuid + " xml");
|
|
||||||
var re = /\s+$/g;
|
|
||||||
var length = udump.search (re);
|
|
||||||
|
|
||||||
if (length == -1)
|
|
||||||
length = udump.length;
|
|
||||||
|
|
||||||
udump = udump.substring (0, length);
|
|
||||||
|
|
||||||
var xudump = new XML (udump);
|
|
||||||
var uuid = xudump.headers['Other-Leg-Unique-ID'].toString ();
|
|
||||||
|
|
||||||
if (uuid == "") {
|
|
||||||
session.sayPhrase ("voicemail_invalid_extension", "#", "", on_dtmf, "");
|
|
||||||
session.hangup ();
|
|
||||||
exit ();
|
|
||||||
}
|
|
||||||
|
|
||||||
var requester_id_number = session.getVariable ("caller_id_number");
|
|
||||||
|
|
||||||
if (requester_id_number.search (extRE) == -1) {
|
|
||||||
session.sayPhrase ("voicemail_invalid_extension", "#", "", on_dtmf, "");
|
|
||||||
session.hangup ();
|
|
||||||
exit ();
|
|
||||||
}
|
|
||||||
|
|
||||||
apiExecute ("uuid_setvar", uuid + " hangup_after_bridge false");
|
|
||||||
apiExecute ("uuid_transfer", uuid + " *5" + requester_id_number + " XML default");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Provide confirmation beeps followed by some silence.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var confirmation = "tone_stream://L=3;%(100,100,350,440)";
|
|
||||||
|
|
||||||
session.execute ("playback", confirmation);
|
|
||||||
|
|
||||||
var i;
|
|
||||||
|
|
||||||
for (i = 0; session.ready () && i < 100; i++)
|
|
||||||
session.execute("sleep", "100");
|
|
||||||
|
|
||||||
exit ();
|
|
|
@ -1,236 +0,0 @@
|
||||||
/*
|
|
||||||
* File: s25vmail.js
|
|
||||||
* Purpose: Invoke voicemail based on AT&T System 25 PBX voicemail mode codes
|
|
||||||
* Machine: OS:
|
|
||||||
* Author: John Wehle Date: June 24, 2008
|
|
||||||
*
|
|
||||||
* The message waiting indicator is handled by a separate program.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2008 Feith Systems and Software, Inc.
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of the original author; nor the names of any contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
var id_digits_required = 3;
|
|
||||||
|
|
||||||
var digitTimeOut = 3000;
|
|
||||||
var interDigitTimeOut = 1000;
|
|
||||||
var absoluteTimeOut = 10000;
|
|
||||||
|
|
||||||
|
|
||||||
var dtmf_digits = "";
|
|
||||||
|
|
||||||
function on_dtmf (session, type, obj, arg)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (type == "dtmf") {
|
|
||||||
dtmf_digits += obj.digit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function prompt_for_id ()
|
|
||||||
{
|
|
||||||
var dto;
|
|
||||||
var id;
|
|
||||||
var index;
|
|
||||||
var repeat;
|
|
||||||
|
|
||||||
dtmf_digits = "";
|
|
||||||
id = "";
|
|
||||||
repeat = 0;
|
|
||||||
|
|
||||||
while (session.ready () && repeat < 3) {
|
|
||||||
session.flushDigits ();
|
|
||||||
|
|
||||||
/* play phrase - if digit keyed while playing callback will catch them*/
|
|
||||||
session.sayPhrase ("voicemail_enter_id", "#", "", on_dtmf, "");
|
|
||||||
|
|
||||||
if (! session.ready ())
|
|
||||||
return "";
|
|
||||||
|
|
||||||
id = dtmf_digits;
|
|
||||||
|
|
||||||
if (id.indexOf ('#') == -1) {
|
|
||||||
dto = digitTimeOut;
|
|
||||||
if (dtmf_digits.length != 0)
|
|
||||||
dto = interDigitTimeOut;
|
|
||||||
dtmf_digits = session.getDigits (5, '#', dto,
|
|
||||||
interDigitTimeOut, absoluteTimeOut);
|
|
||||||
id += dtmf_digits;
|
|
||||||
id += '#';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* a valid id must meet the minimum length requirements */
|
|
||||||
if ((index = id.indexOf ('#')) >= id_digits_required) {
|
|
||||||
id = id.substring (0,index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtmf_digits = "";
|
|
||||||
id = "";
|
|
||||||
repeat++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var start = "";
|
|
||||||
var mode = "";
|
|
||||||
var from = "";
|
|
||||||
var to = "";
|
|
||||||
|
|
||||||
var domain = session.getVariable ("domain");
|
|
||||||
|
|
||||||
session.answer ();
|
|
||||||
|
|
||||||
start = session.getDigits (1, '', digitTimeOut,
|
|
||||||
interDigitTimeOut, absoluteTimeOut);
|
|
||||||
|
|
||||||
if (start != "#") {
|
|
||||||
var destination_number = session.getVariable ("destination_number");
|
|
||||||
|
|
||||||
console_log ("err", destination_number + " received an invalid VMAIL start code from PBX\n");
|
|
||||||
if (session.ready ())
|
|
||||||
session.sayPhrase ("voicemail_goodbye", "#", "", on_dtmf, "");
|
|
||||||
else
|
|
||||||
console_log ("err", "Possibly due to early hangup from PBX\n");
|
|
||||||
session.hangup ();
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
mode = session.getDigits (5, '#', digitTimeOut,
|
|
||||||
interDigitTimeOut, absoluteTimeOut);
|
|
||||||
|
|
||||||
from = session.getDigits (5, '#', digitTimeOut,
|
|
||||||
interDigitTimeOut, absoluteTimeOut);
|
|
||||||
|
|
||||||
to = session.getDigits (5, '#', digitTimeOut,
|
|
||||||
interDigitTimeOut, absoluteTimeOut);
|
|
||||||
|
|
||||||
session.execute("sleep", "1000");
|
|
||||||
|
|
||||||
// Verify that the proper parameters are present
|
|
||||||
switch (mode) {
|
|
||||||
|
|
||||||
// Direct Inside Access
|
|
||||||
case "00":
|
|
||||||
if (isNaN (parseInt (from, 10))) {
|
|
||||||
console_log ("err", "Invalid VMAIL calling PDC from PBX\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.setVariable ("voicemail_authorized", "false");
|
|
||||||
session.execute ("voicemail", "check default " + domain + " " + from);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Direct Dial Access
|
|
||||||
case "01":
|
|
||||||
from = prompt_for_id ();
|
|
||||||
|
|
||||||
if (! session.ready ()) {
|
|
||||||
session.hangup ();
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNaN (parseInt (from, 10))) {
|
|
||||||
console_log ("err", "Invalid VMAIL mailbox from caller\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.setVariable ("voicemail_authorized", "false");
|
|
||||||
session.execute ("voicemail", "check default " + domain + " " + from);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Coverage - caller is inside
|
|
||||||
case "02":
|
|
||||||
if (isNaN (parseInt (from, 10)) || isNaN (parseInt (to, 10))) {
|
|
||||||
console_log ("err", "Invalid VMAIL calling or called PDC from PBX\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.setVariable ("effective_caller_id_name", "inside caller");
|
|
||||||
session.setVariable ("effective_caller_id_number", from);
|
|
||||||
|
|
||||||
session.execute ("voicemail", "default " + domain + " " + to);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Coverage - caller is dial
|
|
||||||
case "03":
|
|
||||||
if (isNaN (parseInt (to, 10))) {
|
|
||||||
console_log ("err", "Invalid VMAIL called PDC from PBX\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.setVariable ("effective_caller_id_name", "outside caller");
|
|
||||||
session.setVariable ("effective_caller_id_number", "Unknown");
|
|
||||||
|
|
||||||
session.execute ("voicemail", "default " + domain + " " + to);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Coverage - not yet defined
|
|
||||||
case "04":
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Leave Word Calling
|
|
||||||
case "05":
|
|
||||||
if (isNaN (parseInt (from, 10)) || isNaN (parseInt (to, 10))) {
|
|
||||||
console_log ("err", "Invalid VMAIL calling or called PDC from PBX\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Refresh MW lamps
|
|
||||||
case "06":
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Voice Port failed to answer
|
|
||||||
case "08":
|
|
||||||
if (isNaN (parseInt (to, 10))) {
|
|
||||||
console_log ("err", "Invalid VMAIL PDC from PBX\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
console_log ("err", "PBX reports problem with VMAIL PDC " + to + "\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Unknown
|
|
||||||
default:
|
|
||||||
console_log ("err", "Invalid VMAIL mode code from PBX\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
exit();
|
|
|
@ -1,983 +0,0 @@
|
||||||
/*
|
|
||||||
* File: s25vmail_mwi.c
|
|
||||||
* Purpose: Send AT&T System 25 PBX MWI DTMF based on MWI events
|
|
||||||
* Machine: OS:
|
|
||||||
* Author: John Wehle Date: July 24, 2008
|
|
||||||
*
|
|
||||||
* Tested using a Zyxel U90e configured using:
|
|
||||||
*
|
|
||||||
* at OK at&f OK at&d3&y2q2 OK ats0=0s2=255s15.7=0s18=4s35.1=0 OK
|
|
||||||
* ats38.3=1s42.3=1s42.6=1 OK atl0 OK at&w OK at&v
|
|
||||||
*
|
|
||||||
* though just about any modem should work. Preferred settings are
|
|
||||||
*
|
|
||||||
* DTR OFF causes hangup and reset from profile 0
|
|
||||||
* RTS / CTS flow control
|
|
||||||
* allow abort during modem handshake
|
|
||||||
* auto answer off
|
|
||||||
* ring message off
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2008 Feith Systems and Software, Inc.
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of the original author; nor the names of any contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <memory.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <termios.h>
|
|
||||||
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define LOGFILE "/var/log/s25vmail_mwi.log"
|
|
||||||
|
|
||||||
|
|
||||||
static const char *MyName = "s25vmail_mwi";
|
|
||||||
|
|
||||||
static int daimon = 0;
|
|
||||||
static int error_msg_throttle = 0;
|
|
||||||
static volatile int shutdown_server = 0;
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
debugmsg (const char *fmt, ...)
|
|
||||||
{
|
|
||||||
char message[256];
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
if (daimon)
|
|
||||||
return;
|
|
||||||
|
|
||||||
va_start (args, fmt);
|
|
||||||
vsprintf (message, fmt, args);
|
|
||||||
va_end (args);
|
|
||||||
|
|
||||||
fprintf (stderr, "%s: %s", MyName, message);
|
|
||||||
if ( !strchr (message, '\n'))
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fflush (stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
errmsg (const char *fmt, ...)
|
|
||||||
{
|
|
||||||
char time_stamp[256];
|
|
||||||
struct tm *tmp;
|
|
||||||
time_t now;
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
if (! daimon) {
|
|
||||||
fprintf (stderr, "%s: ", MyName);
|
|
||||||
|
|
||||||
va_start (args, fmt);
|
|
||||||
vfprintf (stderr, fmt, args);
|
|
||||||
va_end (args);
|
|
||||||
|
|
||||||
if (! strchr (fmt, '\n'))
|
|
||||||
fputc ('\n', stderr);
|
|
||||||
|
|
||||||
fflush (stderr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_msg_throttle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
time (&now);
|
|
||||||
|
|
||||||
if ( !(tmp = localtime (&now)) ) {
|
|
||||||
fprintf (stderr, "%s: errmsg -- localtime failed.\n", MyName);
|
|
||||||
perror (MyName);
|
|
||||||
fflush (stderr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
strftime (time_stamp, sizeof (time_stamp), "%b %d %H:%M:%S", tmp);
|
|
||||||
fprintf (stderr, "%s %s[%d]: ", time_stamp, MyName, (int)getpid ());
|
|
||||||
|
|
||||||
va_start (args, fmt);
|
|
||||||
vfprintf (stderr, fmt, args);
|
|
||||||
va_end (args);
|
|
||||||
|
|
||||||
if (! strchr (fmt, '\n'))
|
|
||||||
fputc ('\n', stderr);
|
|
||||||
|
|
||||||
fflush (stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
catch_signal ()
|
|
||||||
{
|
|
||||||
|
|
||||||
shutdown_server = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
daemonize()
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef SIGTSTP
|
|
||||||
(void)signal(SIGTSTP, SIG_IGN);
|
|
||||||
#endif
|
|
||||||
#ifdef SIGTTIN
|
|
||||||
(void)signal(SIGTTIN, SIG_IGN);
|
|
||||||
#endif
|
|
||||||
#ifdef SIGTTOU
|
|
||||||
(void)signal(SIGTTOU, SIG_IGN);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
switch (fork ()) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case -1:
|
|
||||||
fprintf (stderr, "%s: daemonize -- fork failed.", MyName);
|
|
||||||
perror (MyName);
|
|
||||||
exit (1);
|
|
||||||
/* NOTREACHED */
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
exit (0);
|
|
||||||
/* NOTREACHED */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
setsid();
|
|
||||||
|
|
||||||
close (0);
|
|
||||||
close (1);
|
|
||||||
close (2);
|
|
||||||
|
|
||||||
(void)open ("/dev/null", O_RDWR);
|
|
||||||
(void)open ("/dev/null", O_RDWR);
|
|
||||||
(void)open (LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0644);
|
|
||||||
|
|
||||||
daimon = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
install_signal_handlers ()
|
|
||||||
{
|
|
||||||
struct sigaction act;
|
|
||||||
|
|
||||||
memset (&act, '\0', sizeof (act));
|
|
||||||
|
|
||||||
act.sa_handler = catch_signal;
|
|
||||||
sigemptyset (&act.sa_mask);
|
|
||||||
act.sa_flags = 0;
|
|
||||||
|
|
||||||
if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
|
|
||||||
sigaction (SIGHUP, &act, NULL);
|
|
||||||
if (signal (SIGINT, SIG_IGN) != SIG_IGN)
|
|
||||||
sigaction (SIGINT, &act, NULL);
|
|
||||||
(void)sigaction (SIGTERM, &act, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
connect_to_service (const char *hostname, const char *port)
|
|
||||||
{
|
|
||||||
int sock;
|
|
||||||
struct hostent *hp;
|
|
||||||
struct in_addr address;
|
|
||||||
struct servent *servp;
|
|
||||||
struct sockaddr_in sin;
|
|
||||||
|
|
||||||
if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("socket failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset (&sin, 0, sizeof (sin));
|
|
||||||
|
|
||||||
if (isalpha (hostname[0])) {
|
|
||||||
if ( !(hp = gethostbyname (hostname))) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("gethostbyname failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
close (sock);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (hp->h_addrtype != AF_INET) {
|
|
||||||
errmsg ("gethostbyname returned unsupported family\n");
|
|
||||||
close (sock);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy (&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
address.s_addr = inet_addr (hostname);
|
|
||||||
|
|
||||||
if ((long)address.s_addr == -1) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("inet_addr failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
close (sock);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sin.sin_addr.s_addr = address.s_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isalpha (*port)) {
|
|
||||||
if ( !(servp = getservbyname(port, "tcp"))) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("getservbyname failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
close (sock);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sin.sin_port = servp->s_port;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
sin.sin_port = htons ((unsigned short)atoi (port));
|
|
||||||
|
|
||||||
sin.sin_family = AF_INET;
|
|
||||||
|
|
||||||
if (connect (sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("connect failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
close (sock);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
debugmsg ("Connected to service\n");
|
|
||||||
|
|
||||||
return sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
read_line (int fd, char *buf, size_t buf_len)
|
|
||||||
{
|
|
||||||
size_t l;
|
|
||||||
ssize_t nbytes_read;
|
|
||||||
|
|
||||||
l = 0;
|
|
||||||
|
|
||||||
for ( ; ; ) {
|
|
||||||
nbytes_read = read (fd, &buf[l], 1);
|
|
||||||
|
|
||||||
if (nbytes_read < 0) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("read failed in middle of line\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read == 0) {
|
|
||||||
if (l)
|
|
||||||
errmsg ("EOF in middle of line\n");
|
|
||||||
return l ? -1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf[l] == '\n') {
|
|
||||||
while (l && buf[l - 1] == '\r')
|
|
||||||
l--;
|
|
||||||
buf[l++] = '\0';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
l++;
|
|
||||||
|
|
||||||
if (l == buf_len) {
|
|
||||||
errmsg ("line too long\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
read_trailing_newline(int fd)
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
ssize_t nbytes_read;
|
|
||||||
|
|
||||||
nbytes_read = read (fd, &c, 1);
|
|
||||||
|
|
||||||
if (nbytes_read < 0) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("read failed in trailing newline\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read == 0) {
|
|
||||||
errmsg ("EOF in trailing newline\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c != '\n') {
|
|
||||||
errmsg ("missing trailing newline\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static char *
|
|
||||||
retrieve_message (int fd)
|
|
||||||
{
|
|
||||||
char cl_buf[64];
|
|
||||||
char ct_buf[64];
|
|
||||||
char *h;
|
|
||||||
char *m;
|
|
||||||
ssize_t cl;
|
|
||||||
ssize_t nbytes_read;
|
|
||||||
size_t l;
|
|
||||||
size_t nbytes_to_read;
|
|
||||||
|
|
||||||
if (shutdown_server)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read / parse Content-Length and Content-Type.
|
|
||||||
*/
|
|
||||||
|
|
||||||
nbytes_read = read_line (fd, cl_buf, sizeof (cl_buf));
|
|
||||||
|
|
||||||
if (nbytes_read < 0) {
|
|
||||||
errmsg ("read_line failed\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read == 0) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* EOF
|
|
||||||
*/
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
nbytes_read = read_line (fd, ct_buf, sizeof (ct_buf));
|
|
||||||
|
|
||||||
if (nbytes_read < 0) {
|
|
||||||
errmsg ("read_line failed\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read == 0) {
|
|
||||||
errmsg ("EOF in middle of headers\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
h = "Content-Length: ";
|
|
||||||
l = strlen (h);
|
|
||||||
|
|
||||||
if (strncmp (cl_buf, h, l) != 0) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the message header doesn't being with Content-Length,
|
|
||||||
* then it needs to be a Content-Type we understand.
|
|
||||||
*/
|
|
||||||
|
|
||||||
h = "Content-Type: ";
|
|
||||||
l = strlen (h);
|
|
||||||
|
|
||||||
if (strncmp (cl_buf, h, l) != 0) {
|
|
||||||
errmsg ("missing Content-Type\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (&cl_buf[l], "auth/request") != 0
|
|
||||||
&& strcmp (&cl_buf[l], "command/reply") != 0) {
|
|
||||||
errmsg ("Unsupported Content-Type\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ct_buf[0])
|
|
||||||
if (read_trailing_newline (fd) < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
m = malloc (strlen (cl_buf) + 1 + strlen (ct_buf) + 1 + 1);
|
|
||||||
|
|
||||||
if (! m) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("malloc failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf (m, "%s\n%s\n", cl_buf, ct_buf);
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
cl = atoi (&cl_buf[l]);
|
|
||||||
|
|
||||||
if (cl <= 0) {
|
|
||||||
errmsg ("Content-Length must be greater than zero\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
h = "Content-Type: ";
|
|
||||||
l = strlen (h);
|
|
||||||
|
|
||||||
if (strncmp (ct_buf, h, l) != 0) {
|
|
||||||
errmsg ("missing Content-Type\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (&ct_buf[l], "text/event-plain") != 0) {
|
|
||||||
errmsg ("Unsupported Content-Type\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read_trailing_newline (fd) < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read the event.
|
|
||||||
*/
|
|
||||||
|
|
||||||
m = malloc (cl);
|
|
||||||
|
|
||||||
if (! m) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("malloc failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (nbytes_to_read = cl; nbytes_to_read; nbytes_to_read -= nbytes_read) {
|
|
||||||
nbytes_read = read (fd, m + (cl - nbytes_to_read), nbytes_to_read);
|
|
||||||
|
|
||||||
if (nbytes_read < 0) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("read failed in middle of message\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
free (m);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read == 0) {
|
|
||||||
errmsg ("EOF in middle of message\n");
|
|
||||||
free (m);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m[cl - 2] != '\n' || m[cl - 1] != '\n') {
|
|
||||||
errmsg ("Message is missing trailing newlines\n");
|
|
||||||
free (m);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
send_password (int fd, const char *passwd)
|
|
||||||
{
|
|
||||||
char *h;
|
|
||||||
char *last;
|
|
||||||
char *m;
|
|
||||||
char *p;
|
|
||||||
int l;
|
|
||||||
size_t ml;
|
|
||||||
|
|
||||||
m = retrieve_message (fd);
|
|
||||||
if (! m)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
p = strtok_r (m, "\n", &last);
|
|
||||||
|
|
||||||
h = "Content-Type: auth/request";
|
|
||||||
|
|
||||||
if (strcmp (p, h) != 0) {
|
|
||||||
errmsg ("Content-Type wasn't auth/request\n");
|
|
||||||
free (m);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free (m);
|
|
||||||
|
|
||||||
l = snprintf (NULL, 0, "auth %s\n\n", passwd);
|
|
||||||
if (l <= 0) {
|
|
||||||
errmsg ("snprintf failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
l++;
|
|
||||||
|
|
||||||
m = malloc (l);
|
|
||||||
if (! m) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("malloc failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ml = snprintf (m, l, "auth %s\n\n", passwd);
|
|
||||||
if ((ml + 1) != l) {
|
|
||||||
errmsg ("snprintf failed\n");
|
|
||||||
free (m);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write (fd, m, ml) != ml) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("write failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
free (m);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
m = retrieve_message (fd);
|
|
||||||
if (! m )
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
p = strtok_r (m, "\n", &last);
|
|
||||||
|
|
||||||
h = "Content-Type: command/reply";
|
|
||||||
|
|
||||||
if (! p || strcmp (p, h) != 0) {
|
|
||||||
errmsg ("Content-Type wasn't command/reply\n");
|
|
||||||
free (m);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = strtok_r (NULL, "\n", &last);
|
|
||||||
|
|
||||||
h = "Reply-Text: +OK accepted";
|
|
||||||
|
|
||||||
if (! p || strcmp (p, h) != 0) {
|
|
||||||
errmsg ("auth wasn't accepted\n");
|
|
||||||
free (m);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free (m);
|
|
||||||
|
|
||||||
debugmsg ("Logged into service\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
enable_mwi_event (int fd)
|
|
||||||
{
|
|
||||||
char *h;
|
|
||||||
char *last;
|
|
||||||
char *m;
|
|
||||||
char *p;
|
|
||||||
size_t ml;
|
|
||||||
|
|
||||||
m = "event plain MESSAGE_WAITING\n\n";
|
|
||||||
ml = strlen (m);
|
|
||||||
|
|
||||||
if (write (fd, m, ml) != ml) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("write failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
m = retrieve_message (fd);
|
|
||||||
if (! m )
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
p = strtok_r (m, "\n", &last);
|
|
||||||
|
|
||||||
h = "Content-Type: command/reply";
|
|
||||||
|
|
||||||
if (! p || strcmp (p, h) != 0) {
|
|
||||||
errmsg ("Content-Type wasn't command/reply\n");
|
|
||||||
free (m);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = strtok_r (NULL, "\n", &last);
|
|
||||||
|
|
||||||
h = "Reply-Text: +OK event listener enabled plain";
|
|
||||||
|
|
||||||
if (! p || strcmp (p, h) != 0) {
|
|
||||||
errmsg ("event wasn't enabled\n");
|
|
||||||
free (m);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free (m);
|
|
||||||
|
|
||||||
debugmsg ("Enabled message waiting event\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
process_mwi_event (char *m, const char *device)
|
|
||||||
{
|
|
||||||
char cbuf[64];
|
|
||||||
char rbuf[64];
|
|
||||||
char *h;
|
|
||||||
char *last;
|
|
||||||
char *ma;
|
|
||||||
char *mw;
|
|
||||||
char *p;
|
|
||||||
int fd;
|
|
||||||
int mwi_off;
|
|
||||||
int mwi_on;
|
|
||||||
int r;
|
|
||||||
int w;
|
|
||||||
size_t l;
|
|
||||||
ssize_t ml;
|
|
||||||
ssize_t nbytes_read;
|
|
||||||
struct termios tio;
|
|
||||||
|
|
||||||
debugmsg ("Processing MWI event\n");
|
|
||||||
|
|
||||||
ma = NULL;
|
|
||||||
mw = NULL;
|
|
||||||
|
|
||||||
p = m;
|
|
||||||
|
|
||||||
while ( (p = strtok_r (p, "\n", &last)) ) {
|
|
||||||
h = "MWI-Messages-Waiting: ";
|
|
||||||
l = strlen (h);
|
|
||||||
|
|
||||||
if (strncmp (p, h, l) == 0)
|
|
||||||
mw = p + l;
|
|
||||||
|
|
||||||
h = "MWI-Message-Account: ";
|
|
||||||
l = strlen (h);
|
|
||||||
|
|
||||||
if (strncmp (p, h, l) == 0)
|
|
||||||
ma = p + l;
|
|
||||||
|
|
||||||
p = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! (ma && mw) ) {
|
|
||||||
errmsg ("message account or message waiting missing\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = strchr (ma, '\n');
|
|
||||||
if (p)
|
|
||||||
*p = '\n';
|
|
||||||
|
|
||||||
p = strchr (mw, '\n');
|
|
||||||
if (p)
|
|
||||||
*p = '\n';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The account is considered to be a System 25 extension if
|
|
||||||
* it's of the form:
|
|
||||||
*
|
|
||||||
* numeric_string@host
|
|
||||||
*/
|
|
||||||
|
|
||||||
p = strchr (ma, '%');
|
|
||||||
if (! p)
|
|
||||||
p = strchr (ma, '@');
|
|
||||||
|
|
||||||
if (! p || (strncmp (p, "%40", 3) != 0 && strncmp (p, "@", 1) != 0)) {
|
|
||||||
debugmsg (" %s is not a System 25 extension\n", ma);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*p = '\0';
|
|
||||||
|
|
||||||
for (p = ma; *p; p++)
|
|
||||||
if (! isdigit (*p)) {
|
|
||||||
debugmsg (" %s is not a System 25 extension\n", ma);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mwi_off = strcasecmp (mw, "no") == 0;
|
|
||||||
mwi_on = strcasecmp (mw, "yes") == 0;
|
|
||||||
|
|
||||||
if (mwi_off == mwi_on) {
|
|
||||||
errmsg ("Unsupported Messages-Waiting\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (r = 0; r < 3; r++) {
|
|
||||||
if ((fd = open (device, O_RDWR)) < 0) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("open failed for device node <%s>.\n", device);
|
|
||||||
errmsg (errstr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cfmakeraw (&tio);
|
|
||||||
|
|
||||||
tio.c_cflag = CS8 | CREAD | HUPCL | CCTS_OFLOW | CRTS_IFLOW;
|
|
||||||
|
|
||||||
tio.c_cc[VMIN] = 0;
|
|
||||||
tio.c_cc[VTIME] = 50;
|
|
||||||
|
|
||||||
cfsetispeed (&tio, B9600);
|
|
||||||
cfsetospeed (&tio, B9600);
|
|
||||||
|
|
||||||
if (tcsetattr (fd, TCSAFLUSH, &tio) < 0) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("tcsetattr failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
m = "AT";
|
|
||||||
ml = strlen (m);
|
|
||||||
|
|
||||||
if (write (fd, m, ml) != ml
|
|
||||||
|| write (fd, "\r\n", 2) != 2) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("write failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (w = 0; w < 2; w++) {
|
|
||||||
nbytes_read = read_line (fd, rbuf, sizeof (rbuf));
|
|
||||||
if (nbytes_read > 0 && (rbuf[0] == '\0' || strcmp (rbuf, m) == 0))
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read < 0) {
|
|
||||||
errmsg ("read_line failed\n");
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read == 0
|
|
||||||
|| strcmp (rbuf, "OK") != 0) {
|
|
||||||
errmsg ("modem failed to wake up\n");
|
|
||||||
close (fd);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
m = cbuf;
|
|
||||||
ml = snprintf (cbuf, sizeof (cbuf),
|
|
||||||
"ATDT%s%s", (mwi_on ? "#90" : "#91"), ma);
|
|
||||||
if (ml <= 0 || ml >= sizeof (cbuf)) {
|
|
||||||
errmsg ("snprintf failed.\n");
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write (fd, m, ml) != ml
|
|
||||||
|| write (fd, "\r\n", 2) != 2) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("write failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep (5);
|
|
||||||
|
|
||||||
if (write (fd, "\r\n", 2) != 2) {
|
|
||||||
char *errstr = strerror (errno);
|
|
||||||
|
|
||||||
errmsg ("write failed\n");
|
|
||||||
errmsg (errstr);
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (w = 0; w < 2; w++) {
|
|
||||||
nbytes_read = read_line (fd, rbuf, sizeof (rbuf));
|
|
||||||
if (nbytes_read > 0 && (rbuf[0] == '\0' || strcmp (rbuf, m) == 0))
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read < 0) {
|
|
||||||
errmsg ("read_line failed\n");
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read > 0 && strcmp (rbuf, "NO DIALTONE") == 0) {
|
|
||||||
errmsg ("modem failed to detect dialtone\n");
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbytes_read == 0
|
|
||||||
|| strcmp (rbuf, "NO CARRIER") != 0) {
|
|
||||||
errmsg ("modem failed to update MWI\n");
|
|
||||||
close (fd);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
close (fd);
|
|
||||||
|
|
||||||
debugmsg (" message waiting indicator updated for %s\n", ma);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
errmsg (" failed to update message waiting indicator for %s\n", ma);
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
const char *device = "/dev/cuad0";
|
|
||||||
const char *machine = "localhost";
|
|
||||||
const char *port = "8021";
|
|
||||||
const char *passwd = "ClueCon";
|
|
||||||
char *m;
|
|
||||||
int c;
|
|
||||||
int debug;
|
|
||||||
int fd;
|
|
||||||
struct stat statbuf;
|
|
||||||
|
|
||||||
debug = 0;
|
|
||||||
|
|
||||||
while ((c = getopt (argc, argv, "dm:p:w:")) != -1)
|
|
||||||
switch (c) {
|
|
||||||
case 'd':
|
|
||||||
debug = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'm':
|
|
||||||
machine = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'p':
|
|
||||||
port = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'w':
|
|
||||||
passwd = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
device = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fprintf (stderr,
|
|
||||||
"Usage: %s [-d] [-m machine] [-p port] [-w passwd] [-l device]\n",
|
|
||||||
MyName);
|
|
||||||
exit(1);
|
|
||||||
/* NOTREACHED */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stat (device, &statbuf) < 0 || ! S_ISCHR (statbuf.st_mode)) {
|
|
||||||
fprintf (stderr, "%s: stat failed for path <%s>\n", MyName, device);
|
|
||||||
fprintf (stderr, "%s: or the path isn't a character special file.\n",
|
|
||||||
MyName);
|
|
||||||
perror (MyName);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
install_signal_handlers ();
|
|
||||||
|
|
||||||
if (! debug)
|
|
||||||
daemonize ();
|
|
||||||
|
|
||||||
while (! shutdown_server) {
|
|
||||||
sleep (5);
|
|
||||||
|
|
||||||
fd = connect_to_service (machine, port);
|
|
||||||
if (fd < 0) {
|
|
||||||
error_msg_throttle = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (send_password (fd, passwd) < 0
|
|
||||||
|| enable_mwi_event (fd) < 0) {
|
|
||||||
error_msg_throttle = 1;
|
|
||||||
close (fd);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
error_msg_throttle = 0;
|
|
||||||
|
|
||||||
while (m = retrieve_message (fd)) {
|
|
||||||
process_mwi_event (m, device);
|
|
||||||
free (m);
|
|
||||||
}
|
|
||||||
|
|
||||||
close (fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (0);
|
|
||||||
}
|
|
Loading…
Reference in New Issue