You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/sms/libsms_getsms.c

546 lines
12 KiB

/*
SMS Server Tools
Copyright (C) 2000-2002 Stefan Frings
This program is free software unless you got it under another license directly
from the author. You can redistribute it and/or modify it under the terms of
the GNU General Public License as published by the Free Software Foundation.
Either version 2 of the License, or (at your option) any later version.
http://www.isis.de/members/~s.frings
mailto:s.frings@mail.isis.de
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include "../../ut.h"
#include "libsms_charset.h"
#include "libsms_modem.h"
#include "libsms_sms.h"
#include "sms_funcs.h"
#define set_date(_date,_Pointer) {\
(_date)[0] = (_Pointer)[3];\
(_date)[1] = (_Pointer)[2];\
(_date)[2] = '-';\
(_date)[3] = (_Pointer)[5];\
(_date)[4] = (_Pointer)[4];\
(_date)[5] = '-';\
(_date)[6] = (_Pointer)[1];\
(_date)[7] = (_Pointer)[0];}
#define set_time( _time , _Pointer) {\
(_time)[0] = (_Pointer)[1];\
(_time)[1] = (_Pointer)[0];\
(_time)[2] = ':';\
(_time)[3] = (_Pointer)[3];\
(_time)[4] = (_Pointer)[2];\
(_time)[5] = ':';\
(_time)[6] = (_Pointer)[5];\
(_time)[7] = (_Pointer)[4];}
/* converts an octet to a 8-Bit value */
static inline int octet2bin(char* octet)
{
int result=0;
if (octet[0]>57)
result=octet[0]-55;
else
result=octet[0]-48;
result=result<<4;
if (octet[1]>57)
result+=octet[1]-55;
else
result+=octet[1]-48;
return result;
}
/* converts a PDU-String to Ascii; the first octet is the length
return the length of ascii */
static int pdu2ascii(char* pdu, char* ascii)
{
int bitposition=0;
int byteposition;
int byteoffset;
int charcounter;
int bitcounter;
int count;
int octetcounter;
char c;
char binary[500];
/* First convert all octets to bytes */
count=octet2bin(pdu);
for (octetcounter=0; octetcounter<count; octetcounter++)
binary[octetcounter]=octet2bin(pdu+(octetcounter<<1)+2);
/* Then convert from 8-Bit to 7-Bit encapsulated in 8 bit */
for (charcounter=0; charcounter<count; charcounter++) {
c=0;
for (bitcounter=0; bitcounter<7; bitcounter++) {
byteposition=bitposition/8;
byteoffset=bitposition%8;
if (binary[byteposition]&(1<<byteoffset))
c=c|128;
bitposition++;
c=(c>>1)&127; /* The shift fills with 1, but I want 0 */
}
if (/*cs_convert*/1)
ascii[charcounter]=sms2ascii(c);
else if (c==0)
ascii[charcounter]=183;
else
ascii[charcounter]=c;
}
ascii[count]=0;
return count;
}
static int pdu2binary(char* pdu, char* binary)
{
int count;
int octetcounter;
count=octet2bin(pdu);
for (octetcounter=0; octetcounter<count; octetcounter++)
binary[octetcounter]=octet2bin(pdu+(octetcounter<<1)+2);
binary[count]=0;
return count;
}
/* reads a SMS from the SIM-memory 1-10 */
/* returns number of SIM memory if successful */
/* on digicom the return value can be != sim */
static int fetchsms(struct modem *mdm, int sim, char* pdu)
{
char command[16];
char answer[512];
char* position;
char* beginning;
char* end;
int foo,err;
int clen;
// Digicom reports date+time only with AT+CMGL
if (mdm->mode==MODE_DIGICOM) {
put_command(mdm,"AT+CMGL=\"ALL\"\r",14,answer,
sizeof(answer),200,0);
/* search for beginning of the answer */
position=strstr(answer,"+CMGL: ");
if (position) {
end=position+7;
while (*end<'9' && *end>'0') end++;
if (end==position+7) {
foo = str2s(position+7,end-position-7,&err);
if (!err) {
LM_DBG("Found a message at memory %i\n",foo);
sim=foo;
}
position = 0;
}
position = 0;
}
} else {
LM_DBG("Trying to get stored message %i\n",sim);
clen=sprintf(command,"AT+CMGR=%i\r",sim);
put_command(mdm,command,clen,answer,sizeof(answer),50,0);
/* search for beginning of the answer */
position=strstr(answer,"+CMGR:");
}
/* keine SMS empfangen, weil Modem nicht mit +CMGR
oder +CMGL geantwortet hat */
if (position==0)
return 0;
beginning=position+7;
/* keine SMS, weil Modem mit +CMGR: 0,,0 geantwortet hat */
if (strstr(answer,",,0\r"))
return 0;
/* After that we have the PDU or ASCII string */
for( end=beginning ; *end && *end!='\r' ; end++ );
if ( !*end || end-beginning<4)
return 0;
for( end=end+1 ; *end && *end!='\r' ; end++ );
if ( !*end || end-beginning<4)
return 0;
/* Now we have the end of the PDU or ASCII string */
*end=0;
strcpy(pdu,beginning);
return sim;
}
/* deletes the selected sms from the sim card */
static void deletesms(struct modem *mdm, int sim) {
char command[32];
char answer[128];
int clen;
LM_DBG("Deleting message %i !\n",sim);
clen = sprintf(command,"AT+CMGD=%i\r",sim);
put_command(mdm, command, clen, answer, sizeof(answer), 50, 0);
}
// checks the size of the SIM memory
int check_memory(struct modem *mdm, int flag)
{
char answer[500];
char* posi;
int laenge;
int err,foo;
int j, out;
for(out=0,j=0;!out && j<10; j++)
{
if (put_command(mdm,"AT+CPMS?\r",9,answer,sizeof(answer),50,0)
&& (posi=strstr(answer,"+CPMS:"))!=0 )
{
// Modem supports CPMS command. Read memory size
if ( (posi=strchr(posi,','))!=0 ) {
posi++;
if ( (laenge=strcspn(posi,",\r"))!=0 ) {
if (flag==USED_MEM ) {
foo = str2s(posi,laenge,&err);
if (err) {
LM_ERR("unable to convert into integer used_memory from CPMS"
" response\n");
} else {
return foo;
}
}
posi+=laenge+1;
if ( (laenge=strcspn(posi,",\r"))!=0 ) {
foo = str2s(posi,laenge,&err);
if (err) {
LM_ERR("unable to convert into integer max_memory from CPMS"
" response\n");
} else {
return foo;
}
}
}
} /* if(strstr) */
} /* if(put_command) */
/* if we are here -> some error happend */
if (checkmodem(mdm)!=0) {
LM_WARN("something happend with the modem -> was reinit -> let's retry\n");
} else {
LM_ERR("modem seems to be ok, but we had an error? I give up!\n");
out = 1;
}
} /* for */
if (out==0)
LM_ERR("modem does not respond after 10 retries, give up!\n");
return -1;
}
/* splits an ASCII string into the parts */
/* returns length of ascii */
static int splitascii(struct modem *mdm, char *source, struct incame_sms *sms)
{
char* start;
char* end;
/* the text is after the \r */
for( start=source ; *start && *start!='\r' ; start++ );
if (!*start)
return 1;
start++;
strcpy(sms->ascii,start);
/* get the senders MSISDN */
start=strstr(source,"\",\"");
if (start==0) {
sms->userdatalength=strlen(sms->ascii);
return 1;
}
start+=3;
end=strstr(start,"\",");
if (end==0) {
sms->userdatalength=strlen(sms->ascii);
return 1;
}
*end=0;
strcpy(sms->sender,start);
/* Siemens M20 inserts the senders name between MSISDN and date */
start=end+3;
// Workaround for Thomas Stoeckel //
if (start[0]=='\"')
start++;
if (start[2]!='/') { // if next is not a date is must be the name
end=strstr(start,"\",");
if (end==0) {
sms->userdatalength=strlen(sms->ascii);
return 1;
}
*end=0;
strcpy(sms->name,start);
}
/* Get the date */
start=end+3;
sprintf(sms->date,"%c%c-%c%c-%c%c",start[3],start[4],start[0],start[1],
start[6],start[7]);
/* Get the time */
start+=9;
sprintf(sms->time,"%c%c:%c%c:%c%c",start[0],start[1],start[3],start[4],
start[7],start[7]);
sms->userdatalength=strlen(sms->ascii);
return 1;
}
/* Subroutine for splitpdu() for messages type 0 (SMS-Deliver)
Returns the length of the ascii string
In binary mode ascii contains the binary SMS */
static int split_type_0( char* Pointer,struct incame_sms *sms)
{
int Length;
int padding;
int is_binary;
Length=octet2bin(Pointer);
padding=Length%2;
Pointer+=4;
memcpy(sms->sender,Pointer,Length+padding);
swapchars(sms->sender,Length+padding);
/* remove Padding characters after swapping */
sms->sender[Length]=0;
Pointer=Pointer+Length+padding+3;
is_binary = ((Pointer[0] & 4)==4);
Pointer++;
set_date(sms->date,Pointer);
Pointer=Pointer+6;
set_time(sms->time,Pointer);
Pointer=Pointer+8;
if (is_binary)
sms->userdatalength = pdu2binary(Pointer,sms->ascii);
else
sms->userdatalength = pdu2ascii(Pointer,sms->ascii);
return 1;
}
/* Subroutine for splitpdu() for messages type 2 (Staus Report)
Returns the length of the ascii string. In binary mode ascii
contains the binary SMS */
static int split_type_2( char* position, struct incame_sms *sms)
{
int length;
int padding;
char *p;
/* get from report the sms id */
sms->sms_id = octet2bin(position);
position+=2;
/* get recipient address */
length=octet2bin(position);
padding=length%2;
position+=4;
memcpy(sms->sender,position,length+padding);
sms->sender[length]=0;
swapchars(sms->sender,length);
// get SMSC timestamp
position+=length+padding;
set_date(sms->date,position);
set_time(sms->time,position+6);
// get Discharge timestamp
position+=14;
p = sms->ascii + 2;
set_date(p,position);
*(p+DATE_LEN) = ' ';
set_time(p+DATE_LEN+1,position+6);
// get Status
position+=14;
sms->ascii[0] = (unsigned char)octet2bin(position);
sms->ascii[1] = ' ';
sms->ascii[2+DATE_LEN+1+TIME_LEN] = 0;
sms->userdatalength=2+DATE_LEN+1+TIME_LEN;
return 1;
}
/* Splits a PDU string into the parts */
/* Returns the length of the ascii string. In binary mode ascii contains the binary SMS */
static int splitpdu(struct modem *mdm, char* pdu, struct incame_sms *sms)
{
int Length;
int Type;
char* Pointer;
char* start;
char* end;
/* Get the senders Name if given. Depends on the modem. */
start=strstr(pdu,"\",\"");
if (start!=0) {
start+=3;
end=strstr(start,"\",");
if (end!=0) {
memcpy(sms->name,start,end-start);
sms->name[end-start]=0;
}
} else
end=pdu;
/* the pdu is after the first \r */
for( start=end+1 ; *start && *start!='\r' ; start++ );
if (!*start)
return 0;
pdu=++start;
/* removes unwanted ctrl chars at the beginning */
while ( *pdu && (*pdu<=' '))
pdu++;
Pointer=pdu;
if (mdm->mode!=MODE_OLD) {
/* get senders smsc */
Length=octet2bin(pdu)*2-2;
if (Length>0) {
Pointer=pdu+4;
memcpy(sms->smsc,Pointer,Length);
swapchars(sms->smsc,Length);
/* remove Padding characters after swapping */
if (sms->smsc[Length-1]=='F')
sms->smsc[Length-1]=0;
else
sms->smsc[Length] = 0;
}
Pointer=pdu+Length+4;
}
/* is UDH bit set? udh=(octet2bin(Pointer)&4) */
Type=octet2bin(Pointer) & 3;
Pointer+=2;
if (Type==0) {
sms->is_statusreport = 0; /*SMS Deliver*/
return split_type_0( Pointer, sms);
} else if (Type==2) {
sms->is_statusreport = 1; /*Status Report*/
return split_type_2( Pointer, sms);
}
/*Unsupported type*/
return -1;
}
static inline int decode_pdu( struct modem *mdm, char *pdu, struct incame_sms *sms)
{
int ret;
memset( sms, 0, sizeof(struct incame_sms) );
/* Ok, now we split the PDU string into parts and show it */
if (mdm->mode==MODE_ASCII || mdm->mode==MODE_DIGICOM)
ret = splitascii(mdm, pdu, sms);
else
ret = splitpdu(mdm, pdu, sms);
if (ret==-1) {
LM_ERR("unable split pdu/ascii!\n");
return -1;
}
return 1;
}
int getsms( struct incame_sms *sms, struct modem *mdm, int sim)
{
char pdu[512];
int found;
int ret;
found = fetchsms(mdm,sim,pdu);
if ( !found ) {
LM_ERR("unable to fetch sms %d!\n",sim);
return -1;
}
/* decode the pdu */
ret = decode_pdu(mdm,pdu,sms);
/* delete the sms*/
deletesms(mdm,found);
return ret;
}
int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len)
{
char *data;
char *ptr;
char tmp;
int n;
/* pdu starts after 2 "\r\n" */
ptr = s;
for ( n=0 ; n<2 && (ptr=strstr(ptr,"\r\n")) ; n++,ptr+=2 );
if (n<2) {
LM_ERR("failed to find pdu beginning in CDS!\n");
goto error;
}
data = ptr;
/* pdu end with "\r\n" */
if (!(ptr=strstr(data,"\r\n"))) {
LM_ERR("failed to find pdu end in CDS!\n");
goto error;
}
tmp = ptr[0];
ptr[0] = 0;
/* decode the pdu */
n = decode_pdu(mdm,data-3,sms);
ptr[0] = tmp;
if (n==-1)
goto error;
return 1;
error:
return -1;
}