more clean up

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@13997 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Brian West 2009-06-27 00:33:29 +00:00
parent d53d493ba7
commit f22fb723db
5 changed files with 0 additions and 1532 deletions

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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 ();

View File

@ -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();

View File

@ -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);
}