libsms_getsms.c

Go to the documentation of this file.
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("failed to convert into integer used_memory"
00236                            " from CPMS 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("failed to convert into integer max_memory"
00246                            " from CPMS 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,
00467                                           struct incame_sms *sms)
00468 {
00469    int ret;
00470 
00471    memset( sms, 0, sizeof(struct incame_sms) );
00472    /* Ok, now we split the PDU string into parts and show it */
00473    if (mdm->mode==MODE_ASCII || mdm->mode==MODE_DIGICOM)
00474       ret = splitascii(mdm, pdu, sms);
00475    else
00476       ret = splitpdu(mdm, pdu, sms);
00477 
00478    if (ret==-1) {
00479       LM_ERR("failed to split pdu/ascii!\n");
00480       return -1;
00481    }
00482    return 1;
00483 }
00484 
00485 
00486 
00487 
00488 int getsms( struct incame_sms *sms, struct modem *mdm, int sim)
00489 {
00490    char   pdu[512];
00491    int    found;
00492    int    ret;
00493 
00494    found = fetchsms(mdm,sim,pdu);
00495    if ( !found ) {
00496       LM_ERR("failed to fetch sms %d!\n",sim);
00497       return -1;
00498    }
00499 
00500    /* decode the pdu */
00501    ret = decode_pdu(mdm,pdu,sms);
00502 
00503    /* delete the sms*/
00504    deletesms(mdm,found);
00505 
00506    return ret;
00507 }
00508 
00509 
00510 
00511 
00512 int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len)
00513 {
00514    char *data;
00515    char *ptr;
00516    char tmp;
00517    int  n;
00518 
00519    /* pdu starts after 2 "\r\n" */
00520    ptr = s;
00521    for ( n=0 ; n<2 && (ptr=strstr(ptr,"\r\n")) ; n++,ptr+=2 );
00522    if (n<2) {
00523       LM_ERR("failed to find pdu begining in CDS!\n");
00524       goto error;
00525    }
00526    data = ptr;
00527 
00528    /* pdu end with "\r\n" */
00529    if (!(ptr=strstr(data,"\r\n"))) {
00530       LM_ERR("failed to find pdu end in CDS!\n");
00531       goto error;
00532       }
00533    tmp = ptr[0];
00534    ptr[0] = 0;
00535 
00536    /* decode the pdu */
00537    n = decode_pdu(mdm,data-3,sms);
00538    ptr[0] = tmp;
00539    if (n==-1)
00540       goto error;
00541 
00542    return 1;
00543 error:
00544    return -1;
00545 }
00546 

Generated on Wed May 23 18:00:28 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6