smsc_at.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:36k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /*
  2.  * smsc_at.c - implement interface to wireless modems using AT commands
  3.  *
  4.  * Yann Muller, Nick Clarey - 3G Lab, 2001.
  5.  * 
  6.  * Ericsson code by Chris Blown 30/01/2001 - Hinterlands Aust.
  7.  *
  8.  * Make sure your kannel configuration file contains the following lines
  9.  * to be able to use the AT SMSC:
  10.  *     group = smsc
  11.  *     smsc = at
  12.  *     modemtype = wavecom | premicell | siemens | siemens-tc35 | falcom |
  13.  *                 nokiaphone | ericsson
  14.  *     device = /dev/xxx 
  15.  */
  16. #include <errno.h>
  17. #include <stdarg.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include <fcntl.h>
  23. #include <ctype.h>
  24. #include <termios.h>
  25. #include <sys/time.h>
  26. #include <sys/types.h>
  27. #include <sys/socket.h>
  28. #include <netinet/in.h>
  29. #include <netdb.h>
  30. #include <sys/ioctl.h>
  31. #include <time.h>
  32. #include "gwlib/gwlib.h"
  33. #include "gwlib/charset.h"
  34. #include "smsc.h"
  35. #include "smsc_p.h"
  36. #include "sms.h"
  37. #ifndef CRTSCTS
  38. #define CRTSCTS 0
  39. #endif
  40. /* The number of times to attempt to send a message should sending fail */
  41. #define RETRY_SEND 3
  42. /******************************************************************************
  43.  * Prototypes for private functions
  44.  */
  45. static int at_data_read(int fd, Octstr *ostr);
  46. static int send_modem_command(int fd, char *cmd, int multiline);
  47. static int pdu_extract(SMSCenter *smsc, Octstr **ostr);
  48. static Msg *pdu_decode(Octstr *data);
  49. static Msg *pdu_decode_deliver_sm(Octstr *data);
  50. static int pdu_encode(Msg *msg, unsigned char *pdu, SMSCenter *smsc);
  51. static Octstr *convertpdu(Octstr *pdutext);
  52. static int hexchar(int hexc);
  53. static int encode7bituncompressed(Octstr *input, unsigned char *encoded, int offset);
  54. static int encode8bituncompressed(Octstr *input, unsigned char *encoded);
  55. static void decode7bituncompressed(Octstr *input, int len, Octstr *decoded, int offset);
  56. static int numtext(int num);
  57. /******************************************************************************
  58.  * Types of GSM modems (as used in kannel.conf: at_type=xxxx)
  59.  */
  60. #define WAVECOM         "wavecom"
  61. #define PREMICELL       "premicell"
  62. #define SIEMENS         "siemens"
  63. #define SIEMENS_TC35 "siemens-tc35"
  64. #define FALCOM          "falcom"
  65. #define NOKIAPHONE      "nokiaphone"
  66. #define ERICSSON        "ericsson"
  67. /******************************************************************************
  68.  * Message types defines
  69.  */
  70. #define AT_DELIVER_SM   0
  71. #define AT_SUBMIT_SM    1
  72. /******************************************************************************
  73.  * type of phone number defines
  74.  */
  75. #define PNT_UNKNOWN     0
  76. #define PNT_INTER       1
  77. #define PNT_NATIONAL    2
  78. /******************************************************************************
  79.  * Open the connection
  80.  *
  81.  * returns the file descriptor (fd) if ok, -1 on failure
  82.  */
  83. static int at_open_connection(SMSCenter *smsc) {
  84.         int fd = -1;
  85.         struct termios tios;
  86.         int ret;
  87.         
  88.         fd = open(smsc->at_serialdevice, O_RDWR|O_NONBLOCK|O_NOCTTY);
  89.         if(fd == -1) {
  90.                 error(errno, "at_open_data_link: error opening the character device <%s>",
  91.                       smsc->at_serialdevice);
  92.                 return -1;
  93.         }
  94.         tcgetattr(fd, &tios);
  95.         if((strcmp(smsc->at_modemtype, SIEMENS) == 0) 
  96.    || (strcmp(smsc->at_modemtype, SIEMENS_TC35) == 0)
  97.            || (strcmp(smsc->at_modemtype, NOKIAPHONE) == 0)) {
  98.                 cfsetospeed(&tios, B19200);  /* check radio pad parameter*/
  99.                 cfsetispeed(&tios, B19200);
  100.         } else {
  101.                 cfsetospeed(&tios, B9600);  /* check radio pad parameter*/
  102.                 cfsetispeed(&tios, B9600);
  103.         }
  104.         kannel_cfmakeraw(&tios);
  105.         /* parameters:
  106.          * IGNBRK, IGNPAR: ignore BREAK and PARITY errors
  107.          * INPCK: enable parity check
  108.          * CSIZE: for CS8
  109.          * HUPCL: hang up on close
  110.          * CREAD: enable receiver
  111.          * CRTSCTS: enable flow control */
  112.         tios.c_iflag |= IGNBRK | IGNPAR | INPCK;
  113.         tios.c_cflag |= CSIZE | HUPCL | CREAD | CRTSCTS;
  114.         if (strcmp(smsc->at_modemtype, NOKIAPHONE) == 0)
  115.                 tios.c_cflag ^= PARODD;
  116.         tios.c_cflag |=CS8;
  117.         ret = tcsetattr(fd, TCSANOW, &tios); /* apply changes now */
  118.         if(ret == -1){
  119.                 error(errno,"at_data_link: fail to set termios attribute");
  120.                 goto error;
  121.         }
  122.         tcflush(fd, TCIOFLUSH);
  123.         return fd;
  124.   
  125.  error:
  126.         return -1;
  127. }
  128. /******************************************************************************
  129.  * Open the (Virtual) SMSCenter
  130.  */
  131. SMSCenter *at_open(char *serialdevice, char *modemtype, char *pin,
  132.                    char *validityperiod, int alt_dcs) 
  133. {
  134.         SMSCenter *smsc;
  135.         char setpin[20];
  136.         int ret;
  137.         
  138.         smsc = smscenter_construct();
  139.         if(smsc == NULL)
  140.                 goto error;
  141.         smsc->type = SMSC_TYPE_AT;
  142.         smsc->at_serialdevice = gw_strdup(serialdevice);
  143.         if (validityperiod != NULL)
  144.                 smsc->at_validityperiod = gw_strdup(validityperiod);
  145.         smsc->at_modemtype = gw_strdup(modemtype);
  146.         if(pin)
  147.                 smsc->at_pin = gw_strdup(pin);
  148.         smsc->at_received = list_create();
  149.         smsc->at_inbuffer = octstr_create("");
  150. smsc->at_alt_dcs = alt_dcs;
  151.         
  152.         smsc->at_fd = at_open_connection(smsc);
  153.         if (smsc->at_fd < 0)
  154.                 goto error;
  155.         /* Nokia 7110 and 6210 need some time between opening
  156.          * the connection and sending the first AT commands */
  157.         if (strcmp(smsc->at_modemtype, NOKIAPHONE) == 0) 
  158.                 sleep(1);
  159. /* lets initialize the modem to a safe state */
  160.         send_modem_command(smsc->at_fd, "AT", 0);
  161.         send_modem_command(smsc->at_fd, "AT&F", 0);
  162.         send_modem_command(smsc->at_fd, "AT", 0);
  163.         /* Turn Echo off on the modem: we don't need it */
  164.         if(send_modem_command(smsc->at_fd, "ATE0", 0) == -1)
  165.   {
  166.   /* ok that was the first command we tried. */
  167.                 goto error;
  168. }
  169. /* Let's collect some information from modem */
  170. if(send_modem_command(smsc->at_fd, "ATI", 0) == -1)
  171.     goto error;
  172. sleep(1);
  173. if(send_modem_command(smsc->at_fd, "ATI1", 0) == -1)
  174.     goto error;
  175. sleep(1);
  176. if(send_modem_command(smsc->at_fd, "ATI2", 0) == -1)
  177.     goto error;
  178. sleep(1);
  179. if(send_modem_command(smsc->at_fd, "ATI3", 0) == -1)
  180.     goto error;
  181. sleep(1);
  182. if(send_modem_command(smsc->at_fd, "ATI4", 0) == -1)
  183.     goto error;
  184. sleep(1);
  185.         /* Check does the modem require a PIN and, if so, send it
  186.          * This is not supported by the Nokia Premicell */
  187.         if(strcmp(smsc->at_modemtype, PREMICELL) != 0) {
  188.                 ret = send_modem_command(smsc->at_fd, "AT+CPIN?", 0); 
  189.                 if(ret == -1)
  190.                         goto error;
  191.                 if(ret == -2) {
  192.                         if(smsc->at_pin == NULL)
  193.                                 goto error;
  194.                         sprintf(setpin, "AT+CPIN=%s", smsc->at_pin);
  195.                         if(send_modem_command(smsc->at_fd, setpin, 0) == -1)
  196.                                 goto error;
  197.                 }
  198.         }
  199.         /* Set the modem to PDU mode and autodisplay of new messages */
  200.         if(send_modem_command(smsc->at_fd, "AT+CMGF=0", 0) == -1)
  201.                 goto error;
  202. sleep(1);
  203.         /* The Ericsson GM12 modem requires different new message 
  204.          * indication options from the other modems */ 
  205.         if(strcmp(smsc->at_modemtype, ERICSSON) == 0) {
  206.                 if(send_modem_command(smsc->at_fd, "AT+CNMI=3,2,0,0", 0) == -1)
  207.                         goto error;
  208.         }
  209.         else if(strcmp(smsc->at_modemtype, SIEMENS_TC35) == 0) {
  210.                 if(send_modem_command(smsc->at_fd, "AT+CSMS=1", 0) == -1)
  211.                         goto error;
  212.                 if(send_modem_command(smsc->at_fd, "AT+CNMI=1,2,0,0,1",0)== -1)
  213. goto error;
  214. }
  215.         else {
  216.                 if(send_modem_command(smsc->at_fd, "AT+CNMI=1,2,0,0,0", 0) == -1)
  217.                         goto error;
  218.         }
  219.                 
  220.         sprintf(smsc->name, "AT: %s", smsc->at_serialdevice); 
  221.         
  222.         info(0, "AT SMSC successfully opened.");
  223.         return smsc;
  224.   
  225.  error:
  226.         return NULL;
  227. }
  228. /******************************************************************************
  229.  * Re-Open the AT (Virtual) SMSCenter
  230.  */
  231. int at_reopen(SMSCenter *smsc) {
  232.         /* Do we really have an open connection to start with? */
  233.         if (smsc->at_fd == -1) {
  234.                 info(0, "trying to close already closed AT, ignoring");
  235.         }
  236.         /* If we do, then try to close the file descriptor */
  237.         else if (close(smsc->at_fd) == -1) {
  238.                 /* This situation could occur as a result of errors not being reported until
  239.                    the serial connection is closed. Supposing we do get here, we
  240.                    should reset the at_fd value to -1 to stop infinitely retrying the close. We
  241.                    also need to printing out the error message reported, just in case it's
  242.                    significant.
  243.                 */
  244.                 smsc->at_fd = -1;
  245.                 error(errno, "Attempt to close connection to modem `%s' failed. Forcing reset.",
  246.                       smsc->at_serialdevice);
  247.         }
  248.         /* Reopen the connection. Note that the at_open_connection call returns
  249.            a file descriptor, which we should set in the smsc structure */
  250.         smsc->at_fd = at_open_connection(smsc);
  251.         /* Supposing that failed */
  252.         if (smsc->at_fd == -1) {
  253.                 error(0, "Attempt to open connection to modem '%s' failed.",
  254.                       smsc->at_serialdevice);
  255.                 /* Give up */
  256.                 return -1;
  257.         }
  258.         /* Report success */
  259.         return 0;
  260. }
  261. /******************************************************************************
  262.  * Close the SMSCenter
  263.  */
  264. int at_close(SMSCenter *smsc) {
  265.         /* Do we really have an open connection to start with? */
  266.         if (smsc->at_fd == -1) {
  267.                 info(0, "trying to close already closed AT, ignoring");
  268.         }
  269.         /* If we do, then try to close the file descriptor */
  270.         else if (close(smsc->at_fd) == -1) {
  271.                 error(errno, "Attempt to close connection to modem `%s' failed. Forcing reset.",
  272.                       smsc->at_serialdevice);
  273.         }
  274.         
  275.         /* Our file descriptor can now be safely declared closed */
  276.         smsc->at_fd = -1;
  277.         
  278.         /* Deallocate any miscellany */
  279.         smscenter_destruct(smsc);
  280.         /* Report success */
  281.         return 0;
  282. }
  283. /******************************************************************************
  284.  * Check for pending messages
  285.  */
  286. int at_pending_smsmessage(SMSCenter *smsc) {
  287.         Octstr *pdu = NULL;
  288.         int ret=0;
  289.         Msg *msg = NULL;
  290.         /* Receive raw data */
  291.         ret = at_data_read(smsc->at_fd, smsc->at_inbuffer);
  292.         if(ret == -1) {
  293.                 ret = at_reopen(smsc);
  294.                 if(ret == -1) goto error;
  295.                 return 0;
  296.         } 
  297.         ret = 0;
  298.         while( pdu_extract(smsc, &pdu) == 1) {
  299.                 msg = pdu_decode(pdu);
  300.                 if(msg != NULL) {
  301.                         list_append(smsc->at_received, (void *)msg);
  302.                         ret = 1;
  303.                 }
  304.                 octstr_destroy(pdu);
  305.         }
  306.         if(list_len(smsc->at_received) > 0)
  307.                 ret = 1;
  308.         return ret; 
  309.  error:
  310.         error(errno,"at_pending message: device error");
  311.         return -1;
  312. }
  313. /******************************************************************************
  314.  * Send a message
  315.  */
  316. int at_submit_msg(SMSCenter *smsc, Msg *msg) {
  317.         unsigned char command[500], pdu[500];
  318.         int ret = -1; 
  319.         char sc[3];
  320.         int retries = RETRY_SEND;
  321.         /* Some modem types need a '00' prepended to the PDU
  322.          * to indicate to use the default SC.
  323.          * NB: This extra padding is not counted in the CMGS byte count */
  324.         sc[0] = '';
  325.         if((strcmp(smsc->at_modemtype, WAVECOM ) == 0) || 
  326.            (strcmp(smsc->at_modemtype, SIEMENS ) == 0) ||
  327.            (strcmp(smsc->at_modemtype, SIEMENS_TC35 ) == 0) ||
  328.            (strcmp(smsc->at_modemtype, NOKIAPHONE ) == 0) ||
  329.            (strcmp(smsc->at_modemtype, ERICSSON ) == 0))
  330.                 strcpy(sc, "00");
  331.         
  332.         if(msg_type(msg)==sms) {
  333.                 pdu_encode(msg, &pdu[0], smsc);            
  334.                 sprintf(command, "AT+CMGS=%d", strlen(pdu)/2);
  335.                 if(send_modem_command(smsc->at_fd, command, 1) == 0)
  336.                 {
  337.                         sprintf(command, "%s%s%c", sc, pdu, 26);
  338.                         ret = send_modem_command(smsc->at_fd, command, 0);
  339.                         debug("AT", 0, "send command status: %d", ret);
  340.                         while (ret != 0 && retries > 0) {
  341. sprintf(command, "AT+CMGS=%d", strlen(pdu)/2);
  342. if(! send_modem_command(smsc->at_fd, command, 1) == 0)
  343.     break;
  344.                                sprintf(command, "%s%s%c", sc, pdu, 26);
  345.                                ret = send_modem_command(smsc->at_fd, command, 0);
  346.                                debug("AT", 0, "send command status: %d", ret);
  347.                                retries--;
  348.                         }
  349. if (retries<0){
  350.   debug("AT", 0, "AT: Retries exceeded nSMS send failure :");
  351.   debug("AT", 0, "Phone number: %s", octstr_get_cstr(msg->sms.receiver));
  352.   debug("AT", 0, "SMS data: %s", octstr_get_cstr(msg->sms.msgdata));
  353. }
  354.                 }
  355.         }
  356.         return ret;
  357. }
  358. /******************************************************************************
  359.  * There are messages to read!
  360.  */
  361. int at_receive_msg(SMSCenter *smsc, Msg **msg) {
  362.         *msg = list_consume(smsc->at_received);
  363.         if(*msg == NULL)
  364.                 goto error;
  365.                 
  366.         return 1;
  367.         
  368.  error:
  369.         return -1;
  370. }
  371. /******************************************************************************
  372.  * Reads from the modem
  373.  */
  374. static int at_data_read(int fd, Octstr *ostr) {
  375.         int ret;
  376.         fd_set read_fd;
  377.         struct timeval tv;
  378.         size_t readall;
  379.         char cbuffer[257];
  380.      
  381.         tv.tv_sec = 0;
  382.         tv.tv_usec = 1000;
  383.      
  384.         readall = 0;
  385.         for (;;) {
  386.                 memset(&cbuffer, 0, sizeof(cbuffer));
  387.         
  388.                 FD_ZERO(&read_fd);
  389.                 FD_SET(fd, &read_fd);
  390.                 ret = select(fd + 1, &read_fd, NULL, NULL, &tv);
  391.                 if (ret == -1) {
  392.                         if(errno==EINTR) goto got_data;
  393.                         if(errno==EAGAIN) goto got_data;
  394.                         error(errno, "Error doing select for fd");
  395.                         goto error;
  396.                 } else if (ret == 0)
  397.                         goto got_data;
  398.                 ret = read(fd, cbuffer, 256);
  399.                 if (ret == -1) {
  400.                         goto error;
  401.                 }
  402.                 if (ret == 0)
  403.                         goto eof;
  404.                 octstr_append_data(ostr, cbuffer, strlen(cbuffer));
  405.                 if(ret > 0)
  406.                         break;
  407.         }
  408.      
  409.  eof:
  410.         ret = 1;
  411.         goto unblock;
  412.      
  413.  got_data:
  414.         ret = 0;
  415.         goto unblock;
  416.  error:
  417.         error(errno," read device file");
  418.         ret = -1;
  419.         goto unblock;
  420.      
  421.  unblock:
  422.         return ret;  
  423. }
  424. /******************************************************************************
  425.  * Send an AT command to the modem
  426.  * returns 0 if OK, -1 on failure, -2 on SIM PIN needed.
  427.  * Set multiline to 1 if the command will expect more data to be sent.
  428.  */
  429. static int send_modem_command(int fd, char *cmd, int multiline) {
  430.         Octstr *ostr;
  431.         int ret, i;
  432.         ostr = octstr_create("");
  433.         /* debug */
  434.         debug("bb.smsc.at", 0, "AT: Command: %s", cmd);
  435.         
  436.         /* DEBUG !!! - pretend to send but just return success (0)*/
  437.         /* return 0; */
  438.         
  439.         /* send the command */  
  440.         write(fd, cmd, strlen(cmd));
  441.         write(fd, "r", 1);
  442.         /* We don't want to wait forever -
  443.          * This is not perfect but OK for now */
  444.         for( i=0; i<1000; i++) {
  445.                 ret = at_data_read(fd, ostr);
  446.                 /* debug */
  447.                 /*
  448.                   if(octstr_len(ostr)) {
  449.                   printf("Read from modem: ");
  450.                   for(i=0; i<octstr_len(ostr); i++) {
  451.                   if(octstr_get_char(ostr, i) <32)
  452.                   printf("[%02x] ", octstr_get_char(ostr, i));
  453.                   else
  454.                   printf("%c ", octstr_get_char(ostr, i));
  455.                   }
  456.                   printf("n");
  457.                   }*/
  458.                 
  459.                 if(ret == -1)
  460.                         goto error;
  461.                 ret = octstr_search(ostr, 
  462.                                     octstr_imm("SIM PIN"), 0);
  463.                 if(ret != -1) {
  464. ret = -2;
  465.                         goto error;
  466.                 }
  467.                 if(multiline)
  468.                         ret = octstr_search(ostr, 
  469.                                             octstr_imm(">"), 
  470.                                             0);
  471.                 else {
  472.                         ret = octstr_search(ostr, 
  473.                                             octstr_imm("OK"), 
  474.                                             0);
  475.                         if(ret == -1)
  476.                                 ret = octstr_search(ostr, 
  477.                                                     octstr_imm("READY"), 0);
  478.                         if(ret == -1)
  479.                                 ret = octstr_search(ostr,
  480.                                                     octstr_imm("CMGS"), 0);
  481.                 }
  482.                 if(ret != -1) {
  483. ret = 0;
  484.                         goto error;
  485.                 }
  486.                 ret = octstr_search(ostr, 
  487.                                     octstr_imm("ERROR"), 0);
  488.                 if(ret != -1) {
  489. ret = -1;
  490. goto error;
  491.                 }
  492.         }
  493. ret = -1;
  494. goto error;
  495.         
  496.  error:
  497. for (i=0; i< octstr_len(ostr); i++) {
  498.     if (octstr_get_char(ostr, i) < 32)
  499. octstr_set_char(ostr, i, ' ');
  500. }
  501. octstr_strip_blanks(ostr);
  502. debug("AT", 0, "Read from modem: '%s'",octstr_get_cstr(ostr) );
  503.         octstr_destroy(ostr);
  504.         return ret;
  505. }
  506. /******************************************************************************
  507.  * Extract the first PDU in the string
  508.  */
  509. static int pdu_extract(SMSCenter *smsc, Octstr **pdu) {
  510.         Octstr *buffer;
  511.         long len = 0;
  512.         int pos = 0;
  513.         int tmp;
  514.         buffer = smsc->at_inbuffer;
  515.         
  516.         /* find the beginning of a message from the modem*/     
  517.         pos = octstr_search(buffer, octstr_imm("+CMT:"), 0);
  518.         if(pos == -1) 
  519.                 goto nomsg;
  520.         pos += 5;
  521.         pos = octstr_search(buffer, octstr_imm(","), pos);
  522.         if(pos == -1)
  523.                 goto nomsg;
  524.         pos++;
  525.         /* The message length is after the comma */
  526.         pos = octstr_parse_long(&len, buffer, pos, 10);
  527.         if(pos == -1)
  528.                 goto nomsg;
  529.         /* skip the spaces and line return */
  530.         while( isspace(octstr_get_char(buffer, pos)))
  531.                 pos++;
  532.         
  533.         /* skip the SMSC address on some modem types */
  534.         if((strcmp(smsc->at_modemtype, WAVECOM) == 0) ||
  535.            (strcmp(smsc->at_modemtype, SIEMENS) == 0) ||
  536.            (strcmp(smsc->at_modemtype, ERICSSON) == 0) ||
  537.            (strcmp(smsc->at_modemtype, NOKIAPHONE) == 0) ) {
  538.                 tmp = hexchar(octstr_get_char(buffer, pos))*16
  539.                         + hexchar(octstr_get_char(buffer, pos+1));
  540.                 if (tmp < 0)
  541.                         goto nomsg;
  542.                 tmp = 2 + tmp * 2;
  543.                 pos += tmp;
  544.         }
  545.         
  546.         /* check if the buffer is long enough to contain the full message */
  547.         if( octstr_len(buffer) < len * 2 + pos)
  548.                 goto nomsg;
  549.         
  550.         /* copy the PDU then remove it from the input buffer*/
  551.         *pdu = octstr_copy(buffer, pos, len*2);
  552.         octstr_delete(buffer, 0, pos+len*2);
  553.         return 1;
  554.  nomsg:
  555.         return 0;
  556. }
  557. /******************************************************************************
  558.  * Decode a raw PDU into a Msg
  559.  */
  560. static Msg *pdu_decode(Octstr *data) {
  561.         int type;
  562.         Msg *msg = NULL;
  563.         /* Get the PDU type */
  564.         type = octstr_get_char(data, 1) & 3;
  565.         
  566.         switch(type) {
  567.         case AT_DELIVER_SM:
  568.                 msg = pdu_decode_deliver_sm(data);
  569.                 break;
  570.                 /* Add other message types here: */
  571.         
  572.         }
  573.         return msg;
  574. }
  575. /******************************************************************************
  576.  * Decode a DELIVER PDU
  577.  */
  578. static Msg *pdu_decode_deliver_sm(Octstr *data) {
  579.         int len, pos, i;
  580.         char origaddr[21];
  581.         int udhi, dcs, udhlen;
  582.         Octstr *origin = NULL;
  583.         Octstr *udh = NULL;
  584.         Octstr *text = NULL, *tmpstr;
  585.         Octstr *pdu = NULL;
  586.         Msg *message = NULL;
  587.         struct universaltime mtime;     /* time structure */
  588.         long stime;             /* time in seconds */
  589.         /* Note: some parts of the PDU are not decoded because they are
  590.          * not needed for the Msg type. */
  591.         /* convert the pdu to binary format for ease of processing */
  592.         pdu = convertpdu(data);
  593.         
  594.         /* UDH Indicator */
  595.         udhi = (octstr_get_char(pdu, 0) & 64) >> 6;
  596.         /* originating address */
  597.         len = octstr_get_char(pdu, 1);
  598.         pos = 3;
  599.         for(i=0; i<len; i+=2, pos++) {
  600.                 origaddr[i] = (octstr_get_char(pdu, pos) & 15) + 48 ;
  601.                 origaddr[i+1] = (octstr_get_char(pdu, pos) >> 4) + 48;
  602.         }
  603.         origaddr[i] = '';
  604.         origin = octstr_create_from_data(origaddr, len);
  605.         /* skip the PID for now */
  606.         pos++;
  607.         /* DCS */
  608. dcs = octstr_get_char(pdu, pos); 
  609.         pos++;
  610.         
  611.         /* get the timestamp */
  612.         mtime.year   = octstr_get_char(pdu, pos) + 1900; pos++;
  613.         mtime.month  = octstr_get_char(pdu, pos); pos++;
  614.         mtime.day    = octstr_get_char(pdu, pos); pos++;
  615.         mtime.hour   = octstr_get_char(pdu, pos); pos++;
  616.         mtime.minute = octstr_get_char(pdu, pos); pos++;
  617.         mtime.second = octstr_get_char(pdu, pos); pos++;
  618.         /* time zone: */
  619.         /* XXX handle negative time zones */
  620.         mtime.hour  += octstr_get_char(pdu, pos); pos++;
  621.         stime = date_convert_universal(&mtime);
  622.         
  623.         /* get data length */
  624.         len = octstr_get_char(pdu, pos);
  625.         pos++;
  626.         /* if there is a UDH */
  627.      udhlen = 0;
  628.         if(udhi) {
  629.                 udhlen = octstr_get_char(pdu, pos);
  630.                 pos++;
  631.                 udh = octstr_copy(pdu, pos, udhlen);
  632.                 pos += udhlen;
  633.                 len -= udhlen +1;
  634.         }
  635.         /* build the message */         
  636.         message = msg_create(sms);
  637. if (!dcs_to_fields(&message, dcs)) {
  638.     /* XXX Should reject this message ? */
  639.     debug("AT", 0, "Invalid DCS");
  640.     dcs_to_fields(&message, 0);
  641. }
  642.         /* deal with the user data -- 7 or 8 bit encoded */     
  643.         tmpstr = octstr_copy(pdu,pos,len);
  644.         if(message->sms.coding == DC_8BIT || message->sms.coding == DC_UCS2) {
  645.                 text = octstr_duplicate(tmpstr);
  646.         } else {
  647.                 int offset=0;
  648.                 text = octstr_create("");
  649.                 if (udhi && message->sms.coding == DC_7BIT) {
  650.                         int nbits;
  651.                         nbits = (udhlen + 1)*8;
  652.                         offset = (((nbits/7)+1)*7-nbits)%7;     /* Fill bits for UDH to septet boundary */
  653.                 }
  654.                 decode7bituncompressed(tmpstr, len, text, offset);
  655.         }
  656.         message->sms.sender = origin;
  657.         /* Put a dummy address in the receiver for now (SMSC requires one) */
  658.         message->sms.receiver = octstr_create_from_data("1234", 4);
  659.         /*message->sms.receiver = destination;*/
  660.         if (udhi) {
  661.                 message->sms.udhdata = udh;
  662.         }
  663.         message->sms.msgdata = text;
  664.         message->sms.time = stime;
  665.         /* cleanup */
  666.         octstr_destroy(pdu);
  667.         octstr_destroy(tmpstr);
  668.         
  669.         return message;
  670. }
  671. /******************************************************************************
  672.  * Encode a Msg into a PDU
  673.  */
  674. static int pdu_encode(Msg *msg, unsigned char *pdu, SMSCenter *smsc) {
  675.         int pos = 0, i,len, setvalidity=0;
  676.         int ntype = PNT_UNKNOWN; /* number type default */
  677.         int nstartpos = 0;       /* offset for the phone number */
  678.         int dcs;  /* data coding scheme (GSM 03.38) */
  679.         
  680.         /* The message is encoded directly in the text representation of 
  681.          * the hex values that will be sent to the modem.
  682.          * Each octet is coded with two characters. */
  683.         
  684.         /* message type SUBMIT
  685.          *    01010001 = 0x51 indicating add. UDH, TP-VP(Rel) & MSG_SUBMIT
  686.          * or 00010001 = 0x11 for just TP-VP(Rel) & MSG_SUBMIT */
  687.         pdu[pos] = octstr_len(msg->sms.udhdata) ? numtext(5) : numtext(1);
  688.         pos++;
  689.         pdu[pos] = numtext(AT_SUBMIT_SM);
  690.         pos++;
  691.         
  692.         /* message reference (0 for now) */
  693.         pdu[pos] = numtext(0);
  694.         pos++;
  695.         pdu[pos] = numtext(0);
  696.         pos++;
  697.         
  698.         /* destination address */
  699.         octstr_strip_blanks(msg->sms.receiver); /* strip blanks before length calculation */
  700.         len = octstr_len(msg->sms.receiver);
  701.         /* Check for international numbers
  702.          * number starting with '+' or '00' are international,
  703.          * others are national. */
  704.         if (strncmp(octstr_get_cstr(msg->sms.receiver), "+", 1) == 0) {
  705.                 debug("AT", 0, "international starting with + (%s)",octstr_get_cstr(msg->sms.receiver) );
  706.                 nstartpos++;
  707.                 ntype = PNT_INTER; /* international */
  708.         } else if (strncmp(octstr_get_cstr(msg->sms.receiver), "00", 2) == 0) {
  709.                 debug("AT", 0, "international starting with 00 (%s)",octstr_get_cstr(msg->sms.receiver) );
  710.                 nstartpos += 2;
  711.                 ntype = PNT_INTER; /* international */
  712.         }
  713.         
  714.         /* address length */
  715.         pdu[pos] = numtext(((len - nstartpos) & 240) >> 4);
  716.         pos++;
  717.         pdu[pos] = numtext((len - nstartpos) & 15);
  718.         pos++;
  719.                         
  720.         /* Type of number */
  721.         pdu[pos] = numtext(8 + ntype);
  722.         pos++;
  723.         /* numbering plan: ISDN/Telephone numbering plan */
  724.         pdu[pos] = numtext(1);
  725.         pos++;
  726.         
  727.         /* make sure there is no blank in the phone number and encode
  728.          * an even number of digits */
  729.         octstr_strip_blanks(msg->sms.receiver);
  730.         for(i=nstartpos; i<len; i+=2) {
  731.                 if (i+1 < len) {
  732.                         pdu[pos] = octstr_get_char(msg->sms.receiver, i+1);
  733.                 } else {
  734.                         pdu[pos] = numtext (15);
  735.                 }
  736.                 pos++;
  737.                 pdu[pos] = octstr_get_char(msg->sms.receiver, i);
  738.                 pos++;
  739.         }
  740.         
  741.         /* protocol identifier */
  742.         /* 0x00 implicit */
  743.         pdu[pos] = numtext(0);
  744.         pos++;
  745.         pdu[pos] = numtext(0);
  746.         pos++;
  747.         
  748.         /* data coding scheme */
  749. dcs = fields_to_dcs(msg, smsc->at_alt_dcs);
  750.         pdu[pos] = numtext(dcs >> 4);
  751.         pos++;
  752.         pdu[pos] = numtext(dcs % 16);
  753.         pos++;
  754.         /* Validity-Period (TP-VP)
  755.          * see GSM 03.40 section 9.2.3.12
  756.          * defaults to 24 hours = 167 if not set */
  757. if ( msg->sms.validity) {
  758.     if (msg->sms.validity > 635040)
  759. setvalidity = 255;
  760.     if (msg->sms.validity >= 50400 && msg->sms.validity <= 635040)
  761. setvalidity = (msg->sms.validity - 1) / 7 / 24 / 60 + 192 + 1;
  762.     if (msg->sms.validity > 43200 && msg->sms.validity < 50400)
  763. setvalidity = 197;
  764.     if (msg->sms.validity >= 2880 && msg->sms.validity <= 43200)
  765. setvalidity = (msg->sms.validity - 1) / 24 / 60 + 166 + 1;
  766.     if (msg->sms.validity > 1440 && msg->sms.validity < 2880)
  767. setvalidity = 168;
  768.     if (msg->sms.validity >= 750 && msg->sms.validity <= 1440)
  769. setvalidity = (msg->sms.validity - 720 - 1) / 30 + 143 + 1;
  770.     if (msg->sms.validity > 720 && msg->sms.validity < 750)
  771. setvalidity = 144;
  772.     if (msg->sms.validity >= 5 && msg->sms.validity <= 720)
  773. setvalidity = (msg->sms.validity - 1) / 5 - 1 + 1;
  774.     if (msg->sms.validity < 5)
  775. setvalidity = 0;
  776. } else 
  777.     setvalidity = (smsc->at_validityperiod != NULL ? atoi(smsc->at_validityperiod) : 167);
  778.         
  779.         if (setvalidity >= 0 && setvalidity <= 143)
  780.                 debug("AT", 0, "TP-Validity-Period: %d minutes", 
  781.                       (setvalidity+1)*5);
  782.         else if (setvalidity >= 144 && setvalidity <= 167)
  783.                 debug("AT", 0, "TP-Validity-Period: %3.1f hours", 
  784.                       ((float)(setvalidity-143)/2)+12);
  785.         else if (setvalidity >= 168 && setvalidity <= 196)
  786.                 debug("AT", 0, "TP-Validity-Period: %d days", 
  787.                       (setvalidity-166));
  788.         else 
  789.                 debug("AT", 0, "TP-Validity-Period: %d weeks", 
  790.                       (setvalidity-192));
  791.         pdu[pos] = numtext((setvalidity & 240) >> 4);
  792.         pos++;
  793.         pdu[pos] = numtext(setvalidity & 15);
  794.         pos++;
  795.         /* user data length - include length of UDH if it exists*/
  796.         len = octstr_len(msg->sms.msgdata);
  797.         if(octstr_len(msg->sms.udhdata)) {
  798.                 if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
  799.     len += octstr_len(msg->sms.udhdata);
  800.                 } else {
  801.                         /* The reason we branch here is because UDH data length is determined
  802.                            in septets if we are in GSM coding, otherwise it's in octets. Adding 6
  803.                            will ensure that for an octet length of 0, we get septet length 0,
  804.                            and for octet length 1 we get septet length 2.*/
  805.                         len += (((8*octstr_len(msg->sms.udhdata)) + 6)/7);                
  806.         }
  807.         }
  808.         pdu[pos] = numtext((len & 240) >> 4);
  809.         pos++;
  810.         pdu[pos] = numtext(len & 15);
  811.         pos++;
  812.         
  813.         /* udh */
  814.         if(octstr_len(msg->sms.udhdata)) {
  815.                         pos += encode8bituncompressed(msg->sms.udhdata, &pdu[pos]);
  816.         }
  817.         /* user data */
  818.         /* if the data is too long, it is cut */
  819.         if(msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
  820.                 pos += encode8bituncompressed(msg->sms.msgdata, &pdu[pos]);
  821.         } else {
  822.                 int offset=0;
  823.                 if (octstr_len(msg->sms.udhdata)) {                                   /* Have UDH */
  824.                         int nbits = octstr_len(msg->sms.udhdata)*8;     /* Includes UDH length byte */
  825.                         offset = (((nbits/7)+1)*7-nbits)%7;                     /* Fill bits */
  826.                 }
  827.                 pos += encode7bituncompressed(msg->sms.msgdata, &pdu[pos],offset);
  828.         }
  829.         pdu[pos] = 0;
  830.         return 0;
  831. }
  832. /******************************************************************************
  833.  * Converts the text representation of hexa to binary
  834.  */
  835. static Octstr *convertpdu(Octstr *pdutext) {
  836.         Octstr *pdu;
  837.         int i;
  838.         int len = octstr_len(pdutext);
  839.         pdu = octstr_create("");
  840.         for (i=0; i<len; i+=2) {
  841.                 octstr_append_char(pdu, hexchar(octstr_get_char(pdutext,i))*16 
  842.                                    + hexchar(octstr_get_char(pdutext,i+1)));
  843.         }
  844.         return pdu;
  845. }
  846. /**********************************************************************
  847.  * Encode 7bit uncompressed user data
  848.  */
  849. int ermask[8] = { 0, 1, 3, 7, 15, 31, 63, 127 };
  850. int elmask[8] = { 0, 64, 96, 112, 120, 124, 126, 127 };
  851. static int encode7bituncompressed(Octstr *input, unsigned char *encoded,int offset) {
  852.         unsigned char prevoctet, tmpenc;
  853.         int i;
  854.         int c = 1;
  855.         int r = 7;
  856.         int pos = 0;
  857.         int len;
  858.         unsigned char enc7bit[256];
  859.         int j,encpos = 0;
  860.         charset_latin1_to_gsm(input);
  861.         len = octstr_len(input);
  862.         /* prevoctet is set to the first character and we'll start the loop
  863.          * at the following char. */
  864.         prevoctet = octstr_get_char(input ,0);
  865.         for(i=1; i<octstr_len(input); i++) {
  866.                 /* a byte is encoded with what is left of the previous character
  867.                  * and filled with as much as possible of the current one. */
  868.                 tmpenc = prevoctet + ((octstr_get_char(input,i) & ermask[c]) << r);
  869.                 enc7bit[encpos] = tmpenc; encpos++;
  870.                 c = (c>6)? 1 : c+1;
  871.                 r = (r<2)? 7 : r-1;
  872.                 /* prevoctet becomes the part of the current octet that hasn't
  873.                  * been copied to 'encoded' or the next char if the current has
  874.                  * been completely copied already. */
  875.                 prevoctet = (octstr_get_char(input,i) & elmask[r]) >> (c-1);
  876.                 if(r == 7) {
  877.                         i++;
  878.                         prevoctet = octstr_get_char(input, i);
  879.                 }
  880.         }
  881.         /* if the length of the message is a multiple of 8 then we
  882.          * are finished. Otherwise prevoctet still contains part of a 
  883.          * character so we add it. */
  884.         if((len/8)*8 != len) {
  885.                 enc7bit[encpos] = prevoctet;encpos++;
  886.         }
  887.         /* Now shift the buffer by the offset */
  888.         if (offset > 0) {
  889.                 unsigned char nextdrop, lastdrop;
  890.           nextdrop = lastdrop = 0;
  891.                 for (i = 0; i < encpos; i++) {
  892.                         nextdrop = enc7bit[i] >> (8 - offset);          /* This drops off by shifting */
  893.                         if (i == 0)
  894.                                 enc7bit[i] = enc7bit[i] << offset;              /* This drops off by shifting */
  895.                         else
  896.                                 enc7bit[i] = (enc7bit[i] << offset) | lastdrop;
  897.                         lastdrop = nextdrop;
  898.                 }
  899.                 if (offset > ((len*7) % 8)) {
  900.                         enc7bit [i] = nextdrop;
  901.                         i++;
  902.                 }
  903.         }
  904.         else
  905.                 i = encpos;
  906.         for (j = 0; j < i; j++) {
  907.                 encoded[pos] = numtext((enc7bit [j] & 240) >> 4); pos++;
  908.                 encoded[pos] = numtext(enc7bit [j] & 15); pos++;
  909.         }
  910.         return pos;
  911.                 
  912. }
  913. /**********************************************************************
  914.  * Encode 8bit uncompressed user data
  915.  */
  916. static int encode8bituncompressed(Octstr *input, unsigned char *encoded) {
  917.         int len, i;
  918.         len = octstr_len(input);
  919.         
  920.         for(i=0; i<len; i++) {
  921.                 /* each character is encoded in its hex representation (2 chars) */
  922.                 encoded[i*2] = numtext((octstr_get_char(input, i) & 240) >> 4);
  923.                 encoded[i*2+1] = numtext(octstr_get_char(input, i) & 15);
  924.         }
  925.         return len*2;
  926. }
  927. /**********************************************************************
  928.  * Decode 7bit uncompressed user data
  929.  */
  930. int rmask[8] = { 0, 1, 3, 7, 15, 31, 63, 127 };
  931. int lmask[8] = { 0, 128, 192, 224, 240, 248, 252, 254 };
  932. static void decode7bituncompressed(Octstr *input, int len, Octstr *decoded, int offset) {
  933.         unsigned char septet, octet, prevoctet;
  934.         int i;
  935.         int r = 1;
  936.         int c = 7;
  937.         int pos = 0;
  938.         
  939.         /* Shift the buffer offset bits to the left */
  940.         if (offset > 0) {
  941.                 unsigned char *ip;
  942.                 for (i = 0, ip = octstr_get_cstr(input); i < octstr_len(input); i++) {
  943.                         if (i == octstr_len(input) - 1)
  944.                                 *ip = *ip >> offset;
  945.                         else
  946.                                 *ip = (*ip >> offset) | (*(ip + 1) << (8 - offset));
  947.                         ip++;
  948.                 }
  949.         }
  950.         octet = octstr_get_char(input, pos);
  951.         prevoctet = 0;
  952.         for(i=0; i<len; i++) {
  953.                 septet = ((octet & rmask[c]) << (r-1)) + prevoctet;
  954.                 octstr_append_char(decoded, septet);
  955.                 prevoctet = (octet & lmask[r]) >> c;
  956.         
  957.                 /* When r=7 we have a full character in prevoctet*/
  958.                 if((r==7) && (i<len-1)){
  959.                         i++;
  960.                         octstr_append_char(decoded, prevoctet);
  961.                         prevoctet = 0;
  962.                 }
  963.                 r = (r>6)? 1 : r+1;
  964.                 c = (c<2)? 7 : c-1;
  965.                 pos++;
  966.                 octet = octstr_get_char(input, pos);
  967.         }
  968.         charset_gsm_to_latin1(decoded);
  969. }
  970. /**********************************************************************
  971.  * Code a half-byte to its text hexa representation
  972.  */
  973. static int numtext(int num) {
  974.         return (num > 9) ? (num+55) : (num+48);
  975. }
  976. /**********************************************************************
  977.  * Get the numeric value of the text hex
  978.  */
  979. static int hexchar(int hexc) {
  980.         hexc = toupper(hexc) - 48;
  981.         return (hexc>9) ? hexc-7 : hexc;
  982. }