X-Git-Url: https://git.jankratochvil.net/?p=mdsms.git;a=blobdiff_plain;f=mdsms.c;h=21d69992354c48f2f2e821855dae8778b31085cc;hp=2b7edd1d795a576593d123e8de9823c47e1d4a2d;hb=30abdc172e133a85bd88a9831450d0fffac1f258;hpb=f6eb34fe532a8392a1aa3a0f2d93e16896b8eba5 diff --git a/mdsms.c b/mdsms.c index 2b7edd1..21d6999 100644 --- a/mdsms.c +++ b/mdsms.c @@ -1,3 +1,4 @@ +#define WANT_DECLARATIONS 1 #include "config.h" #ifndef lint static char rcsid[] ATTR_UNUSED = "$Id$"; @@ -38,6 +39,9 @@ static char rcsid[] ATTR_UNUSED = "$Id$"; #ifdef HAVE_SYS_TYPES_H #include #endif +#ifdef HAVE_SYS_WAIT_H +#include +#endif #ifdef HAVE_SYS_STAT_H #include #endif @@ -66,6 +70,13 @@ static char rcsid[] ATTR_UNUSED = "$Id$"; #include "getopt.h" #endif + +/* Always override possible system defintions as it is safe (used by glib) */ +#undef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#undef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) + #define NELEM(x) (sizeof((x))/sizeof(*(x))) #ifndef DEBUG @@ -96,9 +107,29 @@ static int verbose static char *pname; static int dis_cleanup=0,devfd=-1; -static char *phone,*device,*logname,*lockfile,*smsc,*maxretry,*readtime,*chartime,*cmdtime,*baud,*restore; +static char *phone,*device,*logname,*lockfile,*smsmode,*pdusmscmode,*smsc,*maxretry,*readtime,*chartime,*cmdtime,*baud,*restore; static int readbody; static long maxretryn=DEF_MAXRETRY,readtimen=-1,chartimen=DEF_CHARTIME,cmdtimen=DEF_CMDTIME,baudn=DEF_BAUD; +#ifdef HAVE_CRTSCTS +static int handshake_rtscts; +static unsigned handshake_stamp; +#else +#define handshake_rtscts (0) +#endif +static enum { + FSM_AUTO=0, + FSM_PDU, + FSM_TEXT + } force_smsmode=FSM_AUTO; +static enum { + FPSM_AUTO=0, + FPSM_COUNT_IN, + FPSM_COUNT_OUT, + FPSM_NONE + } force_pdusmscmode=FPSM_AUTO, +#define FPSM_MIN (FPSM_COUNT_IN) +#define FPSM_MAX (FPSM_NONE) + try_pdusmscmode=FPSM_MIN; static size_t bodylen; /* --send / --send-mobildock / --receive specific */ static char *body; @@ -235,11 +266,13 @@ static void usage(void) %s\ \n\ Usage: %s [-c|--config ] [-d|--device ]\n\ - {--send | --send-mobildock | --receive | --logo-send}\n\ - [-L|--log ] [-b|--baud ]\n\ - [-l|--lockfile ] [-s|--smsc ] [-m|--maxretry <#>]\n\ + [-L|--log ] [-l|--lockfile ]\n\ + [-b|--baud ] [-x|--xonxoff] [-C|--rtscts]\n\ + [-M|--smsmode ] [-P|--pdusmscmode ]\n\ + [-s|--smsc ] [-m|--maxretry <#>]\n\ [-r|--readtime ] [-t|--chartime ] [-T|--cmdtime ]\n\ [-v|--verbose] [-h|--help] [-V|--version]\n\ + {--send | --send-mobildock | --receive | --logo-send}\n\ --send / --send-mobildock:\n\ [-f|--file] \n\ --receive:\n\ @@ -253,9 +286,13 @@ Usage: %s [-c|--config ] [-d|--device ]\n\ \t\t(def. \"%s\" and \"$HOME%s\")\n\ -d, --device\tMobile on this serial device (def. \"%s\")\n\ -L, --log\tLog all important messages to this file (def. \"%s\")\n\ - -b, --baud\tSet baudrate, 2400-57600 supported (def. %d)\n\ -l, --lockfile\tLock serial port by this file, \"%%s\" is basename of device\n\ \t\t(def. \"%s\")\n\ + -b, --baud\tSet baudrate, 2400-57600 supported (def. %d)\n\ + -x, --xonxoff\tUse XON/XOFF (AKA software) serial port handshaking - default\n\ + -C, --rtscts\tUse RTS/CTS (AKA hardware) serial port handshaking%s\n\ + -M, --smsmode\tForce SMS as: \"pdu\" or 0: PDU mode, \"text\" or 1: text mode\n\ + -P, --pdusmscmode\tForce PDU as: \"count-in\", \"count-out\", \"none\"\n\ -s, --smsc\tUse this SMS Center number (def. query from mobile)\n\ -m, --maxretry\tMaximum retries of any command before giving up (def. %d)\n\ -r, --readtime\tSeconds for maximum wait time for response\n\ @@ -273,17 +310,24 @@ Usage: %s [-c|--config ] [-d|--device ]\n\ \tProgram to run on receive, message will be on stdin\n\ \t\tFollowing substitutes are recognized:\n\ \t\t%%p - source phone number\n\ -\t\t%%T - timestamp from SMSC as number of seconds from 1970\n\ -\t\t%%t - ctime(3) style timestamp (e.g. \"Wed Jun 30 21:49:08 1993\")\n\ +\t\t%%T - timestamp from SMSC as # of seconds from 1970 (-1 if invalid)\n\ +\t\t%%t - ctime(3) style timestamp (e.g. \"Wed Jun 30 21:49:08 1993\"),\n\ +\t\t empty string if invalid\n\ +\t\t%%s - originating SMSC number, if available (else empty)\n\ --logo-send:\n\ \t* Oper. logo: Enter custom network code MccMnc, e.g. 23002\n\ \t\t* Oper. logo: Specify \"%s\" to read network code from NOL file\n\ \t\t* Group gfx : Specify \"%s\" to send logo as group graphics\n\ \n\ You may need to use the following line to catch all of this help text:\n\ -./mdsms 2>&1|more\n\ -\n"),version,PACKAGE,CONFIG_MAIN,CONFIG_HOME,DEF_DEVICE,DEF_LOGNAME,DEF_BAUD,DEF_LOCKFILE,DEF_MAXRETRY, -DEF_READTIME,DEF_READTIME_MOBILDOCK,EXT_READTIME,DEF_CHARTIME,DEF_CMDTIME, +./mdsms -h 2>&1|more\n\ +\n"),version,PACKAGE,CONFIG_MAIN,CONFIG_HOME,DEF_DEVICE,DEF_LOGNAME,DEF_LOCKFILE,DEF_BAUD, +#ifdef HAVE_CRTSCTS + "", +#else + _("\n\t\t(Not supported on this platform!)"), +#endif +DEF_MAXRETRY,DEF_READTIME,DEF_READTIME_MOBILDOCK,EXT_READTIME,DEF_CHARTIME,DEF_CMDTIME, WORD_NET,WORD_GROUP); exit(EXIT_FAILURE); } @@ -301,20 +345,25 @@ static const struct option longopts[]={ {"recv" ,0,0,MODE_RECEIVE}, {"logo" ,0,0,MODE_LOGO_SEND}, {"ring" ,0,0,MODE_RING_SEND}, -{"config" ,1,0,'c'}, -{"device" ,1,0,'d'}, -{"log" ,1,0,'L'}, -{"baud" ,1,0,'b'}, -{"lockfile",1,0,'l'}, -{"smsc" ,1,0,'s'}, -{"maxretry",1,0,'m'}, -{"readtime",1,0,'r'}, -{"chartime",1,0,'t'}, -{"cmdtime" ,1,0,'T'}, -{"file" ,0,0,'f'}, -{"verbose" ,0,0,'v'}, -{"help" ,0,0,'h'}, -{"version" ,0,0,'V'}}; +{"config" ,1,0,'c'}, +{"device" ,1,0,'d'}, +{"log" ,1,0,'L'}, +{"lockfile" ,1,0,'l'}, +{"baud" ,1,0,'b'}, +{"xonxoff" ,0,0,'x'}, +{"rtscts" ,0,0,'C'}, +{"smsmode" ,1,0,'M'}, +{"pdusmscmode" ,1,0,'P'}, +{"smsc" ,1,0,'s'}, +{"maxretry" ,1,0,'m'}, +{"readtime" ,1,0,'r'}, +{"chartime" ,1,0,'t'}, +{"cmdtime" ,1,0,'T'}, +{"file" ,0,0,'f'}, +{"verbose" ,0,0,'v'}, +{"help" ,0,0,'h'}, +{"version" ,0,0,'V'}, +{NULL ,0,0,0 }}; static void processargs(int argp,char **args,const char *from); @@ -510,15 +559,17 @@ static struct { char **const var; unsigned stamp; } optset[]={ - { 'd',&device }, - { 'L',&logname }, - { 'b',&baud }, - { 'l',&lockfile }, - { 's',&smsc }, - { 'm',&maxretry }, - { 'r',&readtime }, - { 't',&chartime }, - { 'T',&cmdtime }, + { 'd',&device }, + { 'L',&logname }, + { 'l',&lockfile }, + { 'b',&baud }, + { 'M',&smsmode }, + { 'P',&pdusmscmode }, + { 's',&smsc }, + { 'm',&maxretry }, + { 'r',&readtime }, + { 't',&chartime }, + { 'T',&cmdtime }, }; static void processargs(int argp,char **args,const char *from) @@ -529,7 +580,7 @@ int i; seq++; optarg=NULL; optind=0; /* FIXME: Possible portability problem. */ - while ((optc=getopt_long(argp,args,"c:d:L:b:l:s:m:r:t:T:fvhV",longopts,NULL))!=EOF) switch (optc) { + while ((optc=getopt_long(argp,args,"c:d:L:l:b:xCM:P:s:m:r:t:T:fvhV",longopts,NULL))!=EOF) switch (optc) { case 'c': if (cfgstacki>=NELEM(cfgstack)) { error(_("Looping (%d) during attempt to read config file \"%s\", break-out"),NELEM(cfgstack),from); @@ -537,7 +588,7 @@ int i; } chk(cfgstack[cfgstacki++]=strdup(optarg)); break; - case 'd': case 'L': case 'b': case 'l': case 's': case 'm': case 'r': case 't': case 'T': + case 'd': case 'L': case 'b': case 'l': case 'M': case 'P': case 's': case 'm': case 'r': case 't': case 'T': for (i=0;i=0 && ++retrycnt>maxretryn) error(_("!Maximum command retry count (%ld) exceeded"),maxretryn); + if (maxretryn>=0 && ++retrycnt>=maxretryn) error(_("!Maximum command retry count (%ld) exceeded"),maxretryn); if (verbose>=2) error(_(".Retrying phase, %d out of %ld.."),retrycnt,maxretryn); } -static char *reform(const char *s,int slot) +static const char *reform(const char *s,int slot) { static struct formslot { char *s; @@ -878,6 +941,8 @@ off_t o=d-fs->s; return(fs->s); } +static char devcmd_empty_return='\0'; /* returned as catch when only newlines found */ + static char *devcmd(const char *term,const char *catch,const char *send,...) ATTR_PRINTFORMAT(3,4); static char *devcmd(const char *term,const char *catch,const char *send,...) { @@ -887,21 +952,27 @@ static size_t bufl; ssize_t got; char *hit,*s; va_list ap; -char errout,extend,noconvcr,edata; +char errout,extend,catch_any,edata; long alarmtime; const char *osend; static const char emptystring[]=""; +size_t discard; if (!term) term="\nOK\n"; if (!strcmp(send," ")) send=NULL; /* GCC formatstring-check workaround */ + if (verbose>=2) error(_(".devcmd(sendfmt=%s,term=%s,catch=%s)"), + reform(send,0),reform(term,1),reform(catch,2)); if (!(osend=send)) send=""; - if ((noconvcr=(catch && *catch=='@'))) catch++; + if ((catch_any=(catch && !strcmp(catch,"@")))) catch=NULL; if ((errout=(*send=='!'))) send++; errout|=(maxretryn==-1); if ((extend=(*send=='~'))) send++; alarmtime=readtimen*(extend?EXT_READTIME:1); - d8("devcmd(), alarmtime=%ld, errout=%d, extend=%d, noconvcr=%d, osend=%p, bufl=%d, buf: %s\n", - alarmtime,errout,extend,noconvcr,osend,bufl,reform(buf,0)); + buf[bufl]='\0'; /* for d8() below */ + d8("devcmd(), alarmtime=%ld, errout=%d, extend=%d, catch_any=%d, osend=%p, bufl=%d, buf: %s\n", + alarmtime,errout,extend,catch_any,osend,bufl,reform(buf,0)); + assert(!catch || !strchr(catch,'\r')); /* we are no longer supporting 'noconvcr'! */ + assert(!term || !strchr(term ,'\r')); if (0) { err: alarm(0); @@ -916,8 +987,7 @@ err: l=VARVPRINTF(buf,send,ap); bufl=l+(!!osend); va_end(ap); if (bufl>=sizeof(buf)-1) error(_("!Command too big (%d>%d)"),bufl,sizeof(buf)-1); - if (verbose>=2) error(_(".devcmd(send=%s,term=%s,catch=%s,timeout=%ld)"), - reform(buf,0),reform(term,1),reform(catch,2),alarmtime); + if (verbose>=2) error(_(".devcmd formatted send=%s%s"),reform(buf,0),(osend?"+\"\\r\"":"")); if (osend) buf[l]='\r'; for (offs=0,got=0;offs=2) + error(_(".Blank (%d newlines) input read, ignoring it"),bufl); + bufl=0; /* discard newlines */ + return(&devcmd_empty_return); + } if (wasalarm) error(_("Maximum response timeout (%lds) exceeded"),alarmtime); else error(_("^Couldn't read device data (ret=%d)"),got); goto err; } -skipread: bufl2=bufl+got; buf[bufl2]='\0'; s=buf+bufl; while (buf+bufl2>s && (s=memchr(s,'\0',buf+bufl2-s))) *s++=REPL_NULLCHAR; if (verbose>=3) error(_("\nGot chunk of data from device: %s"),reform(buf+bufl,0)); - if (!noconvcr) { + /* convert CR */ { s=buf+bufl; while (buf+bufl2>s && (s=memchr(s,'\r',buf+bufl2-s))) *s++='\n'; } bufl=bufl2; +skipread: catched(buf+bufl,edata); assert(!record || record==buf+bufl); assert(bufl=catchl && (hit=strstr(buf,catch))) { +/* "record" may get NULLed here after successful 'catch' + * but "recordend" will never be NULLed + */ + if (catch && !recordend && bufl>=catchl && (hit=strstr(buf,catch))) { record=hit+catchl; catched(buf+bufl,edata); assert(!record || record==buf+bufl); } - if ( bufl>= terml && (hit=strstr(buf,term))) { + if (catch_any && !recordend && buf[discard=strspn(buf,"\n" /* accept */)]) { + record=buf+discard; + catched(buf+bufl,edata); assert(!record || record==buf+bufl); + } + if (((!catch && !catch_any) || catchdatal) && bufl>= terml + && (hit=strstr((recordend?recordend:buf),term))) { memmove(buf,hit+terml,(bufl2=(buf+bufl)-(hit+terml))); bufl=bufl2; break; } if (bufl=2) error(_(".Returning data %s for cmd %s"),reform(catchdata,0),reform(send,1)); @@ -1058,7 +1144,6 @@ static inline void textconv(char *d,unsigned char *s,size_t len) static inline void smscset(void) { char *s,*t,*e,*serr; -long l; unsigned char bin[2+(MAXNUMLEN+1)/2]; if (smsc) devcmd(NULL,NULL,"\r\nAT+CSCA=\"%s\"",smsc); @@ -1067,19 +1152,22 @@ unsigned char bin[2+(MAXNUMLEN+1)/2]; if (!*s || !strcmp(s,"EMPTY")) error(_("!No SMSC set in mobile station found, please use option \"-s\"")); if (verbose>=1) error(_("\nFound default SMSC in mobile: %s"),s); - if (*s!='"') error(_("!No left-quote found in: %s"),s); - if (!(t=strrchr(s+1,'"'))) error(_("!No right-quote found in: %s"),s); - if (s+1==t) + if (*s++!='"') error(_("!No left-quote found in: %s"),s); + if (!(t=strrchr(s,'"'))) error(_("!No right-quote found in: %s"),s); + if (s==t) error(_("!No SMS set in mobile station found, please use option \"-s\"")); e=t++; while (isspace(*t)) t++; - if (*t++!=',') error(_("!No comma found after quotes in: %s"),s); - while (isspace(*t)) t++; - l=strtol(t,&serr,10); - if ((l!=ADDR_NAT && l!=ADDR_INT) || (serr && *serr)) - error(_("!Type parse error in: %s"),s); - if (l==ADDR_NAT || s[1]=='+') s++; - else *s='+'; + if (*t) { +long l; + + if (*t++!=',') error(_("!No comma found after quotes in: %s"),s); + while (isspace(*t)) t++; + l=strtol(t,&serr,10); + if ((l!=ADDR_NAT && l!=ADDR_INT) || (serr && *serr)) + error(_("!Type parse error in: %s"),s); + if (l==ADDR_INT && *s!='+') *--s='+'; + } *e='\0'; if (verbose>=2) error(_("\nDecoded SMSC address: %s"),s); if (!NEED_PDUSMSC()) return; @@ -1088,7 +1176,7 @@ unsigned char bin[2+(MAXNUMLEN+1)/2]; textconv(pdusmsc,bin,bin[0]+1); } -static inline unsigned char charconv(char c,size_t offs) +static inline unsigned char charconv_send(char c,size_t offs) { switch (c) { case '@': return(0); @@ -1105,6 +1193,17 @@ static inline unsigned char charconv(char c,size_t offs) #endif } +static inline unsigned char charconv_recv(char c,size_t offs) +{ /* FIXME: unify with charconv_send() */ + switch (c) { + case 0: return('@'); + case 2: return('$'); + default: + return(c); + } +/* strict checking not done, see charconv_send */ +} + /* Logo format shamelessly stolen from GNokii-0.3.0: http://www.gnokii.org/ * Beware - Nokia Smart Messaging specification 1.0.0 and 2.0.0 is incompatible * with Nokia current product line implementation @@ -1243,19 +1342,19 @@ long size; #define WORD(n) (((unsigned char)buf[(n)])|(((unsigned char)buf[(n)+1])<<8)) - if (!(f=fopen(logoname,"rb"))) - error(_("^!Cannot open ring file \"%s\" for r/o"),logoname); - if ((size=getfilesize(f,logoname))==-1) + if (!(f=fopen(ringname,"rb"))) + error(_("^!Cannot open ring file \"%s\" for r/o"),ringname); + if ((size=getfilesize(f,ringname))==-1) error(_("!File size determination is essential to continue operation")); if (size<0x103) error(_("!File \"%s\" size %ld too small (must >=0x103)! Is it .000 file?"), - logoname,size); + ringname,size); if (fseek(f,0x100,SEEK_SET)) - error(_("^Seeking error on \"%s\", ignoring"),logoname); + error(_("^Seeking error on \"%s\", ignoring"),ringname); size-=0x100; if (size<=BIN1_PAYLOAD) { if ((got=fread(bin1+7,1,size,f))!=size) - error(_("^Read error on \"%s\", wanted %ld, got %d"),logoname,size,got); + error(_("^Read error on \"%s\", wanted %ld, got %d"),ringname,size,got); error(_("\nSending ring tone \"%s\" as single SMS (size %ld, max %d)"), ringname,size,BIN1_PAYLOAD); nokiaprep(bin1,7+size); @@ -1276,12 +1375,12 @@ long size; binn[11]=fragn; want=MIN(size,BINN_PAYLOAD); if ((got=fread(binn+12,1,want,f))!=want) - error(_("^Read error on \"%s\", wanted %d, got %d"),logoname,want,got); + error(_("^Read error on \"%s\", wanted %d, got %d"),ringname,want,got); nokiaprep(binn,12+want); size-=want; } } - chkfclose(f,logoname); + chkfclose(f,ringname); #undef WORD } @@ -1311,7 +1410,7 @@ size_t offs=0; while (bodylen || inb) { if (!inb) { assert(bodylen>0); assert(!!*body); - inreg=charconv(*bodyr++,offs++); + inreg=charconv_send(*bodyr++,offs++); bodylen--; inb=7; } @@ -1376,12 +1475,13 @@ fd_set rfds,xfds; assert(devfd>=0); retry: - errno=0; - + if (!immed && verbose>=2) + error(_(".Waiting for device incoming data..")); #ifdef HAVE_POLL ufd.fd=devfd; ufd.events=POLLIN; ufd.revents=0; + errno=0; i=poll(&ufd,1,(immed?0:-1)); #else /* HAVE_POLL */ #ifdef HAVE_FD_SETSIZE @@ -1391,9 +1491,12 @@ retry: #endif /* HAVE_FD_SETSIZE */ FD_ZERO(&rfds); FD_SET(devfd,&rfds); FD_ZERO(&xfds); FD_SET(devfd,&xfds); + errno=0; i=select(devfd+1,&rfds,NULL,&xfds,NULL); #endif /* HAVE_POLL */ if (immed && i==0) return(0); + if (i==-1 && errno==EINTR) + goto retry; /* silent retry, for example SIGCHLD could occur */ if (i!=1) error(_("^Failed (retval %d) while waiting for data, ignoring"),i); @@ -1419,8 +1522,8 @@ retry: static char *check_format(const char *fmt,const char *string) { static char err[LINE_MAX],sub[50]; -char *subp,cf,cs; -const char *sf,*ss; +char cf,cs; +const char *sf,*ss,*subp; for (sf=fmt,ss=string;(cf=*sf) && (cs=*ss);sf++,ss++) { subp=NULL; @@ -1457,13 +1560,9 @@ const char *sf,*ss; return(NULL); } -static char *receive_number; +static char *receive_number,*receive_smsc; static time_t receive_time; -/* +CMT: "+420602431329",,"99/10/25,03:21:03-00" */ -static int receive_headerparse(char *buf) -{ -char *s,*s1,*err; struct tm tm; static const struct { off_t strpos; @@ -1480,9 +1579,37 @@ static const struct { TP_ENT(18,tm_sec ,0,59,N_("second")), /* Time zone ignored */ }; -int i,val; +#define GETTIME(i) (*(int *)(((char *)&tm)+timeparse[(i)].tmpos)) -#define DIGIT2(s) (((s)[0]-'0')*10+((s)[1]-'0')) +static void maketime(const char *string) +{ +int val; +int i; + + for (i=0;itimeparse[i].max) { + error(_("Weird value of %s, is %d but expected %d..%d, setting to %d"), + _(timeparse[i].name),val,timeparse[i].min,timeparse[i].max,timeparse[i].min); + GETTIME(i)=timeparse[i].min; + } + } + if (tm.tm_year<70) tm.tm_year+=100; + tm.tm_mon--; + d7("mktime(y%dm%dd%dh%dm%ds%d)\n", + tm.tm_year,tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec); + tm.tm_isdst=-1; /* "timezone" info not available */ + if ((receive_time=mktime(&tm))==-1) + error(_("^mktime(3) failed for %s"),string); +} + +/* +CMT: "+420602431329",,"99/10/25,03:21:03-00" */ +static int receive_headerparse(char *buf) +{ +char *s,*s1,*err; +int i; + +#define DIGIT2ASC(s) (((s)[0]-'0')*10+((s)[1]-'0')) for (s=buf;*s==' ';s++); if (*s++!='"') { @@ -1494,6 +1621,7 @@ int i,val; error(_("Only one '\"' found in CMT header: %s"),buf); return(0); } + free(receive_smsc); receive_smsc=NULL; free(receive_number); chk(receive_number=malloc(s-s1+1)); memcpy(receive_number,s1,s-s1); receive_number[s-s1]='\0'; @@ -1504,28 +1632,32 @@ int i,val; return(0); } memset(&tm,0,sizeof(tm)); /* may be redundant */ - for (i=0;itimeparse[i].max) { - error(_("Weird value of %s, is %d but expected %d..%d, setting to %d"), - _(timeparse[i].name),val,timeparse[i].min,timeparse[i].max,timeparse[i].min); - val=timeparse[i].min; - } - *(int *)(((char *)&tm)+timeparse[i].tmpos)=val; - } + for (i=0;i=2) + error(_(".Child process w/PID %d has exited, %s, status=%d"), + pid,(WIFEXITED(status)? _("normally") : _("abnormally")),(WIFEXITED(status) ? WEXITSTATUS(status) : -1)); + } } -static void receive_accept(char *bodyline) +static void receive_text(char *bodyline) { char *buf,*s,*s1,*s2,*s3; pid_t pid; @@ -1533,13 +1665,18 @@ char tbuf[32]; int i; FILE *f; - d2("receive_accept: %s\n",bodyline); + d2("receive_text: %s\n",bodyline); + signal(SIGCHLD,(RETSIGTYPE (*)(int))signal_chld); #if RECEIVE_TEST pid=0; #else pid=fork(); #endif - if (pid>0) return; /* parent context */ + if (pid>0) { + if (verbose>=2) + error(_(".Spawned child receive-SMS process w/PID %d"),pid); + return; /* parent context */ + } if (pid==-1) { error(_("Can't fork(2), process spawning may block receive")); } @@ -1550,7 +1687,7 @@ FILE *f; s1=s; do { s1=strchr(s1+(s1!=s),'%'); - } while (s1 && s1[1]!='p' && s1[1]!='T' && s1[1]!='t'); + } while (s1 && s1[1]!='p' && s1[1]!='T' && s1[1]!='t' && s1[1]!='s'); if (!s1) { pushargstack_one(s,0); break; @@ -1568,6 +1705,7 @@ FILE *f; pushargstack_one(tbuf,0); break; case 't': + if (receive_time==-1) break; if (!(s2=ctime(&receive_time))) { error(_("Failing ctime(3), ignoring substitution")); break; @@ -1575,6 +1713,9 @@ FILE *f; if ((s3=strchr(s2,'\n'))) *s3='\0'; pushargstack_one(s2,0); break; + case 's': + if (receive_smsc) pushargstack_one(receive_smsc,0); + break; default: assert(0); } } @@ -1593,6 +1734,172 @@ err: exit(EXIT_SUCCESS); /* cleanup() has been disabled */ } +static inline unsigned char fromhex(unsigned c) +{ + c&=0xDF; + return(c<'A'?c-('0'&0xDF):(c-('A'&0xDF))+0xA); +} + +static int teldecode(char *text,unsigned char *bin,size_t digits) +{ +unsigned char b; +int r=0,i; + + for (i=0;i>4; + b&=0x0F; + if (b<=0x09) + *text=b+'0'; + else { + *text='?'; + r++; + } + } + *text='\0'; + return(r); +} + +static void sctsparse(unsigned char *bin,const char *pduline,int offs) +{ +#define DIGIT2BIN(v) (((v)&0x0F)*10+(((v)>>4)&0x0F)) +int i; + + receive_time=-1; + memset(&tm,0,sizeof(tm)); /* may be redundant */ + for (i=0;i0x09 || (*bin&0xF0)>0x90) { + error(_("Invalid value of \"%s\" at offset %d in: %s"), + timeparse[i].name,offs,pduline); + return; + } + GETTIME(i)=DIGIT2BIN(*bin); + bin++; + } + maketime(pduline); + +#undef DIGIT2BIN +} + +static void receive_pdu(char *pduline) +{ +unsigned char pdu[140+0x100],*pdup,*pdue,oalen,inreg; +char text[160+1],*textp,*s,*pdulinescan; +size_t pdulinel=strlen(pduline),want; +size_t udl,udlb; +int inb,outb,xb; + + d2("receive_pdu: %s\n",pduline); + if (pdulinel>2*sizeof(pdu)) + { error(_("PDU too long (%d/2) to be valid: %s"),pdulinel,pduline); return; } + if (pdulinel&1) + { error(_("PDU length odd (%d): %s"),pdulinel,pduline); return; } + if (pdulinel<2*13) + { error(_("PDU length %d too small (min. 2*%d): %s"),pdulinel,13,pduline); return; } + for (pdup=pdu,pdulinescan=pduline;*pdulinescan;pdulinescan+=2) { + if (!isxdigit(pdulinescan[0]) || !(isxdigit(pdulinescan[1]))) + { error(_("Invalid hex byte: %c%c on byte %d in: %s"), + pdulinescan[0],pdulinescan[1],pdup-pdu,pduline); return; } + *pdup++=(fromhex(pdulinescan[0])<<4)|fromhex(pdulinescan[1]); + } + pdue=pdup; + free(receive_smsc); + if (*pdu<=1) { + receive_smsc=NULL; + } + else { + if (*pdu>10) + { error(_("SMSC length too large (%d, max. %d): %s"),*pdu,10,pduline); return; } + chk(receive_smsc=malloc(1+2*(*pdu)+1)); + s=receive_smsc; + if (pdu[1]==ADDR_INT) *s++='+'; + else { + if (pdu[1]!=ADDR_NAT) + error(_("Unknown address type 0x%02X of %s, ignoring in PDU: %s"),pdu[1],_("SMSC"),pduline); return; + } + if (teldecode(s,pdu+2,2*(*pdu-1)-((pdu[1+(*pdu)]&0xF0)==0xF0))) + error(_("Some digits unrecognized in %s \"%s\", ignoring in PDU: %s"),_("SMSC"),receive_smsc,pduline); + } + pdup=pdu+1+(*pdu); + if (*pdup&0x03) /* PDU type */ + error(_("Unrecognized PDU type 0x%02X at offset %d, dropping: %s"),*pdup,pdup-pdu,pduline); + pdup++; + free(receive_number); + if ((oalen=*pdup++)>2*0x10) /* OA len */ + { error(_("Originating number too large (0x%X, max. 2*0x%X): %s"),oalen,0x10,pduline); return; } + if (pdup+(want=1+(oalen+1)/2+10)>pdue) + { error(_("PDU length too short (want %d, is %d): %s"),(pdup-pdu)+want,pdue-pdu,pduline); return; } + chk(receive_number=malloc(1+2*(*pdup)+1)); + s=receive_number; + if (*pdup==ADDR_INT) *s++='+'; + else { + if (*pdup!=ADDR_NAT) + error(_("Unknown address type 0x%02X of %s, ignoring in PDU: %s"),*pdup,_("originating number"),pduline); return; + } + pdup++; + if (teldecode(s,pdup,oalen)) + error(_("Some digits unrecognized in %s \"%s\", ignoring in PDU at offset %d: %s"), + _("originating number"),receive_number,pdup-pdu,pduline); + pdup+=(oalen+1)/2; + if (*pdup) /* PID */ + error(_("PID number %02X unsupported, ignoring: %s"),*pdup,pduline); + pdup++; + if (*pdup) { /* DCS */ + if ((*pdup&0xF4)==0xF4) + { error(_("DCS 0x%02X indicates 8-bit data, unsupported, dropping: %s"),*pdup,pduline); return; } + error(_("DCS 0x%02X unsupported, will attempt decoding: %s"),*pdup,pduline); + } + pdup++; + sctsparse(pdup,pduline,pdup-pdu); + pdup+=7; + /* UDL */ + udl=*pdup++; + if (pdue-pdup>140) { + error(_("PDU data (%d) exceed maximum length of %d bytes, cut: %s"), + pdue-pdup,140,pduline); + pdue=pdup+140; + } + udlb=(udl*7+7)/8; + if (pdup+udlb>pdue) { +size_t udl1,udlb1; + + udlb1=pdue-pdup; + udl1=(udlb1*8)/7; + error(_("PDU data length (%d/7->%d/8) longer than data (%d), cut to %d/7->%d/8: %s"), + udl,udlb,pdue-pdup,udl1,udlb1,pduline); + udl=udl1; udlb=udlb1; + } + else + assert(pdup+udlb==pdue); /* should be checked by 'PDU length too short' above */ + textp=text; + inb=outb=0; + inreg=0; /* GCC happiness */ + while (udl) { + if (!inb) { + inreg=*pdup++; + inb=8; + } + if (!outb) { + assert(textp>(unsigned)(8-inb))&((1<=1) + error(_(".Using AT+CMGF=1 (text mode)..")); + cmgf=1; + } + else { + /* CMGF=0 */ + if (verbose>=1) + error(_(".Using AT+CMGF=0 (PDU mode)..")); + cmgf=0; + } + } while (cmgf==-1); + } switch (mode) { case MODE_SEND: -retrysendcmgf: - if (!devcmd(NULL,NULL,"!\r\nAT+CMGF=0")) { - if (!devcmd(NULL,NULL,"!\r\nAT+CMGF=1")) - { retrying(); goto retrysendcmgf; } - /* CMGF=1 */ + if (cmgf) { devcmd("\n> ",NULL,"\r\nAT+CMGS=\"%s\"",phone); s=devcmd(NULL,"\n+CMGS:","!~%s\032",body); } else { - /* CMGF=0 */ - devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",(strlen(pdusmsc)+strlen(pdudata))/2); - s=devcmd(NULL,"\n+CMGS:","!~%s%s\032",pdusmsc,pdudata); + devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",( + (try_pdusmscmode==FPSM_COUNT_IN ? strlen(pdusmsc) : 0) + +strlen(pdudata))/2); + s=devcmd(NULL,"\n+CMGS:","!~%s%s\032", + (try_pdusmscmode!=FPSM_NONE ? pdusmsc : ""), + pdudata); + if (!s && force_pdusmscmode==FPSM_AUTO) { + if (FPSM_MAX==try_pdusmscmode++) + try_pdusmscmode=FPSM_MIN; + else + retrycnt--; + } } break; case MODE_LOGO_SEND: @@ -1819,58 +2171,69 @@ struct hexdata *hd; restore="\r\nAT+CSMP=17,,0,0"; devcmd(NULL,NULL,"\r\nAT+CSMP=81,,0,245"); - devcmd("\n> ",NULL,"\r\nAT+CMGS=\"%s\"",phone); while ((hd=hexdata)) { + devcmd("\n> ",NULL,"\r\nAT+CMGS=\"%s\"",phone); if (!(s=devcmd(NULL,"\n+CMGS:","!~%s\032",hd->data))) break; - hexdata=hd->next; + if ((hexdata=hd->next)) pushargstack_one(s,0); free(hd); + parts++; } } break; case MODE_RECEIVE: - devcmd(NULL,NULL,"\r\nAT+CMGF=1"); restore="\r\nAT+CNMI=,0"; - devcmd(NULL,NULL,"\r\nAT+CSDH=0"); devcmd(NULL,NULL,"\r\nAT+CNMI=,2"); - unlockdevice(0); - /* Never bail-out when we got up to this point */ - if (maxretryn!=-1 && verbose>=1) - error(_(".Initialization successful, infinite retry count set")); - maxretryn=-1; + devcmd(NULL,NULL,"\r\nAT+CSDH=0"); + for (;;) { +continue_receive: + unlockdevice(0); + /* Never bail-out when we got up to this point */ + if (maxretryn!=-1 && verbose>=1) + error(_(".Initialization successful, infinite retry count set")); + maxretryn=-1; #if RECEIVE_TEST receive_headerparse(" \"+420602123456\",,\"99/10/25,03:21:03-00\""); receive_accept("TESTBODY"); exit(EXIT_SUCCESS); #endif - datawait(0); - if (!lockdevice(1)) { - if (verbose>=1) - error(_(".Dialout detected, waiting for lock..")); - lockdevice(0); - goto retryall; - } - d1("Lock-device succeeded\n"); - do { - d1("Reading a message for us...\n"); - if (!(s=devcmd("\r","@+CMT:"," "))) - goto retryall; - if (!(i=receive_headerparse(s))) - error(_("Receive-header parsing failed on: %s"),s); - if (!(s=devcmd("\r","@\n"," "))) + datawait(0); + if (!lockdevice(1)) { + if (verbose>=1) + error(_(".Dialout detected, waiting for lock..")); + lockdevice(0); goto retryall; - if (i) receive_accept(s); - if (!devcmd("\n",NULL," ")) /* eat last '\n' */ - goto retryall; - } while (datawait(1)); - goto retryall; + } + d1("Lock-device succeeded\n"); + do { + d1("Reading a message for us...\n"); + if (!(s=devcmd("\n","+CMT:"," "))) + goto retryall; + if (s==&devcmd_empty_return) /* only newlines found */ + goto continue_receive; + if (cmgf && !(i=receive_headerparse(s))) + error(_("Receive-header parsing failed on: %s"),s); + if (!(s=devcmd("\n","@"," "))) + goto retryall; + if (cmgf) { + if (i) receive_text(s); + } + else receive_pdu(s); + } while (datawait(1)); + } /* return to 'continue receive' point */ break; default: assert(0); } if (!s) { retrying(); goto retryall; } - while (isspace(*s)) s++; - if (verbose>=1) error(_("\nMessage successfuly sent with MR (Message Reference): %s"),s); + pushargstack_one(s,0); s=NULL; + if (verbose>=1) while ((s=nextargstack())) { + while (isspace(*s)) s++; + error(_("\nMessage successfuly sent with MR (Message Reference): %s"),s); + } devcmd(NULL,NULL,"\r\nAT"); - logmsg(_("SMS sent (after %d retries), message reference: %s"),retrycnt,s); + if (parts) + logmsg(_("SMS sent (after %d retries), %d part(s)"),retrycnt,parts); + else + logmsg(_("SMS sent (after %d retries)"),retrycnt); return(EXIT_SUCCESS); }