libsms_getsms.c

00001 /*
00002 SMS Server Tools
00003 Copyright (C) 2000-2002 Stefan Frings
00004 
00005 This program is free software unless you got it under another license directly
00006 from the author. You can redistribute it and/or modify it under the terms of
00007 the GNU General Public License as published by the Free Software Foundation.
00008 Either version 2 of the License, or (at your option) any later version.
00009 
00010 http://www.isis.de/members/~s.frings
00011 mailto:s.frings@mail.isis.de
00012  */
00013 
00014 
00015 
00016 #include <sys/types.h>
00017 #include <sys/stat.h>
00018 #include <fcntl.h>
00019 #include <termios.h>
00020 #include <stdio.h>
00021 #include <string.h>
00022 #include <unistd.h>
00023 #include <syslog.h>
00024 #include "../../ut.h"
00025 #include "libsms_charset.h"
00026 #include "libsms_modem.h"
00027 #include "libsms_sms.h"
00028 #include "sms_funcs.h"
00029 
00030 
00031 
00032 #define set_date(_date,_Pointer) {\
00033         (_date)[0] = (_Pointer)[3];\
00034         (_date)[1] = (_Pointer)[2];\
00035         (_date)[2] = '-';\
00036         (_date)[3] = (_Pointer)[5];\
00037         (_date)[4] = (_Pointer)[4];\
00038         (_date)[5] = '-';\
00039         (_date)[6] = (_Pointer)[1];\
00040         (_date)[7] = (_Pointer)[0];}
00041 
00042 #define set_time( _time , _Pointer) {\
00043         (_time)[0] = (_Pointer)[1];\
00044         (_time)[1] = (_Pointer)[0];\
00045         (_time)[2] = ':';\
00046         (_time)[3] = (_Pointer)[3];\
00047         (_time)[4] = (_Pointer)[2];\
00048         (_time)[5] = ':';\
00049         (_time)[6] = (_Pointer)[5];\
00050         (_time)[7] = (_Pointer)[4];}
00051 
00052 
00053 
00054 
00055 /* converts an octet to a 8-Bit value */
00056 static inline int octet2bin(char* octet)
00057 {
00058         int result=0;
00059 
00060         if (octet[0]>57)
00061                 result=octet[0]-55;
00062         else
00063                 result=octet[0]-48;
00064         result=result<<4;
00065         if (octet[1]>57)
00066                 result+=octet[1]-55;
00067         else
00068                 result+=octet[1]-48;
00069         return result;
00070 }
00071 
00072 
00073 
00074 
00075 /* converts a PDU-String to Ascii; the first octet is the length
00076    return the length of ascii */
00077 static int pdu2ascii(char* pdu, char* ascii)
00078 {
00079         int bitposition=0;
00080         int byteposition;
00081         int byteoffset;
00082         int charcounter;
00083         int bitcounter;
00084         int count;
00085         int octetcounter;
00086         char c;
00087         char binary[500];
00088 
00089         /* First convert all octets to bytes */
00090         count=octet2bin(pdu);
00091         for (octetcounter=0; octetcounter<count; octetcounter++)
00092                 binary[octetcounter]=octet2bin(pdu+(octetcounter<<1)+2);
00093 
00094         /* Then convert from 8-Bit to 7-Bit encapsulated in 8 bit */
00095         for (charcounter=0; charcounter<count; charcounter++) {
00096                 c=0;
00097                 for (bitcounter=0; bitcounter<7; bitcounter++) {
00098                         byteposition=bitposition/8;
00099                         byteoffset=bitposition%8;
00100                         if (binary[byteposition]&(1<<byteoffset))
00101                                 c=c|128;
00102                         bitposition++;
00103                         c=(c>>1)&127; /* The shift fills with 1, but I want 0 */
00104                 }
00105                 if (/*cs_convert*/1)
00106                         ascii[charcounter]=sms2ascii(c);
00107                 else if (c==0)
00108                         ascii[charcounter]=183;
00109                 else
00110                         ascii[charcounter]=c;
00111         }
00112         ascii[count]=0;
00113         return count;
00114 }
00115 
00116 
00117 
00118 
00119 static int pdu2binary(char* pdu, char* binary)
00120 {
00121         int count;
00122         int octetcounter;
00123 
00124         count=octet2bin(pdu);
00125         for (octetcounter=0; octetcounter<count; octetcounter++)
00126                 binary[octetcounter]=octet2bin(pdu+(octetcounter<<1)+2);
00127         binary[count]=0;
00128         return count;
00129 }
00130 
00131 
00132 
00133 
00134 /* reads a SMS from the SIM-memory 1-10 */
00135 /* returns number of SIM memory if successful */
00136 /* on digicom the return value can be != sim */
00137 static int fetchsms(struct modem *mdm, int sim, char* pdu)
00138 {
00139         char command[16];
00140         char answer[512];
00141         char* position;
00142         char* beginning;
00143         char* end;
00144         int  foo,err;
00145         int  clen;
00146 
00147         // Digicom reports date+time only with AT+CMGL
00148         if (mdm->mode==MODE_DIGICOM) {
00149                 put_command(mdm,"AT+CMGL=\"ALL\"\r",14,answer,
00150                         sizeof(answer),200,0);
00151                 /* search for beginning of the answer */
00152                 position=strstr(answer,"+CMGL: ");
00153                 if (position) {
00154                         end=position+7;
00155                         while (*end<'9' && *end>'0') end++;
00156                         if (end==position+7) {
00157                                 foo = str2s(position+7,end-position-7,&err);
00158                                 if (!err) {
00159                                         LM_DBG("Found a message at memory %i\n",foo);
00160                                         sim=foo;
00161                                 }
00162                                 position = 0;
00163                         }
00164                         position = 0;
00165                 }
00166         } else {
00167                 LM_DBG("Trying to get stored message %i\n",sim);
00168                 clen=sprintf(command,"AT+CMGR=%i\r",sim);
00169                 put_command(mdm,command,clen,answer,sizeof(answer),50,0);
00170                 /* search for beginning of the answer */
00171                 position=strstr(answer,"+CMGR:");
00172         }
00173 
00174         /* keine SMS empfangen, weil Modem nicht mit +CMGR 
00175         oder +CMGL geantwortet hat */
00176         if (position==0)
00177                 return 0;
00178         beginning=position+7;
00179         /* keine SMS, weil Modem mit +CMGR: 0,,0 geantwortet hat */
00180         if (strstr(answer,",,0\r"))
00181                 return 0;
00182 
00183         /* After that we have the PDU or ASCII string */
00184         for( end=beginning ; *end && *end!='\r' ; end++ );
00185         if ( !*end || end-beginning<4)
00186                 return 0;
00187         for( end=end+1 ; *end && *end!='\r' ; end++ );
00188         if ( !*end || end-beginning<4)
00189                 return 0;
00190         /* Now we have the end of the PDU or ASCII string */
00191         *end=0;
00192         strcpy(pdu,beginning);
00193 
00194         return sim;
00195 }
00196 
00197 
00198 
00199 
00200 /* deletes the selected sms from the sim card */
00201 static void deletesms(struct modem *mdm, int sim) {
00202         char command[32];
00203         char answer[128];
00204         int  clen;
00205 
00206         LM_DBG("Deleting message %i !\n",sim);
00207         clen = sprintf(command,"AT+CMGD=%i\r",sim);
00208         put_command(mdm, command, clen, answer, sizeof(answer), 50, 0);
00209 }
00210 
00211 
00212 
00213 
00214 // checks the size of the SIM memory
00215 int check_memory(struct modem *mdm, int flag)
00216 {
00217         char  answer[500];
00218         char* posi;
00219         int   laenge;
00220         int   err,foo;
00221         int   j, out;
00222 
00223         for(out=0,j=0;!out && j<10; j++) 
00224         {
00225                 if (put_command(mdm,"AT+CPMS?\r",9,answer,sizeof(answer),50,0)
00226                 && (posi=strstr(answer,"+CPMS:"))!=0 )
00227                 {
00228                         // Modem supports CPMS command. Read memory size
00229                         if ( (posi=strchr(posi,','))!=0 ) {
00230                                 posi++;
00231                                 if ( (laenge=strcspn(posi,",\r"))!=0 ) {
00232                                         if (flag==USED_MEM ) {
00233                                                 foo = str2s(posi,laenge,&err);
00234                                                 if (err) {
00235                                                         LM_ERR("unable to convert into integer used_memory from CPMS"
00236                                                                 " response\n");
00237                                                 } else {
00238                                                         return foo;
00239                                                 }
00240                                         }
00241                                         posi+=laenge+1;
00242                                         if ( (laenge=strcspn(posi,",\r"))!=0 ) {
00243                                                 foo = str2s(posi,laenge,&err);
00244                                                 if (err) {
00245                                                         LM_ERR("unable to convert into integer max_memory from CPMS"
00246                                                                 " response\n");
00247                                                 } else {
00248                                                         return foo;
00249                                                 }
00250                                         }
00251                                 }
00252                         } /* if(strstr) */
00253                 } /* if(put_command) */
00254                 /* if we are here ->  some error happend */
00255                 if (checkmodem(mdm)!=0) {
00256                         LM_WARN("something happend with the modem -> was reinit -> let's retry\n");
00257                 } else {
00258                         LM_ERR("modem seems to be ok, but we had an error? I give up!\n");
00259                         out = 1;
00260                 }
00261         } /* for */
00262 
00263         if (out==0)
00264                 LM_ERR("modem does not respond after 10 retries, give up!\n");
00265 
00266         return -1;
00267 }
00268 
00269 
00270 
00271 
00272 /* splits an ASCII string into the parts */
00273 /* returns length of ascii */
00274 static int splitascii(struct modem *mdm, char *source, struct incame_sms *sms)
00275 {
00276         char* start;
00277         char* end;
00278 
00279         /* the text is after the \r */
00280         for( start=source ; *start && *start!='\r' ; start++ );
00281         if (!*start)
00282                 return 1;
00283         start++;
00284         strcpy(sms->ascii,start);
00285         /* get the senders MSISDN */
00286         start=strstr(source,"\",\"");
00287         if (start==0) {
00288                 sms->userdatalength=strlen(sms->ascii);
00289                 return 1;
00290         }
00291         start+=3;
00292         end=strstr(start,"\",");
00293         if (end==0) {
00294                 sms->userdatalength=strlen(sms->ascii);
00295                 return 1;
00296         }
00297         *end=0;
00298         strcpy(sms->sender,start);
00299         /* Siemens M20 inserts the senders name between MSISDN and date */
00300         start=end+3;
00301         // Workaround for Thomas Stoeckel //
00302         if (start[0]=='\"')
00303                 start++;
00304         if (start[2]!='/')  { // if next is not a date is must be the name
00305                 end=strstr(start,"\",");
00306                 if (end==0) {
00307                         sms->userdatalength=strlen(sms->ascii);
00308                         return 1;
00309                 }
00310                 *end=0;
00311                 strcpy(sms->name,start);
00312         }
00313         /* Get the date */
00314         start=end+3;
00315         sprintf(sms->date,"%c%c-%c%c-%c%c",start[3],start[4],start[0],start[1],
00316                 start[6],start[7]);
00317         /* Get the time */
00318         start+=9;
00319         sprintf(sms->time,"%c%c:%c%c:%c%c",start[0],start[1],start[3],start[4],
00320                 start[7],start[7]);
00321         sms->userdatalength=strlen(sms->ascii);
00322         return 1;
00323 }
00324 
00325 
00326 
00327 
00328 /* Subroutine for splitpdu() for messages type 0 (SMS-Deliver)
00329    Returns the length of the ascii string
00330    In binary mode ascii contains the binary SMS */
00331 static int split_type_0( char* Pointer,struct incame_sms *sms)
00332 {
00333         int Length;
00334         int padding;
00335         int is_binary;
00336 
00337         Length=octet2bin(Pointer);
00338         padding=Length%2;
00339         Pointer+=4;
00340         memcpy(sms->sender,Pointer,Length+padding);
00341         swapchars(sms->sender,Length+padding);
00342         /* remove Padding characters after swapping */
00343         sms->sender[Length]=0;
00344         Pointer=Pointer+Length+padding+3;
00345         is_binary = ((Pointer[0] & 4)==4);
00346         Pointer++;
00347         set_date(sms->date,Pointer);
00348         Pointer=Pointer+6;
00349         set_time(sms->time,Pointer);
00350         Pointer=Pointer+8;
00351         if (is_binary)
00352                 sms->userdatalength = pdu2binary(Pointer,sms->ascii);
00353         else
00354                 sms->userdatalength = pdu2ascii(Pointer,sms->ascii);
00355         return 1;
00356 }
00357 
00358 
00359 
00360 
00361 /* Subroutine for splitpdu() for messages type 2 (Staus Report)
00362    Returns the length of the ascii string. In binary mode ascii 
00363    contains the binary SMS */
00364 static int split_type_2( char* position, struct incame_sms *sms)
00365 {
00366         int  length;
00367         int  padding;
00368         char *p;
00369 
00370         /* get from report the sms id */
00371         sms->sms_id = octet2bin(position);
00372         position+=2;
00373         /* get recipient address */
00374         length=octet2bin(position);
00375         padding=length%2;
00376         position+=4;
00377         memcpy(sms->sender,position,length+padding);
00378         sms->sender[length]=0;
00379         swapchars(sms->sender,length);
00380         // get SMSC timestamp
00381         position+=length+padding;
00382         set_date(sms->date,position);
00383         set_time(sms->time,position+6);
00384         // get Discharge timestamp
00385         position+=14;
00386         p = sms->ascii + 2;
00387         set_date(p,position);
00388         *(p+DATE_LEN) = ' ';
00389         set_time(p+DATE_LEN+1,position+6);
00390         // get Status
00391         position+=14;
00392         sms->ascii[0] = (unsigned char)octet2bin(position);
00393         sms->ascii[1] = ' ';
00394         sms->ascii[2+DATE_LEN+1+TIME_LEN] = 0;
00395 
00396         sms->userdatalength=2+DATE_LEN+1+TIME_LEN;
00397         return 1;
00398 }
00399 
00400 
00401 
00402 
00403 /* Splits a PDU string into the parts */
00404 /* Returns the length of the ascii string. In binary mode ascii contains the binary SMS */
00405 static int splitpdu(struct modem *mdm, char* pdu, struct incame_sms *sms)
00406 {
00407         int Length;
00408         int Type;
00409         char* Pointer;
00410         char* start;
00411         char* end;
00412 
00413         /* Get the senders Name if given. Depends on the modem. */
00414         start=strstr(pdu,"\",\"");
00415         if (start!=0) {
00416                 start+=3;
00417                 end=strstr(start,"\",");
00418                 if (end!=0) {
00419                         memcpy(sms->name,start,end-start);
00420                         sms->name[end-start]=0;
00421                 }
00422         } else
00423                 end=pdu;
00424 
00425         /* the pdu is after the first \r */
00426         for( start=end+1 ; *start && *start!='\r' ; start++ );
00427         if (!*start)
00428                 return 0;
00429         pdu=++start;
00430         /* removes unwanted ctrl chars at the beginning */
00431         while ( *pdu && (*pdu<=' '))
00432                 pdu++;
00433         Pointer=pdu;
00434         if (mdm->mode!=MODE_OLD) {
00435                 /* get senders smsc */
00436                 Length=octet2bin(pdu)*2-2;
00437                 if (Length>0) {
00438                         Pointer=pdu+4;
00439                         memcpy(sms->smsc,Pointer,Length);
00440                         swapchars(sms->smsc,Length);
00441                         /* remove Padding characters after swapping */
00442                         if (sms->smsc[Length-1]=='F')
00443                                 sms->smsc[Length-1]=0;
00444                         else
00445                                 sms->smsc[Length] = 0;
00446                 }
00447                 Pointer=pdu+Length+4;
00448         }
00449         /* is UDH bit set? udh=(octet2bin(Pointer)&4) */
00450         Type=octet2bin(Pointer) & 3;
00451         Pointer+=2;
00452         if (Type==0) {
00453                 sms->is_statusreport = 0; /*SMS Deliver*/
00454                 return split_type_0( Pointer, sms);
00455         } else if (Type==2) {
00456                 sms->is_statusreport = 1; /*Status Report*/
00457                 return split_type_2( Pointer, sms);
00458         }
00459         /*Unsupported type*/
00460         return -1;
00461 }
00462 
00463 
00464 
00465 
00466 static inline int decode_pdu( struct modem *mdm, char *pdu, struct incame_sms *sms)
00467 {
00468         int ret;
00469 
00470         memset( sms, 0, sizeof(struct incame_sms) );
00471         /* Ok, now we split the PDU string into parts and show it */
00472         if (mdm->mode==MODE_ASCII || mdm->mode==MODE_DIGICOM)
00473                 ret = splitascii(mdm, pdu, sms);
00474         else
00475                 ret = splitpdu(mdm, pdu, sms);
00476 
00477         if (ret==-1) {
00478                 LM_ERR("unable split pdu/ascii!\n");
00479                 return -1;
00480         }
00481         return 1;
00482 }
00483 
00484 
00485 
00486 
00487 int getsms( struct incame_sms *sms, struct modem *mdm, int sim)
00488 {
00489         char   pdu[512];
00490         int    found;
00491         int    ret;
00492 
00493         found = fetchsms(mdm,sim,pdu);
00494         if ( !found ) {
00495                 LM_ERR("unable to fetch sms %d!\n",sim);
00496                 return -1;
00497         }
00498 
00499         /* decode the pdu */
00500         ret = decode_pdu(mdm,pdu,sms);
00501 
00502         /* delete the sms*/
00503         deletesms(mdm,found);
00504 
00505         return ret;
00506 }
00507 
00508 
00509 
00510 
00511 int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len)
00512 {
00513         char *data;
00514         char *ptr;
00515         char tmp;
00516         int  n;
00517 
00518         /* pdu starts after 2 "\r\n" */
00519         ptr = s;
00520         for ( n=0 ; n<2 && (ptr=strstr(ptr,"\r\n")) ; n++,ptr+=2 );
00521         if (n<2) {
00522                 LM_ERR("failed to find pdu beginning in CDS!\n");
00523                 goto error;
00524         }
00525         data = ptr;
00526 
00527         /* pdu end with "\r\n" */
00528         if (!(ptr=strstr(data,"\r\n"))) {
00529                 LM_ERR("failed to find pdu end in CDS!\n");
00530                 goto error;
00531                 }
00532         tmp = ptr[0];
00533         ptr[0] = 0;
00534 
00535         /* decode the pdu */
00536         n = decode_pdu(mdm,data-3,sms);
00537         ptr[0] = tmp;
00538         if (n==-1)
00539                 goto error;
00540 
00541         return 1;
00542 error:
00543         return -1;
00544 }
00545