diff --git a/scripts/s25vmail/README b/scripts/s25vmail/README deleted file mode 100644 index 040bf5ae23..0000000000 --- a/scripts/s25vmail/README +++ /dev/null @@ -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 - - - - - - - -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; - } - } diff --git a/scripts/s25vmail/README.park b/scripts/s25vmail/README.park deleted file mode 100644 index a6f1519803..0000000000 --- a/scripts/s25vmail/README.park +++ /dev/null @@ -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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -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. diff --git a/scripts/s25vmail/s25park.js b/scripts/s25vmail/s25park.js deleted file mode 100644 index a6be33c967..0000000000 --- a/scripts/s25vmail/s25park.js +++ /dev/null @@ -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 (); diff --git a/scripts/s25vmail/s25vmail.js b/scripts/s25vmail/s25vmail.js deleted file mode 100644 index 5c4e8643f6..0000000000 --- a/scripts/s25vmail/s25vmail.js +++ /dev/null @@ -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(); diff --git a/scripts/s25vmail/s25vmail_mwi.c b/scripts/s25vmail/s25vmail_mwi.c deleted file mode 100644 index d9c6798d7c..0000000000 --- a/scripts/s25vmail/s25vmail_mwi.c +++ /dev/null @@ -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 -#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); - }