Logo Search packages:      
Sourcecode: smstools version File versions  Download package

putsms.c

/*
SMS Server Tools
Copyright (C) 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.meinemullemaus.de/
mailto: smstools@meinemullemaus.de
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include "charset.h"
#include "version.h"
#include "modeminit.h"
#include "logging.h"
#include "alarm.h"
#include "extras.h"
#include <limits.h>
#include <errno.h>

char message[500]={0};
int alphabet=0;  // 0=GSM, 1=8bit Data, 2=UCS2, 3=reserved
int udh=1;       // enable UDH bit
int messagelen=0; // length of message
char filename[PATH_MAX]={0};
char logfile[PATH_MAX]={0};
char alarmhandler[PATH_MAX]={0};
int alarmlevel=LOG_WARNING;
int loglevel=9;
char to[50]={0};
int cs_convert=0;
int report=0;
int flash_sms=0;
int quick=0;
char idfile[PATH_MAX]={0};

void help()
{
  printf("putsms sends an SMS through a GSM 07.05 compatible modem.\n");
  printf("Usage:\n");
  printf("              putsms [options] destination [\"message\"]\n");
  printf("Options:\n");
  printf("              -ax  set alarm handler to x (default none)\n");
  printf("              -Ax  set alarm level to x (default %i)\n",alarmlevel);
  printf("              -bx  set baudrate to x (default %i)\n",baudrate);
  printf("              -c   use character set conversion\n");
  printf("              -dx  set modem device to x (default %s)\n",device);
  printf("              -Dx  delay sending characters by x milliseconds (default %d)\n",send_delay);
  printf("              -ex  wait x seconds before retry (default %d)\n",errorsleeptime);
  printf("              -fx  message file x (ascii)\n");
  printf("              -Fx  message file x (binary, only in pdu mode)\n");
  printf("              -gx  message file x (UCS2, only in pdu mode)\n");
  printf("              -h   this help\n");
  printf("              -H   disable hardware handshake RTS/CTS\n");
  printf("              -ix  modem init string x\n");
  printf("              -I   send as a flash SMS (class 0)\n");
  printf("              -lx  use logfile x (filename or handle) (default syslog)\n");
  printf("              -Lx  use loglevel x (default %i)\n",loglevel);
  printf("              -mx  set the PDU mode to x (default %s)\n",mode);
  printf("                   x can be old, new, ascii or digicom\n");
  printf("              -nx  set modem name to x\n");
  printf("              -px  set pin to x (only needed if modem not initialized)\n");
  printf("              -q   quick, no modem initialisation\n");
  printf("              -r   request status report SMS (not in ascii mode)\n");
  printf("              -Rx  report message ID in file x\n");
  printf("              -sx  set SMSC Number to x\n");
  printf("              -u   no user data header (UDH bit). Only used with -F.\n");
  printf("              -V   print version info and copyright\n\n");
  printf("Return codes:\n");
  printf("              0   success\n");
  printf("              1   cannot open device or file\n");
  printf("              2   PIN is wrong\n");
  printf("              3   problem with modem initialisation\n");
  printf("              4   SMS could not be sent\n");
  printf("              5   error in arguments\n\n");
  printf("The message file can contain a header but this is not used.\n");
  exit(0);
}

void cut_ctrl(char* message) /* removes all ctrl chars */
{
  char tmp[500];
  int posdest=0;
  int possource;
  int count;
  count=strlen(message);
  for (possource=0; possource<=count; possource++)
  {
    if ((message[possource]>=' ') || (message[possource]==0))
      tmp[posdest++]=message[possource];
  }
  strcpy(message,tmp);
}

void loadmessagefile()
{
  int File;
  int readcount;
  char* position;

  /* load Message-File (if set) */
  if (filename[0])
  {
    /* load the whole file */
    File = open(filename, O_RDONLY);
    if (File<0)
    {
      writelogfile(LOG_ERR,"Could not open file %s, cause: %s",filename,strerror(errno));
      alarm_handler(LOG_ERR,"","Could not open file %s, cause: %s",filename,strerror(errno));
      exit(1);
    }
    else
    {
      readcount=read(File,message,sizeof(message)-1);
      message[readcount]=0;
      messagelen=readcount;
      /* skip newline-characters */
      if ((position=strstr(message,"\n\n")))
        position+=2;
      else if ((position=strstr(message,"\r\n\r\n")))
        position+=4;
      if (position) /* if the double newline was found, cut the whole header out */
      {
        messagelen=messagelen-(position-message);
        memmove(message,position,messagelen);
        message[messagelen]=0;
      }
      close(File);
    }
  }
  else
    messagelen=strlen(message);
}

void parsearguments(int argc,char** argv)
{
  int result;

  /* set default values */
  strcpy(device,"/dev/ttyS0");
  strcpy(mode,"new");
  pin[0]=0;
  filename[0]=0;
  smsc[0]=0;
  baudrate=19200;
  send_delay=1;
  alphabet=0;
  initstring[0]=0;
  initstring2[0]=0;
  report=0;
  errorsleeptime=10;
  modemname[0]=0;
  smsc[0]=0;
  rtscts=1;
  quick=0;
  flash_sms=0;
  idfile[0]=0;
  /* parse arguments */
  do
  {
    result=getopt(argc,argv,"a:A:n:b:l:L:ce:rR:qhHIi:j:d:D:p:m:s:f:F:g:Vu");
    switch (result)
    {
      case 'h': help();
                break;
      case 'a': strcpy(alarmhandler,optarg);
                  break;
      case 'A': alarmlevel=atoi(optarg);
                  break;
      case 'b': baudrate=atoi(optarg);
                break;
      case 'D': send_delay=atoi(optarg);
                break;
      case 'c': cs_convert=1;
                break;
      case 'I': flash_sms=1;
                break;
      case 'e': errorsleeptime=atoi(optarg);
                  break;
      case 'r': report=1;
            break;
      case 'R': strcpy(idfile,optarg);
                break;
      case 'q': quick=1;
                break;
      case 'H': rtscts=0;
            break;
      case 'u': udh=0;
            break;
      case 'd': strcpy(device,optarg);
                break;
      case 'p': strcpy(pin,optarg);
                break;
      case 'l': strcpy(logfile,optarg);
                break;
      case 'L': loglevel=atoi(optarg);
                break;
      case 'm': strcpy(mode,optarg);
                break;
      case 'n': strcpy(modemname,optarg);
                break;
      case 's': strcpy(smsc,optarg);
                break;
      case 'f': strcpy(filename,optarg);
            alphabet=0;
                break;
      case 'i': strcpy(initstring,optarg);
                    strcat(initstring,"\r");
            break;
      case 'j': strcpy(initstring2,optarg);
                strcat(initstring2,"\r");
                break;
      case 'F': strcpy(filename,optarg);
            alphabet=1;
            break;
      case 'g': strcpy(filename,optarg);
            alphabet=2;
            break;
      case 'V': printf("Version %s, Copyright (c) by Stefan Frings, smstools@meinemullemaus.de\n",putsms_version);
                exit(0);
    }
  }
  while (result>0);
  if (modemname[0]==0)
    strcpy(modemname,device);

}

void check_arguments(int argc,char** argv)
{
  switch (baudrate)
  {
    case 300:    baudrate=B300; break;
    case 1200:   baudrate=B1200; break;
    case 2400:   baudrate=B2400; break;
    case 9600:   baudrate=B9600; break;
    case 19200:  baudrate=B19200; break;
    case 38400:  baudrate=B38400; break;
#ifdef B57600
    case 57600:  baudrate=B57600; break;
#endif
#ifdef B115200
    case 115200: baudrate=B115200; break;
#endif
#ifdef B230400
    case 230400: baudrate=B230400; break;
#endif
    default: writelogfile(LOG_ERR,"Baudrate not supported"); exit(1);
  }

  /* parse number and text  */
  if (optind==(argc-2)) /* number and text as arguments defined */
  {
    if (filename[0])
    {
      fprintf(stderr,"Message as file AND as argument specified.\n");
      exit(5);
    }
    strcpy(message,argv[optind+1]);
    strcpy(to,argv[optind]);
  }
  else if (optind==(argc-1)) /* Only number as text defined */
    strcpy(to,argv[optind]);

  loadmessagefile();

  /* Check number and text*/
  if (messagelen==0)
  {
    writelogfile(LOG_ERR,"You did not specify a message or a message file");
    exit(5);
  }
  if (to[0]==0)
  {
    writelogfile(LOG_ERR,"You did not specify a destination or a message file");
    exit(5);
  }
  /* Check if binary file allowed */
  if (alphabet!=0 && (strcmp(mode,"ascii")==0))
  {
    writelogfile(LOG_ERR,"Binary and UCS2 files are not allowed in ascii mode");
    exit(5);
  }

}

/* Work with the complex bit building to generate a 7 bit PDU string encapsulated in 8 bit. Returns number of 7 Bit charcters */

int ascii2pdu(char* ascii,char* pdu)
{
  char tmp[500];
  char octett[10];
  int pdubitposition;
  int pdubyteposition=0;
  int asciiLength;
  int character;
  int bit;
  int pdubitnr;
  char converted;
  int counted_characters=0;
  asciiLength=strlen(ascii);
  if (asciiLength>160)
    asciiLength=160;
  //clear the tmp buffer
  for (character=0;character<sizeof(tmp);character++)
    tmp[character]=0;
  for (character=0;character<asciiLength;character++)
  {
    // replace 0xB7 by 0x00, because this is a replacement for 0x00 that is the @ character is GSM alphabet 
    if (ascii[character]=='\xB7')
      converted='\x00';
    else
      converted=ascii[character];
    counted_characters++;
    for (bit=0;bit<7;bit++)
    {
      pdubitnr=7*character+bit;
      pdubyteposition=pdubitnr/8;
      pdubitposition=pdubitnr%8;
      if (converted & (1<<bit))
        tmp[pdubyteposition]=tmp[pdubyteposition] | (1<<pdubitposition);
      else
        tmp[pdubyteposition]=tmp[pdubyteposition] & ~(1<<pdubitposition);
    }
  }
  tmp[pdubyteposition+1]=0;
  pdu[0]=0;
  for (character=0;character<=pdubyteposition; character++)
  {
    sprintf(octett,"%02X",(unsigned char) tmp[character]);
    strcat(pdu,octett);
  }
  return counted_characters;
}

/* Create a HEX Dump */
void binary2pdu(char* binary, int length, char* pdu)
{
  int character;
  char octett[10];
  if (length>140)
    length=140;
  pdu[0]=0;
  for (character=0;character<length; character++)
  {
    sprintf(octett,"%02X",(unsigned char) binary[character]);
    strcat(pdu,octett);
  }
}

/* make the PDU string. The destination variable pdu has to be big enough. */
void make_pdu(char* nummer, char* message, int messagelen, char* pdu)
{
  int coding;
  int flags;
  char tmp[50];
  char tmp2[500];
  char tmp3[500];
  int numberformat;
  int numberlength;

  if (nummer[0]=='s')  // Is number starts with s, then send it without number format indicator
  {
    numberformat=129;
    strcpy(tmp,nummer+1);
  }
  else
  {
    numberformat=145;
    strcpy(tmp,nummer);
  }
  numberlength=strlen(tmp);
  // terminate the number with F if the length is odd
  if (numberlength%2)
    strcat(tmp,"F");
  // Swap every second character
  swapchars(tmp);
  flags=1; // SMS-Sumbit MS to SMSC
  
  if (alphabet==1)
  {
    coding=244; // 8 bit binary
    if (udh)
      flags+=64; // User Data Header
  }
  else if (alphabet==2)
    coding=24; // UCS2
  else
    coding=240; // 7bit text

  if (flash_sms)
    coding+=0; // Class 0
  else
    coding+=1; // Class1

  if (strcmp(mode,"old")!=0)
    flags+=16; // Validity field
  if (report)
    flags+=32; /* Request Status Report */
  /* Create the PDU string of the message */
  if (alphabet==1 || alphabet==2)
    binary2pdu(message,messagelen,tmp2);
  else
  {
    // Convert character set if source=ISO and conversion enabled
    if ((alphabet==0) && (cs_convert))
    {
      messagelen=iso2gsm(message,tmp3,sizeof(tmp3));
      messagelen=ascii2pdu(tmp3,tmp2);
    }
    else
      messagelen=ascii2pdu(message,tmp2);
  }
  /* concatenate the first part of the PDU string */
  if (strcmp(mode,"old")==0)
    sprintf(pdu,"%02X00%02X%02X%s00%02X%02X",flags,numberlength,numberformat,tmp,coding,messagelen);
  else
   sprintf(pdu,"00%02X00%02X%02X%s00%02XA7%02X",flags,numberlength,numberformat,tmp,coding,messagelen);
  /* concatenate the text to the PDU string */
  strcat(pdu,tmp2);
}

/* send sms */
void putsms()
{
  char command[1024];
  char command2[1024];
  char answer[1024];
  char pdu[1024];
  int retries;
  char* posi1;
  char* posi2;
  FILE *fd;
  make_pdu(to,message,messagelen,pdu);
  if (strcmp(mode,"old")==0)
    sprintf(command,"AT+CMGS=%i\r",strlen(pdu)/2);
  else if (strcmp(mode,"ascii")==0)
    sprintf(command,"AT+CMGS=\"+%s\"\r",to);
    // Workaround for a buggy Siemens M20
    // sprintf(command,"AT+CMGS=\"%s\",129,\r",to);
  else
    sprintf(command,"AT+CMGS=%i\r",strlen(pdu)/2-1);
  if (strcmp(mode,"ascii")==0)
    sprintf(command2,"%s\x1A",message);
  else
    sprintf(command2,"%s\x1A",pdu);
  retries=0;
  while (1)
  {
    retries+=1;
    put_command(command,answer,sizeof(answer),50,"(\\>)|(ERROR)");
    if (! strstr(answer,"ERROR"))
      put_command(command2,answer,sizeof(answer),300,"(OK)|(ERROR)");
    if (strstr(answer,"ERROR"))
    {
      writelogfile(LOG_ERR,"Uups, the modem said ERROR.");
      alarm_handler(LOG_ERR,"","Uups, the modem said ERROR.");
      tcsetattr(modem,TCSANOW,&oldtio);
      if (retries<3)
      {
        writelogfile(LOG_NOTICE,"Waiting %i sec. before retrying",errorsleeptime);
      sleep(errorsleeptime);
      // Force a new initialisation after an ERROR
      initmodem();
      }
      else
        exit(4);
    }
    else if (strstr(answer,"OK"))
    {
      writelogfile(LOG_NOTICE,"SMS sent, To: %s",to);
      alarm_handler(LOG_NOTICE,"","SMS sent, To: %s",to);
      // If the modem answered with an ID number then write into id file.
      posi1=strstr(answer,"CMGS: ");
      if ((posi1) && (idfile[0]))
      {
        posi1+=6;
      posi2=strchr(posi1,'\r');
      if (! posi2) 
        posi2=strchr(posi1,'\n');
      if (posi2)
        posi2[0]=0;
      fd=fopen(idfile,"w");
      fprintf(fd,"%s   \n",posi1);
      fclose(fd);
      }
      return;
    }  
    else
    {
      writelogfile(LOG_WARNING,"Maybe could not send message, modem did not confirm submission.");
      alarm_handler(LOG_WARNING,"","Maybe could not send message, modem did not confirm submission.");
      tcsetattr(modem,TCSANOW,&oldtio);
      exit(4);
    }
  }
}

int main(int argc,char** argv)
{
  char tmp[100];
  parsearguments(argc,argv);
  snprintf(tmp,sizeof(tmp),"putsms (%s)",modemname); tmp[sizeof(tmp)-1]=0;
  openlogfile(tmp,logfile,LOG_DAEMON,loglevel);
  set_alarmhandler(alarmhandler,alarmlevel,modemname);
  check_arguments(argc,argv);
  writelogfile(LOG_INFO,"Sending SMS");
  openmodem();
  setmodemparams();
  if (quick==0)
    initmodem();
  putsms();
  tcsetattr(modem,TCSANOW,&oldtio);
  return 0;
}


Generated by  Doxygen 1.6.0   Back to index