Version: 1.5.3 -> 1.5.4cvs
[mdsms.git] / mdsms.c
1 #define WANT_DECLARATIONS 1
2 #include "config.h"
3 #ifndef lint
4 static char rcsid[] ATTR_UNUSED = "$Id$";
5 #endif
6
7 #include "setup.h"
8
9 #ifdef HAVE_STDIO_H
10 #include <stdio.h>
11 #endif
12 #ifdef HAVE_STDLIB_H
13 #include <stdlib.h>
14 #endif
15 #ifdef HAVE_STRING_H
16 #include <string.h>
17 #endif
18 #ifdef HAVE_SIGNAL_H
19 #include <signal.h>
20 #endif
21 #ifdef HAVE_STDARG_H
22 #include <stdarg.h>
23 #endif
24 #ifdef HAVE_LIMITS_H
25 #include <limits.h>
26 #endif
27 #ifdef HAVE_CTYPE_H
28 #include <ctype.h>
29 #endif
30 #ifdef HAVE_TERMIOS_H
31 #include <termios.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #ifdef HAVE_ASSERT_H
37 #include <assert.h>
38 #endif
39 #ifdef HAVE_SYS_TYPES_H
40 #include <sys/types.h>
41 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
45 #ifdef HAVE_SYS_STAT_H
46 #include <sys/stat.h>
47 #endif
48 #ifdef HAVE_FCNTL_H
49 #include <fcntl.h>
50 #endif
51 #ifdef HAVE_ERRNO_H
52 #include <errno.h>
53 #endif
54 #ifdef HAVE_TIME_H
55 #include <time.h>
56 #endif
57 #ifdef HAVE_SYS_TIME_H
58 #include <sys/time.h>
59 #endif
60 #ifdef HAVE_SYS_POLL_H
61 #include <sys/poll.h>
62 #endif
63 #ifdef HAVE_LOCALE_H
64 #include <locale.h>
65 #endif
66 #ifdef HAVE_STDDEF_H
67 #include <stddef.h>
68 #endif
69
70 #ifdef HAVE_GETOPT_LONG
71 #include <getopt.h>
72 #else
73 #include "getopt.h"
74 #endif
75
76
77 /* Always override possible system defintions as it is safe (used by glib) */
78 #undef MAX
79 #define MAX(a,b) ((a)>(b)?(a):(b))
80 #undef MIN
81 #define MIN(a,b) ((a)<(b)?(a):(b))
82
83 #define NELEM(x) (sizeof((x))/sizeof(*(x)))
84
85 #ifndef DEBUG
86 #define dbg(cmd)
87 #else
88 #define dbg(cmd) cmd
89 #endif
90 /* ANSI C does not allow macro with variable arguments */
91 #define dO stderr
92 #define dB(a) dbg(fprintf a)
93
94 #define d1(n1)                      dB((dO,n1                     ))
95 #define d2(n1,n2)                   dB((dO,n1,n2                  ))
96 #define d3(n1,n2,n3)                dB((dO,n1,n2,n3               ))
97 #define d4(n1,n2,n3,n4)             dB((dO,n1,n2,n3,n4            ))
98 #define d5(n1,n2,n3,n4,n5)          dB((dO,n1,n2,n3,n4,n5         ))
99 #define d6(n1,n2,n3,n4,n5,n6)       dB((dO,n1,n2,n3,n4,n5,n6      ))
100 #define d7(n1,n2,n3,n4,n5,n6,n7)    dB((dO,n1,n2,n3,n4,n5,n6,n7   ))
101 #define d8(n1,n2,n3,n4,n5,n6,n7,n8) dB((dO,n1,n2,n3,n4,n5,n6,n7,n8))
102
103 static const char version[]="Mobile Device SMS tool (" PACKAGE " " VERSION ")\n";
104
105 static int verbose
106 #ifdef DEBUG
107         =0xFFFF
108 #endif
109         ;
110 static char *pname;
111 static int dis_cleanup=0,devfd=-1;
112
113 static char *phone,*device,*logname,*lockfile,*smsmode,*pdusmscmode,*smsc,*maxretry,*readtime,*chartime,*cmdtime,*waittime,*baud,*restore;
114 static int readbody;
115 static long maxretryn=DEF_MAXRETRY,readtimen=-1,chartimen=DEF_CHARTIME,cmdtimen=DEF_CMDTIME,waittimen=DEF_WAITTIME,baudn=DEF_BAUD;
116 #ifdef HAVE_CRTSCTS
117 static int handshake_rtscts;
118 static unsigned handshake_stamp;
119 #else
120 #define handshake_rtscts (0)
121 #endif
122 static enum {
123         FSM_AUTO=0,
124         FSM_PDU,
125         FSM_TEXT
126         } force_smsmode=FSM_AUTO;
127 static enum {
128         FPSM_AUTO=0,
129         FPSM_COUNT_IN,
130         FPSM_COUNT_OUT,
131         FPSM_NONE
132         } force_pdusmscmode=FPSM_AUTO,
133 #define FPSM_MIN (FPSM_COUNT_IN)
134 #define FPSM_MAX (FPSM_NONE)
135                 try_pdusmscmode=FPSM_MIN;
136 static size_t bodylen;
137 /* --send / --send-mobildock / --receive specific */
138 static char *body;
139 /* --logo-send specific */
140 static char *logoname,*gsmnet;
141 /* --ring-send specific */
142 static char *ringname;
143 /* --picture-send specific */
144 static char *picturename;
145
146 static enum modenum {
147   MODE_UNKNOWN=0,
148 /* must differ from regular char-s */
149   MODE_FIRST         =0x3400,
150         MODE_SEND          =MODE_FIRST+0, /* --send / --send-mobildock */
151         MODE_SEND_MOBILDOCK=MODE_FIRST+1, /* --send-mobildock in before readtimen is set */
152         MODE_RECEIVE       =MODE_FIRST+2, /* --receive */
153         MODE_LOGO_SEND     =MODE_FIRST+3, /* --logo-send */
154         MODE_RING_SEND     =MODE_FIRST+4, /* --ring-send */
155         MODE_PICTURE_SEND  =MODE_FIRST+5  /* --picture-send */
156         } mode=MODE_UNKNOWN;
157 #define MODE_ORDER(x) ((x)-MODE_FIRST)
158 #define MODE_NAME(x) (longopts[MODE_ORDER((x))].name)
159 #define MODE_BIT(x) (1<<MODE_ORDER((x)))
160
161 static unsigned mode_stamp;
162
163 /* pdusmsc variable has to be filled in */
164 #define NEED_PDUSMSC() (mode==MODE_SEND)
165
166 static char *devicename; /* path stripped */
167 static char lockreal[512],locked;
168
169 static struct termios restios,tios;
170 static char restios_yes;
171 static FILE *logf;
172
173 static void vlogmsg(char pm,char fatal,const char *fmt,va_list ap) ATTR_PRINTFORMAT(3,0);
174 static void vlogmsg(char pm,char fatal,const char *fmt,va_list ap)
175 {
176 time_t stamp;
177 char *ctm,*s;
178 pid_t mypid=-1;
179 char host[LINE_MAX];
180
181         if (!logf) return;
182         if (mypid==-1) {
183                 mypid=getpid();
184                 if (gethostname(host,sizeof(host))) strcpy(host,_("<ERROR>"));
185                 }
186         time(&stamp);
187         ctm=ctime(&stamp);
188         if ((s=strchr(ctm,'\n'))) *s='\0';
189         fprintf(logf,"%s %s %s[%d]: ",ctm,host,pname,mypid);
190         vfprintf(logf,fmt,ap);
191         if (pm) { fputs(": ",logf); fputs(strerror(errno),logf); }
192         if (fatal!='\n') fputc((fatal=='.'?'.':'!'),logf);
193         fputc('\n',logf);
194         fflush(logf);
195 }
196
197 static void logmsg(const char *fmt,...) ATTR_PRINTFORMAT(1,2);
198 static void logmsg(const char *fmt,...)
199 {
200 va_list ap;
201         va_start(ap,fmt);
202         vlogmsg(0,'\n',fmt,ap);
203         va_end(ap);
204 }
205
206 static void error(const char *fmt,...) ATTR_PRINTFORMAT(1,2);
207 static void error(const char *fmt,...)
208 {
209 va_list ap;
210 char fatal,pm;
211
212         if ((pm=(*fmt=='^'))) fmt++;
213         fatal=*fmt;
214         if (fatal=='!' || fatal=='.' || fatal=='\n') fmt++;
215         else fatal=0;
216
217         fprintf(stderr,"%s: ",pname);
218         va_start(ap,fmt);
219         vfprintf(stderr,fmt,ap);
220         if (fatal=='!') vlogmsg(pm,fatal,fmt,ap);
221         va_end(ap);
222         if (pm) { fputs(": ",stderr); fputs(strerror(errno),stderr); }
223         if (fatal!='\n') fputc((fatal=='.'?'.':'!'),stderr);
224         fputc('\n',stderr);
225         if (fatal=='!') exit(EXIT_FAILURE);
226 }
227
228 static void chk(const void *p)
229 {
230         if (p) return;
231         error(_("!Virtual memory exhausted"));
232 }
233
234 static char *devcmd(const char *term,const char *catch,const char *send,...) ATTR_PRINTFORMAT(3,4);
235
236 static void unlockdevice(int hard)
237 {
238         d2("unlockdevice(), locked=%d\n",locked);
239         if (!locked || !*lockreal) return;
240         if (!hard && locked>1) { locked--; return; }
241         d2("Removing lockfile \"%s\"\n",lockreal);
242         if (unlink(lockreal))
243                 error(_("^Error removing my device lockfile \"%s\""),lockreal);
244         locked=0;
245 }
246
247 static void cleanup(void)
248 {
249         d1("cleanup()\n");
250         if (dis_cleanup) return;
251         if (restore) {
252 char *cmd=restore;
253                 restore=NULL;
254                 devcmd(NULL,NULL,"\r\nAT");
255                 devcmd(NULL,NULL,cmd);
256                 devcmd(NULL,NULL,"\r\nAT");
257                 }
258         if (restios_yes) {
259                 if (tcsetattr(devfd,TCSANOW,&restios))
260                         error(_("^Error restoring termios for device"));
261                 restios_yes=0;
262                 }
263         unlockdevice(1);
264         dis_cleanup=1;
265         exit(EXIT_FAILURE);
266 }
267
268 static void usage(void)
269 {
270         fprintf(stderr,_("\
271 \n\
272 %s\
273 \n\
274 Usage: %s [-c|--config <cfgfile>] [-d|--device <device>]\n\
275              [-L|--log <file>] [-l|--lockfile <lock>]\n\
276              [-b|--baud <rate>] [-x|--xonxoff] [-C|--rtscts]\n\
277              [-M|--smsmode <mode>] [-P|--pdusmscmode <mode>]\n\
278              [-s|--smsc <smsc #>] [-m|--maxretry <#>]\n\
279              [-r|--readtime <sec>] [-t|--chartime <msec>] [-T|--cmdtime <msec>]\n\
280              [-w|--waittime <sec>] [-v|--verbose] [-h|--help] [-V|--version]\n\
281              {--send | --send-mobildock | --receive | --logo-send}\n\
282   --send / --send-mobildock:\n\
283              [-f|--file] <dest. phone> <msg text|msg filename>\n\
284   --receive:\n\
285              <command name>\n\
286   --logo-send:\n\
287              <dest. phone> <logo filename> [<GSMnet id>]\n\
288   --ring-send:\n\
289              <dest. phone> <ring filename>\n\
290   --picture-send:\n\
291              <dest. phone> <picture .ras filename>\n\
292 \n\
293  -c, --config\tRead this additional config file\n\
294 \t\t(def. \"%s\" and \"$HOME%s\")\n\
295  -d, --device\tMobile on this serial device (def. \"%s\")\n\
296  -L, --log\tLog all important messages to this file (def. \"%s\")\n\
297  -l, --lockfile\tLock serial port by this file, \"%%s\" is basename of device\n\
298 \t\t(def. \"%s\")\n\
299  -b, --baud\tSet baudrate, 2400-57600 supported (def. %d)\n\
300  -x, --xonxoff\tUse XON/XOFF (AKA software) serial port handshaking - default\n\
301  -C, --rtscts\tUse RTS/CTS (AKA hardware) serial port handshaking%s\n\
302  -M, --smsmode\tForce SMS as: \"pdu\" or 0: PDU mode, \"text\" or 1: text mode\n\
303  -P, --pdusmscmode\tForce PDU as: \"count-in\", \"count-out\", \"none\"\n\
304  -s, --smsc\tUse this SMS Center number (def. query from mobile)\n\
305  -m, --maxretry\tMaximum retries of any command before giving up (def. %d)\n\
306  -r, --readtime\tSeconds for maximum wait time for response\n\
307 \t\t(def. %ds standard, %ds for MobilDock modes,\n\
308 \t\t multiplied %dx for long cmds)\n\
309  -t, --chartime\tMilliseconds between each char (def. %dms)\n\
310  -T, --cmdtime\tMilliseconds before each whole AT command (def. %dms)\n\
311  -w, --waittime\tSeconds to prevent timeout of 9110 FaxModem (def. %ds)\n\
312  -v, --verbose\tIncrease verbosity level, more \"-v\"s give more messages\n\
313  -h, --help\tPrint a summary of the options\n\
314  -V, --version\tPrint the version number\n\
315 \n\
316 --send / --send-mobildock:\n\
317  -f, --file\tRead contents of message from file instead\n\
318 --receive:\n\
319  <command name>\tProgram to run on receive, message will be on stdin\n\
320 \t\tFollowing substitutes are recognized:\n\
321 \t\t%%p - source phone number\n\
322 \t\t%%T - timestamp from SMSC as # of seconds from 1970 (-1 if invalid)\n\
323 \t\t%%t - ctime(3) style timestamp (e.g. \"Wed Jun 30 21:49:08 1993\"),\n\
324 \t\t     empty string if invalid\n\
325 \t\t%%s - originating SMSC number, if available (else empty)\n\
326 --logo-send:\n\
327  <GSMnet id>\t* Oper. logo: Enter custom network code MccMnc, e.g. 23002\n\
328 \t\t* Oper. logo: Specify \"%s\" to read network code from NOL file\n\
329 \t\t* Group gfx : Specify \"%s\" to send logo as group graphics\n\
330 \n\
331 You may need to use the following line to catch all of this help text:\n\
332 ./mdsms -h 2>&1|more\n\
333 \n"),version,PACKAGE,CONFIG_MAIN,CONFIG_HOME,DEF_DEVICE,DEF_LOGNAME,DEF_LOCKFILE,DEF_BAUD,
334 #ifdef HAVE_CRTSCTS
335                 "",
336 #else
337                 _("\n\t\t(Not supported on this platform!)"),
338 #endif
339 DEF_MAXRETRY,DEF_READTIME,DEF_READTIME_MOBILDOCK,EXT_READTIME,DEF_CHARTIME,DEF_CMDTIME,DEF_WAITTIME,
340 WORD_NET,WORD_GROUP);
341         exit(EXIT_FAILURE);
342 }
343
344 static const struct option longopts[]={
345 /* Modes has to be in-order on exact positions */
346 {"send"          ,0,0,MODE_SEND},
347 {"send-mobildock",0,0,MODE_SEND_MOBILDOCK},
348 {"receive"       ,0,0,MODE_RECEIVE},
349 {"logo-send"     ,0,0,MODE_LOGO_SEND},
350 {"ring-send"     ,0,0,MODE_RING_SEND},
351 {"picture-send"  ,0,0,MODE_PICTURE_SEND},
352 /* Mode aliases may follow in no particular order *
353  * as long as no non-mode options is between them */
354 {"send-md"       ,0,0,MODE_SEND_MOBILDOCK},
355 {"recv"          ,0,0,MODE_RECEIVE},
356 {"logo"          ,0,0,MODE_LOGO_SEND},
357 {"ring"          ,0,0,MODE_RING_SEND},
358 {"picture"       ,0,0,MODE_PICTURE_SEND},
359 {"config"      ,1,0,'c'},
360 {"device"      ,1,0,'d'},
361 {"log"         ,1,0,'L'},
362 {"lockfile"    ,1,0,'l'},
363 {"baud"        ,1,0,'b'},
364 {"xonxoff"     ,0,0,'x'},
365 {"rtscts"      ,0,0,'C'},
366 {"smsmode"     ,1,0,'M'},
367 {"pdusmscmode" ,1,0,'P'},
368 {"smsc"        ,1,0,'s'},
369 {"maxretry"    ,1,0,'m'},
370 {"readtime"    ,1,0,'r'},
371 {"chartime"    ,1,0,'t'},
372 {"cmdtime"     ,1,0,'T'},
373 {"waittime"    ,1,0,'w'},
374 {"file"        ,0,0,'f'},
375 {"verbose"     ,0,0,'v'},
376 {"help"        ,0,0,'h'},
377 {"version"     ,0,0,'V'},
378 {NULL          ,0,0,0  }};
379
380 static void processargs(int argp,char **args,const char *from);
381
382 static char *cfgstack[MAXCFGLOOP];
383 static unsigned cfgstacki=0;
384
385 static void chkfclose(FILE *f,const char *fname)
386 {
387         if (fclose(f))
388                 error(_("^Error closing \"%s\""),fname);
389 }
390
391 static long getfilesize(FILE *f,const char *fname)
392 {
393 long size;
394
395         if (fseek(f,0,SEEK_END))
396                 error(_("^Error seeking to end of \"%s\""),fname);
397         if ((size=ftell(f))<0)
398                 size=-1,error(_("^Error measuring length of \"%s\""),fname);
399         rewind(f);
400         return(size);
401 }
402
403 static void readfile(const char *fname,char quiet)
404 {
405 FILE *f;
406 size_t got;
407 char *args[MAXCFGARGS],*d,*s,blank,quote;
408 unsigned argp;
409 char *buf;
410 long size;
411 static unsigned tot=0;
412
413         if (tot++>=MAXCFGNUM) {
414                 if (tot==MAXCFGNUM+1) error(_("Too many config files to read, max is %d, break-out"),MAXCFGNUM);
415                 return;
416                 }
417         if (!(f=fopen(fname,"rt"))) {
418                 if (!quiet) error(_("^Can't open config file \"%s\" for r/o"),fname);
419                 return;
420                 }
421                 
422         if (verbose>=2) error(_(".Reading config file \"%s\""),fname);
423         if ((size=getfilesize(f,fname))==-1) size=0;
424         if (size>MAXCONFIG) 
425                 error(_("File \"%s\" is too long, read only %d bytes"),fname,MAXCONFIG);
426         chk(buf=malloc((size?size:MAXCONFIG)+1));
427         got=fread(buf,1,(size?size:MAXCONFIG),f);
428         if (size && got!=size)
429                 error(_("File \"%s\" read error, got only %u bytes of %ld"),fname,got,size);
430         chkfclose(f,fname);
431         buf[got]='\0';
432         args[0]=pname;
433         for (argp=1,d=s=buf,blank=1,quote=0;s<buf+got;s++) {
434 char c=*s;
435
436                 if (!quote && isspace(c)) {
437                         if (!blank) {
438                                 *d='\0';
439                                 blank=1;
440                                 if (verbose>=2) error(_("\nConfig \"%s\": arg#%d: %s"),fname,argp-1,args[argp-1]);
441                                 }
442                         continue;
443                         }
444                 if (blank) {
445                         if (argp>=NELEM(args)-1) {
446                                 error(_("Too many arguments in \"%s\", from offset %d ignored"),fname,s-buf);
447                                 break;
448                                 }
449                         args[argp++]=s;
450                         d=s;
451                         blank=0;
452                         }
453                 if (c=='\\') { *d++=*++s; continue; }
454                 if (c=='"' || c=='\'') {
455                         if (!quote   ) { quote=c; continue; }
456                         if ( quote==c) { quote=0; continue; }
457                         /* FALLTHRU */
458                         }
459                 *d++=c;
460                 }
461         args[argp]=NULL;
462         processargs(argp,args,fname);
463         free(buf);
464 }
465
466 static struct argstack {
467         struct argstack *next;
468         int num,offset;
469         const char *from;
470         char *arg[1];
471         } *argstack;
472
473 static struct argstack **argstack_tail=&argstack;
474 static size_t lastargstack_len;
475 static const char *lastargstack_from;
476 static int lastargstack_index;
477
478 static size_t argstack_size;
479 static int argstack_num;
480
481 static void pushargstack(char **args,int num,int offset,const char *from,char stack)
482 {
483 struct argstack *as;
484 int i;
485
486         if (!num) return;
487         assert(num>=1);
488         chk(as=malloc(sizeof(*as)+sizeof(as->arg)*(num-1)));
489         as->num=num;
490         as->offset=offset;
491         if (!from) as->from=NULL;
492         else chk(as->from=strdup(from));
493         for (i=0;i<num;i++) {
494                 chk(as->arg[i]=strdup(args[i]));
495                 argstack_size+=strlen(args[i]);
496                 }
497         argstack_num+=num;
498         if (stack) {
499                 as->next=argstack;
500                 argstack=as;
501                 }
502         else {
503                 as->next=NULL;
504                 *argstack_tail=as;
505                 argstack_tail=&as->next;
506                 }
507 }
508
509 static void pushargstack_one(char *s,char stack)
510 { pushargstack(&s,1,0,NULL,stack); }
511
512 static char *nextargstack(void)
513 {
514 static int order=0;
515 char *r;
516
517         if (argstack && order==argstack->num) {
518 struct argstack *as=argstack;
519                 if (!(argstack=as->next))
520                         argstack_tail=&argstack;
521                 order=0;
522                 free((char *)as->from);
523                 lastargstack_from=NULL;
524                 free(as);
525                 }
526         if (!argstack) {
527                 assert(!argstack_num); assert(!argstack_size);
528                 return(NULL);
529                 }
530         assert(order<argstack->num);
531         lastargstack_index=argstack->offset+order;
532         r=argstack->arg[order++];
533         assert(argstack_num>0); argstack_num--;
534         lastargstack_len=strlen(r);
535         assert(argstack_size>=lastargstack_len); argstack_size-=lastargstack_len;
536         lastargstack_from=argstack->from;
537         return(r);
538 }
539
540 static char *glueargstack(size_t *destlenp,const char *glue)
541 {
542 size_t gluel=(glue?strlen(glue):0),destlen;
543 char *dest,*d,*s;
544
545         if (!argstack_num) {
546                 chk(dest=strdup(""));
547                 if (destlenp) *destlenp=0;
548                 return(dest);
549                 }
550         destlen=argstack_size+(argstack_num-1)*gluel;
551         if (destlenp) *destlenp=destlen;
552         chk(dest=malloc(destlen+1));
553         for (d=dest;(s=nextargstack());) {
554                 memcpy(d,s,lastargstack_len);
555                 free(s);
556                 d+=lastargstack_len;
557                 if (!argstack_num) break;
558                 if (!glue) continue;
559                 memcpy(d,glue,gluel);
560                 d+=gluel;
561                 assert(d<=dest+destlen);
562                 }
563         assert(!argstack_num);
564         assert(d==dest+destlen);
565         *d='\0';
566         return(dest);
567 }
568
569 static struct {
570         const char c;
571         char **const var;
572         unsigned stamp;
573         } optset[]={
574                 { 'd',&device       },
575                 { 'L',&logname      },
576                 { 'l',&lockfile     },
577                 { 'b',&baud         },
578                 { 'M',&smsmode      },
579                 { 'P',&pdusmscmode  },
580                 { 's',&smsc         },
581                 { 'm',&maxretry     },
582                 { 'r',&readtime     },
583                 { 't',&chartime     },
584                 { 'T',&cmdtime      },
585                 { 'w',&waittime     },
586         };
587
588 static void processargs(int argp,char **args,const char *from)
589 {
590 int optc;
591 static unsigned seq=0;
592 int i;
593
594         seq++;
595         optarg=NULL; optind=0; /* FIXME: Possible portability problem. */
596         while ((optc=getopt_long(argp,args,"c:d:L:l:b:xCM:P:s:m:r:t:T:w:fvhV",longopts,NULL))!=EOF) switch (optc) {
597                 case 'c':
598                         if (cfgstacki>=NELEM(cfgstack)) {
599                                 error(_("Looping (%d) during attempt to read config file \"%s\", break-out"),NELEM(cfgstack),from);
600                                 break;
601                                 }
602                         chk(cfgstack[cfgstacki++]=strdup(optarg));
603                         break;
604                 case 'd': case 'L': case 'b': case 'l': case 'M': case 'P': case 's': case 'm': case 'r': case 't': case 'T': case 'w':
605                         for (i=0;i<NELEM(optset);i++)
606                                 if (optset[i].c==optc) {
607                                         if (optset[i].stamp && optset[i].stamp!=seq) {
608                                                 assert(!!*optset[i].var);
609                                                 break;
610                                                 }
611                                         free(*optset[i].var);
612                                         chk(*optset[i].var=strdup(optarg));
613                                         optset[i].stamp=seq;
614                                         break;
615                                         }
616                         assert(i<NELEM(optset));
617                         break;
618                 case 'x': case 'C':
619 #ifdef HAVE_CRTSCTS
620                         if (handshake_stamp && handshake_stamp!=seq) break;
621                         handshake_rtscts=(optc=='C');
622                         handshake_stamp=seq;
623 #else
624                         if (optc=='C')
625                                 error(_("!RTS/CTS handshake NOT supported on this platform! Occured during parsing option %d from \"%s\""),optind-1,from);
626 #endif
627                         break;
628                 case MODE_SEND:
629                 case MODE_SEND_MOBILDOCK:
630                 case MODE_RECEIVE:
631                 case MODE_LOGO_SEND:
632                 case MODE_RING_SEND:
633                 case MODE_PICTURE_SEND:
634                         if (mode_stamp && mode_stamp!=seq) break;
635                         mode=optc;
636                         mode_stamp=seq;
637                         break;
638                 case 'f':
639                         readbody++;
640                         break;
641                 case 'v':
642 #ifndef DEBUG   /* prevent suppositious overflow */
643                         verbose++;
644 #endif
645                         break;
646                 case 'V':
647                         fprintf(stderr,version);
648                         exit(EXIT_FAILURE);
649                 default:
650                         if (optc!='h')
651                                 error(_("\nLast getopt(3) error occured during parsing option %d from \"%s\"! Follows help:"),optind-1,from);
652                         usage();
653                         break;
654                 }
655         pushargstack(args+optind,argp-optind,optind,from,1);
656         while (cfgstacki) {
657 char *s=cfgstack[--cfgstacki];
658
659                 assert(cfgstacki>=0);
660                 readfile(s,0);
661                 free(s);
662                 assert(cfgstacki>=0 && cfgstacki<NELEM(cfgstack));
663                 }
664 }
665
666 static const struct nullcheck {
667         char **var;
668         enum modenum reqd;
669         const char *name;
670         } nullcheck[]={
671                 {&phone,MODE_BIT(MODE_SEND)
672                        |MODE_BIT(MODE_SEND_MOBILDOCK)
673                        |MODE_BIT(MODE_LOGO_SEND)
674                        |MODE_BIT(MODE_RING_SEND)
675                        |MODE_BIT(MODE_PICTURE_SEND),
676                         N_("destination phone number")},
677                 {&   logoname,MODE_BIT(   MODE_LOGO_SEND),N_(   "logo filename")},
678                 {&   ringname,MODE_BIT(   MODE_RING_SEND),N_(   "ring filename")},
679                 {&picturename,MODE_BIT(MODE_PICTURE_SEND),N_("picture filename")},
680                 {&body,MODE_BIT(MODE_RECEIVE),N_("body text")}, /* we allow empty bodies for SENDs */
681 #if 0
682                 {&gsmnet,MODE_BIT(MODE_LOGO_SEND),N_("GSM operator network code")},
683                 {&device,0,N_("device for communication")},
684 #endif
685         };
686 static char **emptycheck[]={&logname,&smsc,&logoname,&gsmnet};
687
688 static inline void emptyclean(void)
689 {
690 int i;
691
692         for (i=0;i<NELEM(emptycheck);i++)
693                 if (*emptycheck[i] && !**emptycheck[i]) {
694                         free(*emptycheck[i]);
695                              *emptycheck[i]=NULL;
696                         }
697 }
698
699 static inline void cmdline_done(void)
700 {
701 char *s;
702         while ((s=nextargstack())) {
703                 error(_("\nExcessive option %d from \"%s\" ignored: %s"),
704                         lastargstack_index,lastargstack_from,s);
705                 free(s);
706                 }
707         emptyclean();
708 }
709
710 static char *check_phone(const char *phone)
711 {
712 const char *s,*s1;
713 static char err[LINE_MAX];
714
715         for (s=s1=(phone+(*phone=='+'));*s && s-s1<MAXNUMLEN;s++)
716                 if (!isdigit(*s)) {
717                         VARPRINTF2(err,_("Invalid digit '%c' in phone number - at offset %d"),
718                                 *s,s-phone);
719                         return(err);
720                         }
721         if (!*s) return(NULL);
722         VARPRINTF2(err,_("Phone number too long (%d), max. %d digits allowed"),
723                 strlen(s1),MAXNUMLEN);
724         return(err);
725 }
726
727 static void cmdline_phone(void)
728 {
729         if (!phone && (phone=nextargstack())) {
730 char *s;
731
732                 if ((s=check_phone(phone)))
733                         error(_("!%s in option %d from \"%s\": %s"),
734                                         s,lastargstack_index,lastargstack_from,phone);
735                 }
736 }
737
738 static inline void cmdline_receive(void)
739 {
740 char *s;
741
742         if (!body && argstack_num) {
743                 body=glueargstack(&bodylen," ");
744                 for (s=body;(s=strchr(s,'%'));s++)
745                         switch (*++s) {
746                                 case 'p': case 'T': case 't': case 's': break;
747                                 default:
748                                         error(_("Unknown formatsymbol '%c' (use \"%%%%%c\" to fix it) at pos %d in: %s"),
749                                                 *s,*s,s-body,body);
750                                 }
751                 }
752 }
753
754 static inline void cmdline_send(void)
755 {
756         cmdline_phone();
757         if (!body && argstack_num)
758                 body=glueargstack(&bodylen," ");
759 }
760
761 static inline void cmdline_logo_send(void)
762 {
763 char *ogsmnet;
764
765         cmdline_phone();
766         if (!logoname) logoname=nextargstack();
767         if (!gsmnet && (ogsmnet=nextargstack())) {
768 char *s,*d,e=0;
769
770                 chk(gsmnet=strdup(ogsmnet));
771                 if (strtrycasecmp(gsmnet,WORD_NET) && strtrycasecmp(gsmnet,WORD_GROUP)) {
772                         for (d=s=gsmnet;*s;s++) {
773                                 if (isdigit(*s)) { *d++=*s; continue; }
774                                 if (isspace(*s)) continue;
775                                 error(_("\nInvalid characted '%c' in GSMnet at offs %d: %s"),
776                                         *s,s-gsmnet,ogsmnet);
777                                 e=1;
778                                 break;
779                                 }
780                         if ((d-gsmnet)!=5) {
781                                 error(_("\nGSMnet is required to have exactly 5 digits or to be\n\
782 either \"%s\" or \"%s\", but found length %d: %s"),
783                                         WORD_NET,WORD_GROUP,d-gsmnet,ogsmnet);
784                                 e=1;
785                                 }
786                         if (!e) *d='\0';
787                         else {
788                                 error(_("\nGSMnet option %d from \"%s\" rejected due to previous errors: %s"),
789                                         lastargstack_index,lastargstack_from,ogsmnet);
790                                 free(gsmnet);
791                                 gsmnet=NULL;
792                                 }
793                         }
794                 }
795 }
796
797 static inline void cmdline_ring_send(void)
798 {
799         cmdline_phone();
800         if (!ringname) ringname=nextargstack();
801 }
802
803 static inline void cmdline_picture_send(void)
804 {
805         cmdline_phone();
806         if (!picturename) picturename=nextargstack();
807 }
808
809 static void lockclose(int fd)
810 {
811         if (close(fd))
812                 error(_("Error closing lockfile \"%s\""),lockreal);
813 }
814
815 static int lockdevice(int attempt)
816 {
817 int fd=-1;
818 char buf[64];
819 ssize_t got;
820 int delay=0;
821 char empty=0;
822 pid_t pid;
823
824         d2("lockdevice(), locked=%d\n",locked);
825         if (locked) return (++locked);
826         for (;;) {
827                 if (fd!=-1) lockclose(fd);
828 recheck:
829                 if (delay) sleep(delay);
830                 delay=DEVLOCK_PERIOD;
831                 if (verbose>=3) error(_(".Checking the lockfile \"%s\".."),lockreal);
832                 if ((fd=open(lockreal,O_RDONLY))==-1) break;
833                 if ((got=read(fd,buf,sizeof(buf)-1))<=0) {
834 isempty:
835                         if (empty>=DEVLOCK_MAXEMPTY) {
836                                 error(_(".Lockfile \"%s\" is still not valid, removing it"),lockreal);
837                                 goto remove;
838                                 }
839                         empty++;
840                         continue;
841                         }
842                 assert(got<sizeof(buf));
843                 buf[got]='\0';
844                 if (sscanf(buf,"%d",&pid)!=1) goto isempty;
845                 empty=0;
846                 errno=0;
847                 if (kill(pid,0) && errno!=ESRCH && errno!=EPERM)
848                         error(_("^Error during checking consciousness of PID %d"),pid);
849                 if (errno!=ESRCH) {
850                         if (attempt) return(0);
851                         continue;
852                         }
853                 error(_(".Lockfile \"%s\" is stale (PID %d), removing it"),lockreal,pid);
854 remove:
855                 lockclose(fd);
856                 if (unlink(lockreal))
857                         error(_("^Error removing foreign lockfile \"%s\""),lockreal);
858                 break;
859                 }
860         errno=0;
861         if ((fd=open(lockreal,O_WRONLY|O_CREAT|O_EXCL,0644))==-1) {
862                 if (errno==EEXIST) goto recheck;
863                 error(_("^!Error creating lockfile \"%s\""),lockreal);
864                 }
865         locked=1;
866         got=VARPRINTF(buf,"%010d\n",getpid()); assert(got==11);
867         if (write(fd,buf,got)!=got)
868                 error(_("^!Error writing data to lockfile \"%s\""),lockreal);
869         lockclose(fd);
870         return((locked=1));
871 }
872
873 static char wasalarm=0;
874 static void sigalarm(int signo);
875
876 static void setalarm(void)
877 {
878         signal(SIGALRM,(RETSIGTYPE (*)(int))sigalarm);
879 #ifdef HAVE_SIGINTERRUPT
880         siginterrupt(SIGALRM,1);
881 #endif
882 }
883
884 static void sigalarm(int signo)
885 {
886         setalarm();
887         wasalarm=1;
888         if (verbose>=1) error(_("Timed out"));
889 }
890
891 static void blocking(char yes)
892 {
893 static char state=-1;
894         if (state==yes) return;
895         if (fcntl(devfd,F_SETFL,(yes?0:O_NONBLOCK)))
896                 error(_("^!fcntl() on device for %s mode"),(yes?_("blocking"):_("non-blocking")));
897         state=yes;
898 }
899
900 static const char *record,*recordend;
901 static char *catchdata;
902 static size_t catchdatal,catchdatasiz;
903
904 static const char *reform(const char *s,int slot);
905 static void catched(const char *end,char edata)
906 {
907 size_t len;
908 const char *p;
909
910         if (!record) return;
911         assert(end>=record);
912         p=memchr(record,edata,end-record);
913         if ((len=(p?p:end)-record)) {
914                 if (!catchdata)
915                         chk(catchdata=malloc((catchdatasiz=LINE_MAX)));
916                 if (catchdatal+len>catchdatasiz)
917                         chk(catchdata=realloc(catchdata,
918                                 (catchdatasiz=(catchdatal+len)*2)));
919                 memcpy(catchdata+catchdatal,record,len);
920                 catchdatal+=len;
921                 }
922         record   =(p?NULL:end);
923         recordend=(p?p   :end);
924         assert(catchdatal<=catchdatasiz);
925 }
926
927 static int retrycnt=0;
928 static void retrying(void)
929 {
930         if (maxretryn>=0 && ++retrycnt>=maxretryn) error(_("!Maximum command retry count (%ld) exceeded"),maxretryn);
931         if (verbose>=2) error(_(".Retrying phase, %d out of %ld.."),retrycnt,maxretryn);
932 }
933
934 static const char *reform(const char *s,int slot)
935 {
936 static struct formslot {
937         char *s;
938         size_t l;
939         } arr[3];
940 char c,*d;
941 struct formslot *fs;
942
943         assert(slot>=0 && slot<NELEM(arr));
944         if (!s) return(_("<unset>"));
945         if (!(fs=&arr[slot])->s)
946                 chk(fs->s=malloc(fs->l=LINE_MAX));
947         d=fs->s;
948         for (*d++='"';(c=*s);s++) {
949                 if (d>=fs->s+fs->l-10) {
950 off_t o=d-fs->s;
951                         chk(fs->s=realloc(fs->s,(fs->l=(fs->l?fs->l*2:LINE_MAX))));
952                         d=fs->s+o;
953                         }
954                 if (c!='\\' && c!='"' && isprint(c)) { *d++=c; continue; }
955                 *d++='\\';
956                 switch (c) {
957                         case '\\': case '"': *d++=c; break;
958                         case '\n': *d++='n'; break;
959                         case '\r': *d++='r'; break;
960                         case '\032': *d++='Z'; break;
961                         case '\033': *d++='e'; break;
962                         default:
963                                 d+=sprintf(d,"x%02X",(unsigned char)c);
964                                 break;
965                         }
966                 }
967         *d++='"'; *d='\0';
968         return(fs->s);
969 }
970
971 static char devcmd_empty_return='\0'; /* returned as catch when only newlines found */
972
973 static char *devcmd(const char *term,const char *catch,const char *send,...) ATTR_PRINTFORMAT(3,4);
974 static char *devcmd(const char *term,const char *catch,const char *send,...)
975 {
976 size_t l,bufl2,terml,catchl=0 /* GCC happiness */,fragl,offs;
977 static char buf[LINE_MAX];
978 static size_t bufl;
979 ssize_t got;
980 char *hit,*s;
981 va_list ap;
982 char errout,extend,catch_any,edata;
983 long alarmtime;
984 const char *osend;
985 static const char emptystring[]="";
986 size_t discard;
987
988         if (!term) term="\nOK\n";
989         if (!strcmp(send," ")) send=NULL; /* GCC formatstring-check workaround */
990         if (verbose>=2) error(_(".devcmd(sendfmt=%s,term=%s,catch=%s)"),
991                 reform(send,0),reform(term,1),reform(catch,2));
992         if (!(osend=send)) send="";
993         if ((catch_any=(catch && !strcmp(catch,"@")))) catch=NULL;
994         if ((errout=(*send=='!'))) send++;
995         errout|=(maxretryn==-1);
996         if ((extend=(*send=='~'))) send++;
997         alarmtime=readtimen*(extend?EXT_READTIME:1);
998         buf[bufl]='\0'; /* for d8() below */
999         d8("devcmd(), alarmtime=%ld, errout=%d, extend=%d, catch_any=%d, osend=%p, bufl=%d, buf: %s\n",
1000                 alarmtime,errout,extend,catch_any,osend,bufl,reform(buf,0));
1001         assert(!catch || !strchr(catch,'\r')); /* we are no longer supporting 'noconvcr'! */
1002         assert(!term  || !strchr(term ,'\r'));
1003         if (0) {
1004 err:
1005                 alarm(0);
1006                 if (errout) return(NULL);
1007                 retrying();
1008                 }
1009         catchdatal=0;
1010         if (osend) {
1011                 bufl=0;
1012                 d1("Resetting bufl.\n");
1013                 va_start(ap,send);
1014                 l=VARVPRINTF(buf,send,ap); bufl=l+(!!osend);
1015                 va_end(ap);
1016                 if (bufl>=sizeof(buf)-1) error(_("!Command too big (%d>%d)"),bufl,sizeof(buf)-1);
1017                 if (verbose>=2) error(_(".devcmd formatted send=%s%s"),reform(buf,0),(osend?"+\"\\r\"":""));
1018                 if (osend) buf[l]='\r';
1019                 for (offs=0,got=0;offs<bufl;offs++) {
1020                         alarm(MAXSENDTIME);
1021                         usleep((offs?chartimen:cmdtimen)*1000);
1022                         if (!offs && tcflush(devfd,TCIOFLUSH))
1023                                 error(_("^Error flushing I/O queue of device"));
1024                         if (write(devfd,buf+offs,1)!=1) break;
1025                         got++;
1026                         if (tcdrain(devfd))
1027                                 error(_("^Error forcing output of char at pos %d of cmd %s"),offs,reform(buf,0));
1028                         }
1029                 alarm(0);
1030                 if (got!=bufl) {
1031                         error(_("^Wrote only %d of %d bytes of command"),got,bufl);
1032                         goto err;
1033                         }
1034                 }
1035
1036         if (!(terml=strlen(term))) {
1037                 assert(!catch); assert(!catch_any);
1038                 return(NULL);
1039                 }
1040         if (catch) {
1041                 catchl=strlen(catch);
1042                 fragl=MAX(terml,catchl);
1043                 }
1044         else fragl=terml;
1045         fragl=MAX(fragl,MAX(strlen(ERROR_SUBSTR1),strlen(ERROR_SUBSTR2)));
1046         record=recordend=NULL;
1047         wasalarm=0;
1048         alarm(alarmtime);
1049         edata='\n';
1050         if (!osend) {
1051                 /* "bufl" important */
1052                 goto skipread;
1053                 }
1054         for (;;) {
1055                 blocking(0);
1056                 errno=0;
1057                 got=read(devfd,buf+bufl,sizeof(buf)-1-bufl);
1058                 if (got==-1 && errno==EAGAIN) {
1059                         blocking(1);
1060                         errno=0;
1061                         got=read(devfd,buf+bufl,1);
1062                         }
1063                 if (got<=0) {
1064                         buf[bufl]='\0'; /* for strspn() below */
1065                         if (!strcmp(term,"\n") && catch /* written to trap on 'devcmd("\n","+CMT:"," ")' */
1066                             && bufl==strspn(buf,"\n" /* accept */)) { /* only newlines found */
1067                                 if (verbose>=2)
1068                                         error(_(".Blank (%d newlines) input read, ignoring it"),bufl);
1069                                 bufl=0; /* discard newlines */
1070                                 return(&devcmd_empty_return);
1071                                 }
1072                         if (wasalarm) error(_("Maximum response timeout (%lds) exceeded"),alarmtime);
1073                         else error(_("^Couldn't read device data (ret=%d)"),got);
1074                         goto err;
1075                         }
1076                 bufl2=bufl+got;
1077                 buf[bufl2]='\0';
1078                 s=buf+bufl;
1079                 while (buf+bufl2>s && (s=memchr(s,'\0',buf+bufl2-s))) *s++=REPL_NULLCHAR;
1080                 if (verbose>=3)
1081                         error(_("\nGot chunk of data from device: %s"),reform(buf+bufl,0));
1082                 /* convert CR */ {
1083                         s=buf+bufl;
1084                         while (buf+bufl2>s && (s=memchr(s,'\r',buf+bufl2-s))) *s++='\n';
1085                         }
1086                 bufl=bufl2;
1087 skipread:
1088                 catched(buf+bufl,edata); assert(!record || record==buf+bufl);
1089                 assert(bufl<sizeof(buf)-1);
1090                 buf[bufl]='\0';
1091                 assert(strlen(buf)==bufl);
1092                 /* d3(">%s|%s<\n",buf,term); */
1093                 if (strstr(buf,ERROR_SUBSTR1) || strstr(buf,ERROR_SUBSTR2)) {
1094                         error(_("Found ERROR response on command %s"),reform(send,0));
1095                         goto err;
1096                         }
1097 /* "record" may get NULLed here after successful 'catch'
1098  * but "recordend" will never be NULLed
1099  */
1100                 if (catch && !recordend && bufl>=catchl && (hit=strstr(buf,catch))) {
1101                         record=hit+catchl;
1102                         catched(buf+bufl,edata); assert(!record || record==buf+bufl);
1103                         }
1104                 if (catch_any && !recordend && buf[discard=strspn(buf,"\n" /* accept */)]) {
1105                         record=buf+discard;
1106                         catched(buf+bufl,edata); assert(!record || record==buf+bufl);
1107                         }
1108                 if (((!catch && !catch_any) || catchdatal) && bufl>= terml
1109                     && (hit=strstr((recordend?recordend:buf),term))) {
1110                         memmove(buf,hit+terml,(bufl2=(buf+bufl)-(hit+terml))); bufl=bufl2;
1111                         break;
1112                         }
1113                 if (bufl<fragl) continue;
1114                 memmove(buf,buf+bufl-(fragl-1),(bufl2=fragl-1));
1115                 if (record   ) record-=bufl-bufl2;
1116                 if (recordend) recordend-=bufl-bufl2;
1117                 bufl=bufl2;
1118                 }
1119         alarm(0);
1120         if (!catchdatal) {
1121                 assert(!catch && !catch_any);
1122                 return("");
1123                 }
1124         assert(!!catch || catch_any);
1125         record=emptystring;
1126         catched(record+1,edata);
1127         if (verbose>=2) error(_(".Returning data %s for cmd %s"),reform(catchdata,0),reform(send,1));
1128         return(catchdata);
1129 }
1130
1131 static int prepaddr(unsigned char *d,const char *addr)
1132 {
1133 int tot=0;
1134 char flip=0,plus;
1135 unsigned char n;
1136
1137         if ((plus=(*addr=='+'))) addr++;
1138         *++d=(plus?ADDR_INT:ADDR_NAT);
1139         while (*addr) {
1140                 if (*addr<'0' || *addr>'9')
1141                         error(_("!Error during conversion of number at: %s"),addr);
1142                 tot++;
1143                 n=(*addr++)-'0';
1144                 if ((flip=!flip)) *++d=0xF0|n;
1145                 else *d=(*d&0x0F)|(n<<4U);
1146                 }
1147         return(tot);
1148 }
1149
1150 static char *finalsmsc;
1151 #define SMSCBINSIZE (1+1+(MAXNUMLEN+1)/2)
1152 static char pdusmsc[SMSCBINSIZE*2+1];
1153
1154 static inline char tohex(unsigned char x)
1155 {
1156         x&=0x0F;
1157         if (x<10) return(x   +'0');
1158                   return(x-10+'A');
1159 }
1160
1161 static inline void textconv(char *d,unsigned char *s,size_t len)
1162 {
1163         while (len--) {
1164                 *d++=tohex(*s>>4U);
1165                 *d++=tohex(*s    );
1166                 s++;
1167                 }
1168         *d='\0';
1169 }
1170
1171 static inline void smscset(void)
1172 {
1173 char *s,*t,*e,*serr;
1174 unsigned char bin[2+(MAXNUMLEN+1)/2];
1175
1176         if (smsc) devcmd(NULL,NULL,"\r\nAT+CSCA=\"%s\"",smsc);
1177         s=devcmd(NULL,"\n+CSCA:","\r\nAT+CSCA?");
1178         while (isspace(*s)) s++;
1179         if (!*s || !strcmp(s,"EMPTY"))
1180                 error(_("!No SMSC set in mobile station found, please use option \"-s\""));
1181         if (verbose>=1) error(_("\nFound default SMSC in mobile: %s"),s);
1182         if (*s++!='"') error(_("!No left-quote found in: %s"),s);
1183         if (!(t=strrchr(s,'"'))) error(_("!No right-quote found in: %s"),s);
1184         if (s==t)
1185                 error(_("!No SMS set in mobile station found, please use option \"-s\""));
1186         e=t++;
1187         while (isspace(*t)) t++;
1188         if (*t) {
1189 long l;
1190
1191                 if (*t++!=',') error(_("!No comma found after quotes in: %s"),s);
1192                 while (isspace(*t)) t++;
1193                 l=strtol(t,&serr,10);
1194                 if ((l!=ADDR_NAT && l!=ADDR_INT) || (serr && *serr))
1195                         error(_("!Type parse error in: %s"),s);
1196                 if (l==ADDR_INT && *s!='+') *--s='+';
1197                 }
1198         *e='\0';
1199         if (verbose>=2) error(_("\nDecoded SMSC address: %s"),s);
1200         if (!NEED_PDUSMSC()) return;
1201         chk(finalsmsc=strdup(s));
1202         bin[0]=1+(prepaddr(bin,finalsmsc)+1)/2;
1203         textconv(pdusmsc,bin,bin[0]+1);
1204 }
1205
1206 static inline unsigned char charconv_send(char c,size_t offs)
1207 {
1208         switch (c) {
1209                 case '@': return(0);
1210                 case '$': return(2);
1211                 case 0: assert(0);
1212                 default:
1213                         return(c&0x7F);
1214                 }
1215 #if 0
1216         if ((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9')) return(c);
1217         error(_("Can't convert character '%c' (0x%02X) at offs %d (0-based), substituted '?'"),
1218                 c,(unsigned char)c,offs);
1219         return('?');
1220 #endif
1221 }
1222
1223 static inline unsigned char charconv_recv(char c,size_t offs)
1224 { /* FIXME: unify with charconv_send() */
1225         switch (c) {
1226                 case 0: return('@');
1227                 case 2: return('$');
1228                 default:
1229                         return(c);
1230                 }
1231 /* strict checking not done, see charconv_send */
1232 }
1233
1234 /* Logo format shamelessly stolen from GNokii-0.3.0: http://www.gnokii.org/
1235  * Beware - Nokia Smart Messaging specification 1.0.0 and 2.0.0 is incompatible
1236  * with Nokia current product line implementation
1237  * http://www.forum.nokia.com/developers/smartmsg/download/ssm2_0_0.pdf
1238  */
1239
1240 static char *pdudata;
1241 static struct hexdata {
1242         struct hexdata *next;
1243         char data[140*2+1];
1244         } *hexdata,**hexdatatail=&hexdata;
1245
1246 static void nokiaprep(unsigned char *bin,size_t w)
1247 {
1248 struct hexdata *hd;
1249         assert(w<=140);
1250         chk(hd=malloc(sizeof(*hd)));
1251         *hexdatatail=hd;
1252         hd->next=NULL;
1253         hexdatatail=&hd->next;
1254         textconv(hd->data,bin,w);
1255         if (verbose>=2) error(_("\nWill send hexdata: %s"),hd->data);
1256 }
1257
1258 static inline void logoread(void)
1259 {
1260 FILE *f;
1261 char buf[32+140*8+1];
1262 unsigned char bin[140]={
1263         0x06, /* UDH length */
1264         0x05, /* IEI */
1265         0x04, /* IEDL */
1266         0x15, 0x83, /* dest port (group gfx) */
1267         0x15, 0x83  /* src port (unused) */
1268         };
1269 size_t got,r=0 /* GCC happiness */,w;
1270 ssize_t chars,bits;
1271 char gsmnetf[10];
1272 int sizex,sizey,bit;
1273
1274 #define WORD(n) (((unsigned char)buf[(n)])|(((unsigned char)buf[(n)+1])<<8))
1275
1276         if (!(f=fopen(logoname,"rb")))
1277                 error(_("^!Cannot open logo file \"%s\" for r/o"),logoname);
1278         got=fread(buf,1,sizeof(buf),f);
1279         chkfclose(f,logoname);
1280              if (got>=20 && !memcmp(buf,"NOL",4)) {
1281                 VARPRINTF2(gsmnetf,"%03.3u%02.2u",WORD(6),WORD(8));
1282                 assert(strlen(gsmnetf)==5);
1283                 r=10;
1284                 if (verbose>=1) error(_(".Reading NOL file \"%s\", GSMnet \"%s\", word@4=%d.."),
1285                         logoname,gsmnetf,WORD(4));
1286                 }
1287         else if (got>=16 && !memcmp(buf,"NGG",4)) {
1288                 r=6;
1289                 if (verbose>=1) error(_(".Reading NGG file \"%s\", word@4=%d.."),
1290                         logoname,WORD(4));
1291                 }
1292         else error(_("!Unknown file format of logo file \"%s\""),logoname);
1293         if (gsmnet && !strtrycasecmp(gsmnet,WORD_NET)) {
1294                 if (!*gsmnetf) error(_("!NOL network code detection requested but NOL file not loaded, please specify network code"));
1295                 gsmnet=gsmnetf;
1296                 }
1297         if (!gsmnet || !strtrycasecmp(gsmnet,WORD_GROUP) || !*gsmnet) {
1298                 error(_("\nSending logo as: group graphics"));
1299                 gsmnet=NULL;
1300                 }
1301         else {
1302                 error(_("\nSending logo as: operator logo for \"%s\""),gsmnet);
1303                 bin[4]=0x82; /* dest port 0x1582 */
1304                 }
1305         
1306         sizex=WORD(r); sizey=WORD(r+2);
1307         if (verbose>=2) error(_(".Magic words: @+4=%d, @+6=%d, @+8=%d"),
1308                         WORD(r+4),WORD(r+6),WORD(r+8));
1309         r+=10;
1310         if (sizex<1 || sizex>255
1311          || sizey<1 || sizey>255) error(_("!Invalid size: %dx%d"),sizex,sizey);
1312         chars=((bits=sizex*sizey)+7)/8;
1313         if (r+bits>got) error(_("!Logo file \"%s\" too short - actual=%d, need(%dx%d)=%d"),
1314                 logoname,got,sizex,sizey,r+chars);
1315         else if (r+bits<got)
1316                 if (verbose>=1) error(_("Ignoring trailing garbage in \"%s\", used only %d bytes"),logoname,r+bits);
1317         if ((got=(7+(gsmnet?3:0)+4+chars))>140)
1318                 error(_("!SMS size would be %d bytes but 140 is maximum"),got);
1319         w=7;
1320         if (gsmnet) {
1321                 bin[w++]=((gsmnet[1]&0x0F)<<4)|(gsmnet[0]&0x0F);
1322                 bin[w++]=0xF0                 |(gsmnet[2]&0x0F);
1323                 bin[w++]=((gsmnet[4]&0x0F)<<4)|(gsmnet[3]&0x0F);
1324                 }
1325         bin[w++]=0x00; /* RFU by Nokia */
1326         bin[w++]=sizex; bin[w++]=sizey;
1327         bin[w++]=0x01; /* one B/W plane */
1328         while (chars--) {
1329                 bin[w]=0;
1330                 for (bit=0x80;(bits>0) && (bit>0);bits--,bit>>=1) {
1331                         if (buf[r]!='0' && buf[r]!='1')
1332                                 error(_("!Invalid character (neither '0' nor '1') in logo file \"%s\" at offset 0x%X"),
1333                                         logoname,r);
1334                         if (buf[r++]=='1') bin[w]|=bit;
1335                         }
1336                 w++;
1337                 }
1338         assert(chars==-1); assert(bits==0); assert(w==got);
1339         nokiaprep(bin,w);
1340 #undef WORD
1341 }
1342
1343 static inline void ringread(void)
1344 {
1345 FILE *f;
1346 unsigned char bin1[140]={
1347         6, /* UDH length */
1348         0x05, /* IEI */
1349         0x04, /* IEDL */
1350         0x15, 0x81, /* dest port (ring tones) */
1351         0x15, 0x81  /* src port (unused) */
1352 #define BIN1_PAYLOAD (140-7)
1353         };
1354 unsigned char binn[140]={
1355         11, /* UDH length */
1356         0x05, /* IEI */
1357         0x04, /* IEDL */
1358         0x15, 0x81, /* dest port (ring tones) */
1359         0x15, 0x81, /* src port (unused) */
1360         0x00, 0x03, /* multipart */
1361         /* 0x??, unique serial ID */
1362         /* 0x??, total messages */
1363         /* 0x??, message number (# from 1) */
1364 #define BINN_PAYLOAD (140-12)
1365         };
1366 size_t got,want;
1367 int totn,fragn;
1368 long size;
1369
1370 #define WORD(n) (((unsigned char)buf[(n)])|(((unsigned char)buf[(n)+1])<<8))
1371
1372         if (!(f=fopen(ringname,"rb")))
1373                 error(_("^!Cannot open ring file \"%s\" for r/o"),ringname);
1374         if ((size=getfilesize(f,ringname))==-1)
1375                 error(_("!File size determination is essential to continue operation"));
1376         if (size<0x103)
1377                 error(_("!File \"%s\" size %ld too small (must >=0x103)! Is it .000 file?"),
1378                         ringname,size);
1379         if (fseek(f,0x100,SEEK_SET))
1380                 error(_("^Seeking error on \"%s\", ignoring"),ringname);
1381         size-=0x100;
1382         if (size<=BIN1_PAYLOAD) {
1383                 if ((got=fread(bin1+7,1,size,f))!=size)
1384                         error(_("^Read error on \"%s\", wanted %ld, got %d"),ringname,size,got);
1385                 error(_("\nSending ring tone \"%s\" as single SMS (size %ld, max %d)"),
1386                         ringname,size,BIN1_PAYLOAD);
1387                 nokiaprep(bin1,7+size);
1388                 }
1389         else {
1390                 totn=(size+BINN_PAYLOAD-1)/BINN_PAYLOAD;
1391                 if (totn>0xFF)
1392                         error(_("!File size %ld too large even for multi-SMS ring upload (max=%d)"),
1393                                 size,BINN_PAYLOAD*0xFF);
1394                 binn[10]=totn;
1395                 if (verbose>=1)
1396                         error(_("\nSending ring tone \"%s\" as %d multi-SMSes (size %ld, max %d, frag %d)"),
1397                                 ringname,totn,size,BIN1_PAYLOAD,BINN_PAYLOAD);
1398                 binn[9]=time(NULL)&0x100; /* rand() would be better but it is a compatibility pain */
1399                 if (verbose>=1)
1400                         error(_("\nUsing unique multi-SMS ID 0x%02X"),(unsigned)binn[9]);
1401                 for (fragn=1;fragn<=totn;fragn++) {
1402                         binn[11]=fragn;
1403                         want=MIN(size,BINN_PAYLOAD);
1404                         if ((got=fread(binn+12,1,want,f))!=want)
1405                                 error(_("^Read error on \"%s\", wanted %d, got %d"),ringname,want,got);
1406                         nokiaprep(binn,12+want);
1407                         size-=want;
1408                         }
1409                 }
1410         chkfclose(f,ringname);
1411 #undef WORD
1412 }
1413
1414 #define PICTURE_WIDTH  (72)
1415 #define PICTURE_HEIGHT (28)
1416
1417 static inline void pictureread(void)
1418 {
1419 FILE *f;
1420 unsigned char bin1[140]={
1421         6, /* UDH length */
1422         0x05, /* IEI */
1423         0x04, /* IEDL */
1424         0x15, 0x8A, /* dest port (ring tones) */
1425         0x15, 0x8A  /* src port (unused) */
1426 #define BIN1_PAYLOAD (140-7)
1427         };
1428 unsigned char binn[140]={
1429         11, /* UDH length */
1430         0x05, /* IEI */
1431         0x04, /* IEDL */
1432         0x15, 0x8A, /* dest port (ring tones) */
1433         0x15, 0x8A, /* src port (unused) */
1434         0x00, 0x03, /* multipart */
1435         /* 0x??, unique serial ID */
1436         /* 0x??, total messages */
1437         /* 0x??, message number (# from 1) */
1438 #define BINN_PAYLOAD (140-12)
1439         };
1440 unsigned char header[]={
1441         0x30,   /* version string '0' */
1442         0x02,   /* item=OTA bitmap */
1443 #define PICTURE_BYTES ((PICTURE_WIDTH*PICTURE_HEIGHT+7)/8)
1444 #define PICTURE_BYTES_INCL_HEADER (PICTURE_BYTES +4/*header*/)
1445         PICTURE_BYTES_INCL_HEADER>>8,PICTURE_BYTES_INCL_HEADER&0xFF,    /* picture size in bytes incl. header */
1446         0x00, /* animation pictures - 0=static picture */
1447         PICTURE_WIDTH,PICTURE_HEIGHT,   /* picture size in pixels */
1448         0x01,   /* picture depth - B/W */
1449         };
1450 size_t got,want;
1451 int totn,fragn;
1452 long size;
1453
1454 #define WORD(n) (((unsigned char)buf[(n)])|(((unsigned char)buf[(n)+1])<<8))
1455
1456         if (!(f=fopen(picturename,"rb")))
1457                 error(_("^!Cannot open picture file \"%s\" for r/o"),picturename);
1458         if ((size=getfilesize(f,picturename))==-1)
1459                 error(_("!File size determination is essential to continue operation"));
1460         if (size!=PICTURE_BYTES)
1461                 error(_("!File \"%s\" size %ld doesn't match .res size for %dx%d picture"),
1462                         picturename,size,PICTURE_WIDTH,PICTURE_HEIGHT);
1463         if (size<=BIN1_PAYLOAD-sizeof(header)) {
1464                 memcpy(bin1+7,header,sizeof(header));
1465                 if ((got=fread(bin1+7+sizeof(header),1,size,f))!=size)
1466                         error(_("^Read error on \"%s\", wanted %ld, got %d"),picturename,size,got);
1467                 error(_("\nSending picture \"%s\" as single SMS (size %ld, max %d)"),
1468                         picturename,size,BIN1_PAYLOAD-sizeof(header));
1469                 nokiaprep(bin1,7+sizeof(header)+size);
1470                 }
1471         else {
1472                 memcpy(binn+12,header,sizeof(header));
1473                 totn=(sizeof(header)+size+BINN_PAYLOAD-1)/BINN_PAYLOAD;
1474                 if (totn>0xFF)
1475                         error(_("!File size %ld too large even for multi-SMS picture upload (max=%d)"),
1476                                 size,BINN_PAYLOAD*0xFF-sizeof(header));
1477                 binn[10]=totn;
1478                 if (verbose>=1)
1479                         error(_("\nSending picture \"%s\" as %d multi-SMSes (size %ld, max %d, frag %d, header %d)"),
1480                                 picturename,totn,size,BIN1_PAYLOAD,BINN_PAYLOAD,sizeof(header));
1481                 binn[9]=time(NULL)&0x100; /* rand() would be better but it is a compatibility pain */
1482                 if (verbose>=1)
1483                         error(_("\nUsing unique multi-SMS ID 0x%02X"),(unsigned)binn[9]);
1484                 for (fragn=1;fragn<=totn;fragn++) {
1485 size_t isheader=(fragn==1 ? sizeof(header) : 0);
1486
1487                         binn[11]=fragn;
1488                         want=MIN(size,BINN_PAYLOAD-isheader);
1489                         if ((got=fread(binn+12+isheader,1,want,f))!=want)
1490                                 error(_("^Read error on \"%s\", wanted %d, got %d"),picturename,want,got);
1491                         nokiaprep(binn,12+isheader+want);
1492                         size-=want;
1493                         }
1494                 }
1495         chkfclose(f,picturename);
1496 #undef WORD
1497 }
1498
1499 static inline void genpdu(void)
1500 {
1501 static unsigned char pdu[64+MAXNUMLEN/2+(MAXBODYLEN*7)/8];
1502 unsigned char *d=pdu;
1503 int i;
1504 char inb=0,outb=0,xb,*bodyr;
1505 unsigned char inreg=0 /* GCC happiness */;
1506 size_t offs=0;
1507
1508         *d++=PDU_TYPE;
1509         *d++=PDU_MR;
1510         i=prepaddr(d,phone);
1511         *d=i; d+=1+1+(i+1)/2;
1512         *d++=PDU_PID;
1513         *d++=PDU_DCS;
1514         *d++=PDU_VP;
1515         if (bodylen>MAXBODYLEN) {
1516                 error(_("Body too large (%d>%d), cut"),bodylen,MAXBODYLEN);
1517                 body[(bodylen=MAXBODYLEN)]='\0';
1518                 }
1519         bodyr=body;
1520         *d=bodylen;
1521         assert(d<pdu+sizeof(pdu));
1522         while (bodylen || inb) {
1523                 if (!inb) {
1524                         assert(bodylen>0); assert(!!*body);
1525                         inreg=charconv_send(*bodyr++,offs++);
1526                         bodylen--;
1527                         inb=7;
1528                         }
1529                 if (!outb) {
1530                         *++d=0x00;
1531                         outb=8;
1532                         }
1533                 xb=MIN(inb,outb);
1534 #if 0
1535                 d4("inb=%d,outb=%d,xb=%d\n",inb,outb,xb);
1536 #endif
1537                 *d|=((inreg>>(unsigned)(7-inb))&((1<<xb)-1))<<(unsigned)(8-outb);
1538                 inb-=xb; outb-=xb;
1539                 }
1540         d++;
1541         assert(d<pdu+sizeof(pdu));
1542         pdudata=malloc(2*(d-pdu)+1);
1543         textconv(pdudata,pdu,d-pdu);
1544 }
1545
1546 static inline void preparebody(void)
1547 {
1548 FILE *fin=NULL /* GCC happiness */;
1549 char *finame;
1550
1551         if (body && readbody) {
1552                 finame=body;
1553                 body=NULL;
1554                 }
1555         else {
1556                 finame=NULL;
1557                 fin=stdin;
1558                 }
1559         if (body) return;
1560         readbody=0;
1561         if (!finame) {
1562                 if (verbose>=1)
1563                         error(_("\nPlease enter the SMS text body, end with EOF (ctrl-D):"));
1564                 }
1565         else {
1566                 if (!(fin=fopen(finame,"rt")))
1567                         error(_("^!Can't open data file \"%s\" for r/o"),finame);
1568                 }
1569         chk(body=malloc(BODYLOAD));
1570         bodylen=fread(body,1,BODYLOAD,fin);
1571         if (bodylen==-1)
1572                 error(_("^!Error reading stream \"%s\""),(finame?finame:_("<stdin>")));
1573         if (finame) {
1574                 chkfclose(fin,finame);
1575                 free(finame);
1576                 }
1577 }
1578
1579 static int datawait(int timeout)
1580 {
1581 int i;
1582 #ifdef HAVE_POLL
1583 struct pollfd ufd;
1584 #else /* HAVE_POLL */
1585 fd_set rfds,xfds;
1586 #endif /* HAVE_POLL */
1587
1588         assert(devfd>=0);
1589 retry:
1590         if (timeout && verbose>=2)
1591                 error(_(".Waiting for device incoming data.."));
1592 #ifdef HAVE_POLL
1593         ufd.fd=devfd;
1594         ufd.events=POLLIN;
1595         ufd.revents=0;
1596         errno=0;
1597         i=poll(&ufd,1,timeout*1000);
1598 #else /* HAVE_POLL */
1599 #ifdef HAVE_FD_SETSIZE
1600         if (devfd>=FD_SETSIZE)
1601                 error(_("!Device file descriptor %d can't fit in select() FD_SETSIZE (%d)"),
1602                         devfd,FD_SETSIZE);
1603 #endif /* HAVE_FD_SETSIZE */
1604         FD_ZERO(&rfds); FD_SET(devfd,&rfds);
1605         FD_ZERO(&xfds); FD_SET(devfd,&xfds);
1606         errno=0;
1607         i=select(devfd+1,&rfds,NULL,&xfds,NULL);
1608 #endif /* HAVE_POLL */
1609         if (i==0) return(0);
1610         if (i==-1 && errno==EINTR)
1611                 goto retry; /* silent retry, for example SIGCHLD could occur */
1612         if (i!=1)
1613                 error(_("^Failed (retval %d) while waiting for data, ignoring"),i);
1614
1615 #ifdef HAVE_POLL
1616         if (ufd.revents&(POLLERR|POLLHUP))
1617 #else /* HAVE_POLL */
1618         if (FD_ISSET(devfd,&xfds))
1619 #endif /* HAVE_POLL */
1620                 error(_("^Error while waiting for data, ignoring"));
1621
1622 #ifdef HAVE_POLL
1623         if (!(ufd.revents&POLLIN))
1624 #else /* HAVE_POLL */
1625         if (!(FD_ISSET(devfd,&rfds)))
1626 #endif /* HAVE_POLL */
1627                 {
1628                 error(_("^No data input after waited for data, retrying"));
1629                 goto retry;
1630                 }
1631         return(1);
1632 }
1633
1634 static char *check_format(const char *fmt,const char *string)
1635 {
1636 static char err[LINE_MAX],sub[50];
1637 char cf,cs;
1638 const char *sf,*ss,*subp;
1639
1640         for (sf=fmt,ss=string;(cf=*sf) && (cs=*ss);sf++,ss++) {
1641                 subp=NULL;
1642                 switch (cf) {
1643                         case '?':
1644                                 break;
1645                         case '9':
1646                                 if (isdigit(cs)) break;
1647                                 subp=_("digit");
1648                                 break;
1649                         case '+':
1650                                 if (cs=='+' || cs=='-') break;
1651                                 subp=_("+/- sign");
1652                                 break;
1653                         default:
1654                                 if (cf==cs) break;
1655                                 VARPRINTF(sub,"'%c'",cf); subp=sub;
1656                         }
1657                 if (!subp) continue;
1658                 VARPRINTF5(err,_("Expected %s, found '%c' at pos %d of string [%s], formatstring [%s]"),
1659                         subp,cs,ss-string,string,fmt);
1660                 return(err);
1661                 }
1662         if (*sf) {
1663                 VARPRINTF2(err,_("String too short for format, string [%s], formatstring [%s]"),
1664                         string,fmt);
1665                 return(err);
1666                 }
1667         if (*ss) {
1668                 VARPRINTF2(err,_("Trailing garbage in string [%s], formatstring [%s]"),
1669                         string,fmt);
1670                 return(err);
1671                 }
1672         return(NULL);
1673 }
1674
1675 static char *receive_number,*receive_smsc;
1676 static time_t receive_time;
1677
1678 struct tm tm;
1679 static const struct {
1680         off_t strpos;
1681         off_t tmpos;
1682         int min,max;
1683         const char *name;
1684         } timeparse[]={
1685 #define TP_ENT(a,b,c,d,e) { a,offsetof(struct tm,b),c,d,e }
1686                 TP_ENT( 3,tm_year,0,99,N_("year")),
1687                 TP_ENT( 6,tm_mon ,1,12,N_("month")),
1688                 TP_ENT( 9,tm_mday,1,31,N_("day of month")),
1689                 TP_ENT(12,tm_hour,0,23,N_("hour")),
1690                 TP_ENT(15,tm_min ,0,59,N_("minute")),
1691                 TP_ENT(18,tm_sec ,0,59,N_("second")),
1692                 /* Time zone ignored */
1693                 };
1694 #define GETTIME(i) (*(int *)(((char *)&tm)+timeparse[(i)].tmpos))
1695
1696 static void maketime(const char *string)
1697 {
1698 int val;
1699 int i;
1700
1701         for (i=0;i<NELEM(timeparse);i++) {
1702                 val=GETTIME(i);
1703                 if (val<timeparse[i].min || val>timeparse[i].max) {
1704                         error(_("Weird value of %s, is %d but expected %d..%d, setting to %d"),
1705                                 _(timeparse[i].name),val,timeparse[i].min,timeparse[i].max,timeparse[i].min);
1706                         GETTIME(i)=timeparse[i].min;
1707                         }
1708                 }
1709         if (tm.tm_year<70) tm.tm_year+=100;
1710         tm.tm_mon--;
1711         d7("mktime(y%dm%dd%dh%dm%ds%d)\n",
1712                 tm.tm_year,tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
1713         tm.tm_isdst=-1; /* "timezone" info not available */
1714         if ((receive_time=mktime(&tm))==-1)
1715                 error(_("^mktime(3) failed for %s"),string);
1716 }
1717
1718 /* +CMT: "+420602431329",,"99/10/25,03:21:03-00" */
1719 static int receive_headerparse(char *buf)
1720 {
1721 char *s,*s1,*err;
1722 int i;
1723
1724 #define DIGIT2ASC(s) (((s)[0]-'0')*10+((s)[1]-'0'))
1725
1726         for (s=buf;*s==' ';s++);
1727         if (*s++!='"') {
1728                 error(_("Cannot find initial '\"' in CMT header: %s"),buf);
1729                 return(0);
1730                 }
1731         for (s1=s;*s && *s!='"';s++);
1732         if (!*s) {
1733                 error(_("Only one '\"' found in CMT header: %s"),buf);
1734                 return(0);
1735                 }
1736         free(receive_smsc); receive_smsc=NULL;
1737         free(receive_number);
1738         chk(receive_number=malloc(s-s1+1));
1739         memcpy(receive_number,s1,s-s1); receive_number[s-s1]='\0';
1740         s++;
1741         if ((err=check_phone(receive_number)) ||
1742             (err=check_format(",,\"99/99/99,99:99:99+99\"",s))) {
1743                 error(_("%s in CMT header: %s"),err,buf);
1744                 return(0);
1745                 }
1746         memset(&tm,0,sizeof(tm)); /* may be redundant */
1747         for (i=0;i<NELEM(timeparse);i++)
1748                 GETTIME(i)=DIGIT2ASC(s+timeparse[i].strpos);
1749         maketime(s+2);
1750         return(1);
1751 #undef DIGIT2ASC
1752 }
1753
1754 static void signal_chld(int signo)
1755 {
1756 int status;
1757 pid_t pid;
1758
1759         signal(SIGCHLD,(RETSIGTYPE (*)(int))signal_chld);
1760         /* we don't care about siginterrupt(3) as it doesn't matter how it is set */
1761
1762         d2("signal_chld: signo=%d\n",signo);
1763         while (0<(pid=waitpid(-1 /* ANY process */,&status,WNOHANG /* options */))) {
1764                 if (verbose>=2)
1765                         error(_(".Child process w/PID %d has exited, %s, status=%d"),
1766                                         pid,(WIFEXITED(status)? _("normally") : _("abnormally")),(WIFEXITED(status) ? WEXITSTATUS(status) : -1));
1767                 }
1768 }
1769
1770 static void receive_text(char *bodyline)
1771 {
1772 char *buf,*s,*s1,*s2,*s3;
1773 pid_t pid;
1774 char tbuf[32];
1775 int i;
1776 FILE *f;
1777
1778         d2("receive_text: %s\n",bodyline);
1779         signal(SIGCHLD,(RETSIGTYPE (*)(int))signal_chld);
1780 #if RECEIVE_TEST
1781         pid=0;
1782 #else
1783         pid=fork();
1784 #endif
1785         if (pid>0) {
1786                 if (verbose>=2)
1787                         error(_(".Spawned child receive-SMS process w/PID %d"),pid);
1788                 return; /* parent context */
1789                 }
1790         if (pid==-1) {
1791                 error(_("Can't fork(2), process spawning may block receive"));
1792                 }
1793         else { /* child process */
1794                 dis_cleanup=1;
1795                 }
1796         for (s=body;*s;) {
1797                 s1=s;
1798                 do {
1799                         s1=strchr(s1+(s1!=s),'%');
1800                         } while (s1 && s1[1]!='p' && s1[1]!='T' && s1[1]!='t' && s1[1]!='s');
1801                 if (!s1) {
1802                         pushargstack_one(s,0);
1803                         break;
1804                         }
1805                 *s1='\0';
1806                 pushargstack_one(s,0);
1807                 *s1++='%';
1808                 s=s1;
1809                 switch (*s++) {
1810                         case 'p':
1811                                 pushargstack_one(receive_number,0);
1812                                 break;
1813                         case 'T':
1814                                 VARPRINTF(tbuf,"%ld",receive_time);
1815                                 pushargstack_one(tbuf,0);
1816                                 break;
1817                         case 't':
1818                                 if (receive_time==-1) break;
1819                                 if (!(s2=ctime(&receive_time))) {
1820                                         error(_("Failing ctime(3), ignoring substitution"));
1821                                         break;
1822                                         }
1823                                 if ((s3=strchr(s2,'\n'))) *s3='\0';
1824                                 pushargstack_one(s2,0);
1825                                 break;
1826                         case 's':
1827                                 if (receive_smsc) pushargstack_one(receive_smsc,0);
1828                                 break;
1829                         default: assert(0);
1830                         }
1831                 }
1832         buf=glueargstack(NULL,NULL); assert(buf);
1833         if (!(f=popen(buf,"w"))) {
1834                 error(_("^Failing spawn of receive command: %s"),buf);
1835                 goto err;
1836                 }
1837         if (fputs(bodyline,f)<0 || putc('\n',f)!='\n')
1838                 error(_("^Failing write to child receive command: %s"),buf);
1839         if ((i=pclose(f)))
1840                 error(_("^Spawned receive command failure (code %d): %s"),i,buf);
1841 err:
1842         free(buf);
1843         if (pid==-1) return;
1844         exit(EXIT_SUCCESS); /* cleanup() has been disabled */
1845 }
1846
1847 static inline unsigned char fromhex(unsigned c)
1848 {
1849         c&=0xDF;
1850         return(c<'A'?c-('0'&0xDF):(c-('A'&0xDF))+0xA);
1851 }
1852
1853 static int teldecode(char *text,unsigned char *bin,size_t digits)
1854 {
1855 unsigned char b;
1856 int r=0,i;
1857
1858         for (i=0;i<digits;text++,i++) {
1859                 if (!(i&1)) b=*bin;
1860                 else b=(*bin++)>>4;
1861                 b&=0x0F;
1862                 if (b<=0x09)
1863                         *text=b+'0';
1864                 else {
1865                         *text='?';
1866                         r++;
1867                         }
1868                 }
1869         *text='\0';
1870         return(r);
1871 }
1872
1873 static void sctsparse(unsigned char *bin,const char *pduline,int offs)
1874 {
1875 #define DIGIT2BIN(v) (((v)&0x0F)*10+(((v)>>4)&0x0F))
1876 int i;
1877
1878         receive_time=-1;
1879         memset(&tm,0,sizeof(tm)); /* may be redundant */
1880         for (i=0;i<NELEM(timeparse);offs++,i++) {
1881                 if ((*bin&0x0F)>0x09 || (*bin&0xF0)>0x90) {
1882                         error(_("Invalid value of \"%s\" at offset %d in: %s"),
1883                                 timeparse[i].name,offs,pduline);
1884                         return;
1885                         }
1886                 GETTIME(i)=DIGIT2BIN(*bin);
1887                 bin++;
1888                 }
1889         maketime(pduline);
1890
1891 #undef DIGIT2BIN
1892 }
1893
1894 static void receive_pdu(char *pduline)
1895 {
1896 unsigned char pdu[140+0x100],*pdup,*pdue,oalen,inreg;
1897 char text[160+1],*textp,*s,*pdulinescan;
1898 size_t pdulinel=strlen(pduline),want;
1899 size_t udl,udlb;
1900 int inb,outb,xb;
1901
1902         d2("receive_pdu: %s\n",pduline);
1903         if (pdulinel>2*sizeof(pdu))
1904                 { error(_("PDU too long (%d/2) to be valid: %s"),pdulinel,pduline); return; }
1905         if (pdulinel&1)
1906                 { error(_("PDU length odd (%d): %s"),pdulinel,pduline); return; }
1907         if (pdulinel<2*13)
1908                 { error(_("PDU length %d too small (min. 2*%d): %s"),pdulinel,13,pduline); return; }
1909         for (pdup=pdu,pdulinescan=pduline;*pdulinescan;pdulinescan+=2) {
1910                 if (!isxdigit(pdulinescan[0]) || !(isxdigit(pdulinescan[1])))
1911                 { error(_("Invalid hex byte: %c%c on byte %d in: %s"),
1912                         pdulinescan[0],pdulinescan[1],pdup-pdu,pduline); return; }
1913                 *pdup++=(fromhex(pdulinescan[0])<<4)|fromhex(pdulinescan[1]);
1914                 }
1915         pdue=pdup;
1916         free(receive_smsc);
1917         if (*pdu<=1) {
1918                 receive_smsc=NULL;
1919                 }
1920         else {
1921                 if (*pdu>10)
1922                         { error(_("SMSC length too large (%d, max. %d): %s"),*pdu,10,pduline); return; }
1923                 chk(receive_smsc=malloc(1+2*(*pdu)+1));
1924                 s=receive_smsc;
1925                 if (pdu[1]==ADDR_INT) *s++='+';
1926                 else {
1927                         if (pdu[1]!=ADDR_NAT)
1928                                 error(_("Unknown address type 0x%02X of %s, ignoring in PDU: %s"),pdu[1],_("SMSC"),pduline); return;
1929                         }
1930                 if (teldecode(s,pdu+2,2*(*pdu-1)-((pdu[1+(*pdu)]&0xF0)==0xF0)))
1931                         error(_("Some digits unrecognized in %s \"%s\", ignoring in PDU: %s"),_("SMSC"),receive_smsc,pduline);
1932                 }
1933         pdup=pdu+1+(*pdu);
1934         if (*pdup&0x03) /* PDU type */
1935                 error(_("Unrecognized PDU type 0x%02X at offset %d, dropping: %s"),*pdup,pdup-pdu,pduline);
1936         pdup++;
1937         free(receive_number);
1938         if ((oalen=*pdup++)>2*0x10) /* OA len */
1939                 { error(_("Originating number too large (0x%X, max. 2*0x%X): %s"),oalen,0x10,pduline); return; }
1940         if (pdup+(want=1+(oalen+1)/2+10)>pdue)
1941                 { error(_("PDU length too short (want %d, is %d): %s"),(pdup-pdu)+want,pdue-pdu,pduline); return; }
1942         chk(receive_number=malloc(1+2*(*pdup)+1));
1943         s=receive_number;
1944         if (*pdup==ADDR_INT) *s++='+';
1945         else {
1946                 if (*pdup!=ADDR_NAT)
1947                         error(_("Unknown address type 0x%02X of %s, ignoring in PDU: %s"),*pdup,_("originating number"),pduline); return;
1948                 }
1949         pdup++;
1950         if (teldecode(s,pdup,oalen))
1951                 error(_("Some digits unrecognized in %s \"%s\", ignoring in PDU at offset %d: %s"),
1952                         _("originating number"),receive_number,pdup-pdu,pduline);
1953         pdup+=(oalen+1)/2;
1954         if (*pdup) /* PID */
1955                 error(_("PID number %02X unsupported, ignoring: %s"),*pdup,pduline);
1956         pdup++;
1957         if (*pdup) { /* DCS */
1958                 if ((*pdup&0xF4)==0xF4)
1959                         { error(_("DCS 0x%02X indicates 8-bit data, unsupported, dropping: %s"),*pdup,pduline); return; }
1960                 error(_("DCS 0x%02X unsupported, will attempt decoding: %s"),*pdup,pduline);
1961                 }
1962         pdup++;
1963         sctsparse(pdup,pduline,pdup-pdu);
1964         pdup+=7;
1965         /* UDL */
1966         udl=*pdup++;
1967         if (pdue-pdup>140) {
1968                 error(_("PDU data (%d) exceed maximum length of %d bytes, cut: %s"),
1969                         pdue-pdup,140,pduline);
1970                 pdue=pdup+140;
1971                 }
1972         udlb=(udl*7+7)/8;
1973         if (pdup+udlb>pdue) {
1974 size_t udl1,udlb1;
1975
1976                 udlb1=pdue-pdup;
1977                 udl1=(udlb1*8)/7;
1978                 error(_("PDU data length (%d/7->%d/8) longer than data (%d), cut to %d/7->%d/8: %s"),
1979                         udl,udlb,pdue-pdup,udl1,udlb1,pduline);
1980                 udl=udl1; udlb=udlb1;
1981                 }
1982         else
1983                 assert(pdup+udlb==pdue); /* should be checked by 'PDU length too short' above */
1984         textp=text;
1985         inb=outb=0;
1986         inreg=0; /* GCC happiness */
1987         while (udl) {
1988                 if (!inb) {
1989                         inreg=*pdup++;
1990                         inb=8;
1991                         }
1992                 if (!outb) {
1993                         assert(textp<text+160);
1994                         *textp=0x00;
1995                         outb=7;
1996                         }
1997                 xb=MIN(inb,outb);
1998 #if 0
1999                 d4("inb=%d,outb=%d,xb=%d\n",inb,outb,xb);
2000 #endif
2001                 *textp|=((inreg>>(unsigned)(8-inb))&((1<<xb)-1))<<(unsigned)(7-outb);
2002                 inb-=xb; outb-=xb;
2003                 if (!outb) {
2004                         *textp=charconv_recv(*textp,textp-text);
2005                         textp++;
2006                         udl--;
2007                         }
2008                 }
2009         *textp=0;
2010         receive_text(text);
2011 }
2012
2013 static struct {
2014         char **sp;
2015         long *ip;
2016         const char *const msg;
2017         } numarg[]={
2018                 { &maxretry,&maxretryn,"maxretry" },
2019                 { &readtime,&readtimen,"readtime" },
2020                 { &chartime,&chartimen,"chartime" },
2021                 { &cmdtime ,&cmdtimen ,"cmdtime"  },
2022                 { &waittime,&waittimen,"waittime" },
2023                 { &baud    ,&baudn    ,"baud"     },
2024         };
2025
2026 int main(int argc,char **argv)
2027 {
2028 char *s;
2029 int i,cmgf=-1 /* GCC happiness */;
2030 unsigned fatal=0;
2031 speed_t portbaud=0 /* GCC happiness */;
2032 enum modenum argsmode;
2033 unsigned parts=0;
2034
2035         if ((s=strrchr((pname=*argv),'/'))) pname=s+1;
2036
2037 #ifdef ENABLE_NLS
2038         /* Initialize the i18n stuff */
2039         bindtextdomain(PACKAGE,LOCALEDIR);
2040         if (!setlocale(LC_ALL,""))
2041                 error("Locale not supported by C library");
2042         textdomain(PACKAGE);
2043 #endif
2044
2045 #ifdef HAVE_ATEXIT
2046         atexit(cleanup);
2047 #else
2048         error(_("atexit(3) not available at compilation time, device cleanup may be missed"));
2049 #endif
2050         signal(SIGTERM,(RETSIGTYPE (*)(int))cleanup);
2051         signal(SIGQUIT,(RETSIGTYPE (*)(int))cleanup);
2052         signal(SIGINT ,(RETSIGTYPE (*)(int))cleanup);
2053         signal(SIGHUP ,(RETSIGTYPE (*)(int))cleanup);
2054         assert(mode==MODE_UNKNOWN);
2055         for (i=0;i<NELEM(longopts);i++) {
2056                 if (longopts[i].val<MODE_FIRST) break;
2057                 if (!strstr(pname,longopts[i].name)) continue;
2058                 if (mode==MODE_UNKNOWN || mode==longopts[i].val) {
2059                         mode=longopts[i].val;
2060                         continue;
2061                         }
2062                 mode=MODE_UNKNOWN;
2063                 break;
2064                 }
2065         argsmode=mode;
2066         processargs(argc,argv,_("<command-line>"));
2067         if ((s=getenv("HOME"))) {
2068 size_t l=strlen(s);
2069 char *buf=malloc(l+50);
2070
2071                 memcpy(buf,s,l);
2072                 strcpy(buf+l,CONFIG_HOME);
2073                 readfile(buf,1);
2074                 free(buf);
2075                 }
2076         readfile(CONFIG_MAIN,1);
2077         if (verbose>=1) {
2078                 if (argsmode)
2079                         error(_(".Detected mode \"%s\" from my program name \"%s\""),MODE_NAME(argsmode),pname);
2080                 else
2081                         error(_(".Automatic mode detection unsuccessul for my progam name \"%s\""),pname);
2082                 }
2083
2084         if (!mode)
2085                 error(_("!Operation mode unset, use --send or similiar command, see help (-h)"));
2086         error(_(".Running program in mode \"%s\""),MODE_NAME(mode));
2087         switch (mode) {
2088
2089                 case MODE_SEND:           /* FALLTHRU */
2090                 case MODE_SEND_MOBILDOCK: cmdline_send        (); break;
2091                 case MODE_LOGO_SEND:      cmdline_logo_send   (); break;
2092                 case MODE_RING_SEND:      cmdline_ring_send   (); break;
2093                 case MODE_PICTURE_SEND:   cmdline_picture_send(); break;
2094                 case MODE_RECEIVE:        cmdline_receive     (); break;
2095                 default: assert(0);
2096                 }
2097         cmdline_done();
2098         for (i=0;i<NELEM(nullcheck);i++) {
2099 const struct nullcheck *n=nullcheck+i;
2100
2101                 if (*n->var) continue;
2102                 if (n->reqd && !(MODE_BIT(mode)&n->reqd)) continue;
2103                 error(_("Missing parameter \"%s\""),_(n->name));
2104                 fatal++;
2105                 }
2106         if (fatal) error(_("!Previous %s considered unrecoverable"),(fatal==1?_("error"):_("errors")));
2107         emptyclean();
2108         if (!logname) logname=DEF_LOGNAME;
2109         if (!lockfile) lockfile=DEF_LOCKFILE;
2110         if (!device) device=DEF_DEVICE;
2111
2112         for (i=0;i<NELEM(numarg);i++) {
2113 char *serr;
2114                 if (!*numarg[i].sp) continue;
2115                 *numarg[i].ip=strtol(*numarg[i].sp,&serr,0);
2116                 if (*numarg[i].ip<0 || *numarg[i].ip>=LONG_MAX || !serr || *serr)
2117                         error(_("!Number parse error for parameter \"%s\" of \"%s\" at: %s"),
2118                                 numarg[i].msg,*numarg[i].sp,serr);
2119                 }
2120         if (readtimen==-1)
2121                 readtimen=(mode==MODE_SEND_MOBILDOCK?DEF_READTIME_MOBILDOCK:DEF_READTIME);
2122         if (mode==MODE_SEND_MOBILDOCK) mode=MODE_SEND;
2123
2124         if (!strchr(device,'/')) {
2125 size_t l=strlen(device);
2126                 chk(s=malloc(5+l+1));
2127                 strcpy(s,"/dev/");
2128                 strcpy(s+5,device);
2129                 free(device);
2130                 device=s;
2131                 }
2132         devicename=strrchr(device,'/')+1; assert(!!(devicename-1));
2133         for (i=0,s=lockfile;*s;s++) {
2134                 if (*s!='%') continue;
2135                 s++;
2136                 if (*s=='%') continue;
2137                 if (*s=='s') {
2138                         if (i) error(_("!Only one \"%%s\" permitted in lockfile format-string"));
2139                         i=1; continue;
2140                         }
2141                 error(_("!Invalid format-character '%c' in lockfile format-string, only \"%%s\" allowed"),*s);
2142                 }
2143         
2144         if (*logname) {
2145                 if (!(logf=fopen(logname,"a")))
2146                         error(_("^!Error opening log \"%s\" for append"),logname);
2147                 logmsg(_("Starting up: %s"),PACKAGE " " VERSION);
2148                 }
2149
2150         switch (mode) {
2151                 case MODE_SEND:
2152                         preparebody();
2153                         genpdu();
2154                         readbody=0;
2155                         break;
2156                 case MODE_LOGO_SEND:
2157                         logoread();
2158                         break;
2159                 case MODE_RING_SEND:
2160                         ringread();
2161                         break;
2162                 case MODE_PICTURE_SEND:
2163                         pictureread();
2164                         break;
2165                 case MODE_RECEIVE: break;
2166                 default: assert(0);
2167                 }
2168         if (readbody)
2169                 error(_("Warning: -f / --file is forbidden with mode \"%s\""),MODE_NAME(mode));
2170         if (smsmode) {
2171                      if (!strcmp(smsmode,"pdu" ) || !strcmp(smsmode,"0"))
2172                         force_smsmode=FSM_PDU ;
2173                 else if (!strcmp(smsmode,"text") || !strcmp(smsmode,"1"))
2174                         force_smsmode=FSM_TEXT;
2175                 else
2176                         error(_("!Unrecognized %s argument \"%s\", supported only: %s"),"-M/--smsmode",smsmode,"pdu/0/text/1");
2177                 }
2178         if (pdusmscmode) {
2179                      if (!strcmp(pdusmscmode,"count-in"))
2180                         force_pdusmscmode=FPSM_COUNT_IN;
2181                 else if (!strcmp(pdusmscmode,"count-out"))
2182                         force_pdusmscmode=FPSM_COUNT_OUT;
2183                 else if (!strcmp(pdusmscmode,"none"))
2184                         force_pdusmscmode=FPSM_NONE;
2185                 else
2186                         error(_("!Unrecognized %s argument \"%s\", supported only: %s"),"-P/--pdusmscmode",pdusmscmode,"count-in/count-out/none");
2187                 try_pdusmscmode=force_pdusmscmode;
2188                 }
2189
2190         switch (baudn) {
2191                 case  2400: portbaud= B2400; break;
2192                 case  4800: portbaud= B4800; break;
2193                 case  9600: portbaud= B9600; break;
2194                 case 19200: portbaud=B19200; break;
2195                 case 38400: portbaud=B38400; break;
2196                 case 57600: portbaud=B57600; break;
2197                 default:
2198                         error(_("!Specified baudrate %ld is not supported"),baudn);
2199                 }
2200         if (verbose>=2) error(_(".Will use baudrate %ld with hexval 0x%X"),baudn,portbaud);
2201                 
2202         if (lockfile && *lockfile && VARPRINTF(lockreal,lockfile,devicename)>0) {
2203 time_t start,end;
2204                 if (verbose>=1) error(_(".Locking device \"%s\" by \"%s\".."),device,lockreal);
2205                 time(&start);
2206                 lockdevice(0);
2207                 time(&end);
2208                 if ((end-=start)>LOCKREPORT)
2209                         logmsg(_("Device lock succeeded after %ld seconds"),(long)end);
2210                 }
2211
2212 retryopen:
2213
2214         if (verbose>=1) error(_(".Opening device \"%s\".."),device);
2215         if ((devfd=open(device,O_RDWR|O_NDELAY))<0)
2216                 error(_("^!Cannot open device \"%s\" for r/w access"),device);
2217         
2218 retryall:
2219
2220                 if (tcgetattr(devfd,&restios))
2221                         error(_("^Unable to get termios settings"));
2222                 else {
2223                         restios.c_cflag=(restios.c_cflag&~(CBAUD|CBAUDEX))|B0|HUPCL;
2224                         restios_yes=1;
2225                         }
2226                 tios.c_iflag=IGNBRK|IGNPAR|(handshake_rtscts ? 0 : IXON|IXOFF);
2227                 tios.c_oflag=0;
2228                 tios.c_cflag=CS8|CREAD|CLOCAL|HUPCL|portbaud|(handshake_rtscts ? CRTSCTS : 0);
2229                 tios.c_lflag=IEXTEN|NOFLSH;
2230                 memset(tios.c_cc,_POSIX_VDISABLE,sizeof(tios.c_cc));
2231                 tios.c_cc[VTIME]=0;
2232                 tios.c_cc[VMIN ]=1;
2233                                 cfsetispeed(&tios,portbaud);
2234                 if (cfsetospeed(&tios,portbaud)|cfsetispeed(&tios,portbaud))
2235                         error(_("^Error setting termios baudrate on device"));
2236                 if (tcflush(devfd,TCIOFLUSH))
2237                         error(_("^Error flushing termios (TCIOFLUSH) on device"));
2238                 if (tcsetattr(devfd,TCSANOW,&tios))
2239                         error(_("^!Unable to set initial termios device settings"));
2240
2241                 setalarm();
2242
2243                 devcmd("",NULL,"\r\nAT\033\032"); /* ESCAPE, CTRL-Z */
2244                 devcmd(NULL,NULL,"\r\nAT");
2245                 smscset();
2246                 if (mode==MODE_SEND || mode==MODE_RECEIVE) {
2247                         cmgf=-1;
2248                         do {
2249                                 /* condition is _negative_ here: */
2250                                 if (force_smsmode==FSM_TEXT || !devcmd(NULL,NULL,"!\r\nAT+CMGF=0")) {
2251                                         if (force_smsmode==FSM_PDU || !devcmd(NULL,NULL,"!\r\nAT+CMGF=1"))
2252                                                 { retrying(); continue; }
2253                 /* CMGF=1 */
2254                                         if (verbose>=1)
2255                                                 error(_(".Using AT+CMGF=1 (text mode).."));
2256                                         cmgf=1;
2257                                         }
2258                                 else {
2259                 /* CMGF=0 */
2260                                         if (verbose>=1)
2261                                                 error(_(".Using AT+CMGF=0 (PDU mode).."));
2262                                         cmgf=0;
2263                                         }
2264                                 } while (cmgf==-1);
2265                         }
2266                 switch (mode) {
2267                         case MODE_SEND:
2268                                 if (cmgf) {
2269                                         devcmd("\n> ",NULL,"\r\nAT+CMGS=\"%s\"",phone);
2270                                         s=devcmd(NULL,"\n+CMGS:","!~%s\032",body);
2271                                         }
2272                                 else {
2273                                         devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",(
2274                                                         (try_pdusmscmode==FPSM_COUNT_IN ? strlen(pdusmsc) : 0)
2275                                                         +strlen(pdudata))/2);
2276                                         s=devcmd(NULL,"\n+CMGS:","!~%s%s\032",
2277                                                         (try_pdusmscmode!=FPSM_NONE ? pdusmsc : ""),
2278                                                         pdudata);
2279                                         if (!s && force_pdusmscmode==FPSM_AUTO) {
2280                                                 if (FPSM_MAX==try_pdusmscmode++)
2281                                                         try_pdusmscmode=FPSM_MIN;
2282                                                 else
2283                                                         retrycnt--;
2284                                                 }
2285                                         }
2286                                 break;
2287                         case MODE_LOGO_SEND:
2288                         case MODE_RING_SEND:
2289                         case MODE_PICTURE_SEND: {
2290 struct hexdata *hd;
2291
2292                                 restore="\r\nAT+CSMP=17,,0,0";
2293                                 devcmd(NULL,NULL,"\r\nAT+CSMP=81,,0,245");
2294                                 while ((hd=hexdata)) {
2295                                         devcmd("\n> ",NULL,"\r\nAT+CMGS=\"%s\"",phone);
2296                                         if (!(s=devcmd(NULL,"\n+CMGS:","!~%s\032",hd->data))) break;
2297                                         if ((hexdata=hd->next)) pushargstack_one(s,0);
2298                                         free(hd);
2299                                         parts++;
2300                                         }
2301                                 } break;
2302                         case MODE_RECEIVE: {
2303 int gotdatawait;
2304
2305                                 restore="\r\nAT+CNMI=,0";
2306                                 devcmd(NULL,NULL,"\r\nAT+CNMI=,2");
2307                                 devcmd(NULL,NULL,"\r\nAT+CSDH=0");
2308 continue_receive:
2309                                 unlockdevice(0);
2310                                 /* Never bail-out when we got up to this point */
2311                                 if (maxretryn!=-1 && verbose>=1)
2312                                         error(_(".Initialization successful, infinite retry count set"));
2313                                 maxretryn=-1;
2314 #if RECEIVE_TEST
2315         receive_headerparse(" \"+420602123456\",,\"99/10/25,03:21:03-00\"");
2316         receive_accept("TESTBODY");
2317         exit(EXIT_SUCCESS);
2318 #endif
2319                                 gotdatawait=datawait(waittimen);
2320                                 if (!lockdevice(1)) {
2321                                         if (verbose>=1)
2322                                                 error(_(".Dialout detected, waiting for lock.."));
2323                                         if (verbose>=1) error(_(".Closing device \"%s\".."),device);
2324                                         if (close(devfd))
2325                                                 error(_("Error closing device \"%s\""),device);
2326                                         lockdevice(0);
2327                                         goto retryopen;
2328                                         }
2329                                 d1("Lock-device succeeded\n");
2330                                 do {
2331                                         if (!(s=devcmd("\n","+CMT:"," ")))
2332                                                 goto retryall;
2333                                         if (s==&devcmd_empty_return) { /* only newlines found */
2334                                                 if (gotdatawait==0) /* timeout, rather reinitialize the modem */
2335                                                         goto retryall;
2336                                                 goto continue_receive;
2337                                                 }
2338                                         d1("Reading a message for us...\n");
2339                                         if (cmgf && !(i=receive_headerparse(s)))
2340                                                 error(_("Receive-header parsing failed on: %s"),s);
2341                                         if (!(s=devcmd("\n","@"," ")))
2342                                                 goto retryall;
2343                                         if (cmgf) {
2344                                                 if (i) receive_text(s);
2345                                                 }
2346                                         else receive_pdu(s);
2347                                         } while (datawait(0));
2348                                 goto retryall;
2349                                 }
2350
2351                         default: assert(0);
2352                         }
2353                 if (!s) { retrying(); goto retryall; }
2354
2355         pushargstack_one(s,0); s=NULL;
2356         if (verbose>=1) while ((s=nextargstack())) {
2357                 while (isspace(*s)) s++;
2358                 error(_("\nMessage successfuly sent with MR (Message Reference): %s"),s);
2359                 }
2360         devcmd(NULL,NULL,"\r\nAT");
2361
2362         if (parts)
2363                 logmsg(_("SMS sent (after %d retries), %d part(s)"),retrycnt,parts);
2364         else
2365                 logmsg(_("SMS sent (after %d retries)"),retrycnt);
2366         return(EXIT_SUCCESS);
2367 }