Initial untested implementation of --ring-send.
[mdsms.git] / mdsms.c
diff --git a/mdsms.c b/mdsms.c
index 845c629..849035d 100644 (file)
--- a/mdsms.c
+++ b/mdsms.c
@@ -101,6 +101,8 @@ static size_t bodylen;
 static char *body;
 /* --logo-send specific */
 static char *logoname,*gsmnet;
+/* --ring-send specific */
+static char *ringname;
 
 static enum modenum {
   MODE_UNKNOWN=0,
@@ -109,7 +111,8 @@ static enum modenum {
        MODE_SEND          =MODE_FIRST+0, /* --send / --send-mobildock */
        MODE_SEND_MOBILDOCK=MODE_FIRST+1, /* --send-mobildock in before readtimen is set */
        MODE_RECEIVE       =MODE_FIRST+2, /* --receive */
-       MODE_LOGO_SEND     =MODE_FIRST+3  /* --logo-send */
+       MODE_LOGO_SEND     =MODE_FIRST+3, /* --logo-send */
+       MODE_RING_SEND     =MODE_FIRST+4  /* --ring-send */
        } mode=MODE_UNKNOWN;
 #define MODE_ORDER(x) ((x)-MODE_FIRST)
 #define MODE_NAME(x) (longopts[MODE_ORDER((x))].name)
@@ -240,6 +243,8 @@ Usage: " PACKAGE " [-c|--config <cfgfile>] [-d|--device <device>]\n\
              <command name>\n\
   --logo-send:\n\
              <dest. phone> <logo filename> [<GSMnet id>]\n\
+  --ring-send:\n\
+             <dest. phone> <ring filename>\n\
 \n\
  -c, --config\tRead this additional config file\n\
 \t\t(def. \"" CONFIG_MAIN "\" and \"$HOME" CONFIG_HOME "\")\n\
@@ -284,10 +289,13 @@ static const struct option longopts[]={
 {"send-mobildock",0,0,MODE_SEND_MOBILDOCK},
 {"receive"       ,0,0,MODE_RECEIVE},
 {"logo-send"     ,0,0,MODE_LOGO_SEND},
+{"ring-send"     ,0,0,MODE_RING_SEND},
 /* Mode aliases may follow in no particular order *
  * as long as no non-mode options is between them */
 {"send-md"       ,0,0,MODE_SEND_MOBILDOCK},
 {"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'},
@@ -314,6 +322,18 @@ static void chkfclose(FILE *f,const char *fname)
                error("^Error closing \"%s\"",fname);
 }
 
+static long getfilesize(FILE *f,const char *fname)
+{
+long size;
+
+       if (fseek(f,0,SEEK_END))
+               error("^Error seeking to end of \"%s\"",fname);
+       if ((size=ftell(f))<0)
+               size=-1,error("^Error measuring \"%s\"",fname);
+       rewind(f);
+       return(size);
+}
+
 static void readfile(const char *fname,char quiet)
 {
 FILE *f;
@@ -334,14 +354,10 @@ static unsigned tot=0;
                }
                
        if (verbose>=2) error(".Reading config file \"%s\"",fname);
-       if (fseek(f,0,SEEK_END))
-               error("^Error seeking to end of \"%s\"",fname);
-       if ((size=ftell(f))<0)
-               size=0,error("^Error measuring \"%s\"",fname);
+       if ((size=getfilesize(f,fname))==-1) size=0;
        if (size>MAXCONFIG) 
                error("File \"%s\" is too long, read only %d bytes",fname,MAXCONFIG);
        chk(buf=malloc((size?size:MAXCONFIG)+1));
-       rewind(f);
        got=fread(buf,1,(size?size:MAXCONFIG),f);
        if (size && got!=size)
                error("File \"%s\" read error, got only %u bytes of %ld",fname,got,size);
@@ -534,6 +550,7 @@ int i;
                case MODE_SEND_MOBILDOCK:
                case MODE_RECEIVE:
                case MODE_LOGO_SEND:
+               case MODE_RING_SEND:
                        if (mode_stamp && mode_stamp!=seq) break;
                        mode=optc;
                        mode_stamp=seq;
@@ -572,6 +589,7 @@ static const struct nullcheck {
                {&phone,MODE_BIT(MODE_SEND)|MODE_BIT(MODE_SEND_MOBILDOCK)|MODE_BIT(MODE_LOGO_SEND),
                        "destination phone number"},
                {&logoname,MODE_BIT(MODE_LOGO_SEND),"logo filename"},
+               {&ringname,MODE_BIT(MODE_RING_SEND),"ring filename"},
                {&body,MODE_BIT(MODE_RECEIVE),"body text"}, /* we allow empty bodies for SENDs */
 #if 0
                {&gsmnet,"GSM operator network code",MODE_LOGO_SEND},
@@ -689,6 +707,12 @@ either \"" WORD_NET "\" or \"" WORD_GROUP "\", but found length %d: %s",
                }
 }
 
+static inline void cmdline_ring_send(void)
+{
+       cmdline_phone();
+       if (!ringname) ringname=nextargstack();
+}
+
 static void lockclose(int fd)
 {
        if (close(fd))
@@ -946,7 +970,7 @@ skipread:
                buf[bufl2]='\0';
                s=buf+bufl;
                while (buf+bufl2>s && (s=memchr(s,'\0',buf+bufl2-s))) *s++=REPL_NULLCHAR;
-               if (verbose>=2)
+               if (verbose>=3)
                        error("\nGot chunk of data from device: %s",reform(buf+bufl,0));
                if (convcr) {
                        s=buf+bufl;
@@ -1084,7 +1108,22 @@ static inline unsigned char charconv(char c,size_t offs)
  */
 
 static char *pdudata;
-static char hexdata[140*2+1];
+static struct hexdata {
+       struct hexdata *next;
+       char data[140*2+1];
+       } *hexdata,**hexdatatail=&hexdata;
+
+static void nokiaprep(unsigned char *bin,size_t w)
+{
+struct hexdata *hd;
+       assert(w<=140);
+       chk(hd=malloc(sizeof(*hd)));
+       *hexdatatail=hd;
+       hd->next=NULL;
+       hexdatatail=&hd->next;
+       textconv(hd->data,bin,w);
+       if (verbose>=2) error("\nWill send hexdata: %s",hd->data);
+}
 
 static inline void logoread(void)
 {
@@ -1107,6 +1146,7 @@ int sizex,sizey,bit;
        if (!(f=fopen(logoname,"rb")))
                error("^!Cannot open logo file \"%s\" for r/o",logoname);
        got=fread(buf,1,sizeof(buf),f);
+       chkfclose(f,logoname);
             if (got>=20 && !memcmp(buf,"NOL",4)) {
                VARPRINTF2(gsmnetf,"%03.3u%02.2u",WORD(6),WORD(8));
                assert(strlen(gsmnetf)==5);
@@ -1165,9 +1205,79 @@ int sizex,sizey,bit;
                        }
                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);
+       assert(chars==-1); assert(bits==0); assert(w==got);
+       nokiaprep(bin,w);
+#undef WORD
+}
+
+static inline void ringread(void)
+{
+FILE *f;
+unsigned char bin1[140]={
+       6, /* UDH length */
+       0x05, /* IEI */
+       0x04, /* IEDL */
+       0x15, 0x81, /* dest port (ring tones) */
+       0x15, 0x81  /* src port (unused) */
+#define BIN1_PAYLOAD (140-7)
+       };
+unsigned char binn[140]={
+       11, /* UDH length */
+       0x05, /* IEI */
+       0x04, /* IEDL */
+       0x15, 0x81, /* dest port (ring tones) */
+       0x15, 0x81, /* src port (unused) */
+       0x00, 0x03, /* multipart */
+       /* 0x??, unique serial ID */
+       /* 0x??, total messages */
+       /* 0x??, message number (# from 1) */
+#define BINN_PAYLOAD (140-12)
+       };
+size_t got,want;
+int totn,fragn;
+long size;
+
+#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",logoname);
+       if ((size=getfilesize(f,logoname))==-1)
+               error("!File size is essential to continue operation");
+       if (size<0x103)
+               error("!File \"%s\" size %ld too small (must >=0x103)! Is it .000 file?",
+                       logoname,size);
+       if (fseek(f,0x100,SEEK_SET))
+               error("^Seeking error on \"%s\", ignoring",logoname);
+       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("\nSending ring tone \"%s\" as single SMS (size %ld, max %d)",
+                       ringname,size,BIN1_PAYLOAD);
+               nokiaprep(bin1,7+size);
+               }
+       else {
+               totn=(size+BINN_PAYLOAD-1)/BINN_PAYLOAD;
+               if (totn>0xFF)
+                       error("!File size %ld too large for multi-SMS ring upload (max=%d)",
+                               size,BINN_PAYLOAD*0xFF);
+               binn[10]=totn;
+               if (verbose>=1)
+                       error("\nSending ring tone \"%s\" as %d multi-SMSes (size %ld, max %d, frag %d)",
+                               ringname,totn,size,BIN1_PAYLOAD,BINN_PAYLOAD);
+               binn[9]=time(NULL)&0x100; /* rand() would be better but it is a compatibility pain */
+               if (verbose>=1)
+                       error("\nUsing unique multi-SMS ID 0x%02X",(unsigned)binn[9]);
+               for (fragn=1;fragn<=totn;fragn++) {
+                       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);
+                       nokiaprep(binn,12+want);
+                       size-=want;
+                       }
+               }
+       chkfclose(f,logoname);
 #undef WORD
 }
 
@@ -1513,7 +1623,7 @@ enum modenum argsmode;
        for (i=0;i<NELEM(longopts);i++) {
                if (longopts[i].val<MODE_FIRST) break;
                if (!strstr(pname,longopts[i].name)) continue;
-               if (mode==MODE_UNKNOWN) {
+               if (mode==MODE_UNKNOWN || mode==longopts[i].val) {
                        mode=longopts[i].val;
                        continue;
                        }
@@ -1547,6 +1657,7 @@ char *buf=malloc(l+50);
                case MODE_SEND:           /* FALLTHRU */
                case MODE_SEND_MOBILDOCK: cmdline_send     (); break;
                case MODE_LOGO_SEND:      cmdline_logo_send(); break;
+               case MODE_RING_SEND:      cmdline_ring_send(); break;
                case MODE_RECEIVE:        cmdline_receive  (); break;
                default: assert(0);
                }
@@ -1612,6 +1723,9 @@ size_t l=strlen(device);
                case MODE_LOGO_SEND:
                        logoread();
                        break;
+               case MODE_RING_SEND:
+                       ringread();
+                       break;
                case MODE_RECEIVE: break;
                default: assert(0);
                }
@@ -1687,14 +1801,22 @@ retrysendcmgf:
                                        }
                                break;
                        case MODE_LOGO_SEND:
+                       case MODE_RING_SEND: {
+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);
-                               s=devcmd(NULL,"\n+CMGS:","!~%s\032",hexdata);
-                               break;
+                               while ((hd=hexdata)) {
+                                       if (!(s=devcmd(NULL,"\n+CMGS:","!~%s\032",hd->data))) break;
+                                       hexdata=hd->next;
+                                       free(hd);
+                                       }
+                               } 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 */