From 3410c58a2e0cb5063b2013678f162263cd8d7d8b Mon Sep 17 00:00:00 2001 From: Brian West Date: Mon, 15 Dec 2008 18:16:34 +0000 Subject: [PATCH] FSSCRIPTS-8 Thanks John git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10776 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- scripts/s25vmail/README | 42 ++ scripts/s25vmail/s25vmail.js | 193 +++++++ scripts/s25vmail/s25vmail_mwi.c | 953 ++++++++++++++++++++++++++++++++ 3 files changed, 1188 insertions(+) create mode 100644 scripts/s25vmail/README create mode 100644 scripts/s25vmail/s25vmail.js create mode 100644 scripts/s25vmail/s25vmail_mwi.c diff --git a/scripts/s25vmail/README b/scripts/s25vmail/README new file mode 100644 index 0000000000..05b511e44e --- /dev/null +++ b/scripts/s25vmail/README @@ -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 + + + + + + + +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. diff --git a/scripts/s25vmail/s25vmail.js b/scripts/s25vmail/s25vmail.js new file mode 100644 index 0000000000..0ef17606d9 --- /dev/null +++ b/scripts/s25vmail/s25vmail.js @@ -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(); diff --git a/scripts/s25vmail/s25vmail_mwi.c b/scripts/s25vmail/s25vmail_mwi.c new file mode 100644 index 0000000000..34b52ea7f3 --- /dev/null +++ b/scripts/s25vmail/s25vmail_mwi.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#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); + }