+static inline void preparebody(void)
+{
+FILE *fin;
+char *finame;
+
+ if (body && readbody) {
+ finame=body;
+ body=NULL;
+ }
+ else {
+ finame=NULL;
+ fin=stdin;
+ }
+ if (body) return;
+ readbody=0;
+ if (!finame) {
+ if (verbose>=1)
+ error("\nPlease enter the SMS text body, end with EOF (ctrl-D):");
+ }
+ else {
+ if (!(fin=fopen(finame,"rt")))
+ error("^!Can't open data file \"%s\" for r/o",finame);
+ }
+ chk(body=malloc(BODYLOAD));
+ bodylen=fread(body,1,BODYLOAD,fin);
+ if (bodylen==-1)
+ error("^!Error reading stream \"%s\"",(finame?finame:"<stdin>"));
+ if (finame) {
+ chkfclose(fin,finame);
+ free(finame);
+ }
+}
+
+static int datawait(char immed)
+{
+int i;
+#ifdef HAVE_POLL
+struct pollfd ufd;
+#else /* HAVE_POLL */
+fd_set rfds,xfds;
+#endif /* HAVE_POLL */
+
+ assert(devfd>=0);
+retry:
+ errno=0;
+
+#ifdef HAVE_POLL
+ ufd.fd=devfd;
+ ufd.events=POLLIN;
+ ufd.revents=0;
+ i=poll(&ufd,1,(immed?0:-1));
+#else /* HAVE_POLL */
+#ifdef HAVE_FD_SETSIZE
+ if (devfd>=FD_SETSIZE)
+ error("!Device file descriptor %d can't fit in select() FD_SETSIZE (%d)",
+ devfd,FD_SETSIZE);
+#endif /* HAVE_FD_SETSIZE */
+ FD_ZERO(&rfds); FD_SET(devfd,&rfds);
+ FD_ZERO(&xfds); FD_SET(devfd,&xfds);
+ i=select(devfd+1,&rfds,NULL,&xfds,NULL);
+#endif /* HAVE_POLL */
+ if (immed && i==0) return(0);
+ if (i!=1)
+ error("^Failed (retval %d) while waiting for data, ignoring",i);
+
+#ifdef HAVE_POLL
+ if (ufd.revents&(POLLERR|POLLHUP))
+#else /* HAVE_POLL */
+ if (FD_ISSET(devfd,&xfds))
+#endif /* HAVE_POLL */
+ error("^Error while waiting for data, ignoring");
+
+#ifdef HAVE_POLL
+ if (!(ufd.revents&POLLIN))
+#else /* HAVE_POLL */
+ if (!(FD_ISSET(devfd,&rfds)))
+#endif /* HAVE_POLL */
+ {
+ error("^No data input after waited for data, retrying");
+ goto retry;
+ }
+ return(1);
+}
+
+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;
+
+ for (sf=fmt,ss=string;(cf=*sf) && (cs=*ss);sf++,ss++) {
+ subp=NULL;
+ switch (cf) {
+ case '?':
+ break;
+ case '9':
+ if (isdigit(cs)) break;
+ subp="digit";
+ break;
+ case '+':
+ if (cs=='+' || cs=='-') break;
+ subp="+/- sign";
+ break;
+ default:
+ if (cf==cs) break;
+ VARPRINTF(sub,"'%c'",cf); subp=sub;
+ }
+ if (!subp) continue;
+ VARPRINTF5(err,"Expected %s, found '%c' at pos %d of string [%s], formatstring [%s]",
+ subp,cs,ss-string,string,fmt);
+ return(err);
+ }
+ if (*sf) {
+ VARPRINTF2(err,"String too short for format, string [%s], formatstring [%s]",
+ string,fmt);
+ return(err);
+ }
+ if (*ss) {
+ VARPRINTF2(err,"Trailing garbage in string [%s], formatstring [%s]",
+ string,fmt);
+ return(err);
+ }
+ return(NULL);
+}
+
+static char *receive_number;
+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;
+ off_t tmpos;
+ int min,max;
+ const char *name;
+ } timeparse[]={
+#define TP_ENT(a,b,c,d,e) { a,offsetof(struct tm,b),c,d,e }
+ TP_ENT( 3,tm_year,0,99,"year"),
+ TP_ENT( 6,tm_mon ,1,12,"month"),
+ TP_ENT( 9,tm_mday,1,31,"day of month"),
+ TP_ENT(12,tm_hour,0,23,"hour"),
+ TP_ENT(15,tm_min ,0,59,"minute"),
+ TP_ENT(18,tm_sec ,0,59,"second"),
+ /* Time zone ignored */
+ };
+int i,val;
+
+#define DIGIT2(s) (((s)[0]-'0')*10+((s)[1]-'0'))
+
+ for (s=buf;*s==' ';s++);
+ if (*s++!='"') {
+ error("Cannot find initial '\"' in CMT header: %s",buf);
+ return(0);
+ }
+ for (s1=s;*s && *s!='"';s++);
+ if (!*s) {
+ error("Only one '\"' found in CMT header: %s",buf);
+ return(0);
+ }
+ free(receive_number);
+ chk(receive_number=malloc(s-s1+1));
+ memcpy(receive_number,s1,s-s1); receive_number[s-s1]='\0';
+ s++;
+ if ((err=check_phone(receive_number)) ||
+ (err=check_format(",,\"99/99/99,99:99:99+99\"",s))) {
+ error("%s in CMT header: %s",err,buf);
+ return(0);
+ }
+ memset(&tm,0,sizeof(tm)); /* may be redundant */
+ for (i=0;i<NELEM(timeparse);i++) {
+ val=DIGIT2(s+timeparse[i].strpos);
+ if (val<timeparse[i].min || val>timeparse[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;
+ }
+ 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);
+ if ((receive_time=mktime(&tm))==-1) {
+ error("^mktime(3) failed for %s",s+2);
+ return(0);
+ }
+ return(1);
+#undef DIGIT2
+}
+
+static void receive_accept(char *bodyline)
+{
+char *buf,*s,*s1,*s2,*s3;
+pid_t pid;
+char tbuf[32];
+int i;
+FILE *f;
+
+ d2("receive_accept: %s\n",bodyline);
+#if RECEIVE_TEST
+ pid=0;
+#else
+ pid=fork();
+#endif
+ if (pid>0) return; /* parent context */
+ if (pid==-1) {
+ error("Can't fork(2), process spawning may block receive");
+ }
+ else { /* child process */
+ dis_cleanup=1;
+ }
+ for (s=body;*s;) {
+ s1=s;
+ do {
+ s1=strchr(s1+(s1!=s),'%');
+ } while (s1 && s1[1]!='p' && s1[1]!='T' && s1[1]!='t');
+ if (!s1) {
+ pushargstack_one(s,0);
+ break;
+ }
+ *s1='\0';
+ pushargstack_one(s,0);
+ *s1++='%';
+ s=s1;
+ switch (*s++) {
+ case 'p':
+ pushargstack_one(receive_number,0);
+ break;
+ case 'T':
+ VARPRINTF(tbuf,"%ld",receive_time);
+ pushargstack_one(tbuf,0);
+ break;
+ case 't':
+ if (!(s2=ctime(&receive_time))) {
+ error("Failing ctime(3), ignoring substitution");
+ break;
+ }
+ if ((s3=strchr(s2,'\n'))) *s3='\0';
+ pushargstack_one(s2,0);
+ break;
+ default: assert(0);
+ }
+ }
+ buf=glueargstack(NULL,NULL); assert(buf);
+ if (!(f=popen(buf,"w"))) {
+ error("^Failing spawn of receive command: %s",buf);
+ goto err;
+ }
+ if (fputs(bodyline,f)<0 || putc('\n',f)!='\n')
+ error("^Failing write to child receive command: %s",buf);
+ if ((i=pclose(f)))
+ error("^Spawned receive command failure (code %d): %s",i,buf);
+err:
+ free(buf);
+ if (pid==-1) return;
+ exit(EXIT_SUCCESS); /* cleanup() has been disabled */
+}
+