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