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