1 /*
2  *	binkleyforce -- unix FTN mailer project
3  *
4  *	Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU General Public License as published by
8  *	the Free Software Foundation; either version 2 of the License, or
9  *	(at your option) any later version.
10  *
11  *	$Id: io_modem.c,v 1.1.1.1 2004/09/09 09:52:38 kstepanenkov Exp $
12  */
13 
14 #include "includes.h"
15 #include "confread.h"
16 #include "logger.h"
17 #include "util.h"
18 #include "io.h"
19 
20 const char *modem_errlist[] =
21 {
22 	"No error",
23 	"Modem return ERROR",
24 	"Can't send string to modem",
25 	"Modem not response",
26 	NULL
27 };
28 
29 /* ------------------------------------------------------------------------- */
30 /*  Get connect speed from connect string returned by modem                  */
31 /* ------------------------------------------------------------------------- */
modem_getconnspeed(const char * connstr)32 long modem_getconnspeed(const char *connstr)
33 {
34 	const char *p;
35 
36 	for( p = connstr; *p; p++ )
37 	{
38 		if( isdigit(*p) )
39 			return atol(p);
40 	}
41 
42 	return 0L;
43 }
44 
modem_isgood_phone(const char * str)45 bool modem_isgood_phone(const char *str)
46 {
47 	if( !str || !str[0] )
48 		return FALSE;
49 
50 	if( str[0] == '-' && str[1] == '\0' )
51 		return FALSE;
52 
53 	if( string_casestr(str, "unpublished") )
54 		return FALSE;
55 
56 	if( string_casestr(str, "unknown") )
57 		return FALSE;
58 
59 	if( string_casestr(str, "none") )
60 		return FALSE;
61 
62 	return TRUE;
63 }
64 
65 /* ------------------------------------------------------------------------- */
66 /* Translate phone number using given rules, returned value must be free'ed! */
67 /* ------------------------------------------------------------------------- */
modem_transphone(char * buffer,const char * phone,size_t buflen)68 char *modem_transphone(char *buffer, const char *phone, size_t buflen)
69 {
70 	const char *p;
71 	s_cval_entry *ptrl;
72 
73 	ASSERT(phone != NULL);
74 
75 	DEB((D_MODEM, "translate_phone: want translate \"%s\"", phone));
76 
77 	for( ptrl = conf_first(cf_phone_translate); ptrl;
78 	     ptrl = conf_next(ptrl) )
79 	{
80 		if( !ptrl->d.translate.find )
81 			continue;
82 
83 		if( (p = strstr(phone, ptrl->d.translate.find)) )
84 		{
85 			DEB((D_MODEM, "translate_phone: replace \"%s\" with \"%s\"",
86 				ptrl->d.translate.find, ptrl->d.translate.repl));
87 
88 			strnxcpy(buffer, phone, buflen);
89 
90 			if( p - phone < buflen - 1 )
91 			{
92 				buffer[p - phone] = '\0';
93 
94 				if( ptrl->d.translate.repl )
95 					strnxcat(buffer, ptrl->d.translate.repl, buflen);
96 
97 				strnxcat(buffer, p + strlen(ptrl->d.translate.find), buflen);
98 			}
99 
100 			DEB((D_MODEM, "translate_phone: result is \"%s\"", buffer));
101 
102 			return buffer;
103 		}
104 	}
105 
106 	return strnxcpy(buffer, phone, buflen);
107 }
108 
109 /* ------------------------------------------------------------------------- */
110 /*  Send string to modem, using some control character sequences, like       */
111 /*  '\r' - CR, '\P' - phone number, etc..                                    */
112 /* ------------------------------------------------------------------------- */
modem_putstr(const char * str)113 int modem_putstr(const char *str)
114 {
115 	const char *ptr;
116 	int rc;
117 	bool flushed = FALSE;
118 
119 	DEB((D_MODEM, "modem_putstr: want to send \"%s\"", str));
120 
121 	rc  = TTY_SUCCESS;
122 	ptr = str;
123 
124 	while( *ptr )
125 	{
126 		/* Flush buffer before "special" operations */
127 		if( !flushed && strchr("~`^v", *ptr) )
128 		{
129 			flushed = TRUE;
130 			if( (rc = tty_flushout()) < 0 )
131 				return rc;
132 		}
133 
134 		switch( *ptr ) {
135 		case '~':
136 			sleep(1);
137 			break;
138 		case '`':
139 			usleep(250000L);
140 			break;
141 		case '^':
142 			rc = tio_set_dtr(0, 1);
143 			break;
144 		case 'v':
145 			rc = tio_set_dtr(0, 0);
146 			break;
147 		case '|':
148 		case '\r':
149 			rc = tty_putc('\r', 2);
150 			if( !rc && !(rc = tty_flushout()) )
151 			{
152 				/*
153 				 * Make sure that we will never send
154 				 * anything to the modem immediately
155 				 * after CR
156 				 */
157 				usleep(250000);
158 				flushed = TRUE;
159 			}
160 			break;
161 		case '\\':
162 			++ptr;
163 			switch(*ptr) {
164 			case '\0':
165 				rc = tty_putc('\\', 2);
166 				break;
167 			case 'r':
168 				rc = tty_putc('\r', 2);
169 				break;
170 			case 'n':
171 				rc = tty_putc('\n', 2);
172 				break;
173 			default:
174 				rc = tty_putc(*ptr, 2);
175 			}
176 			flushed = FALSE;
177 			break;
178 		default:
179 			rc = tty_putc(*ptr, 2);
180 			flushed = FALSE;
181 		}
182 
183 		if( rc < 0 )
184 			return rc;
185 
186 		++ptr;
187 	}
188 
189 	if( !flushed && (rc = tty_flushout()) < 0 )
190 		return rc;
191 
192 	return 0;
193 }
194 
modem_clearin(int timeout)195 void modem_clearin(int timeout)
196 {
197 	time_t timer;
198 
199 	timer_set(&timer, timeout);
200 
201 	while( CHARWAIT(1) )
202 	{
203 		CLEARIN();
204 
205 		if( timer_expired(timer) )
206 			return;
207 
208 		usleep(100000); /* 0.1 seconds delay */
209 	}
210 }
211 
212 /* ------------------------------------------------------------------------- */
213 /* Reads in at most one less than $bufsize characters from modem and         */
214 /* stores them into the buffer pointed to by $buf, check for timeout.        */
215 /* $timer must be set with tty_settimer() call!                              */
216 /* ------------------------------------------------------------------------- */
modem_getline(char * buf,int bufsize,time_t timer)217 int modem_getline(char *buf, int bufsize, time_t timer)
218 {
219 	int rc = 0, pos = 0;
220 
221 	ASSERT(buf != NULL || bufsize == 0);
222 
223 	while(1)
224 	{
225 		if( timer_expired(timer) )
226 			return TTY_TIMEOUT;
227 
228 		if( (rc = tty_getc(1)) < 0 && rc != TTY_TIMEOUT )
229 			return rc;
230 		else if( rc == '\r' || rc == '\n' )
231 			return pos;
232 		else if( rc > 0 )
233 		{
234 			if( pos < bufsize-1 )
235 			{
236 				buf[pos++] = rc;
237 				buf[pos  ] = '\0';
238 			} else
239 				return pos;
240 		}
241 	}
242 }
243 
244 /* ------------------------------------------------------------------------- */
245 /* Dial using phone number $phone, if connection will be established -       */
246 /* put connect string to *connstr (must be freed)                            */
247 /* ------------------------------------------------------------------------- */
modem_dial(const char * dialstr,int timeout,char ** connstr)248 int modem_dial(const char *dialstr, int timeout, char **connstr)
249 {
250 	s_cval_entry *ptrl;
251 	char buf[MODEM_MAX_RESP+1];
252 	time_t timer;
253 	int len = 0;
254 
255 	ASSERT(dialstr != NULL && connstr != NULL);
256 
257 	*connstr = NULL;
258 
259 	if( modem_putstr(dialstr) != TTY_SUCCESS )
260 	{
261 		bf_log("error sending dial string \"%s\"", dialstr);
262 		return -1;
263 	}
264 
265 	timer_set(&timer, timeout);
266 
267 	while(1)
268 	{
269 		if( timer_expired(timer) )
270 		{
271 			bf_log("dialing timed out");
272 			return -1;
273 		}
274 
275 		if( (len = modem_getline(buf, sizeof(buf), timer)) < 0 )
276 		{
277 			if( len == TTY_TIMEOUT )
278 				bf_log("dialing timed out");
279 
280 			return -1;
281 		}
282 		else if( len > 0 )
283 		{
284 			DEB((D_MODEM, "modem_dial: got \"%s\"", buf));
285 
286 			for( ptrl = conf_first(cf_modem_dial_response); ptrl;
287 			     ptrl = conf_next(ptrl) )
288 			{
289 				if( ptrl->d.dialresp.mstr && strstr(buf, ptrl->d.dialresp.mstr) )
290 				{
291 					if( ptrl->d.dialresp.retv == RESPTYPE_CONNECT )
292 					{
293 						*connstr = xstrcpy(buf);
294 						return 0;
295 					}
296 					return ptrl->d.dialresp.retv;
297 				}
298 			}
299 			if( *buf )
300 				bf_log("modem: \"%s\"", string_printable(buf));
301 		}
302 	}
303 }
304 
modem_command(const char * command,int timeout,bool logit)305 int modem_command(const char *command, int timeout, bool logit)
306 {
307 	time_t timer;
308 	size_t count = 0;
309 	int len = 0;
310 	char buffer[MODEM_MAX_RESP+1];
311 	char cmdstr[MODEM_MAX_COMMAND+1];
312 	char *n;
313 	char *p;
314 
315 	ASSERT(command != NULL);
316 
317 	strnxcpy(cmdstr, command, sizeof(cmdstr));
318 
319 	DEB((D_MODEM, "modem_command: command string \"%s\"", cmdstr));
320 
321 	CLEARIN();
322 
323 	for( p = string_token(cmdstr, &n, NULL, 1); p;
324 	     p = string_token(NULL, &n, NULL, 1) )
325 	{
326 		DEB((D_MODEM, "modem_command: send \"%s\"",
327 				string_printable(p)));
328 
329 		if( modem_putstr(p) < 0 )
330 			return MODEM_CANTSEND;
331 
332 		count = 0;
333 		timer_set(&timer, timeout);
334 
335 		while(1)
336 		{
337 			if( timer_expired(timer) )
338 				return MODEM_NORESP;
339 
340 			if( (len = modem_getline(buffer, sizeof(buffer), timer)) < 0 )
341 			{
342 				return MODEM_NORESP;
343 			}
344 			else if( len > 0 )
345 			{
346 				count += len;
347 
348 				if( count > MODEM_MAX_RESP_SIZE )
349 				{
350 					bf_log("modem response exceeds limit %ld bytes",
351 							(long)count);
352 					return MODEM_NORESP;
353 				}
354 
355 				DEB((D_MODEM, "modem_command: got \"%s\"",
356 					string_printable(buffer)));
357 
358 				if( !strcmp(buffer, "ERROR") )
359 					return MODEM_ERROR;
360 				else if( !strcmp(buffer, "OK") )
361 					break;
362 				else if( logit )
363 					bf_log("modem: \"%s\"", string_printable(buffer));
364 			}
365 		}
366 	}
367 
368 	return MODEM_OK;
369 }
370 
modem_hangup(const char * command,int timeout)371 int modem_hangup(const char *command, int timeout)
372 {
373 #ifdef MODEM_HANGUP_WATCH_CARRIER
374 	time_t timer;
375 
376 	ASSERT(command != NULL);
377 
378 	if( tio_get_dcd(0) == 1 )
379 	{
380 		DEB((D_MODEM, "modem_hangup: send \"%s\"", command));
381 
382 		if( modem_putstr(command) < 0 )
383 			return MODEM_CANTSEND;
384 
385 		timer_set(&timer, timeout);
386 
387 		while( tio_get_dcd(0) == 1 )
388 		{
389 			if( timer_expired(timer) )
390 				return MODEM_NORESP;
391 
392 			sleep(1);
393 		}
394 	}
395 
396 	sleep(2); CLEARIN(); CLEAROUT();
397 
398 	return MODEM_OK;
399 #else /* MODEM_HANGUP_WATCH_CARRIER */
400 	return modem_command(command, timeout, FALSE);
401 #endif
402 }
403 
modem_candialout(const char * modemdev)404 bool modem_candialout(const char *modemdev)
405 {
406 	char tmp[BF_MAXPATH+1];
407 	const char *p_nodial = conf_string(cf_nodial_flag);
408 	char *p = NULL;
409 
410 	if( p_nodial && *p_nodial )
411 	{
412 		if( access(p_nodial, F_OK) == 0 )
413 			return FALSE;
414 
415 		strnxcpy(tmp, p_nodial, sizeof(tmp));
416 		strnxcat(tmp, ".", sizeof(tmp));
417 		strnxcat(tmp, (p = port_get_name(modemdev)), sizeof(tmp));
418 
419 		if( p )
420 			free(p);
421 
422 		if( access(tmp, F_OK) == 0 )
423 			return FALSE;
424 	}
425 
426 	return TRUE;
427 }
428 
429 /* ------------------------------------------------------------------------- */
430 /* Return pointer to the first not locked now tty                            */
431 /* ------------------------------------------------------------------------- */
modem_getfree_port(const char * lockdir)432 s_modemport *modem_getfree_port(const char *lockdir)
433 {
434 	s_cval_entry *ptrl;
435 
436 	/* Find first not locked modem device */
437 	for( ptrl = conf_first(cf_modem_port); ptrl; ptrl = conf_next(ptrl) )
438 	{
439 		if( port_checklock(lockdir, &ptrl->d.modemport) == LOCKCHECK_NOLOCK
440 		 && modem_candialout(ptrl->d.modemport.name) == TRUE )
441 			return &ptrl->d.modemport;
442 	}
443 
444 	return NULL;
445 }
446 
447 /* ------------------------------------------------------------------------- */
448 /* Return pointer to the first tty whose name contain substring `ttyname'    */
449 /* ------------------------------------------------------------------------- */
modem_getmatch_port(const char * substr)450 s_modemport *modem_getmatch_port(const char *substr)
451 {
452 	s_cval_entry *ptrl;
453 
454 	/* Find first matching modem device */
455 	for( ptrl = conf_first(cf_modem_port); ptrl; ptrl = conf_next(ptrl) )
456 	{
457 		if( strstr(ptrl->d.modemport.name, substr) )
458 			return &ptrl->d.modemport;
459 	}
460 
461 	return NULL;
462 }
463 
464