Many modifications for mdsms -> sms9110 branch transition.
authorshort <>
Tue, 7 Sep 1999 10:31:57 +0000 (10:31 +0000)
committershort <>
Tue, 7 Sep 1999 10:31:57 +0000 (10:31 +0000)
mdsms.c

diff --git a/mdsms.c b/mdsms.c
index a4e6498..891d18e 100644 (file)
--- a/mdsms.c
+++ b/mdsms.c
@@ -29,14 +29,6 @@ static char rcsid[] ATTR_UNUSED = "$Id$";
 #include "getopt.h"
 #endif
 
-#if 0
-char *strdup(const char *s);
-int kill(pid_t pid,int sig);
-int snprintf(char *str,size_t n,const char *format,...);
-int vsnprintf(char *str,size_t n,const char *format,va_list ap);
-void usleep(unsigned long usec);
-#endif
-
 #define NELEM(x) (sizeof((x))/sizeof(*(x)))
 
 #ifndef DEBUG
@@ -53,7 +45,7 @@ void usleep(unsigned long usec);
 #define d3(n1,n2,n3)    dB((dO,n1,n2,n3   ))
 #define d4(n1,n2,n3,n4) dB((dO,n1,n2,n3,n4))
 
-static const char version[]="This is MobilDock SMS sender (" PACKAGE " " VERSION ")\n";
+static const char version[]="This is Nokia 9110 logo SMS sender (" PACKAGE " " VERSION ")\n";
 
 static int verbose
 #ifdef DEBUG
@@ -63,10 +55,8 @@ static int verbose
 static char *pname;
 static int dis_cleanup=0,devfd=-1;
 
-static char *phone,*body,*device,*logname,*lockfile,*smsc,*maxretry,*readtime,*chartime,*cmdtime;
-static int readbody;
-static long maxretryn=DEF_MAXRETRY,readtimen=DEF_READTIME,chartimen=DEF_CHARTIME,cmdtimen=DEF_CMDTIME;
-static size_t bodylen;
+static char *phone,*logoname,*device,*logname,*lockfile,*smsc,*maxretry,*readtime,*chartime,*cmdtime,*baud,*gsmnet;
+static long maxretryn=DEF_MAXRETRY,readtimen=DEF_READTIME,chartimen=DEF_CHARTIME,cmdtimen=DEF_CMDTIME,baudn=DEF_BAUD;
 
 static char *devicename; /* path stripped */
 static char lockreal[512],locked;
@@ -195,42 +185,45 @@ static void usage(void)
 %s\
 \n\
 Usage: " PACKAGE " [-c|--config <cfgfile>] [-d|--device <device>]\n\
-             [-L|--log <file>]\n\
+             [-L|--log <file>] [-b|--baud <rate>]\n\
              [-l|--lockfile <lock>] [-s|--smsc <smsc #>] [-m|--maxretry <#>]\n\
              [-r|--readtime <sec>] [-t|--chartime <msec>] [-T|--cmdtime <msec>]\n\
-             [-f|--file] [-v|--verbose] [-h|--help] [-V|--version]\n\
-             <dest. phone> <msg text|msg filename>\n\
+             [-v|--verbose] [-h|--help] [-V|--version]\n\
+             <dest. phone> <logo filename> [<GSMnet id>]\n\
 \n\
  -c, --config\tRead this additional config file\n\
 \t\t(def. \"" CONFIG_MAIN "\" and \"$HOME" CONFIG_HOME "\")\n\
- -d, --device\tMobilDock on this serial device (def. \"" DEF_DEVICE "\")\n\
+ -d, --device\tNokia on this serial device (def. \"" DEF_DEVICE "\")\n\
  -L, --log\tLog all important messages to this file (def. \"" DEF_LOGNAME "\")\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\
- -s, --smsc\tUse this SMS Center number (def. query from Siemens A1)\n\
+ -s, --smsc\tUse this SMS Center number (def. query from Nokia 9110)\n\
  -m, --maxretry\tMaximum retries of any command before giving up (def. %d)\n\
  -r, --readtime\tSeconds for maximum wait time for response (def. %ds)\n\
- -t, --chartime\tMilliseconds between each char on baud 19200 (def. %dms)\n\
+ -t, --chartime\tMilliseconds between each char (def. %dms)\n\
  -T, --cmdtime\tMilliseconds before each whole AT command (def. %dms)\n\
- -f, --file\tRead contents of message from file instead\n\
  -v, --verbose\tIncrease verbosity level, more \"-v\"s give more messages\n\
  -h, --help\tPrint a summary of the options\n\
  -V, --version\tPrint the version number\n\
-\n",version,DEF_LOCKFILE,DEF_MAXRETRY,DEF_READTIME,DEF_CHARTIME,DEF_CMDTIME);
+ <GSMnet id>\t* Oper. logo: Enter custom network code MccMnc, e.g. 23002\n\
+\t\t* Oper. logo: Specify \"" WORD_NET "\" to read network code from NOL file\n\
+\t\t* Group gfx : Specify \"" WORD_GROUP "\" to send logo as group graphics\n\
+\n",version,DEF_BAUD,DEF_LOCKFILE,DEF_MAXRETRY,DEF_READTIME,DEF_CHARTIME,DEF_CMDTIME);
        exit(EXIT_FAILURE);
 }
 
 static const struct option longopts[]={
 {"config"  ,1,0,'c'},
 {"device"  ,1,0,'d'},
-{"log"     ,1,0,'l'},
+{"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'}};
@@ -319,6 +312,7 @@ static struct {
        } optset[]={
                { 'd',&device   },
                { 'L',&logname  },
+               { 'b',&baud     },
                { 'l',&lockfile },
                { 's',&smsc     },
                { 'm',&maxretry },
@@ -343,7 +337,7 @@ int i;
                                }
                        chk(cfgstack[cfgstacki++]=strdup(optarg));
                        break;
-               case 'd': case 'L': case 'l': case 's': case 'm': case 'r': case 't': case 'T':
+               case 'd': case 'L': case 'b': case 'l': case 's': case 'm': case 'r': case 't': case 'T':
                        for (i=0;i<NELEM(optset);i++)
                                if (optset[i].c==optc) {
                                        if (optset[i].stamp && optset[i].stamp!=seq) {
@@ -357,9 +351,6 @@ int i;
                                        }
                        assert(i<NELEM(optset));
                        break;
-               case 'f':
-                       readbody++;
-                       break;
                case 'v':
                        verbose++;
                        break;
@@ -372,26 +363,51 @@ int i;
                        usage();
                        break;
                }
-       if (!phone && optind<argp)
-               chk(phone=strdup(args[optind++]));
-       if (!body && optind<argp) {
-char *d;
-int i;
+       if (!phone && optind<argp) {
+char *s,*s1;
 
-               for (i=optind,bodylen=argp-optind;i<argp;i++)
-                       bodylen+=strlen(args[i]);
-               chk(body=malloc(bodylen));
-               for (d=body,i=optind;i<argp;i++) {
-size_t l=strlen(args[i]);
-                       memcpy(d,args[i],l);
-                       d+=l;
-                       *d++=' ';
-                       assert(d<=body+bodylen);
+               chk(phone=strdup(args[optind++]));
+               s=(s1=phone)+(*phone=='+');
+               while (*s && s-s1<MAXNUMLEN)
+                       if (!isdigit(*s))
+                               error("!Invalid digit '%c' in destination phone number - at offset %d of option %d from \"%s\": %s",
+                                       *s,s-phone,optind,from,args[optind]);
+               if (*s)
+                       error("!Destination phone number too long, max. %d digits allowed in option %d from \"%s\": %s",
+                               s-s1,optind,from,args[optind]);
+               }
+       if (!logoname && optind<argp)
+               chk(logoname=strdup(args[optind++]));
+       if (!gsmnet && optind<argp) {
+char *s,*d,e=0;
+
+               chk(gsmnet=strdup(args[optind++]));
+               if (strcasecmp(gsmnet,WORD_NET) && strcasecmp(gsmnet,WORD_GROUP)) {
+                       for (d=s=gsmnet;*s;s++) {
+                               if (isdigit(*s)) { *d++=*s; continue; }
+                               if (isspace(*s)) continue;
+                               error("\nInvalid characted '%c' in GSMnet at offs %d: %s",
+                                       *s,s-gsmnet,args[optind]);
+                               e=1;
+                               break;
+                               }
+                       if ((d-gsmnet)!=5) {
+                               error("\nGSMnet is required to have exactly 5 digits or to be\n\
+either \"" WORD_NET "\" or \"" WORD_GROUP "\", but found length %d: %s",
+                                       d-gsmnet,args[optind]);
+                               e=1;
+                               }
+                       if (!e) *d='\0';
+                       else {
+                               error("\nGSMnet option %d from \"%s\" rejected due to previous errors: %s",
+                                       optind,from,args[optind]);
+                               free(gsmnet);
+                               gsmnet=NULL;
+                               }
                        }
-               assert(d==body+bodylen);
-               d[-1]='\0';
-               bodylen--;
                }
+       while (optind<argp)
+               error("\nExcessive option %d from \"%s\" ignored: %s",optind,from,args[optind]);
        while (cfgstacki) {
 char *s=cfgstack[--cfgstacki];
 
@@ -408,11 +424,12 @@ static const struct {
        } nullcheck[]={
                {&phone,"destination phone number"},
 #if 0
-               {&body ,"body text"},
+               {&logoname,"logo filename"},
+               {&gsmnet,"GSM operator network code"},
                {&device,"device for communication"},
 #endif
        };
-static char **emptycheck[]={&logname,&smsc,&body};
+static char **emptycheck[]={&logname,&smsc,&logoname,&gsmnet};
 
 static void lockclose(int fd)
 {
@@ -625,29 +642,6 @@ err:
        return(catchdata);
 }
 
-static int prepaddr(unsigned char *d,const char *addr)
-{
-int tot=0;
-char flip=0,plus;
-unsigned char n;
-
-       if ((plus=(*addr=='+'))) addr++;
-       *++d=(plus?ADDR_INT:ADDR_NAT);
-       while (*addr) {
-               if (*addr<'0' || *addr>'9')
-                       error("!Error during conversion of number at: %s",addr);
-               tot++;
-               n=(*addr++)-'0';
-               if ((flip=!flip)) *++d=0xF0|n;
-               else *d=(*d&0x0F)|(n<<4U);
-               }
-       return(tot);
-}
-
-static char *finalsmsc;
-#define SMSCBINSIZE (1+1+(MAXNUMLEN+1)/2)
-static char pdusmsc[SMSCBINSIZE*2+1];
-
 static inline char tohex(unsigned char x)
 {
        x&=0x0F;
@@ -655,7 +649,7 @@ static inline char tohex(unsigned char x)
                  return(x-10+'A');
 }
 
-static void textconv(char *d,unsigned char *s,size_t len)
+static inline void textconv(char *d,unsigned char *s,size_t len)
 {
        while (len--) {
                *d++=tohex(*s>>4U);
@@ -669,14 +663,13 @@ 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);
        s=devcmd(NULL,"\n+CSCA:","\r\nAT+CSCA?");
        while (isspace(*s)) s++;
        if (!*s || !strcmp(s,"EMPTY"))
-               error("!No SMS set in A1 found");
-       if (verbose>=1) error("\nFound default SMSC in A1: %s",s);
+               error("!No SMS set in Nokia found");
+       if (verbose>=1) error("\nFound default SMSC in Nokia: %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);
        e=t++;
@@ -689,73 +682,99 @@ unsigned char bin[2+(MAXNUMLEN+1)/2];
        if (l==ADDR_NAT || s[1]=='+') s++;
        else *s='+';
        *e='\0';
-       chk(finalsmsc=strdup(s));
-       if (verbose>=2) error("\nDecoded SMSC address: %s",finalsmsc);
-       bin[0]=1+(prepaddr(bin,finalsmsc)+1)/2;
-       textconv(pdusmsc,bin,bin[0]+1);
+       if (verbose>=2) error("\nDecoded SMSC address: %s",s);
 }
 
-static char *pdudata;
+/* 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
+ * http://www.forum.nokia.com/developers/smartmsg/download/ssm2_0_0.pdf
+ */
 
-static inline unsigned char charconv(char c,size_t offs)
-{
-       switch (c) {
-               case '@': return(0);
-               case '$': return(2);
-               case 0: assert(0);
-               default:
-                       return(c&0x7F);
-               }
-#if 0
-       if ((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9')) return(c);
-       error("Can't convert character '%c' (0x%02X) at offs %d (0-based), substituted '?'",
-               c,(unsigned char)c,offs);
-       return('?');
-#endif
-}
+static char hexdata[140*2+1];
 
-static inline void genpdu(void)
+static inline void logoread(void)
 {
-static unsigned char pdu[64+MAXNUMLEN/2+(MAXBODYLEN*7)/8];
-unsigned char *d=pdu;
-int i;
-char inb=0,outb=0,xb;
-unsigned char inreg;
-size_t offs=0;
-
-       *d++=PDU_TYPE;
-       *d++=PDU_MR;
-       i=prepaddr(d,phone);
-       *d=i; d+=1+1+(i+1)/2;
-       *d++=PDU_PID;
-       *d++=PDU_DCS;
-       *d++=PDU_VP;
-       if (bodylen>MAXBODYLEN) {
-               error("Body too large (%d>%d), cut",bodylen,MAXBODYLEN);
-               bodylen=MAXBODYLEN;
-               }
-       *d=bodylen;
-       assert(d<pdu+sizeof(pdu));
-       while (bodylen || inb) {
-               if (!inb) {
-                       assert(bodylen>0); assert(!!*body);
-                       inreg=charconv(*body++,offs++);
-                       bodylen--;
-                       inb=7;
-                       }
-               if (!outb) {
-                       *++d=0x00;
-                       outb=8;
+FILE *f;
+char buf[32+140*8+1];
+unsigned char bin[140]={
+       0x06, /* UDH length */
+       0x05, /* IEI */
+       0x04, /* IEDL */
+       0x15, 0x83, /* dest port (group gfx) */
+       0x00, 0x00  /* src port (unused) */
+       };
+size_t got,r,w;
+ssize_t chars,bits;
+char gsmnetf[10];
+int sizex,sizey,bit;
+
+#define WORD(n) (((unsigned char)buf[(n)])|(((unsigned char)buf[(n)+1])<<8))
+
+       if (!(f=fopen(logoname,"rb")))
+               error("!Cannot open logo file \"%s\" for r/o: %m",logoname);
+       got=fread(buf,1,sizeof(buf),f);
+            if (got>=20 && !memcmp(buf,"NOL",4)) {
+               VARPRINTF2(gsmnetf,"%03.3u%02.2u",WORD(6),WORD(8));
+               assert(strlen(gsmnetf)==5);
+               r=10;
+               if (verbose>=1) error(".Reading NOL file \"%s\", GSMnet \"%s\", word@4=%d..",
+                       logoname,gsmnetf,WORD(4));
+               }
+       else if (got>=16 && !memcmp(buf,"NGG",4)) {
+               r=6;
+               if (verbose>=1) error(".Reading NGG file \"%s\", word@4=%d..",
+                       logoname,gsmnetf,WORD(4));
+               }
+       else error("!Unknown file format of logo file \"%s\"");
+       if (!strcasecmp(gsmnet,WORD_NET)) {
+               if (!*gsmnetf) error("!NOL network code detection requested but NOL file not loaded, please specify network code");
+               gsmnet=gsmnetf;
+               }
+       if (!strcasecmp(gsmnet,WORD_GROUP) || !*gsmnet) {
+               error("\nSending logo as: group graphics");
+               gsmnet=NULL;
+               }
+       else {
+               error("\nSending logo as: operator logo for \"%s\"",gsmnet);
+               bin[4]=0x82; /* dest port 0x1582 */
+               }
+       
+       sizex=WORD(r); sizey=WORD(r+2);
+       if (verbose>=2) error(".Magic words: @+4=%d, @+6=%d, @+8=%d",
+                       WORD(r+4),WORD(r+6),WORD(r+8));
+       r+=10;
+       if (sizex<1 || sizex>255
+        || sizey<1 || sizey>255) error("!Invalid size: %dx%d",sizex,sizey);
+       chars=((bits=sizex*sizey)+7)/8;
+       if (r+bits>got) error("!Logo file \"%s\" too short - actual=%d, need(%dx%d)=%d",
+               logoname,got,sizex,sizey,r+chars);
+       else if (r+bits<got)
+               if (verbose>=1) error("Ignoring trailing garbage in \"%s\", used only %d bytes",logoname,r+bits);
+       if ((got=(7+(gsmnet?3:0)+4+chars))>140)
+               error("!SMS size would be %d bytes but 140 is maximum",got);
+       w=7;
+       if (gsmnet) {
+               bin[w++]=((gsmnet[1]&0x0F)<<4)|(gsmnet[0]&0x0F);
+               bin[w++]=0xF0                 |(gsmnet[2]&0x0F);
+               bin[w++]=((gsmnet[4]&0x0F)<<4)|(gsmnet[3]&0x0F);
+               }
+       bin[w++]=0x00; /* RFU by Nokia */
+       bin[w++]=sizex; bin[w++]=sizey;
+       bin[w++]=0x01; /* one B/W plane */
+       while (chars--) {
+               bin[w]=0;
+               for (bit=0x80;(bits>0) && (bit>0);bits--,bit>>=1) {
+                       if (buf[r]!='0' && buf[r]!='1')
+                               error("!Invalid character (neither '0' nor '1')in logo file \"%s\" at offset 0x%X",
+                                       logoname,r);
+                       if (buf[r++]=='1') bin[w]|=bit;
                        }
-               xb=MIN(inb,outb);
-               d4("inb=%d,outb=%d,xb=%d\n",inb,outb,xb);
-               *d|=((inreg>>(unsigned)(7-inb))&((1<<xb)-1))<<(unsigned)(8-outb);
-               inb-=xb; outb-=xb;
-               }
-       d++;
-       assert(d<pdu+sizeof(pdu));
-       pdudata=malloc(2*(d-pdu)+1);
-       textconv(pdudata,pdu,d-pdu);
+               w++;
+               }
+       assert(chars==-1); assert(bits==0); assert(w==got); assert(w<=140);
+       textconv(hexdata,bin,w);
+       if (verbose>=2) error("\nWill send hexdata: %s",hexdata);
 }
 
 static struct {
@@ -767,14 +786,15 @@ static struct {
                { &readtime,&readtimen,"readtime" },
                { &chartime,&chartimen,"chartime" },
                { &cmdtime ,&cmdtimen ,"cmdtime"  },
+               { &baud    ,&baudn    ,"baud"     },
        };
 
 int main(int argc,char **argv)
 {
-char *s,*finame;
-FILE *fin;
+char *s;
 int i;
 unsigned fatal=0;
+speed_t portbaud;
 
        if ((s=strrchr((pname=*argv),'/'))) pname=s+1;
        atexit(cleanup);
@@ -808,33 +828,7 @@ char *buf=malloc(l+50);
        if (!logname) logname=DEF_LOGNAME;
        if (!lockfile) lockfile=DEF_LOCKFILE;
        if (!device) device=DEF_DEVICE;
-       if (body && readbody) {
-               finame=body;
-               body=NULL;
-               }
-       else {
-               finame=NULL;
-               fin=stdin;
-               }
-       if (!body) {
-               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: %m",finame);
-                       }
-               chk(body=malloc(BODYLOAD));
-               bodylen=fread(body,1,BODYLOAD,fin);
-               if (bodylen==-1)
-                       error("!Error reading stream \"%s\": %m",(finame?finame:"<stdin>"));
-               if (finame) {
-                       chkfclose(fin,finame);
-                       free(finame);
-                       }
-               }
+       logoread();
 
        for (i=0;i<NELEM(numarg);i++) {
 char *serr;
@@ -870,7 +864,17 @@ size_t l=strlen(device);
                        error("!Error opening log \"%s\" for append: %m",logname);
                logmsg("Starting up: " PACKAGE " " VERSION);
                }
-       genpdu();
+       switch (baudn) {
+               case  2400: portbaud= B2400; break;
+               case  4800: portbaud= B4800; break;
+               case  9600: portbaud= B9600; break;
+               case 19200: portbaud=B19200; break;
+               case 38400: portbaud=B38400; break;
+               case 57600: portbaud=B57600; break;
+               default:
+                       error("!Specified baudrate %d is not supported",baudn);
+               }
+       if (verbose>=2) error(".Will use baudrate %d with hexval 0x%X",baudn,portbaud);
                
        if (lockfile && *lockfile && VARPRINTF(lockreal,lockfile,devicename)>0) {
 time_t start,end;
@@ -893,13 +897,13 @@ time_t start,end;
                }
        tios.c_iflag=IGNBRK|IGNPAR|IXON|IXOFF;
        tios.c_oflag=0;
-       tios.c_cflag=CS8|CREAD|CLOCAL|B19200|HUPCL;
+       tios.c_cflag=CS8|CREAD|CLOCAL|HUPCL|portbaud;
        tios.c_lflag=IEXTEN|NOFLSH;
        memset(tios.c_cc,_POSIX_VDISABLE,sizeof(tios.c_cc));
        tios.c_cc[VTIME]=0;
        tios.c_cc[VMIN ]=1;
-           cfsetispeed(&tios,B19200);
-       if (cfsetospeed(&tios,B19200)|cfsetispeed(&tios,B19200))
+           cfsetispeed(&tios,portbaud);
+       if (cfsetospeed(&tios,portbaud)|cfsetispeed(&tios,portbaud))
                error("Error setting termios baudrate on device: %m");
        if (tcflush(devfd,TCIOFLUSH))
                error("Error flushing termios (TCIOFLUSH) on device: %m");
@@ -908,13 +912,18 @@ time_t start,end;
 
        signal(SIGALRM,(RETSIGTYPE (*)(int))sigalarm);
        do {
-               devcmd("",NULL,"\r\nAT\032");
+               devcmd("",NULL,"\r\nAT\033");
                devcmd(NULL,NULL,"\r\nAT");
                smscset();
-               devcmd(NULL,NULL,"\r\nAT+CMGF=0");
-               devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",(strlen(pdusmsc)+strlen(pdudata))/2);
-               if (!(s=devcmd(NULL,"\n+CMGS:","!%s%s\032",pdusmsc,pdudata))) retrying();
-               } while (!s);
+               devcmd(NULL,NULL,"\r\nAT+CSMP=81,,0,245");
+               devcmd("\n> ",NULL,"\r\nAT+CMGS=\"%s\"",phone);
+               if (!(s=devcmd(NULL,"\n+CMGS:","!%s\032",hexdata))) {
+                       retrying();
+                       continue;
+                       }
+               devcmd(NULL,NULL,"\r\nAT+CSMP=17,,0,0");
+               devcmd(NULL,NULL,"\r\nAT");
+               } while (0);
        while (isspace(*s)) s++;
        if (verbose>=1) error("\nMessage successfuly sent with MR: %s",s);
        devcmd(NULL,NULL,"\r\nAT");