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

smsd.c

/*
SMS Server Tools 3
Copyright (C) Keijo Kasvi
http://smstools3.kekekasvi.com/

Based on SMS Server Tools 2 from Stefan Frings
http://www.meinemullemaus.de/

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.
*/

#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>
#ifndef NOSTATS
#include <mm.h>
#endif
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <stdarg.h>
#include "extras.h"
#include "locking.h"
#include "smsd_cfg.h"
#include "stats.h"
#include "version.h"
#include "blacklist.h"
#include "whitelist.h"
#include "logging.h"
#include "alarm.h"
#include "charset.h"
#include "cfgfile.h"
#include "pdu.h"
#include "modeminit.h"

int logfilehandle;  // handle of log file.
int concatenated_id=0; // id number for concatenated messages.
// This indicates that the PDU was read from file, not from SIM.
#define PDUFROMFILE 22222
// workless_delay not yet implemented:
int break_workless_delay; // To break the delay when SIGCONT is received.
int workless_delay;

/* =======================================================================
   Runs checkhandler and returns return code
   ======================================================================= */
   
int run_checkhandler(char* filename)
{
  char cmdline[PATH_MAX+PATH_MAX+32];

  if (checkhandler[0])
  {
    sprintf(cmdline,"%s %s",checkhandler,filename);
    return my_system(cmdline, "checkhandler");
  }
  else
  {
    return 0;
  }
}

/* =======================================================================
   Stops the program if the given file exists
   ======================================================================= */

/* filename1 is checked. The others arguments are used to compose an error message. */

void stop_if_file_exists(char* infotext1, char* filename1, char* infotext2, char* filename2)
{
  int datei;

  datei=open(filename1,O_RDONLY);
  if (datei>=0)
  {
    close(datei);
    // 3.1beta7, 3.0.9: removed strerror(errno), it was irrelevant in here.
    //writelogfile0(LOG_CRIT, process_title, tb_sprintf("Fatal error: %s %s %s %s. %s. Check file and dir permissions.",
    //              infotext1,filename1,infotext2,filename2,strerror(errno)));
    writelogfile0(LOG_CRIT, process_title, tb_sprintf("Fatal error: %s %s %s %s. Check file and dir permissions.",
                  infotext1, filename1, infotext2, filename2));
    alarm_handler0(LOG_CRIT, process_title, tb);
    abnormal_termination(1);
  }
}

/* =======================================================================
   Remove and add headers in a message file
   - remove_headers: "\nHeader1:\nHeader2:" (\n and : are delimiters)
   - add_buffer is an additional header data wich is added after add_headers.
     This data is not processed with format string. For example to be used
     with pdu store which can be very large and there is no reason to alloc
     memory for just passing this data.
   - add_headers: "Header1: value\nHeader2: value\n" (actual line(s) to write)
   ======================================================================= */

int change_headers(char *filename, char *remove_headers, char *add_buffer, char *add_headers, ...)
{
  int result = 0;
  char line[1024];
  char header[1024 +1];
  char *p;
  int in_headers = 1;
  char tmp_filename[PATH_MAX +7];
  FILE *fp;
  FILE *fptmp;
  size_t n;
  va_list argp;
  char new_headers[2048];

  va_start(argp, add_headers);
  vsnprintf(new_headers, sizeof(new_headers), add_headers, argp);
  va_end(argp);

  sprintf(tmp_filename,"%s.XXXXXX", filename);
  close(mkstemp(tmp_filename));
  unlink(tmp_filename);
  if ((fptmp = fopen(tmp_filename, "w")) == NULL)
  {
    writelogfile0(LOG_WARNING, process_title, tb_sprintf("Header handling aborted, creating %s failed", tmp_filename));
    alarm_handler0(LOG_WARNING, process_title, tb);
    result = 1;
  }
  else
  {
    if ((fp = fopen(filename, "r")) == NULL)
    {
      fclose(fptmp);
      unlink(tmp_filename);
      writelogfile0(LOG_WARNING, process_title, tb_sprintf("Header handling aborted, reading %s failed", filename));
      alarm_handler0(LOG_WARNING, process_title, tb);
      result = 2;
    }
    else
    {
      strcpy(header, "\n");
      while (in_headers && fgets(line, sizeof(line), fp))
      {
        if (remove_headers && *remove_headers)
        {
          // Possible old headers are removed:
          if ((p = strchr(line, ':')) != NULL)
          {
            strncpy(header +1, line, p -line +1);
            header[p -line +2] = 0;
            if (strstr(remove_headers, header))
              continue;
          }
        }

        if (line_is_blank(line))
        {
          if (*new_headers)
            fwrite(new_headers, 1, strlen(new_headers), fptmp);
          if (add_buffer && *add_buffer)
            fwrite(add_buffer, 1, strlen(add_buffer), fptmp);
          in_headers = 0;
        }
        fwrite(line, 1, strlen(line), fptmp);
      }

/* Not yet:
      // 3.1beta7: Because of Include feature, all text can be in different file
      // and therefore a delimiter line is not in this file.
      if (in_headers)
      {
        if (*new_headers)
          fwrite(new_headers, 1, strlen(new_headers), fptmp);
        if (add_buffer && *add_buffer)
          fwrite(add_buffer, 1, strlen(add_buffer), fptmp);
      }
*/
      while ((n = fread(line, 1, sizeof(line), fp)) > 0)
        fwrite(line, 1, n, fptmp);

      fclose(fptmp);
      fclose(fp);
      unlink(filename);
      rename(tmp_filename, filename);
    }
  }

  return result;
}

/* ======================================================================= */

void sendsignal2devices(int signum)
{
  int i;

  for (i = 0; i < DEVICES; i++)
    if (device_pids[i] > 0)
      kill(device_pids[i], signum);
}

/* =======================================================================
   Get a field from a modem answer, remove quotes
   ======================================================================= */

void getfield(char* line, int field, char* result)
{
  char* start;
  char* end;
  int i;
  int length;

#ifdef DEBUGMSG
  printf("!! getfield(line=%s, field=%i, ...)\n",line,field);
#endif
  *result=0;
  start=strstr(line,":");
  if (start==0)
    return;
  for (i=1; i<field; i++)
  {
    start=strchr(start+1,',');
    if (start==0)
      return;      
  }
  start++;
  while (start[0]=='\"' || start[0]==' ')
    start++;
  if (start[0]==0)
    return;
  end=strstr(start,",");
  if (end==0)
    end=start+strlen(start)-1;
  while ((end[0]=='\"' || end[0]=='\"' || end[0]==',') && (end>=start))
    end--;
  length=end-start+1;
  strncpy(result,start,length);
  result[length]=0;
#ifdef DEBUGMSG
  printf("!! result=%s\n",result);
#endif    
}

/* =======================================================================
   Read the header of an SMS file
   ======================================================================= */

void readSMSheader(char* filename, /* Filename */
// output variables are:
                   char* to, /* destination number */
                 char* from, /* sender name or number */
                 int*  alphabet, /* -1=GSM 0=ISO 1=binary 2=UCS2 3=unknown */
                   int* with_udh,  /* UDH flag */
                   char* udh_data,  /* UDH data in hex dump format, e.g. "05 00 03 b2 02 01". Only used in alphabet<=0 */
                 char* queue, /* Name of Queue */
                 int*  flash, /* 1 if send as Flash SMS */
                 char* smsc, /* SMSC Number */
                   int*  report,  /* 1 if request status report */
               int*  split,  /* 1 if request splitting */
                   int*  validity, /* requested validity period value */
                   int*  voicecall, /* 1 if request voicecall */
                   int* hex, /* 1 if binary message is presented as hexadecimal */
                   int *replace_msg) /* 1...7, 0=none */
{
  FILE* File;
  char line[256];
  char *ptr;
  to[0]=0;
  from[0]=0;
  *alphabet=0; 
  *with_udh=-1;
  udh_data[0]=0;
  queue[0]=0;
  *flash=0;
  smsc[0]=0;
  *report=-1; 
  *split=-1;
  *validity=-1;
  *voicecall=0;
  *hex=0;
  
#ifdef DEBUGMSG
  printf("!! readSMSheader(filename=%s, ...)\n",filename);
#endif 
 
  File=fopen(filename,"r");  
  // read the header line by line 
  if (File)
  {
    // read until end of file or until an empty line was found
    while (fgets(line,sizeof(line),File))
    {
      if ((line[0]==0) || (line[0]=='\n') || (line[0]=='\r'))
        break;
      if (strstr(line,"To:")==line)
      {
        // remove the To: and spaces
        memmove(line,line+3,strlen(line)-2);
        cutspaces(line);
        // correct phone number if it has wrong syntax
        if (strstr(line,"00")==line)
          strcpy(to,line+2);
        else if ((ptr=strchr(line,'+')) != NULL)
          strcpy(to,ptr+1);
        else
          strcpy(to,line);
        // Truncate after last digit
        if (*to == 's')
          to[strspn(to +1,"1234567890") +1]=0;
        else
          to[strspn(to,"1234567890")]=0;
      }
      else if (strstr(line,"From:")==line)
      {
        strcpy(from,line+5);
        cutspaces(from);
      }
      else if (strstr(line,"SMSC:")==line)
      {
        // remove the SMSC: and spaces
        memmove(line,line+5,strlen(line)-4);
        cutspaces(line);
        // correct phone number if it has wrong syntax
        if (strstr(line,"00")==line)
          strcpy(smsc,line+2);
        else if (strchr(line,'+')==line)
          strcpy(smsc,line+1);
        else
          strcpy(smsc,line);
      }
      else if (strstr(line,"Flash:")==line)
      {
        memmove(line,line+6,strlen(line)-5);
        cutspaces(line);
        *flash=yesno(line);
      }  
      else if (strstr(line,"Provider:")==line)
      {
        strcpy(queue,line+9);
        cutspaces(queue);
      }
      else if (strstr(line,"Queue:")==line)
      {
        strcpy(queue,line+6);
        cutspaces(queue);
      }
      else if (strstr(line,"Binary:")==line)
      {
        memmove(line,line+7,strlen(line)-6);
        cutspaces(line);
        *alphabet=yesno(line);
      }
      else if (strstr(line,"Report:")==line)
      {
        memmove(line,line+7,strlen(line)-6);
        cutspaces(line);
        *report=yesno(line);
      }
      else if (strstr(line,"Autosplit:")==line)
      {
      memmove(line,line+10,strlen(line)-9);
        cutspaces(line);
      *split=atoi(line);
      }
      else if (strstr(line,"Validity:")==line)
      {
        int tmpvalue = 0;

      memmove(line,line+9,strlen(line)-8);
        cutspaces(line);
        tmpvalue = parse_validity(line, -1);
        if (tmpvalue >= 0)
          *validity=tmpvalue;
      }
      //else if (strstr(line,"Voicecall:")==line) possible trouble with "VoiceCall".
      else if (strncasecmp(line, "Voicecall:", 10) == 0) 
      {
        memmove(line,line+10,strlen(line)-9);
        cutspaces(line);
        *voicecall=yesno(line);
      }
      else if (strstr(line,"Hex:")==line)
      {
        memmove(line,line+4,strlen(line)-3);
        cutspaces(line);
        *hex=yesno(line);
      }
      else if (strstr(line,"Replace:")==line)
      {
        memmove(line,line+8,strlen(line)-7);
        cutspaces(line);
        *replace_msg = atoi(line);
        if (*replace_msg < 0 || *replace_msg > 7)
          *replace_msg = 0;
      }
      else if (strstr(line,"Alphabet:")==line)
      {
        memmove(line,line+9,strlen(line)-8);
        cutspaces(line);
        if (strcasecmp(line,"GSM")==0)
          *alphabet=-1;
        else if (strncasecmp(line,"iso",3)==0)
          *alphabet=0;
        else if (strncasecmp(line,"lat",3)==0)
          *alphabet=0;
        else if (strncasecmp(line,"ans",3)==0)
          *alphabet=0;
        else if (strncasecmp(line,"bin",3)==0)
          *alphabet=1;
        else if (strncasecmp(line,"chi",3)==0)
          *alphabet=2;
        else if (strncasecmp(line,"ucs",3)==0)
          *alphabet=2;
        else if (strncasecmp(line,"uni",3)==0)
          *alphabet=2;
        else
          *alphabet=3;
      } 
      else if (strstr(line,"UDH-DATA:")==line)
      {
        strcpy(udh_data,line+9);
        cutspaces(udh_data);
      }
      else if (strstr(line,"UDH-DUMP:")==line) // same as UDH-DATA for backward compatibility
      {
        strcpy(udh_data,line+9);
        cutspaces(udh_data);
      }      
      else if (strstr(line,"UDH:")==line) 
      {
        memmove(line,line+4,strlen(line)-3);
        cutspaces(line);
        *with_udh=yesno(line);
      }      
      else // if the header is unknown, then simply ignore
      {
        ;;
      }
    }
    // End of header reached
    fclose(File);
#ifdef DEBUGMSG
  printf("!! to=%s\n",to);
  printf("!! from=%s\n",from);
  printf("!! alphabet=%i\n",*alphabet);
  printf("!! with_udh=%i\n",*with_udh);
  printf("!! udh_data=%s\n",udh_data);
  printf("!! queue=%s\n",queue);
  printf("!! flash=%i\n",*flash);
  printf("!! smsc=%s\n",smsc);
  printf("!! report=%i\n",*report);
  printf("!! split=%i\n",*split);
  printf("!! validity=%i\n",*validity);
#endif 
  }
  else
  {
    writelogfile0(LOG_ERR, process_title, tb_sprintf("Cannot read sms file %s.",filename));
    alarm_handler0(LOG_ERR, process_title, tb);
  }
}


/* =======================================================================
   Read the message text or binary data of an SMS file
   ======================================================================= */

void readSMStext(char* filename, /* Filename */
                 int do_convert, /* shall I convert from ISO to GSM? Do not try to convert binary data. */
// output variables are:
                 char* text,     /* message text */
                 int* textlen)   /* text length */
{
  int File;
  int readcount;
  char* p;
  char* position;
  char tmp[MAXTEXT];
  int part1_size,part2_size;
  // Initialize result with empty string
  text[0]=0;
  *textlen=0;

#ifdef DEBUGMSG
  printf("readSMStext(filename=%s, do_convert=%i, ...)\n",filename,do_convert);
#endif
  
  File=open(filename,O_RDONLY);  
  // read the header line by line 
  if (File>=0)
  {
    position=0;
    readcount=read(File,tmp,sizeof(tmp)-1);
    // Search empty line
    while (readcount>0 && position==0)
    {
      tmp[readcount] = 0;
      // Search double line feed in the block
      if ((p=strstr(tmp,"\n\n")))
        position=p+2;
      else if ((p=strstr(tmp,"\r\n\r\n")))
        position=p+4;
      // Search single line feed at begin of this block    
      else if (strstr(tmp,"\n\r\n")==tmp)
        position=tmp+3;
      else if (strstr(tmp,"\r\n")==tmp)
        position=tmp+2;
      else if (strstr(tmp,"\n")==tmp)
        position=tmp+1;
      if (position==0)
        readcount=read(File,tmp,sizeof(tmp)-1); 
    }
    // If found, then move to the beginning of tmp and read the rest of file
    if (position)  
    {
      part1_size=readcount-(position-tmp);
      memmove(tmp,position,part1_size); 
      part2_size=read(File,tmp+part1_size,sizeof(tmp)-part1_size); 
      // Convert character set or simply copy 
      if (do_convert==1)
        // *textlen=iso2gsm(tmp,part1_size+part2_size,text,MAXTEXT);
        *textlen=iso_utf8_2gsm(tmp,part1_size+part2_size,text,MAXTEXT);
      else
      {
        memmove(text,tmp,part1_size+part2_size);
        *textlen=part1_size+part2_size;
      }
    }
    close(File);
  }
  else
  {
    writelogfile0(LOG_ERR, process_title, tb_sprintf("Cannot read sms file %s.",filename));
    alarm_handler0(LOG_ERR, process_title, tb);
  }
#ifdef DEBUGMSG
  printf("!! textlen=%i\n",*textlen);
#endif
}

void readSMShex(char *filename/*, int recursion_level*/, char *text, int *textlen/*, char *macros*/)
{
  FILE *fp;
  char line[MAXTEXT +1];
  int in_headers = 1;
  char *p;
  int h;
  int i;
  char *p_length = NULL;
  int too_long = 0;

/* Not yet:
  if (recursion_level == 0)
  {
    text[0]=0;
    *textlen=0;
  }
*/

  if ((fp = fopen(filename, "r")) != NULL)
  {
    while (in_headers && fgets(line, sizeof(line), fp))
    {
/* Not yet:
      if (strstr(line,"Include:")==line)
      {
        memmove(line,line+8,strlen(line)-7);
        cutspaces(line);
        if (recursion_level < 1)
          readSMShex(line, recursion_level +1, text, textlen, macros);
      }
*/
      if (line_is_blank(line))
        in_headers = 0;
    }

    while (fgets(line, sizeof(line), fp) != NULL && !too_long)
    {
      cut_ctrl(line);
      while (*line == ' ' || *line == '\t')
        strcpy(line, line +1);

/* Not yet:
      extract_macros(line, sizeof(line), macros);
*/

      if (strncmp(line, "INLINESTRING:", 13) == 0)
      {
        if (*textlen +strlen(line) +1 -13 +1 >= MAXTEXT)
        {
          too_long = 1;
          break;
        }
        // Inline String follows:
        text[*textlen] = 0x03;
        (*textlen)++;
        // Actual text:
        strcpy(text + *textlen, line +13);
        *textlen += strlen(line) -13;
        // Termination:
        text[*textlen] = 0x00;
        (*textlen)++;
      }
      else
      if (strncmp(line, "STRING:", 7) == 0)
      {
        if (*textlen +strlen(line) -7 >= MAXTEXT)
        {
          too_long = 1;
          break;
        }
        strcpy(text + *textlen, line +7);
        *textlen += strlen(line) -7;
      }
      else
      if (strncmp(line, "LENGTH", 6) == 0)
      {
        if (p_length == NULL)
        {
          if (*textlen +1 >= MAXTEXT)
          {
            too_long = 1;
            break;
          }
          p_length = text + *textlen;
          (*textlen)++;
        }
        else
        {
          *p_length = text + *textlen - p_length -1;
          p_length = NULL;
        }
      }
      else
      {
        if ((p = strstr(line, "/")) != NULL)
          *p = 0;
        if ((p = strstr(line, "'")) != NULL)
          *p = 0;
        if ((p = strstr(line, "#")) != NULL)
          *p = 0;
        if ((p = strstr(line, ":")) != NULL)
          *p = 0;
        while ((p = strchr(line, ' ')) != NULL)
          strcpy(p, p +1);

        if (*line)
        {
          if (strlen(line) % 2 != 0)
          {
            writelogfile0(LOG_ERR, process_title, tb_sprintf("Hex presentation error in sms file %s.",filename));
            alarm_handler0(LOG_ERR, process_title, tb);
            text[0]=0;
            *textlen=0;
            break;
          }

          p = line;
          while (*p)
          {
            if ((i = sscanf(p, "%2x", &h)) == 0)
              break;
            if (*textlen +1 >= MAXTEXT)
            {
              too_long = 1;
              break; // Main loop is breaked by too_long variable.
            }
            text[*textlen] = h;
            (*textlen)++;
            p += 2;
          }

          if (i < 1)
          {
            writelogfile0(LOG_ERR, process_title, tb_sprintf("Hex conversion error in sms file %s: \"%s\"",filename, p));
            alarm_handler0(LOG_ERR, process_title, tb);
            text[0]=0;
            *textlen=0;
            break;
          }
        }
      }
    }

    if (p_length != NULL)
    {
      writelogfile0(LOG_ERR, process_title, tb_sprintf("LENGTH termination error in sms file %s",filename));
      alarm_handler0(LOG_ERR, process_title, tb);
      text[0]=0;
      *textlen=0;
    }

    if (too_long)
    {
      writelogfile0(LOG_ERR, process_title, tb_sprintf("Data is too long in sms file %s",filename));
      alarm_handler0(LOG_ERR, process_title, tb);
      text[0]=0;
      *textlen=0;
    }

    fclose(fp);
  }
  else
  {
    writelogfile0(LOG_ERR, process_title, tb_sprintf("Cannot read sms file %s.",filename));
    alarm_handler0(LOG_ERR, process_title, tb);
  }
}

/* =======================================================================
   Mainspooler (sorts SMS into queues)
   ======================================================================= */

void mainspooler()
{
  char filename[PATH_MAX];
  char to[100];
  char from[100];
  char smsc[100];
  char queuename[100];
  char directory[PATH_MAX];
  char cmdline[PATH_MAX+PATH_MAX+32];
  int with_udh;
  char udh_data[500];
  int queue;
  int alphabet;
  int i;
  int flash;
  int success;
  int report;
  int split;
  int validity;
  int voicecall;
  int hex;
  int replace_msg;

  writelogfile(LOG_INFO, process_title, "outgoing file checker has started.");
  while (terminate==0)
  {
    success=0;
    if (getfile(d_spool,filename))
    {
      readSMSheader(filename,to,from,&alphabet,&with_udh,udh_data,queuename,&flash,smsc,&report,&split,
                    &validity, &voicecall, &hex, &replace_msg);
      // Is the destination set?
      if (to[0]==0)
      {
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("No destination in file %s",filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
      }
      // Does the checkhandler accept the message?
      else if (run_checkhandler(filename))
      {   
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("SMS file %s rejected by checkhandler",filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
      }
      // is To: in the blacklist?
      else if (inblacklist(to))
      {
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("Destination %s in file %s is blacklisted",to,filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
      }
      // is To: in the whitelist?
      else if (! inwhitelist(to))
      {
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("Destination %s in file %s is not whitelisted",to,filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
      }
      // Is the alphabet setting valid?
      else if (alphabet>2)
      {
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("Invalid alphabet in file %s",filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
      }
      // is there is a queue name, then set the queue by this name
      else if ((queuename[0]) && ((queue=getqueue(queuename,directory))==-1))
      {
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("Wrong provider queue %s in file %s",queuename,filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
      }
      // if there is no queue name, set it by the destination phone number
      else if ((queuename[0]==0) && ((queue=getqueue(to,directory))==-1))
      {
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("Destination number %s in file %s does not match any provider",to,filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
      }
      // everything is ok, move the file into the queue
      else
      {
        // 3.0.9:
        if (movefilewithdestlock(filename, directory) == 1)
        {
          writelogfile0(LOG_CRIT, process_title, 
                        tb_sprintf("Conflict with .LOCK file in the spooler: %s %s",
                        filename, directory));
          alarm_handler0(LOG_CRIT, process_title, tb);
          if (movefilewithdestlock(filename, directory) == 0)
            writelogfile0(LOG_CRIT, process_title, 
                          tb_sprintf("Retry solved the spooler conflict: %s %s",
                          filename, directory));
        }

        stop_if_file_exists("Cannot move",filename,"to",directory);
        writelogfile(LOG_INFO, process_title, "Moved file %s to %s",filename,directory);
        success=1;
      }
      if (! success)
      {
        rejected_counter++;
        if (eventhandler[0])
        {
          sprintf(cmdline,"%s %s %s",eventhandler,"FAILED",filename);
          my_system(cmdline, "eventhandler");
        }
        if (d_failed[0])
        {
          movefilewithdestlock(filename,d_failed);
          stop_if_file_exists("Cannot move",filename,"to",d_failed);
        }
        else
        {
          unlink(filename);
          stop_if_file_exists("Cannot delete",filename,"","");
          writelogfile(LOG_INFO, process_title, "Deleted file %s",filename);
        }
      }
    }
    else
    {
      // Sleep a while and output status monitor
      for (i=0; i<delaytime; i++)
      {
        print_status();
      checkwritestats();
        if (terminate==1)
          return;
        sleep(1);
      }
    }
  }
}

/* =======================================================================
   Delete message on the SIM card
   ======================================================================= */

void deletesms(int device, int modem, int sim) /* deletes the selected sms from the sim card */
{
  char command[100];
  char answer[500];

  if (sim == PDUFROMFILE)
    return;

  writelogfile(LOG_INFO, process_title, "Deleting message %i",sim);
  sprintf(command,"AT+CMGD=%i\r",sim);
  put_command(modem, devices[device].name,devices[device].send_delay, command, answer, sizeof(answer), 50, "(OK)|(ERROR)");
}

/* =======================================================================
   Check size of SIM card
   ======================================================================= */

void check_memory(int device, int modem, int *used_memory,int *max_memory) // checks the size of the SIM memory
{
  char answer[500];
  char* start;
  char* end;
  char tmp[100];

  // Set default values in case that the modem does not support the +CPMS command
  *used_memory=1;
  *max_memory=10;

  // Ability to read incoming PDU from file:
  if (devices[device].pdu_from_file[0])
  {
    FILE *fp;
    char filename[PATH_MAX];

    strcpy(filename, devices[device].pdu_from_file);
    if (getpdufile(filename))
    {
      if ((fp = fopen(filename, "r")) != NULL)
      {
        fclose(fp);
        writelogfile(LOG_INFO, process_title, "Found an incoming message file %s", filename);
        return;
      }
    }
  }

  if (devices[device].modem_disabled == 1)
  {
    *used_memory = 0;
    return;
  }

  writelogfile(LOG_INFO, process_title, "Checking memory size");
  put_command(modem, devices[device].name,devices[device].send_delay, "AT+CPMS?\r", answer, sizeof(answer), 50, "(\\+CPMS:.*OK)|(ERROR)");
  if ((start=strstr(answer,"+CPMS:")))
  {
    end=strchr(start,'\r');
    if (end)
    {
      *end=0;
      getfield(start,2,tmp);
      if (tmp[0])
        *used_memory=atoi(tmp);
      getfield(start,3,tmp);
      if (tmp[0])
        *max_memory=atoi(tmp);    
      writelogfile(LOG_INFO, process_title, "Used memory is %i of %i",*used_memory,*max_memory);
      return;
    }
  }
  writelogfile(LOG_INFO, process_title, "Command failed, using defaults.");
}


/* =======================================================================
   Read a memory space from SIM card
   ======================================================================= */

int readsim(int device, int modem, int sim, char* line1, char* line2)  
/* reads a SMS from the given SIM-memory */
/* returns number of SIM memory if successful, otherwise 0 */
/* line1 contains the first line of the modem answer */
/* line2 contains the pdu string */
{                                
  char command[500];
  char answer[1024];
  char* begin1;
  char* begin2;
  char* end1;
  char* end2;

  line2[0]=0;
  line1[0]=0;

#ifdef DEBUGMSG
  printf("!! readsim(device=%i, modem=%i, sim=%i, ...)\n",device,modem,sim);
#endif

  // Ability to read incoming PDU from file:
  if (sim == 1 && devices[device].pdu_from_file[0])
  {
    FILE *fp;
    char *p;
    char filename[PATH_MAX];

    // 3.1beta7, 3.0.9: If a definition is a directory, first file found from there is taken.
    // Note: for safety reasons this directory definition is accepted only if it ends with slash and no dot
    // is used in any position of definition. A single file definition is accepted if a definition does
    // not end with slash and a directory using the same name does not exist.
    strcpy(filename, devices[device].pdu_from_file);
    if (getpdufile(filename))
    {
      if ((fp = fopen(filename, "r")) != NULL)
      {
        int result = PDUFROMFILE;
        char st[1024];

#ifdef DEBUGMSG
  printf("!! reading message from %s\n", filename);
#endif
        writelogfile(LOG_INFO, process_title, "Reading an incoming message from file %s", filename);

        // 3.1beta7, 3.0.9:
        // First check if there is a line starting with "PDU: ". If found, it's taken.
        while (fgets(st, sizeof(st), fp))
        {
          cutspaces(st);
          cut_ctrl(st);
          if (!strncmp(st, "PDU: ", 5) || !strncmp(st, "PDU:_", 5))
          {
            strcpy(line2, st +5);
            break;
          }
        }

        if (*line2 == 0)
        {
          fseek(fp, 0, SEEK_SET);
          while (fgets(st, sizeof(st), fp))
          {
            cutspaces(st);
            cut_ctrl(st);
            if (*st && *st != '#')
            {
              if (*line1 == 0)
                strcpy(line1, st);
              else if (*line2 == 0)
                strcpy(line2, st);
              else
                break;
            }
          }

          if (*line2 == 0)
          {
            // line1 is not necessary. If there is only one line, it should be the PDU string.
            strcpy(line2, line1);
            line1[0] = 0;
          }

          if ((p = strrchr(line2, ' ')) != NULL)
            strcpy(line2, p +1);

          // Incoming PDU is now widely checked, checking here is needed no more.
          //p = line2;
          //while (*p)
          //  if (!isXdigit(*(p++)))
          //  {
          //    *line2 = 0;
          //    break;
          //  }

          if (*line2 == 0)
          {
            result = -1;
            writelogfile(LOG_CRIT, process_title, "Syntax error in the incoming message file.");
          }
        }

        fclose(fp);
        unlink(filename);
#ifdef DEBUGMSG
  printf("!! read result:%i\n", result);
#endif
        return result;
      }
    }
  }

  if (devices[device].modem_disabled == 1)
  {
    writelogfile(LOG_CRIT, process_title, "Cannot try to get stored message %i, MODEM IS DISABLED", sim);
    return 0;
  }

  writelogfile(LOG_INFO, process_title, "Trying to get stored message %i",sim);
  sprintf(command,"AT+CMGR=%i\r",sim);

  // Some modems answer OK in case of empty memory space (added "|(OK)")
  put_command(modem, devices[device].name,devices[device].send_delay, command,answer,sizeof(answer),50,"(\\+CMGR:.*OK)|(ERROR)|(OK)");
  if (strstr(answer,",,0\r")) // No SMS,  because Modem answered with +CMGR: 0,,0 
    return -1;
  if (strstr(answer,"ERROR")) // No SMS,  because Modem answered with ERROR 
    return -1;  
  begin1=strstr(answer,"+CMGR:");
  if (begin1==0)
    return -1;
  end1=strstr(begin1,"\r");
  if (end1==0)
    return -1;
  begin2=end1+1;
  end2=strstr(begin2+1,"\r");
  if (end2==0)
    return -1;
  strncpy(line1,begin1,end1-begin1);
  line1[end1-begin1]=0;
  strncpy(line2,begin2,end2-begin2);
  line2[end2-begin2]=0;
  cutspaces(line1);
  cut_ctrl(line1);
  cutspaces(line2);
  cut_ctrl(line2); 
  if (strlen(line2)==0)
    return -1;
#ifdef DEBUGMSG
  printf("!! line1=%s, line2=%s\n",line1,line2);
#endif
  return sim;
}

/* =======================================================================
   Write a received message into a file 
   ======================================================================= */
   
int received2file(char* line1, char* line2, char* mode, char* modemname, char* filename, int cs_convert, 
                  int *stored_concatenated, int device) // returns 1 if this was a status report
{
  int userdatalength;
  char ascii[MAXTEXT]= {};
  char sendr[100]= {};
  int with_udh=0;
  char udh_data[450]= {};
  char smsc[31]= {};
  char name[64]= {};
  char date[9]= {};
  char Time[9]= {};
  char warning_headers[SIZE_WARNING_HEADERS] = {};
  char status[40]={};
  int alphabet=0;
  int is_statusreport=0;
  FILE* fd;
  int do_decode_unicode_text = 0;
  int do_internal_combine = 0;
  int is_unsupported_pdu = 0;
  int pdu_store_length = 0;
  int result = 1;
  char from_toa[51] = {};
  int report;
  int replace;
  
  if (devices[device].decode_unicode_text == 1 ||
      (devices[device].decode_unicode_text == -1 && decode_unicode_text == 1))
    do_decode_unicode_text = 1;
  if (devices[device].internal_combine == 1 ||
      (devices[device].internal_combine == -1 && internal_combine == 1))
    do_internal_combine = 1;

#ifdef DEBUGMSG
  printf("!! received2file(line1=%s, line2=%s, mode=%s, modemname=%s, filename=%s, cs_convert=%i, decode_unicode_text=%i, internal_combine=%i)\n", 
         line1, line2, mode, modemname, filename, cs_convert, do_decode_unicode_text, do_internal_combine);
#endif

  getfield(line1,1,status);
  getfield(line1,2,name);
  // Check if field 2 was a number instead of a name
  if (atoi(name)>0)
  {
    name[0]=0;//Delete the name because it is missing
  }    
  userdatalength=splitpdu(line2, mode, &alphabet, sendr, date, Time, ascii, smsc, &with_udh, udh_data, &is_statusreport,
                          &is_unsupported_pdu, from_toa, &report, &replace, warning_headers);
  if (alphabet==-1 && cs_convert==1)
    userdatalength=gsm2iso(ascii,userdatalength,ascii,sizeof(ascii));
  else if (alphabet == 2 && do_decode_unicode_text == 1)
  {
    int offset = 0;

    if (strlen(udh_data) >= 18 && octet2bin(udh_data +3) == 0x00 && octet2bin(udh_data +6) == 0x03)
      offset = 12;
    else if (strlen(udh_data) >= 21 && octet2bin(udh_data +3) == 0x08 && octet2bin(udh_data +6) == 0x04)
      offset = 15;

    if (offset)
    {
      // If this is the only part, udh data can be dropped:
      if (strncmp(udh_data +offset, "01 01", 5) == 0)
      {
        *udh_data = '\0';
        with_udh = 0;
      }
    }

    // 3.1beta7, 3.0.9: decoding is always done:
    userdatalength = decode_ucs2(ascii, userdatalength);
    alphabet = 0;
  }
  
#ifdef DEBUGMSG
  printf("!! userdatalength=%i\n",userdatalength);
  printf("!! name=%s\n",name);  
  printf("!! sendr=%s\n",sendr);  
  printf("!! date=%s\n",date);
  printf("!! Time=%s\n",Time); 
  if ((alphabet==-1 && cs_convert==1)||(alphabet==0))
  printf("!! ascii=%s\n",ascii); 
  printf("!! smsc=%s\n",smsc); 
  printf("!! with_udh=%i\n",with_udh);
  printf("!! udh_data=%s\n",udh_data);   
  printf("!! is_statusreport=%i\n",is_statusreport);   
  printf("!! is_unsupported_pdu=%i\n", is_unsupported_pdu);
  printf("!! from_toa=%s\n", from_toa);
  printf("!! report=%i\n", report);
  printf("!! replace=%i\n", replace);
#endif
  writelogfile(LOG_NOTICE, process_title, "SMS received%s, From: %s", (is_statusreport)? " (Report)" : "", sendr);

  *stored_concatenated = 0;
  if (do_internal_combine == 1)
  {
    int offset = 0; // points to the part count byte.

    //if (strlen(udh_data) == 18 && strncmp(udh_data, "05 00 03 ", 9) == 0 && strncmp(udh_data +12, "01", 2) != 0)
    if (strlen(udh_data) >= 18 && octet2bin(udh_data +3) == 0x00 && octet2bin(udh_data +6) == 0x03)
      offset = 12;
    else if (strlen(udh_data) >= 21 && octet2bin(udh_data +3) == 0x08 && octet2bin(udh_data +6) == 0x04)
      offset = 15;
    if (offset)
      if (strncmp(udh_data +offset, "01 01", 5) == 0)
        offset = 0;

    if (offset)
    {
      // This is a part of a concatenated message.
      char con_filename[PATH_MAX];
      int partcount;
      char st[1024];
      char messageid[6];
      int i;
      int found = 0;
      int udlen;
      int ftmp;
      char tmp_filename[PATH_MAX];
      int cmp_start;
      int cmp_length;
      char *p;
      int part;

      // First we store it to the concatenated store of this device:
      sprintf(con_filename,"%s/%s-concatenated",d_incoming,devices[device].name);
      fd = fopen(con_filename, "a");
      if (fd)
      {
        //UDH-DATA: 05 00 03 02 03 02 PDU....
        fprintf(fd, "%s%s\n", udh_data, line2);
        fclose(fd);
        partcount = octet2bin(udh_data +offset);
        userdatalength = 0;
        *ascii = '\0';
        sprintf(messageid, (offset == 12)? "%.2s" : "%.5s", udh_data + 9);
        i = octet2bin(line2);
        cmp_length = octet2bin(line2 +2 +i*2 +2);
        if (cmp_length%2 != 0)
          cmp_length++;
        cmp_start = 2 +i*2 +4;
        // Next we try to find each part, starting at the first one:
        fd = fopen(con_filename, "r");
        for (i = 1; i <= partcount; i++)
        {
          found = 0;
          fseek(fd, 0, SEEK_SET);
          while (fgets(st, sizeof(st), fd))
          {
            //if (strncmp(st +9, messageid, 2) == 0 && octet2bin(st +15) == i &&
            //    strncmp(st +18 +cmp_start, line2 +cmp_start, cmp_length) == 0)
            p = st +(octet2bin(st) +1) *3;
            part = (strncmp(st +3, "00", 2) == 0)? octet2bin(st +15) : octet2bin(st +18);
            if (strncmp(st +9, messageid, strlen(messageid)) == 0 && part == i &&
                strncmp(p +cmp_start, line2 +cmp_start, cmp_length) == 0)
            {
              found = 1;
              pdu_store_length += strlen(p) +5;
              break;
            }
          }

          // If some part was not found, we can take a break.
          if (!found)
            break;
        }

        if (!found)
        {
          fclose(fd);
          *stored_concatenated = 1;
        }
        else
        {
          pdu_store = (char *)malloc(pdu_store_length +1);
          if (pdu_store)
            *pdu_store = 0;

          for (i = 1; i <= partcount; i++)
          {
            fseek(fd, 0, SEEK_SET);
            while (fgets(st, sizeof(st), fd))
            {
              //if (strncmp(st +9, messageid, 2) == 0 && octet2bin(st +15) == i &&
              //    strncmp(st +18 +cmp_start, line2 +cmp_start, cmp_length) == 0)
              p = st +(octet2bin(st) +1) *3;
              part = (strncmp(st +3, "00", 2) == 0)? octet2bin(st +15) : octet2bin(st +18);
              if (strncmp(st +9, messageid, strlen(messageid)) == 0 && part == i &&
                  strncmp(p +cmp_start, line2 +cmp_start, cmp_length) == 0)
              {
                if (pdu_store)
                {
                  strcat(pdu_store, "PDU: ");
                  strcat(pdu_store, p);
                }
                // Correct part was found, concatenate it's text to the buffer:
                udlen=splitpdu(p, mode, &alphabet, sendr, date, Time, ascii +userdatalength, smsc,
                               &with_udh, udh_data, &is_statusreport, &is_unsupported_pdu,
                               from_toa, &report, &replace, warning_headers);
                if (alphabet==-1 && cs_convert==1)
                  udlen=gsm2iso(ascii +userdatalength,udlen,ascii +userdatalength,sizeof(ascii) -userdatalength);
                else if (alphabet == 2 && do_decode_unicode_text == 1)
                {
                  //udlen=unicode2sms(ascii +userdatalength,udlen,ascii +userdatalength,sizeof(ascii) -userdatalength);
                  udlen = decode_ucs2(ascii +userdatalength, udlen);
                  alphabet = 0;
                }
                userdatalength += udlen;
                break;
              }
            }
          }

          sprintf(tmp_filename,"%s/%s.XXXXXX",d_incoming,devices[device].name);
          ftmp = mkstemp(tmp_filename);       
          fseek(fd, 0, SEEK_SET);
          while (fgets(st, sizeof(st), fd))
          {
            //if (!(strncmp(st +9, messageid, 2) == 0 && strncmp(st +18, line2, cmp_length) == 0))
            p = st +(octet2bin(st) +1) *3;
            if (!(strncmp(st +9, messageid, strlen(messageid)) == 0 &&
                strncmp(p +cmp_start, line2 +cmp_start, cmp_length) == 0))
              write(ftmp, &st, strlen(st));
          }

          close(ftmp);
          fclose(fd);
          unlink(con_filename);
          rename(tmp_filename, con_filename);

          // UDH-DATA is not valid anymore:
          *udh_data = '\0';
          with_udh = 0;
        }
      }
      else
      {
        writelogfile0(LOG_ERR, process_title, tb_sprintf("Cannot open file %s!", con_filename));
        alarm_handler0(LOG_ERR, process_title, tb);
        result = 0; //return 0;
      }
    } 
  }

  if (result)
  {
    if (*stored_concatenated)
    {
      // We don't need the incoming file created previously:
      unlink(filename);
      result = 0; //return 0;
    }
    else
    {
      //Replace the temp file by a new file with same name. This resolves wrong file permissions.
      unlink(filename);
      fd = fopen(filename, "w");
      if (fd)
      { 
        // 3.1beta7, 3.0.9: This header can be used to detect that a message has no usual content:
        if (is_unsupported_pdu)
          fprintf(fd, "Error: Cannot decode PDU, see text part for details.\n");
        if (*warning_headers)
          fprintf(fd, warning_headers);
        fprintf(fd, "From: %s\n",sendr);
        if (*from_toa)
          fprintf(fd, "From_TOA: %s\n", from_toa);
        if (name[0])
          fprintf(fd,"Name: %s\n",name);
        if (smsc[0])
          fprintf(fd,"From_SMSC: %s\n",smsc);
        if (date[0] && Time[0])
          fprintf(fd, "Sent: %s %s\n",date,Time);
        // Add local timestamp
        {
          char timestamp[40];
          time_t now;
          time(&now);
          strftime(timestamp,sizeof(timestamp),"%Y-%m-%d %H:%M:%S",localtime(&now));
          strcpy(timestamp, timestamp +2);
          fprintf(fd,"Received: %s\n",timestamp);
        }
        fprintf(fd, "Subject: %s\n",modemname);

        // 3.1beta7, 3.0.9:
        if (devices[device].identity_header[0])
          fprintf(fd, "%s: %s\n", devices[device].identity_header, devices[device].identity);
        if (report >= 0)
          fprintf(fd, "Report: %s\n", (report)? "yes" : "no");
        if (replace >= 0)
          fprintf(fd, "Replace: %i\n", replace);

        if (alphabet==-1)
        {
          if (cs_convert)
            fprintf(fd,"Alphabet: ISO\n");
          else
            fprintf(fd,"Alphabet: GSM\n");
        }
        else if (alphabet==0)
          fprintf(fd,"Alphabet: ISO\n");
        else if (alphabet==1)
          fprintf(fd,"Alphabet: binary\n");
        else if (alphabet==2)
          fprintf(fd,"Alphabet: UCS2\n");
        else if (alphabet==3)
          fprintf(fd,"Alphabet: reserved\n");
        if (udh_data[0])
        {
          fprintf(fd,"UDH-DATA: %s\n",udh_data);
        }
        if (with_udh)
          fprintf(fd,"UDH: true\n");
        else
          fprintf(fd,"UDH: false\n");
        // 3.1beta7, 3.0.9: with value 2 unsupported pdu's were not stored.
        if (store_received_pdu == 3 ||
           (store_received_pdu == 2 && (alphabet == 1 || alphabet == 2)) ||
           (store_received_pdu >= 1 && is_unsupported_pdu == 1))
        {
          if (pdu_store)
            fprintf(fd,"%s", pdu_store);
          else
            fprintf(fd,"PDU: %s\n", line2);
        }
        // Show the error position (first) if possible:
        if (store_received_pdu >= 1 && is_unsupported_pdu == 1)
        {
          char *p;
          char *p2;
          int pos;
          int len = 1;
          int i = 0;

          if ((p = strstr(ascii, "Position ")))
          {
            if ((pos = atoi(p +9)) > 0)
            {
              if ((p = strchr(p +9, ',')))
                if ((p2 = strchr(p, ':')) && p2 > p +1)
                  len = atoi(p +1);
              fprintf(fd, "Pos: ");
              while (i++ < pos -1)
                if (i % 10)
                {   
                  if (i % 5)
                    fprintf(fd, ".");
                  else
                    fprintf(fd, "-");
                }
                else
                  fprintf(fd, "*");

              for (i = 0; i < len; i++)
                fprintf(fd, "^");
              fprintf(fd, "~here(%i)\n", pos);
            }
          }
        }
        // --------------------------------------------
        fprintf(fd,"\n");

        // UTF-8 conversion if necessary:
        if (incoming_utf8 == 1 && alphabet <= 0)
        {
          // 3.1beta7, 3.0.9: GSM alphabet is first converted to ISO
          if (alphabet == -1 && cs_convert == 0)
          {
            userdatalength = gsm2iso(ascii, userdatalength, ascii, sizeof(ascii));
            alphabet = 0; // filename_preview will need this information.
          }

          iso2utf8_file(fd, ascii, userdatalength);
        }
        else
          fwrite(ascii,1,userdatalength,fd);

        fclose(fd);

#ifdef DEBUGMSG
  if ((fd = fopen(filename, "r")))
  {
    char buffer[1024];

    printf("!! FILE: %s\n", filename);
    while (fgets(buffer, sizeof(buffer) -1, fd))
      printf("%s", buffer);
    fclose(fd);
  }
#endif

        result = is_statusreport; //return is_statusreport;
      }
      else
      {
        writelogfile0(LOG_ERR, process_title, tb_sprintf("Cannot create file %s!", filename));
        alarm_handler0(LOG_ERR, process_title, tb);
        result = 0; //return 0;
      }
    }
  }

  if (pdu_store)
  {
    free(pdu_store);
    pdu_store = NULL;
  }
  return result;
}

/* =======================================================================
   Receive one SMS
   ======================================================================= */

int receivesms(int device, int modem, int* quick, int only1st)  
// receive one SMS or as many as the modem holds in memory
// if quick=1 then no initstring
// if only1st=1 then checks only 1st memory space
// Returns 1 if successful
// Return 0 if no SM available
// Returns -1 on error
{
  int max_memory,used_memory;
  int start_time=time(0);  
  int found;
  int foundsomething=0;
  int statusreport;
  int sim;
  char line1[1024];
  char line2[1024];
  char filename[PATH_MAX];
  char cmdline[PATH_MAX+PATH_MAX+32];
  int stored_concatenated;
  int memories;

  if (terminate==1)
    return 0;

#ifdef DEBUGMSG
  printf("receivesms(device=%i, modem=%i, quick=%i, only1st=%i)\n",device,modem,*quick,only1st);
#endif

  statistics[device]->status='r';
  writelogfile(LOG_INFO, process_title, "Checking device for incoming SMS");

  if (*quick==0 && devices[device].modem_disabled == 0)
  {
    // Initialize modem
    if (initmodem(device, modem, devices[device].name, devices[device].send_delay, errorsleeptime, devices[device].pin,
                  devices[device].initstring, devices[device].initstring2, devices[device].mode, devices[device].smsc,
                  &(devices[device].check_network), devices[device].pinsleeptime, devices[device].pre_init) > 0)
    {
      statistics[device]->usage_r+=time(0)-start_time;
      return -1;
    }
  }
  
  // Dual-memory handler:
  for (memories = 0; memories <= 2; memories++)
  {
    if (terminate == 1)
      break;

    if (devices[device].primary_memory[0] && devices[device].secondary_memory[0] && devices[device].modem_disabled == 0)
    {
      char command[128];
      char answer[1024];

      command[0] = 0;
      if (memories == 1)
      {
        if (only1st)
          break;
        sprintf(command, "AT+CPMS=\"%s\"\r", devices[device].secondary_memory);
      }
      else if (memories == 2)
        sprintf(command, "AT+CPMS=\"%s\"\r", devices[device].primary_memory);

      if (*command)
      {
        writelogfile(LOG_INFO, process_title, "Changing memory");
        put_command(modem, devices[device].name,devices[device].send_delay, command, answer, sizeof(answer), 50, "(\\+CPMS:.*OK)|(ERROR)");
      }

      if (memories == 2)
        break;
    }
    else if (memories > 0)
      break;

    // Check how many memory spaces we really can read
    check_memory(device,modem,&used_memory,&max_memory);
    found=0;
    if (used_memory>0)
    {
      if (max_memory == 0 && memories == 1)
        max_memory = devices[device].secondary_memory_max;

      for (sim=devices[device].read_memory_start; sim<=devices[device].read_memory_start+max_memory-1; sim++)
      {
        if (terminate==1)
          break;

        found=readsim(device,modem,sim,line1,line2);
        if (found>=0)
        {
          foundsomething=1;
          *quick=1;
        
          //Create a temp file for received message
          sprintf(filename,"%s/%s.XXXXXX",d_incoming,devices[device].name);
          close(mkstemp(filename));
        
          statusreport=received2file(line1,line2, devices[device].mode, devices[device].name, filename, devices[device].cs_convert, 
                                     &stored_concatenated, device);
          statistics[device]->received_counter++;
          if (stored_concatenated == 0)
            if (eventhandler[0] || devices[device].eventhandler[0])
            {
              if (devices[device].eventhandler[0] && statusreport==1)
                sprintf(cmdline,"%s %s %s",devices[device].eventhandler,"REPORT",filename);
              else if (eventhandler[0] && statusreport==1)
                sprintf(cmdline,"%s %s %s",eventhandler,"REPORT",filename);
              else if (devices[device].eventhandler[0] && statusreport==0)
                sprintf(cmdline,"%s %s %s",devices[device].eventhandler,"RECEIVED",filename);
              else if (eventhandler[0] && statusreport==0)
                sprintf(cmdline,"%s %s %s",eventhandler,"RECEIVED",filename);
              my_system(cmdline, "eventhandler");
            }
          deletesms(device,modem,found);
          used_memory--;
          if (used_memory<1) 
            break; // Stop reading memory if we got everything
        }
        if (only1st)
          break;
      }
    }  
  }
  statistics[device]->usage_r+=time(0)-start_time;
  if (foundsomething)
  {
    return 1;
  }
  else
  {
    writelogfile(LOG_INFO, process_title, "No SMS received");
    return 0;
  }
}

/* ==========================================================================================
   Send a part of a message, this is physically one SM with max. 160 characters or 14 bytes
   ========================================================================================== */

int send_part(int device, int modem, char* from, char* to, char* text, int textlen, int alphabet, int with_udh, char* udh_data,
              int quick, int flash, char* messageid, char* smsc, int report, int validity, int replace_msg)
// alphabet can be -1=GSM 0=ISO 1=binary 2=UCS2
// with_udh can be 0=off or 1=on or -1=auto (auto = 1 for binary messages and text message with udh_data)
// udh_data is the User Data Header, only used when alphabet= -1 or 0. With alphabet=1 or 2, the User Data Header should be included in the text argument.
// smsc is optional. Can be used to override config file setting.
// Output: messageid
{
  char pdu[1024];
  int retries;
  char command[128];
  char command2[1024];
  char answer[1024];
  char* posi1;
  char* posi2;
  char replacestr[41];

#ifdef DEBUGMSG
  printf("!! send_part(device=%i, modem=%i, from=%s, to=%s, text=..., textlen=%i, alphabet=%i, with_udh=%i, udh_data=%s, quick=%i, flash=%i, messageid=..., smsc=%s, report=%i, validity=%i, replace_msg=%i)\n", device, modem, from, to, textlen, alphabet, with_udh, udh_data, quick, flash, smsc, report, validity, replace_msg);
#endif
  
  time_t start_time=time(0);
  // Mark modem as sending
  statistics[device]->status='s';
  // Initialize messageid
  strcpy(messageid,"");
  writelogfile(LOG_INFO, process_title, "Sending SMS from %s to %s",from,to);
  if ((report==1) && (devices[device].incoming==0))
    writelogfile(LOG_NOTICE, process_title, "Cannot receive status report because receiving is disabled");

  if ((quick==0 || *smsc) && devices[device].sending_disabled == 0)
  {
    // Initialize modem
    if (initmodem(device, modem, devices[device].name, devices[device].send_delay, errorsleeptime, devices[device].pin,
                  devices[device].initstring, devices[device].initstring2, devices[device].mode,
                  (*smsc)? smsc : devices[device].smsc,
                  &(devices[device].check_network), devices[device].pinsleeptime, devices[device].pre_init) > 0)
    {
      statistics[device]->usage_s+=time(0)-start_time;
      return 0;
    }
  }
  
  // Use config file setting if report is unset in file header
  // MOVED to the caller function:
  //if (report==-1)
  //  report=devices[device].report;
    
  // Compose the modem command
  make_pdu(to,text,textlen,alphabet,flash,report,with_udh,udh_data,devices[device].mode,pdu,validity, replace_msg);
  if (strcasecmp(devices[device].mode,"old")==0)
    sprintf(command,"AT+CMGS=%i\r",(int)strlen(pdu)/2);
  else
    sprintf(command,"AT+CMGS=%i\r",(int)strlen(pdu)/2-1);

  sprintf(command2,"%s\x1A",pdu);
  
  if (store_sent_pdu)
  {
    char *title = "PDU: ";

    if (!pdu_store)
    {
      if ((pdu_store = (char *)malloc(strlen(title) +strlen(pdu) +2)))
        *pdu_store = 0;
    }
    else
      pdu_store = (char *)realloc((void *)pdu_store, strlen(pdu_store) +strlen(title) +strlen(pdu) +2);

    if (pdu_store)
      sprintf(strchr(pdu_store, 0), "%s%s\n", title, pdu);
  }

  if (devices[device].sending_disabled == 1)
  {
    writelogfile(LOG_NOTICE, process_title, "Test run, NO actual sending: from %s to %s", from, to);
    sleep(1);
    writelogfile(LOG_NOTICE, process_title, "PDU to %s: %s", to, pdu);
    strcpy(messageid, "1");
    statistics[device]->usage_s+=time(0)-start_time;
    statistics[device]->succeeded_counter++;
    return 1;
  }
  else
  {
    retries=0;
    while (1)
    {
      // Send modem command
      put_command(modem, devices[device].name,devices[device].send_delay, command, answer, sizeof(answer), 50, "(>)|(ERROR)");
      // Send message if command was successful
      if (! strstr(answer,"ERROR"))
        put_command(modem, devices[device].name,devices[device].send_delay, command2, answer ,sizeof(answer), 300, "(OK)|(ERROR)");
      // Check answer
      if (strstr(answer,"OK"))
      {
        // If the modem answered with an ID number then copy it into the messageid variable.
        posi1=strstr(answer,"CMGS: ");
        if (posi1)
        {
          posi1+=6;
          posi2=strchr(posi1,'\r');
          if (! posi2) 
            posi2=strchr(posi1,'\n');
          if (posi2)
            posi2[0]=0;
          strcpy(messageid,posi1);
#ifdef DEBUGMSG
  printf("!! messageid=%s\n",messageid);
#endif
        }

        *replacestr = 0;
        if (replace_msg)
          sprintf(replacestr, ", Replace_msg: %i", replace_msg);

        writelogfile(LOG_NOTICE, process_title, "SMS sent, Message_id: %s%s, To: %s", messageid, replacestr, to);
        statistics[device]->usage_s+=time(0)-start_time;
        statistics[device]->succeeded_counter++;
        return 1;
      }  
      else 
      {
        writelogfile0(LOG_ERR, process_title, tb_sprintf("The modem said ERROR or did not answer."));
        alarm_handler0(LOG_ERR, process_title, tb);
        retries+=1;
        if (retries<=2)
        {
          writelogfile(LOG_NOTICE, process_title, "Waiting %i sec. before retrying",errorsleeptime);
          if (t_sleep(errorsleeptime))
          {
            // Cancel if terminating
            statistics[device]->usage_s+=time(0)-start_time;
            statistics[device]->failed_counter++;
            writelogfile0(LOG_WARNING, process_title, tb_sprintf("Sending SMS to %s failed", to));
            alarm_handler0(LOG_WARNING, process_title, tb);
            return 0;
          }

          // Initialize modem after error
          if (initmodem(device, modem, devices[device].name, devices[device].send_delay, errorsleeptime, devices[device].pin,
                        devices[device].initstring, devices[device].initstring2, devices[device].mode, devices[device].smsc,
                        &(devices[device].check_network), devices[device].pinsleeptime, devices[device].pre_init) > 0)
          {
            // Cancel if initializing failed
            statistics[device]->usage_s+=time(0)-start_time;
            statistics[device]->failed_counter++;
            writelogfile0(LOG_WARNING, process_title, tb_sprintf("Sending SMS to %s failed",to));
            alarm_handler0(LOG_WARNING, process_title, tb);
            return 0;
          }
        }
        else
        {
          // Cancel if too many retries
          statistics[device]->usage_s+=time(0)-start_time;
          statistics[device]->failed_counter++;
          writelogfile0(LOG_WARNING, process_title, tb_sprintf("Sending SMS to %s failed",to));
          alarm_handler0(LOG_WARNING, process_title, tb);
          return 0;
        }
      }
    }  
  }
}

/* =======================================================================
   Send a whole message, this can have many parts
   ======================================================================= */

int send1sms(int device, int modem, int* quick, int* errorcounter)    
// Search the queues for SMS to send and send one of them.
// Returns 0 if queues are empty
// Returns -1 if sending failed
// Returns 1 if successful
{
  char filename[PATH_MAX];
  char to[100];
  char from[100];
  char smsc[100];
  char provider[100];
  char text[MAXTEXT];
  int with_udh=-1;
  char udh_data[500];
  int textlen;
  char part_text[maxsms_pdu+1];
  int part_text_size;
  char directory[PATH_MAX];
  char cmdline[PATH_MAX+PATH_MAX+32];
  int q,queue;
  int part;
  int parts;
  int maxpartlen;
  int eachpartlen;
  int alphabet;
  int success=0;
  int flash;
  int report;
  int split;
  int tocopy;
  int reserved;
  char messageid[10]={0};
  int found_a_file=0;
  int validity;
  int voicecall;
  int hex;
  int terminate_written=0;
  int replace_msg = 0;

#ifdef DEBUGMSG
  printf("!! send1sms(device=%i, modem=%i, quick=%i, errorcounter=%i)\n", device, modem, *quick, *errorcounter);
#endif

  // Search for one single sms file  
  for (q=0; q<PROVIDER; q++)
  {
    if (q == 1)
      if (devices[device].queues[q][0] == 0)
        break;

    if (/*(devices[device].queues[q][0]) &&*/
       ((queue=getqueue(devices[device].queues[q],directory))!=-1) &&
       (getfile(directory,filename)) &&
       (lockfile(filename)))
    {
      found_a_file=1;
      break;
    }
  }
 
  // If there is no file waiting to send, then do nothing
  if (found_a_file==0)
  { 
#ifdef DEBUGMSG
  printf("!! No file\n");
  printf("!! quick=%i errorcounter=%i\n",*quick,*errorcounter);
#endif
    return 0;
  }
  else
  {
    readSMSheader(filename,to,from,&alphabet,&with_udh,udh_data,provider,&flash,smsc,&report,&split,&validity,
                  &voicecall, &hex, &replace_msg);

    // SMSC setting is allowed only if there is smsc set in the config file:
    if (devices[device].smsc[0] == 0)
      smsc[0] = 0;

    // Use config file setting if report is unset in file header
    if (report==-1)
      report=devices[device].report;

    // Set a default for with_udh if it is not set in the message file.
    if (with_udh==-1)
    {
      if (alphabet==1 || udh_data[0])
        with_udh=1;
      else
        with_udh=0;
    }
    // If the header includes udh-data then enforce udh flag even if it is not 1.
    if (udh_data[0])
      with_udh=1;
    // if Split is unset, use the default value from config file
    if (split==-1) 
      split=autosplit;
    // disable splitting if udh flag or udh_data is 1
    if (with_udh && split)
    {
      split=0;
      // Keke: possible wrong message, if there is no need to do splitting? Autosplit=0 prevents this message.
      writelogfile(LOG_INFO, process_title, "Cannot split this message because it has an UDH.");      
    }
#ifdef DEBUGMSG
  printf("!! to=%s, from=%s, alphabet=%i, with_udh=%i, udh_data=%s, provider=%s, flash=%i, smsc=%s, report=%i, split=%i\n",to,from,alphabet,with_udh,udh_data,provider,flash,smsc,report,split);
#endif
    // If this is a text message, then read also the text    
    if (alphabet<1 || alphabet == 2)
    {
#ifdef DEBUGMSG
  printf("!! This is %stext message\n", (alphabet == 2)? "unicode " : "");
#endif
      maxpartlen = (alphabet == 2)? maxsms_ucs2 : maxsms_pdu; // ucs2 = 140, pdu = 160
      readSMStext(filename,devices[device].cs_convert && (alphabet==0),text,&textlen);
      // Is the message empty?
      if (textlen==0)
      {
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("The file %s has no text",filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
        parts=0;
        success=-1;
      }
      // The message is not empty
      else
      {
        // In how many parts do we need to split the text?
        if (split>0)
        {
          if (alphabet == 2) // With unicode messages
            if (split == 2)  // part numbering with text is not yet supported,
              split = 3;     // using udh numbering instead. 

          // if it fits into 1 SM, then we need 1 part
          if (textlen<=maxpartlen)
          {
            parts=1;
            reserved=0;
            eachpartlen=maxpartlen;
          }
          else if (split==2) // number with text
        {
            // reserve 4 chars for the numbers
            reserved=4;
            eachpartlen=maxpartlen-reserved;
            parts=(textlen+eachpartlen-1)/(eachpartlen);
          // If we have more than 9 parts, we need to reserve 6 chars for the numbers
            // And recalculate the number of parts.
          if (parts>9)
            {
              reserved=6;
              eachpartlen=maxpartlen-reserved;
              parts=(textlen+eachpartlen-1)/(eachpartlen);  
            }
        }
          else if (split==3) // number with udh
          {
            // reserve 7 chars for the UDH
            reserved=7;
            if (alphabet == 2) // Only six with unicode
              reserved = 6;
            eachpartlen=maxpartlen-reserved;
            parts=(textlen+eachpartlen-1)/(eachpartlen);
            concatenated_id++;
            if (concatenated_id>255)
              concatenated_id=0;
          }
          else // split==0 or 1, no part numbering
          {
            // no numbering, each part can have the full size
            eachpartlen=maxpartlen;
            reserved=0;
            parts=(textlen+eachpartlen-1)/eachpartlen;
          }
        }
        else
        {
          eachpartlen=maxpartlen;
          reserved=0;
          parts=1; 
        }
      if (parts>1)
          writelogfile(LOG_INFO, process_title, "Splitting this message into %i parts of max %i characters%s.",
                       parts, (alphabet == 2)? eachpartlen /2 : eachpartlen, (alphabet == 2)? " (unicode)" : "");
      }
    }
    else
    {
#ifdef DEBUGMSG
  printf("!! This is a binary message.\n");
#endif      
      // Read binary data. No splitting and no conversion.
      // The result is stored in text and textlen.
      maxpartlen=maxsms_binary;
      if (alphabet == 1 && hex == 1)
        readSMShex(filename, text, &textlen);
      else
        readSMStext(filename,0,text,&textlen);
      eachpartlen=maxpartlen;
      reserved=0;
      parts=1;
      // Is the message empty?
      if (textlen==0)
      {
        writelogfile0(LOG_NOTICE, process_title, tb_sprintf("The file %s has no data.",filename));
        alarm_handler0(LOG_NOTICE, process_title, tb);
        parts=0;
        success=-1;
      }
    }
    
    
    // Try to send each part  
    if (parts > 0)
      writelogfile(LOG_INFO, process_title, "I have to send %i short message for %s",parts,filename); 
#ifdef DEBUGMSG
  printf("!! textlen=%i\n",textlen);
#endif 

    // If sending concatenated message, replace_msg should not be used (otherwise previously
    // received part is replaced with a next one...
    if (parts > 1)
      replace_msg = 0;

    for (part=0; part<parts; part++)
    {
      if (split==2 && parts>1) // If more than 1 part and text numbering
      {
        sprintf(part_text,"%i/%i ",part+1,parts);
      tocopy=textlen-(part*eachpartlen);
        if (tocopy>eachpartlen)
          tocopy=eachpartlen;
#ifdef DEBUGMSG
  printf("!! tocopy=%i, part=%i, eachpartlen=%i, reserved=%i\n",tocopy,part,eachpartlen,reserved);
#endif 
        memcpy(part_text+reserved,text+(eachpartlen*part),tocopy);
        part_text_size=tocopy+reserved;
      }
      else if (split==3 && parts>1)  // If more than 1 part and UDH numbering
      {
        // in this case the numbers are not part of the text, but UDH instead
      tocopy=textlen-(part*eachpartlen);
        if (tocopy>eachpartlen)
          tocopy=eachpartlen;
#ifdef DEBUGMSG
  printf("!! tocopy=%i, part=%i, eachpartlen=%i, reserved=%i\n",tocopy,part,eachpartlen,reserved);
#endif 
        memcpy(part_text,text+(eachpartlen*part),tocopy);
        part_text_size=tocopy; 
        sprintf(udh_data,"05 00 03 %02X %02X %02X",concatenated_id,parts,part+1); 
        with_udh=1;  
      }
      else  // No part numbers
      {
        tocopy=textlen-(part*eachpartlen);
        if (tocopy>eachpartlen)
          tocopy=eachpartlen;
#ifdef DEBUGMSG
  printf("!! tocopy=%i, part=%i, eachpartlen=%i\n",tocopy,part,eachpartlen);
#endif 
        memcpy(part_text,text+(eachpartlen*part),tocopy);
        part_text_size=tocopy;
      }

  
      // Some modems cannot send if the memory is full. 
      if ((receive_before_send) && (devices[device].incoming))
      { 
        receivesms(device,modem,quick,1);
      }

      // Voicecall ability:
      if (part == 0 && voicecall == 1)
      {
        char command[128];
        char answer[1024];
        int i;
        int count = 3;
        char *p;

        if (devices[device].modem_disabled == 1)
          writelogfile(LOG_CRIT, process_title, "Cannot make a voice call, MODEM IS DISABLED");
        else
        {
          // Automatic redialing should be turned off in the phone settings!

          part_text[part_text_size] = '\0';
          // Currently the starting header is optional:
          if (strncasecmp(part_text, "TONE:", 5) == 0)
            strcpy(part_text, part_text +5);
          cutspaces(part_text);
          // If there is a space, the first part is taken as count:
          if ((p = strchr(part_text, ' ')) != NULL)
          {
            *p = '\0';
            if ((count = atoi(part_text)) <= 0)
              count = 3;
            cutspaces(strcpy(part_text, p +1));
          }
          if (!(*part_text))
            strcpy(part_text, "1,1,1,#,0,0,0,#,1,1,0,0,1");

          writelogfile(LOG_INFO, process_title, "I have to make a voice call to %s, with (%i times) DTMF %s",
                       to,count,part_text);
          sprintf(command, "ATD+%s;\r", to);
          put_command(modem, devices[device].name,devices[device].send_delay, command, answer,
                      sizeof(answer), 1200, "(OK)|(NO CARRIER)|(BUSY)|(NO ANSWER)");
          writelogfile(LOG_INFO, process_title, "The result of a voice call was %s",answer);

          // Some test results:
          // Dest phone is off: after 1 min 10 sec "NO ANSWER".
          // Dest phone does not answer: after 2 min 10 sec "", after CHUP "NO ANSWER".
          // Dest phone hooks: after couple of seconds "BUSY".
          // Dest phone answers: "OK".

          if (strstr(answer, "OK"))
          {
            // We are talking to the phone now.
            sprintf(command, "AT+VTS=%s\r", part_text);
            for (i = 0; i < count; i++)
            {
              sleep(3);
              put_command(modem, devices[device].name,devices[device].send_delay, command, answer,
                          sizeof(answer), 50, "(OK)|(ERROR)");
              if (strstr(answer, "ERROR"))
                if (i > 0)
                  break;
            }
            sleep(1);
          }

          sprintf(command, "AT+CHUP\r");
          put_command(modem, devices[device].name,devices[device].send_delay, command, answer,
                      sizeof(answer), 50, "(OK)|(ERROR)");

          success=1;
        }

        break;
      }
      else
      // Try to send the sms      
      if (send_part(device, modem, from, to, part_text, part_text_size, alphabet, with_udh, udh_data, *quick, flash ,messageid, smsc, report, validity, replace_msg))
      {
        // Sending was successful
        *quick=1;
        success=1;

        // Possible previous errors are ignored because now the modem worked well:
        *errorcounter=0;
      }
      else
      {
        // Sending failed
        *quick=0;
        success=-1;
        // Do not send the next part if sending failed
        break;
      }

      if (part<parts-1)
      {
        // If receiving has high priority, then receive all messages between sending each part.
        if (devices[device].incoming==2)
          receivesms(device,modem,quick,0);

        // Still part(s) left, possible termination is handled smoothly. This needs sms3 script.
        if (terminate==1 && terminate_written==0 && *infofile)
        {
          FILE *fp;
          char msg[256];

          sprintf(msg, "sending a multipart message, now part %i of %i.", part +1, parts);
          writelogfile(LOG_CRIT, process_title, "Currently %s",msg);
          if ((fp = fopen(infofile, "a")) != NULL)
          {
            fprintf(fp, "%s\n", msg);
            fclose(fp);
            terminate_written=1;
          }
          else
            writelogfile(LOG_CRIT, process_title, "Infofile %s cannot be written.",infofile);
        }
      }
    }
    
    // Mark modem status as idle while eventhandler is running
    statistics[device]->status='i';
    
    // (3.0.6) This differs from 3.1beta:
    {
      char timestamp[40];
      time_t now;
      char remove_headers[1024];
      char add_headers[1024];
      char *p = NULL;

      sprintf(remove_headers, "\nModem:\nSent:\nMessage_id:\nPDU:");
      sprintf(add_headers, "Modem: %s\n", devices[device].name);
      if (success != -1 && report > 0 && messageid[0] != 0)
      {
        time(&now);
        strftime(timestamp,sizeof(timestamp),"%Y-%m-%d %H:%M:%S",localtime(&now));
        strcpy(timestamp, timestamp +2);
        sprintf(strchr(add_headers, 0), "Sent: %s\nMessage_id: %s\n", timestamp, messageid);
      }

      // 3.0.9:
      if (devices[device].identity_header[0])
      {
        sprintf(strchr(remove_headers, 0), "\n%s:", devices[device].identity_header);
        sprintf(strchr(add_headers, 0), "%s: %s\n", devices[device].identity_header, devices[device].identity);
      }
      if (success < 1 ||
         store_sent_pdu == 3 ||
         (store_sent_pdu == 2 && (alphabet == 1 || alphabet == 2)))
        p = pdu_store;
      change_headers(filename, remove_headers, p, "%s", add_headers);
    }

    // Run eventhandler if configured
    if (eventhandler[0] || devices[device].eventhandler[0])
    {
      if (success<1)
        strcpy(text,"FAILED");
      else
        strcpy(text,"SENT");
      if (devices[device].eventhandler[0])
        sprintf(cmdline,"%s %s %s %s",devices[device].eventhandler,text,filename,messageid);
      else
        sprintf(cmdline,"%s %s %s %s",eventhandler,text,filename,messageid);
      my_system(cmdline, "eventhandler");
    }
    
    // If sending failed
    if (success==-1)
    {
      // Move file into failed queue or delete
      if (d_failed[0])
      {
        movefilewithdestlock(filename,d_failed);
        stop_if_file_exists("Cannot move",filename,"to",d_failed);
      writelogfile(LOG_INFO, process_title, "Moved file %s to %s",filename,d_failed);
      }
      else
      {
        unlink(filename);
        stop_if_file_exists("Cannot delete",filename,"","");
      writelogfile(LOG_INFO, process_title, "Deleted file %s",filename);
      }
      unlockfile(filename);
      // Check how often this modem failed and block it if it seems to be broken
      (*errorcounter)++;
      if (*errorcounter>=blockafter)
      {
        writelogfile0(LOG_CRIT, process_title,
                      tb_sprintf("Fatal error: sending failed %i times. Blocking %i sec.", blockafter, blocktime));
        alarm_handler0(LOG_CRIT, process_title, tb);
        statistics[device]->multiple_failed_counter++;
        statistics[device]->status='b';
        sleep(blocktime);
        *errorcounter=0;
      }
    }
    
    // Sending was successful
    else
    {
      // Move file into sent queue or delete
      if (d_sent[0])
      {
        movefilewithdestlock(filename,d_sent);
        stop_if_file_exists("Cannot move",filename,"to",d_sent);
      writelogfile(LOG_INFO, process_title, "Moved file %s to %s",filename,d_sent);
      }
      else
      {
        unlink(filename);
        stop_if_file_exists("Cannot delete",filename,"","");
      writelogfile(LOG_INFO, process_title, "Deleted file %s",filename);
      }
      unlockfile(filename);
    }
    
#ifdef DEBUGMSG
  printf("quick=%i errorcounter=%i\n",*quick,*errorcounter);
  printf("returns %i\n",success);
#endif
    if (pdu_store)
    {
      free(pdu_store);
      pdu_store = NULL;
    }
    return success;
  }
}


/* =======================================================================
   Device-Spooler (one for each modem)
   ======================================================================= */


void devicespooler(int device)
{
  int workless;
  int quick=0;
  int errorcounter;
  int modem;
  int i;
  char *p = "";  

  i = LOG_INFO;
  if (devices[device].outgoing && !devices[device].incoming)
    p = " Will only send messages.";
  else if (!devices[device].outgoing && devices[device].incoming)
    p = " Will only receive messages.";
  else if (!devices[device].outgoing && !devices[device].incoming)
  {
    p = " Nothing to do: sending and receiving are both disabled!";
    i = LOG_CRIT;
  }
  writelogfile(i, process_title, "Modem handler %i has started.%s", device, p);

  errorcounter=0;  
  
  // Open serial port or return if not successful
#ifdef DEBUGMSG
  printf("!! Opening serial port %s\n",devices[device].device);
#endif 
  modem=openmodem(devices[device].device,devices[device].name);
  if (modem==-1)
    return;  
#ifdef DEBUGMSG
  printf("!! Setting modem parameters\n");
#endif   
  put_command_timeouts = 0;
  setmodemparams(modem, devices[device].name,devices[device].rtscts, devices[device].baudrate);

  if (devices[device].sending_disabled == 1 && devices[device].modem_disabled == 0)
  {
    printf("%s: Modem handler %i is in testing mode, SENDING IS DISABLED\n", process_title, device);
    writelogfile(LOG_CRIT, process_title, "Modem handler %i is in testing mode, SENDING IS DISABLED", device);
  }

  if (devices[device].modem_disabled == 1)
  {
    printf("%s: Modem handler %i is in testing mode, MODEM IS DISABLED\n", process_title, device);
    writelogfile(LOG_CRIT, process_title, "Modem handler %i is in testing mode, MODEM IS DISABLED", device);

    devices[device].sending_disabled = 1;
  }

#ifdef DEBUGMSG
  printf("!! Entering endless send/receive loop\n");
#endif    
  while (terminate==0) /* endless loop */
  {
    workless=1;

    // Send SM
    if (devices[device].outgoing)
      while (send1sms(device, modem, &quick, &errorcounter)>0)
      {
        workless=0;
        if (devices[device].incoming==2) // repeat only if receiving has low priority
          break;
        if (terminate==1)
          return;
      }

    if (terminate==1)
      return;

    // Receive SM
    if (devices[device].incoming)
      if (receivesms(device, modem, &quick,0)>0) 
        workless=0;

    if (workless==1) // wait a little bit if there was no SM to send or receive to save CPU usage
    {
      // Disable quick mode if modem was workless
      quick=0;
      statistics[device]->status='i';
      for (i=0; i<delaytime; i++)
      {
        if (terminate==1)
          return;
        sleep(1);
      }
    }
  }
}

/* =======================================================================
   Termination handler
   ======================================================================= */

// Stores termination request when termination signal has been received 
   
void soft_termination_handler (int signum)
{
  if (process_id==-1)
  {
    signal(SIGTERM,SIG_IGN);
    signal(SIGINT,SIG_IGN);
    signal(SIGHUP,SIG_IGN);
    signal(SIGUSR1,SIG_IGN);
    writelogfile(LOG_CRIT, process_title, "Smsd main program received termination signal.");
    if (signum==SIGINT)
      printf("Received SIGINT, smsd will terminate now.\n");
    sendsignal2devices(SIGTERM);
    if (*run_info)
    {
      printf("%s: Currently running: %s. Will wait until it is completed.\n", process_title, run_info);
      writelogfile(LOG_CRIT, process_title, "Currently running: %s. Will wait until it is completed.", run_info);
    }
  }
  else if (process_id>=0)
  {
    signal(SIGTERM,SIG_IGN);
    signal(SIGINT,SIG_IGN);
    signal(SIGHUP,SIG_IGN);
    signal(SIGUSR1,SIG_IGN);
    // process_id has always the same value like device when it is larger than -1
    writelogfile(LOG_CRIT, process_title,
                 "Modem handler %i has received termination signal, will terminate after current task has been finished.",process_id);
    if (*run_info)
    {
      printf("%s: Currently running: %s. Will wait until it is completed.\n", process_title, run_info);
      writelogfile(LOG_CRIT, process_title, "Currently running: %s. Will wait until it is completed.", run_info);
    }
  }
  terminate=1;
}

void abnormal_termination(int all)
{
  if (process_id == -1)
  {
    if (all)
      sendsignal2devices(SIGTERM);
    remove_pid(pidfile);
    if (*infofile)
      unlink(infofile);
    writelogfile(LOG_CRIT, process_title, "Smsd main program terminated abnormally.");
    exit(EXIT_FAILURE);
  }
  else if (process_id >= 0)
  {
    if (all)
      kill((int)getppid(), SIGTERM);

    writelogfile(LOG_CRIT, process_title, "Modem handler %i terminated abnormally.",process_id);
    exit(EXIT_FAILURE);
  }
}

void signal_handler(int signum)
{
  signal(SIGCONT,SIG_IGN);
  //writelogfile(LOG_CRIT, process_title, "signal_handler(%i).", signum);
  if (signum == SIGCONT)
  {
    if (process_id == -1)
    {
      writelogfile(LOG_CRIT, process_title, "Smsd main program received SIGCONT, will continue %s.",
                   (workless_delay)? "immediately" : "without delays");
      sendsignal2devices(SIGCONT);
      break_workless_delay = 1;
    }
    else if (process_id >= 0)
    {
      writelogfile(LOG_INFO, process_title, "Modem handler %i received SIGCONT, will continue %s.",
                   process_id, (workless_delay)? "immediately" : "without delays");
      break_workless_delay = 1;
    }
  }
  signal(SIGCONT,signal_handler);
}

/* =======================================================================
   Main
   ======================================================================= */

int main(int argc,char** argv)
{
  int i;
  struct passwd *pwd;
  struct group *grp;
  int result = 1;
  pid_t pid;

  terminate = 0;
  strcpy(process_title, "smsd");
  signal(SIGTERM,soft_termination_handler);
  signal(SIGINT,soft_termination_handler);
  signal(SIGHUP,soft_termination_handler);
  signal(SIGUSR1,soft_termination_handler);
  signal(SIGUSR2,SIG_IGN);
// Not yet:  signal(SIGCONT,signal_handler);
  // TODO: Some more signals should be ignored or handled too? 
  *run_info = 0;
  pdu_store = NULL;
  getfile_err_store = NULL;
  for (i = 0; i < DEVICES; i++)
    device_pids[i] = 0;
  parsearguments(argc,argv);
  initcfg();
  if (!readcfg())
    exit(EXIT_FAILURE);

  // Command line overrides smsd.conf settings:
  if (*arg_infofile)
    strcpy(infofile, arg_infofile);
  if (*arg_pidfile)
    strcpy(pidfile, arg_pidfile);
  if (*arg_logfile)
    strcpy(logfile, arg_logfile);
  if (*arg_username)
    strcpy(username, arg_username);
  if (*arg_groupname)
    strcpy(groupname, arg_groupname);

  if (getuid() == 0)
  {
    if (*username && strcmp(username, "root") != 0)
    {
      if (*groupname)
      {
        if ((grp = getgrnam(groupname)) == NULL)
        {
          fprintf(stderr, "Group %s not found.\n", groupname);
          result = 0;
        }
        else if (setgid(grp->gr_gid) != 0)
        {
          fprintf(stderr, "Error setting the group id %i (%s).\n", (int)grp->gr_gid, groupname);
          result = 0;
        }
      }
      if (result != 0)
      {
        if ((pwd = getpwnam(username)) == NULL)
        {
          fprintf(stderr, "User %s not found.\n", username);
          result = 0;
        }
        else
        {
          if (!*groupname)
          {
            if (setgid(pwd->pw_gid) != 0)
            {
              fprintf(stderr, "Unable to setgid to %i. errno %i\n", (int)pwd->pw_gid,errno);
              result = 0;
            }
            else if (initgroups(pwd->pw_name, pwd->pw_gid))
            {
              fprintf(stderr, "Unable to initgroups for user id %i (%s).\n", (int)pwd->pw_uid, username);
              result = 0;
            }
          }
          if (result != 0 && setuid(pwd->pw_uid) != 0)
          {
            fprintf(stderr, "Error setting the user id %i (%s).\n", (int)pwd->pw_uid, username);
            result = 0;
          }
        }
      }
    }
  }

  if (result == 0)
  {
    if (startup_err_str)
      free(startup_err_str);
    exit(EXIT_FAILURE);
  }

  logfilehandle=openlogfile(logfile,"smsd",LOG_DAEMON,loglevel);  
  writelogfile(LOG_CRIT, process_title, "Smsd v%s started.",smsd_version);  

  pwd = getpwuid(getuid());
  grp = getgrgid(getgid());
  if (pwd && grp)
    writelogfile(LOG_CRIT, process_title, "Running as %s:%s.", pwd->pw_name, grp->gr_name);

  if (strstr(smsd_version, "beta") != NULL)
  {
    writelogfile(LOG_CRIT, process_title, "# You are running a beta version of SMS Server Tools 3.");
    writelogfile(LOG_CRIT, process_title, "# All feedback is valuable.");
    writelogfile(LOG_CRIT, process_title, "# Please provide you feedback on support website. Thank you.");
  }

  if (startup_check() > 0)
  {
    writelogfile(LOG_CRIT, process_title, "Smsd main program terminated.");
    exit(EXIT_FAILURE);
  }

  initstats();
  loadstats();

#ifdef TERMINAL
  terminal = 1;
#endif
#ifdef DEBUGMSG
  terminal = 1;
#endif
  if (strcmp(logfile, "1") == 0 || strcmp(logfile, "2") == 0)
    terminal = 1;
  if (printstatus)
    terminal = 1;
  if (terminal)
    writelogfile(LOG_CRIT, process_title, "Running in terminal mode.");
  else
  {
    i = fork();
    if (i < 0)
    {
      writelogfile(LOG_CRIT, process_title, "Smsd main program terminated because of the fork() failure.");
      exit(EXIT_FAILURE);
    }

    if (i > 0)
      exit(EXIT_SUCCESS);

    i = setsid();
    if (i < 0)
    {
      writelogfile(LOG_CRIT, process_title, "Smsd main program terminated because of the setsid() failure.");
      exit(EXIT_FAILURE);
    }
  }

  if (write_pid(pidfile) == 0)
  {
    fprintf(stderr, "Smsd main program terminated because the pid file %s cannot be written.\n", pidfile);
    writelogfile(LOG_CRIT, process_title, "Smsd main program terminated because the pid file %s cannot be written.", pidfile);
    exit(EXIT_FAILURE);
  }

  if (!terminal)
  {
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    i = open("/dev/null", O_RDWR);
    dup(i);
    dup(i);
  }

  // Start sub-processes for each modem
  for (i=0; i<DEVICES; i++)
  {
    if (devices[i].name[0])
    {
      pid = fork();
      if (pid > 0)
        device_pids[i] = pid;
      if (pid == 0)
      {
        strcpy(process_title, devices[i].name);
        process_id = i;
        devicespooler(process_id);
        statistics[i]->status='b';
        writelogfile(LOG_CRIT, process_title, "Modem handler %i terminated.",i);
        exit(127);
      }
    }
  } 
  // Start main program
  process_id =- 1;
  mainspooler();
  writelogfile(LOG_CRIT, process_title, "Smsd main program is awaiting the termination of all modem handlers.");
  waitpid(0,0,0);
  savestats();
#ifndef NOSTATS
    MM_destroy();
#endif
  remove_pid(pidfile);
  if (*infofile)
    unlink(infofile);
  writelogfile(LOG_CRIT, process_title, "Smsd main program terminated.");
  return 0;
}

Generated by  Doxygen 1.6.0   Back to index