1 #ident "$Id: ring.c,v 4.21 2008/03/07 14:46:43 gert Exp $ Copyright (c) Gert Doering"
2 
3 /* ring.c
4  *
5  * This module handles incoming RINGs, distinctive RINGs (RING 1, RING 2,
6  * etc.), and out-of-band messages (<DLE>v, "CONNECT", ...).
7  *
8  * Also, on ISDN "modems", multiple subscriber numbers (MSN) are mapped
9  * to distinctive RING types. At least, if the ISDN device returns this
10  * data.  It's known to work for ZyXEL and ELSA products.
11  *
12  * Works closely with "cnd.c" to grab CallerID for analog modems.
13  */
14 
15 #include <stdio.h>
16 #include "syslibs.h"
17 #include <unistd.h>
18 #include <signal.h>
19 #include <string.h>
20 #include <ctype.h>
21 
22 #ifndef EINTR
23 #include <errno.h>
24 #endif
25 
26 #include "mgetty.h"
27 #include "policy.h"
28 #include "tio.h"
29 #include "fax_lib.h"
30 
31 /* strdup variant that returns "<null>" in case of out-of-memory */
safedup(char * in)32 static char * safedup( char * in )
33 {
34     char * p = strdup( in );
35     return ( p == NULL ) ? "<null>" : p;
36 }
37 
38 /* find number given in msn_list, return index */
39 static int find_msn _P2((string, msn_list),
40 			 char * string, char ** msn_list )
41 {
42 int i, len, len2;
43 char * p;
44 
45     lprintf( L_NOISE, "MSN: '%s'", string );
46     CalledNr = safedup(string);			/* save away */
47 
48     if ( msn_list == NULL ) return 0;		/* nothing to match against */
49 
50     len=strlen(string);
51 
52     /* hack off sub-addresses ("<msn>/<subaddr>")
53      * (future versions could do comparisons with and without subaddr...) */
54     p = strchr( string, '/' );
55     if ( p != NULL ) { len = (p - string); }
56 
57     for( i=0; msn_list[i] != NULL; i++ )
58     {
59 	lprintf( L_JUNK, "match: '%s'", msn_list[i] );
60 	len2=strlen( msn_list[i] );
61 	if ( len2 <= len &&
62 	     strncmp( msn_list[i], &string[len-len2], len2 ) == 0 )
63 		{ return i+1; }
64     }
65     return 0;				/* not found -> unspecified */
66 }
67 
68 /* ELSA CallerID data comes in as "RING;<from>[;<to>]"
69  *
70  * this function is also used for others that report the number in
71  * the format [non-digit(s)]<from>[non-digit(s)]<to>
72  */
73 static int ring_handle_ELSA _P2((string, msn_list),
74 				 char * string, char ** msn_list )
75 {
76 char * p;
77 char ch;
78 
79     lprintf( L_MESG, "ELSA: '%s'", string );
80 
81     /* remember first character, for differenciation between
82      * ELSA-style ("RING;from") and ISDN4Linux ("RING/to") [grrr]
83      */
84     ch = *string;
85 
86     /* skip over leading "garbage" */
87     while( *string != '\0' && !isdigit(*string) ) string++;
88 
89     /* grab caller number (all up to the first non-digit) */
90     p=string;
91     while( isdigit(*p) ) p++;
92 
93     if ( *p == '\0' )		/* only one number listed */
94     {
95 	if ( ch == '/' )	/* isdn4linux -> number is MSN */
96 	    return find_msn( string, msn_list );
97 
98 				/* not -> it's caller ID, and no MSN */
99 	CallerId = safedup( string );
100 	return 0;
101     }
102     else			/* MSN listed -> terminate string, get MSN */
103     {
104 	*p = '\0';
105 	CallerId = safedup( string );
106 
107 	p++;
108 	while( *p != '\0' && !isdigit(*p) ) p++;
109 
110 	return find_msn( p, msn_list );
111     }
112 }
113 
114 /* Zoom MX/S CallerID data comes in as "RING: <type> DN<id> <from> <?>"
115  * contributed by Thomas Schuett <info@thomas-schuett.de> */
116 static int ring_handle_ZoomMX _P1((string), char * string)
117 {
118 char * p;
119     lprintf( L_MESG, "Zoom MX/S: '%s'", string );
120 
121     p=&string[8];
122     while( isdigit(*p) ) p++;
123 
124     *p = '\0';
125     CallerId = safedup( &string[8] );
126     return ( string[6]-'0');
127 }
128 
129 /* ZyXEL CallerID data comes in as "FM:<from> [TO:<to>]" or "TO:<to>"
130  *
131  * unless Subadressing is used, in which case this looks like
132  *   [FM:[CallingPN] [/Subaddress/]][TO:[CalledPN] [/Subaddress/]]
133  * for now, subaddresses are completely ignored (here and in find_msn)
134  */
135 static int ring_handle_ZyXEL _P2((string, msn_list),
136 				 char * string, char ** msn_list )
137 {
138 char * p, ch;
139     lprintf( L_MESG, "ZyXEL: '%s'", string );
140 
141     if ( strncmp( string, "FM:", 3 ) == 0 )		/* caller ID */
142     {
143 	string+=3;
144 	/* Blatzheim: FM:<space><number> */
145 	while( isspace(*string) ) { string++; }
146 
147 	p=string;
148 	while( isdigit(*p) ) p++;
149 	ch = *p; *p = '\0';
150 	CallerId = safedup(string);
151 	*p = ch;
152 	while( isspace(*p) ) p++;
153 
154 	/* skip potential sub-addresses ("/<something>/") */
155 	if ( *p == '/' )
156 	{
157 	    p++;
158 	    while ( *p != '\0' && *p != '/' ) { p++; }
159 	    if ( *p != '\0' ) p++;
160 	}
161 	string = p;
162     }
163     if ( strncmp( string, "TO:", 3 ) == 0 )		/* target msn */
164     {
165 	string += 3;
166 	/* Blatzheim: TO:<space><number> */
167 	while( isspace(*string) ) { string++; }
168 
169 	return find_msn( string, msn_list );
170     }
171     return 0;			/* unspecified */
172 }
173 
174 /* handle V.253 DRON/DROF result codes
175  * (signalling "Ring ON" / "Ring OFf" time)
176  *
177  * basically we build a binary word from the RINGs, and use that as
178  * distinctive RING number.  "Long" = 1, "Short" = 0
179  *
180  * the very first code (always DROF) is always "long".
181  *
182  * example cadence (standard verizon "one long RING" call):
183  *  DROF=0
184  *  DRON=11
185  *  RING
186  *  DROF=40
187  *  DRON=20
188  *  RING
189  */
190 static int drox_bitstring;
191 static int drox_count;
ring_handle_DROx(char * p)192 static void ring_handle_DROx( char * p )
193 {
194     int len, bit;
195 
196     /* skip whitespace and '=' (covers "DRON=nnn" and "DRON = nnn")
197      */
198     while( isspace(*p) || *p == '=' ) { p++; }
199 
200     len = atoi( p );
201 
202     bit = ( drox_count == 0 || len > 9 ) ? 1 : 0;
203 
204     lputs( L_NOISE, bit? "<long>": "<short>" );
205 
206     drox_bitstring = (drox_bitstring << 1 ) | bit;
207     drox_count++;
208 }
209 
210 
211 static boolean chat_has_timeout;
chat_timeout(SIG_HDLR_ARGS)212 static RETSIGTYPE chat_timeout(SIG_HDLR_ARGS)
213 {
214     chat_has_timeout = TRUE;
215 }
216 
217 extern boolean virtual_ring;
218 
219 int wait_for_ring _P6((fd, msn_list, timeout,
220 		       actions, action, dist_ring_number),
221 		int fd, char ** msn_list, int timeout,
222 	        chat_action_t actions[], action_t * action,
223 		int * dist_ring_number )
224 {
225 #define BUFSIZE 500
226 char	buf[BUFSIZE], ch, *p;
227 int	i, w, r;
228 int	rc = SUCCESS;
229 boolean	got_dle;		/* for <DLE><char> events (voice mode) */
230 
231     lprintf( L_MESG, "wfr: waiting for ``RING''" );
232     lprintf( L_NOISE, "got: ");
233 
234     w=0;
235     got_dle = FALSE;
236 
237     signal( SIGALRM, chat_timeout );
238     alarm( timeout );
239     chat_has_timeout = FALSE;
240 
241     while(TRUE)
242     {
243 	if ( virtual_ring )
244 	{
245 	    lputs( L_NOISE, "``found''" );
246 	    break;
247 	}
248 
249 	r = mdm_read_byte( fd, &ch );
250 
251 	if ( r <= 0 )				/* unsuccessful */
252 	{
253 	    if ( chat_has_timeout )		/* timeout */
254 		lprintf( L_WARN, "wfr: timeout waiting for RING" );
255 	    else
256 		lprintf( L_ERROR, "wfr: error in read()" );
257 
258 	    if ( action != NULL ) *action = A_TIMOUT;
259 	    rc = FAIL; break;
260 	}
261 
262 	lputc( L_NOISE, ch );
263 
264 	/* In voice mode, modems send <DLE><x> sequences to signal
265 	 * certain events, among them (IS-101) "RING".
266 	 */
267 	if ( got_dle )		/* last char was <DLE> */
268 	{
269 	    switch( ch )
270 	    {
271 		case 'h':
272 		case 'p':	/* handset on hook */
273 		case 'H':
274 		case 'P':	/* handset off hook */
275 		case 'r':	/* ringback detected */
276 		    *dist_ring_number = - (int)ch;
277 		    goto have_ring; break;
278 		case 'R':	/* RING detected */
279 		    *dist_ring_number = 0;
280 		    goto have_ring; break;
281 		default:
282 		    got_dle = FALSE;
283 	    }
284 	}
285 	else
286 	    if ( ch == DLE ) got_dle = TRUE;
287 
288 	/* line termination character? no -> add to buffer and go on */
289 	if ( ch != '\r' && ch != '\n' )
290 	{
291 	    /* skip whitespace at start of buffer */
292 	    if ( w == 0 && isspace(ch) ) continue;
293 
294 	    /* add character to buffer */
295 	    if( w < BUFSIZ-2 )
296 		buf[w++] = ch;
297 
298 	    /* check for "actions" */
299 	    if ( actions != NULL )
300 	      for( i=0; actions[i].expect != NULL; i++ )
301 	    {
302 		int len = strlen( actions[i].expect );
303 		if ( w == len &&
304 		     memcmp( buf, actions[i].expect, len ) == 0 )
305 		{
306 		    lprintf( L_MESG, "wfr: found action string: ``%s''",
307 				     actions[i].expect );
308 		    *action = actions[i].action;
309 		    rc = FAIL; break;
310 		}
311 	    }
312 	    if ( rc == FAIL ) break;		/* break out of while() */
313 
314 	    /* go on */
315 	    continue;
316 	}
317 
318 	/* got a full line */
319 	if ( w == 0 ) { continue; }		/* ignore empty lines */
320 	buf[w] = '\0';
321 	cndfind( buf );				/* grab caller ID */
322 
323 	/* ZyXEL CallerID/MSN display? */
324 	if ( strncmp( buf, "FM:", 3 ) == 0 ||
325 	     strncmp( buf, "TO:", 3 ) == 0 )
326 	    { *dist_ring_number = ring_handle_ZyXEL( buf, msn_list ); break; }
327 
328 	/* Rockwell (et al) caller ID - handled by cndfind(), but
329 	 * we count it as "RING" to be able to pick up immediately
330 	 * instead of waiting for the next "real" RING
331 	 * (but don't do this for V253 DRON/DROF modems!)
332 	 */
333 	if ( strncmp( buf, "NMBR", 4 ) == 0 && drox_count == 0 ) { break; }
334 
335 	/* V.253 ring cadences */
336 	if ( strncmp( buf, "DRON", 4 ) == 0 ||
337 	     strncmp( buf, "DROF", 4 ) == 0 )
338 		{ ring_handle_DROx( buf+4 ); w=0; continue; }
339 
340 	/* now check the different RING types
341 	 * if not "RING<whatever>", clear buffer and get next line
342 	 */
343 	if ( strncmp( buf, "RING", 4 ) != 0 )
344 	    { w = 0; lprintf( L_NOISE, "got: " ); continue; }
345 
346 	p=&buf[4];
347 	while( isspace(*p) ) p++;
348 
349 	if ( *p == '\0' )			/* "classic RING" */
350 	    { break; }
351 
352 	if ( *p == ';' )			/* ELSA type */
353 	    { *dist_ring_number = ring_handle_ELSA( p, msn_list ); break; }
354 
355 	if ( *p== ':' )				/* Zoom MX type */
356 	    { *dist_ring_number = ring_handle_ZoomMX( p ); break; }
357 
358 	if ( strlen(p) > 1 )			/* USR type B: "RING 1234" */
359 	    { *dist_ring_number = ring_handle_ELSA( p, msn_list ); break; }
360 
361 	if ( isdigit( *p ) )			/* RING 1 */
362 	    { *dist_ring_number = *p-'0'; break; }
363 
364 	if ( isalpha( *p ) )			/* RING A */
365 	    { *dist_ring_number = tolower(*p)-'a' +1; break; }
366     }
367 
368 have_ring:
369     alarm(0);
370 
371     if ( drox_count > 0 )
372     {
373 	lprintf( L_NOISE, "wfr: DRON/DROF cadence: %x", drox_bitstring );
374 	*dist_ring_number = drox_bitstring;
375 	drox_count=0; drox_bitstring=0;
376     }
377 
378     lprintf( L_NOISE, "wfr: rc=%d, drn=%d", rc, *dist_ring_number );
379     return rc;
380 }
381