Fixes for 9110 functionality.
[mdsms.git] / mdsms.c
diff --git a/mdsms.c b/mdsms.c
index 845c629..bb64c69 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;
 static char *body;
 /* --logo-send specific */
 static char *logoname,*gsmnet;
+/* --ring-send specific */
+static char *ringname;
 
 static enum modenum {
   MODE_UNKNOWN=0,
 
 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_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)
        } 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\
              <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\
 \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},
 {"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},
 /* 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'},
 {"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);
 }
 
                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;
 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 (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));
        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);
        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_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;
                        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"},
                {&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},
                {&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))
 static void lockclose(int fd)
 {
        if (close(fd))
@@ -784,17 +808,15 @@ static const char *record;
 static char *catchdata;
 static size_t catchdatal,catchdatasiz;
 
 static char *catchdata;
 static size_t catchdatal,catchdatasiz;
 
-static void catched(const char *end)
+static void catched(const char *end,char edata)
 {
 size_t len;
 {
 size_t len;
-const char *p1,*p2;
+const char *p;
 
        if (!record) return;
        assert(end>=record);
 
        if (!record) return;
        assert(end>=record);
-       p1=memchr(record,'\n',end-record);
-       p2=memchr(record,'\r',end-record);
-       if (!p1 || (p1 && p2 && p2<p1)) p1=p2;
-       if ((len=(p1?p1:end)-record)) {
+       p=memchr(record,edata,end-record);
+       if ((len=(p?p:end)-record)) {
                if (!catchdata)
                        chk(catchdata=malloc((catchdatasiz=LINE_MAX)));
                if (catchdatal+len>catchdatasiz)
                if (!catchdata)
                        chk(catchdata=malloc((catchdatasiz=LINE_MAX)));
                if (catchdatal+len>catchdatasiz)
@@ -803,7 +825,7 @@ const char *p1,*p2;
                memcpy(catchdata+catchdatal,record,len);
                catchdatal+=len;
                }
                memcpy(catchdata+catchdatal,record,len);
                catchdatal+=len;
                }
-       record=(p1?NULL:end);
+       record=(p?NULL:end);
        assert(catchdatal<=catchdatasiz);
 }
 
        assert(catchdatal<=catchdatasiz);
 }
 
@@ -860,21 +882,21 @@ static size_t bufl;
 ssize_t got;
 char *hit,*s;
 va_list ap;
 ssize_t got;
 char *hit,*s;
 va_list ap;
-char errout,extend,convcr;
+char errout,extend,noconvcr,edata;
 long alarmtime;
 const char *osend;
 static const char emptystring[]="";
 
        if (!term) term="\nOK\n";
 long alarmtime;
 const char *osend;
 static const char emptystring[]="";
 
        if (!term) term="\nOK\n";
-       convcr=!strchr(term,'\r');
        if (!strcmp(send," ")) send=NULL; /* GCC formatstring-check workaround */
        if (!(osend=send)) send="";
        if (!strcmp(send," ")) send=NULL; /* GCC formatstring-check workaround */
        if (!(osend=send)) send="";
+       if ((noconvcr=(catch && *catch=='@'))) catch++;
        if ((errout=(*send=='!'))) send++;
        errout|=(maxretryn==-1);
        if ((extend=(*send=='~'))) send++;
        alarmtime=readtimen*(extend?EXT_READTIME:1);
        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, convcr=%d, osend=%p, bufl=%d, buf: %s\n",
-               alarmtime,errout,extend,convcr,osend,bufl,reform(buf,0));
+       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));
        if (0) {
 err:
                alarm(0);
        if (0) {
 err:
                alarm(0);
@@ -922,6 +944,7 @@ err:
        record=NULL;
        wasalarm=0;
        alarm(alarmtime);
        record=NULL;
        wasalarm=0;
        alarm(alarmtime);
+       edata=(noconvcr?'\r':'\n');
        if (!osend) {
                got=bufl;
                bufl=0;
        if (!osend) {
                got=bufl;
                bufl=0;
@@ -946,14 +969,14 @@ skipread:
                buf[bufl2]='\0';
                s=buf+bufl;
                while (buf+bufl2>s && (s=memchr(s,'\0',buf+bufl2-s))) *s++=REPL_NULLCHAR;
                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));
                        error("\nGot chunk of data from device: %s",reform(buf+bufl,0));
-               if (convcr) {
+               if (!noconvcr) {
                        s=buf+bufl;
                        while (buf+bufl2>s && (s=memchr(s,'\r',buf+bufl2-s))) *s++='\n';
                        }
                bufl=bufl2;
                        s=buf+bufl;
                        while (buf+bufl2>s && (s=memchr(s,'\r',buf+bufl2-s))) *s++='\n';
                        }
                bufl=bufl2;
-               catched(buf+bufl); assert(!record || record==buf+bufl);
+               catched(buf+bufl,edata); assert(!record || record==buf+bufl);
                assert(bufl<sizeof(buf)-1);
                buf[bufl]='\0';
                assert(strlen(buf)==bufl);
                assert(bufl<sizeof(buf)-1);
                buf[bufl]='\0';
                assert(strlen(buf)==bufl);
@@ -962,9 +985,9 @@ skipread:
                        error("Found ERROR response on command %s",reform(send,0));
                        goto err;
                        }
                        error("Found ERROR response on command %s",reform(send,0));
                        goto err;
                        }
-               if (catch && bufl>=catchl && (hit=strstr(buf,catch))) {
+               if (catch && !record && bufl>=catchl && (hit=strstr(buf,catch))) {
                        record=hit+catchl;
                        record=hit+catchl;
-                       catched(buf+bufl); assert(!record || record==buf+bufl);
+                       catched(buf+bufl,edata); assert(!record || record==buf+bufl);
                        }
                if (         bufl>= terml && (hit=strstr(buf,term))) {
                        memmove(buf,hit+terml,(bufl2=(buf+bufl)-(hit+terml))); bufl=bufl2;
                        }
                if (         bufl>= terml && (hit=strstr(buf,term))) {
                        memmove(buf,hit+terml,(bufl2=(buf+bufl)-(hit+terml))); bufl=bufl2;
@@ -972,7 +995,7 @@ skipread:
                        }
                if (bufl<fragl) continue;
                memmove(buf,buf+bufl-(fragl-1),(bufl2=fragl-1)); bufl=bufl2;
                        }
                if (bufl<fragl) continue;
                memmove(buf,buf+bufl-(fragl-1),(bufl2=fragl-1)); bufl=bufl2;
-               if (record) record=buf+bufl;
+               if (record) record=buf+fragl-1;
                }
        alarm(0);
        if (!catchdatal) {
                }
        alarm(0);
        if (!catchdatal) {
@@ -982,7 +1005,7 @@ skipread:
                }
        assert(!!catch);
        record=emptystring;
                }
        assert(!!catch);
        record=emptystring;
-       catched(record+1);
+       catched(record+1,edata);
        if (verbose>=2) error(".Returning data %s for cmd %s",reform(catchdata,0),reform(send,1));
        return(catchdata);
 }
        if (verbose>=2) error(".Returning data %s for cmd %s",reform(catchdata,0),reform(send,1));
        return(catchdata);
 }
@@ -1084,7 +1107,22 @@ static inline unsigned char charconv(char c,size_t offs)
  */
 
 static char *pdudata;
  */
 
 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)
 {
 
 static inline void logoread(void)
 {
@@ -1107,6 +1145,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);
        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);
             if (got>=20 && !memcmp(buf,"NOL",4)) {
                VARPRINTF2(gsmnetf,"%03.3u%02.2u",WORD(6),WORD(8));
                assert(strlen(gsmnetf)==5);
@@ -1165,9 +1204,79 @@ int sizex,sizey,bit;
                        }
                w++;
                }
                        }
                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
 }
 
 #undef WORD
 }
 
@@ -1513,7 +1622,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;
        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;
                        }
                        mode=longopts[i].val;
                        continue;
                        }
@@ -1547,6 +1656,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_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);
                }
                case MODE_RECEIVE:        cmdline_receive  (); break;
                default: assert(0);
                }
@@ -1612,6 +1722,9 @@ size_t l=strlen(device);
                case MODE_LOGO_SEND:
                        logoread();
                        break;
                case MODE_LOGO_SEND:
                        logoread();
                        break;
+               case MODE_RING_SEND:
+                       ringread();
+                       break;
                case MODE_RECEIVE: break;
                default: assert(0);
                }
                case MODE_RECEIVE: break;
                default: assert(0);
                }
@@ -1687,14 +1800,22 @@ retrysendcmgf:
                                        }
                                break;
                        case MODE_LOGO_SEND:
                                        }
                                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);
                                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";
                        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 */
                                devcmd(NULL,NULL,"\r\nAT+CNMI=,2");
                                unlockdevice(0);
                                /* Never bail-out when we got up to this point */
@@ -1716,11 +1837,11 @@ retrysendcmgf:
                                d1("Lock-device succeeded\n");
                                do {
                                        d1("Reading a message for us...\n");
                                d1("Lock-device succeeded\n");
                                do {
                                        d1("Reading a message for us...\n");
-                                       if (!(s=devcmd("\r","+CMT:"," ")))
+                                       if (!(s=devcmd("\r","@+CMT:"," ")))
                                                goto retryall;
                                        if (!(i=receive_headerparse(s)))
                                                error("Receive-header parsing failed on: %s",s);
                                                goto retryall;
                                        if (!(i=receive_headerparse(s)))
                                                error("Receive-header parsing failed on: %s",s);
-                                       if (!(s=devcmd("\r","\n"," ")))
+                                       if (!(s=devcmd("\r","@\n"," ")))
                                                goto retryall;
                                        if (i) receive_accept(s);
                                        if (!devcmd("\n",NULL," ")) /* eat last '\n' */
                                                goto retryall;
                                        if (i) receive_accept(s);
                                        if (!devcmd("\n",NULL," ")) /* eat last '\n' */