forked from Mirrors/freeswitch
FSSCRIPTS-8 Thanks John
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10776 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
1d7357e6d4
commit
3410c58a2e
42
scripts/s25vmail/README
Normal file
42
scripts/s25vmail/README
Normal file
@ -0,0 +1,42 @@
|
||||
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.
|
193
scripts/s25vmail/s25vmail.js
Normal file
193
scripts/s25vmail/s25vmail.js
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Copyright (c) 2008 Feith Systems and Software, Inc.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The message waiting indicator is handled by a separate program.
|
||||
*/
|
||||
|
||||
|
||||
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 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) {
|
||||
dtmf_digits = session.getDigits (5, '#', digitTimeOut,
|
||||
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 != "#") {
|
||||
console_log ("err", "Invalid VMAIL start code from PBX\n");
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
||||
// Unknown
|
||||
default:
|
||||
console_log ("err", "Invalid VMAIL mode code from PBX\n");
|
||||
break;
|
||||
}
|
||||
|
||||
exit();
|
953
scripts/s25vmail/s25vmail_mwi.c
Normal file
953
scripts/s25vmail/s25vmail_mwi.c
Normal file
@ -0,0 +1,953 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Copyright (c) 2008 Feith Systems and Software, Inc.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
#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
Block a user