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

extras.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/stat.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/wait.h>
#include <time.h>
#include <syslog.h>
#include "extras.h"
#include "locking.h"
#include "smsd_cfg.h"
#include "logging.h"
#include "alarm.h"

char *cut_ctrl(char* message) /* removes all ctrl chars */
{
  // 3.0.9: use dynamic buffer to avoid overflow:
  //char tmp[500];
  char *tmp;
  int posdest=0;
  int possource;
  int count;

  count=strlen(message);
  if ((tmp = (char *)malloc(count +1)))
  {
    for (possource=0; possource<=count; possource++)
    {
      if ((message[possource]>=' ') || (message[possource]==0))
        tmp[posdest++]=message[possource];
    }
    strcpy(message,tmp);
    free(tmp);
  }
  return message;
}


int is_blank(char c)
{
  return (c==9) || (c==32);
}

int line_is_blank(char *line)
{
  int i = 0;

  while (line[i])
    if (strchr("\t \r\n", line[i]))
      i++;
    else
      break;

  return(line[i] == 0);
}

int movefile( char*  filename,  char*  directory)
{
  char newname[PATH_MAX];
  char storage[1024];
  int source,dest;
  int readcount;
  char* cp;
  struct stat statbuf;

  if (stat(filename,&statbuf)<0)
    statbuf.st_mode=0640;
  statbuf.st_mode&=07777;
  cp=strrchr(filename,'/');
  if (cp)
    sprintf(newname,"%s%s",directory,cp);
  else
    sprintf(newname,"%s/%s",directory,filename);
  source=open(filename,O_RDONLY);
  if (source>=0)
  {
    dest=open(newname,O_WRONLY|O_CREAT|O_TRUNC,statbuf.st_mode);
    if (dest>=0)
    {
      while ((readcount=read(source,&storage,sizeof(storage)))>0)
        if (write(dest,&storage,readcount)<readcount)
      {
        close(dest);
        close(source);
        return 0;
      }
      close(dest);
      close(source);
      unlink(filename);
      return 1;
    }
    else
    {
      close(source);
      return 0;
    }
  }
  else
    return 0;
}

// 3.0.9: Return values:
// 0 = OK.
// 1 = lockfile cannot be created. It exists.
// 2 = file copying failed.
// 3 = lockfile removing failed.
int movefilewithdestlock( char*  filename,  char*  directory)
{
  char lockfilename[PATH_MAX];
  char* cp;

  //create lockfilename in destination
  cp=strrchr(filename,'/');
  if (cp)
    sprintf(lockfilename,"%s%s",directory,cp);
  else
    sprintf(lockfilename,"%s/%s",directory,filename);
  //create lock and move file
  if (!lockfile(lockfilename))
    return 1;
  if (!movefile(filename,directory))
  {
    unlockfile(lockfilename);
    return 2;
  }
  if (!unlockfile(lockfilename))
    return 3;
  return 0;
}

// 3.1beta7: Return values:
// 0 = OK.
// 1 = lockfile cannot be created. It exists.
// 2 = file copying failed.
// 3 = lockfile removing failed.
int movefilewithdestlock_new(char* filename, char* directory, int keep_fname, int store_original_fname, char *prefix, char *newfilename)
{
  if (newfilename)
    *newfilename = 0;

  if (keep_fname)
  {
    char lockfilename[PATH_MAX];
    char* cp;

    //create lockfilename in destination
    cp=strrchr(filename,'/');
    if (cp)
      sprintf(lockfilename,"%s%s",directory,cp);
    else
      sprintf(lockfilename,"%s/%s",directory,filename);
    //create lock and move file
    if (!lockfile(lockfilename))
      return 1;
    if (!movefile(filename,directory))
    {
      unlockfile(lockfilename);
      return 2;
    }
    if (!unlockfile(lockfilename))
      return 3;

    if (newfilename)
      strcpy(newfilename, lockfilename);

    return 0;
  }
  else
  {
    // A new unique name is created to the destination directory.
    char newname[PATH_MAX];
    int result = 0;
    char line[1024];
    int in_headers = 1;
    FILE *fp;
    FILE *fpnew;
    size_t n;
    char *p;

    strcpy(line, prefix);
    if (*line)
      strcat(line, ".");
    sprintf(newname,"%s/%sXXXXXX", directory, line);
    close(mkstemp(newname));
    if (!lockfile(newname))
      result = 1;

    unlink(newname);
    if (!result)
    {
      if ((fpnew = fopen(newname, "w")) == NULL)
        result = 2;
      else
      {
        if ((fp = fopen(filename, "r")) == NULL)
        {
          fclose(fpnew);
          unlink(newname);
          result = 2;
        }
        else
        {
          while (in_headers && fgets(line, sizeof(line), fp))
          {
            if (line_is_blank(line))
            {
              if (store_original_fname)
              {
                p = strrchr(filename, '/');
                fprintf(fpnew, "Original_filename: %s\n", (p)? p +1 : filename);
              }
              in_headers = 0;
            }
            fwrite(line, 1, strlen(line), fpnew);
          }

          while ((n = fread(line, 1, sizeof(line), fp)) > 0)
            fwrite(line, 1, n, fpnew);

          fclose(fpnew);
          fclose(fp);
        }
      }
    }

    if (!unlockfile(newname))
    {
      unlink(newname);
      if (!result)
        result = 3;
    }
    else
    {
      unlink(filename);
      if (newfilename)
        strcpy(newfilename, newname);
    }

    return result;
  }
}

void cutspaces(char*  text)
{
  int count;
  int Length;
  int i;
  int omitted;

  /* count ctrl chars and spaces at the beginning */
  count=0;
  while ((text[count]!=0) && ((is_blank(text[count])) || (iscntrl(text[count]))) )
    count++;
  /* remove ctrl chars at the beginning and \r within the text */
  omitted=0;
  Length=strlen(text);
  for (i=0; i<=(Length-count); i++)
    if (text[i+count]=='\r')
      omitted++;
    else
      text[i-omitted]=text[i+count];
  Length=strlen(text);
  while ((Length>0) && ((is_blank(text[Length-1])) || (iscntrl(text[Length-1]))))
  {
    text[Length-1]=0;
    Length--;
  }
}

void cut_emptylines(char*  text)
{
  char* posi;
  char* found;

  posi=text;
  while (posi[0] && (found=strchr(posi,'\n')))
  {
    if ((found[1]=='\n') || (found==text))
      memmove(found,found+1,strlen(found));
    else
      posi++;
  }
}

int is_number( char*  text)
{
  int i;
  int Length;

  Length=strlen(text);
  for (i=0; i<Length; i++)
    if (((text[i]>'9') || (text[i]<'0')) && (text[i]!='-'))
      return 0;
  return 1;
}

int is_highpriority(char *filename)
{
  FILE *fp;
  char line[256];
  int result = 0;

  if ((fp = fopen(filename, "r")) != NULL)
  {
    while (fgets(line, sizeof(line), fp) != NULL)
    {
      if (line_is_blank(line))
        break;

      if (strstr(line,"Priority:")==line)
      {
        memmove(line,line+9,strlen(line)-8);
        cutspaces(line);
        if (strcasecmp(line,"HIGH")==0)
        {
          result = 1;
          break;
        }
      }
    }
    fclose(fp);
  }
  return result;
}

int file_is_writable(char *filename)
{
  int result = 0;
  FILE *fp;

  if ((fp = fopen(filename, "a")) != NULL)
  {
    result = 1;
    fclose(fp);
  }

  return result;
}

int getpdufile(char *filename)
{
  int result = 0;
  struct stat statbuf;
  DIR* dirdata;
  struct dirent* ent;
  char tmpname[PATH_MAX];

  if (*filename)
  {
    if (filename[strlen(filename) -1] != '/')
    {
      if (stat(filename, &statbuf) == 0)
        if (S_ISDIR(statbuf.st_mode) == 0)
          if (file_is_writable(filename))
            result = 1;
    }
    else if (strchr(filename, '.') == NULL)
    {
      if (stat(filename, &statbuf) == 0)
      {
        if (S_ISDIR(statbuf.st_mode))
        {
          if ((dirdata = opendir(filename)))
          {
            while ((ent = readdir(dirdata)))
            {
              if (ent->d_name[0] != '.')
              {
                sprintf(tmpname, "%s%s", filename, ent->d_name);
                stat(tmpname, &statbuf);
                if (S_ISDIR(statbuf.st_mode) == 0)
                {
                  if (file_is_writable(tmpname))
                  {
                    strcpy(filename, tmpname);
                    result = 1;
                    break;
                  }
                }
              }
            }
          }
        }
      }
    }
  }

  return result;
}

int getfile(char *dir, char *filename)
{
  DIR* dirdata;
  struct dirent* ent;
  struct stat statbuf;
  int found=0;
  time_t mtime = 0;
  char fname[PATH_MAX];
  char tmpname[PATH_MAX];
  int found_highpriority = 0;
  int i;

#ifdef DEBUGMSG
  printf("!! getfile(dir=%s, ...)\n", dir);
#endif

  // Oldest file is searched. With heavy traffic the first file found is not necesssary the oldest one.

  if (!(dirdata = opendir(dir)))
  {
    // Something has happened to dir after startup check was done successfully.
    writelogfile0(LOG_CRIT, process_title, tb_sprintf("Stopping. Cannot open dir %s %s", dir, strerror(errno)));
    alarm_handler0(LOG_CRIT, process_title, tb);
    abnormal_termination(1);
  }
  else
  {
    while ((ent = readdir(dirdata)))
    {
#ifdef DEBUGMSG
  printf("**readdir(): %s\n", ent->d_name);
#endif
      sprintf(tmpname, "%s/%s", dir, ent->d_name);
      stat(tmpname, &statbuf);
      if (S_ISDIR(statbuf.st_mode) == 0) /* Is this a directory? */
      {
        if (strcmp(tmpname +strlen(tmpname) -5, ".LOCK") != 0)
        {
          if (!islocked(tmpname))
          {
            char storage_key[PATH_MAX +3];

            sprintf(storage_key, "*%s*\n", tmpname);

            // 3.1beta7, 3.0.10:
            if (os_cygwin)
              if (!check_access(tmpname))
                chmod(tmpname, 0766);

            if (!file_is_writable(tmpname))
            {
              int report = 1;
              char reason[100];

              //if (access(tmpname, R_OK | W_OK) != 0)
              if (!check_access(tmpname))
              {
                //snprintf(reason, sizeof(reason), "%i %s", errno, strerror(errno));
                snprintf(reason, sizeof(reason), "%s", "Access denied.");
                if (getfile_err_store)
                  if (strstr(getfile_err_store, storage_key))
                    report = 0; 

                if (report)
                {
                  if (!getfile_err_store)
                  {
                    if ((getfile_err_store = (char *)malloc(strlen(storage_key) +1)))
                      getfile_err_store[0] = 0;
                  }
                  else
                    getfile_err_store = (char *)realloc((void *)getfile_err_store, strlen(getfile_err_store) +strlen(storage_key) +1);

                  if (getfile_err_store)
                    strcat(getfile_err_store, storage_key);

                  writelogfile0(LOG_ERR, process_title, tb_sprintf("Cannot handle %s: %s", tmpname, reason));
                  alarm_handler0(LOG_ERR, process_title, tb);
                }
              }
            }
            else
            {
              // Forget previous error with this file:
              if (getfile_err_store)
              {
                char *p;
                int l = strlen(storage_key);

                if ((p = strstr(getfile_err_store, storage_key)))
                  strncpy(p, p +l, strlen(p) -l +1);
                if (!(*getfile_err_store))
                {
                  free(getfile_err_store);
                  getfile_err_store = NULL;
                }
              }

              i = is_highpriority(tmpname);
              if (found_highpriority && !i)
              {
#ifdef DEBUGMSG
  printf("**%s %s not highpriority, already have one.\n", dir, ent->d_name);
#endif
                continue;
              }
              if (i && !found_highpriority)
              {
                // Forget possible previous found normal priority file:
                mtime = 0;
                found_highpriority = 1;
              }

#ifdef DEBUGMSG
  printf("**%s %s %i ", dir, ent->d_name, (int)(statbuf.st_mtime));
#endif
              if (mtime == 0 || statbuf.st_mtime < mtime)
              {
#ifdef DEBUGMSG
  printf("taken\n");
#endif
                strcpy(fname, tmpname);
                mtime = statbuf.st_mtime;
                found = 1;
              }
#ifdef DEBUGMSG
              else
                printf("leaved\n");
#endif
            }
          }
        }
      }
    }

#ifdef DEBUGMSG
  if (getfile_err_store)
    printf("!! process: %i, getfile_err_store:\n%s", process_id, getfile_err_store);
#endif

    // Each process has it's own error storage.
    // Mainspooler handles only the outgoing folder.
    // Modem processes handle all queue directories which are defined to the modem.
    // If some problematic file is deleted (outside of smsd), it's name remains in the storage.
    // To avoid missing error messages with the same filename later, storage is checked and cleaned.

    if (getfile_err_store)
    {
      char *p1;
      char *p2;
      char tmp[PATH_MAX];
      struct stat statbuf;

      p1 = getfile_err_store;
      while ((p2 = strchr(p1, '\n')))
      {
        strncpy(tmp, p1 +1, p2 -p1 -2);
        tmp[p2 -p1 -2] = 0;
        //if (access(tmp, F_OK) != 0)
        if (stat(tmp, &statbuf))
          strncpy(p1, p2 +1, strlen(p2));
        else
          p1 = p2 +1;
      }

      if (!(*getfile_err_store))
      {
        free(getfile_err_store);
        getfile_err_store = NULL;
      }
    }

#ifdef DEBUGMSG
  if (getfile_err_store)
    printf("!! process: %i, getfile_err_store:\n%s", process_id, getfile_err_store);
#endif

    if (found)
    {
      /* check if the file grows at the moment (another program writes to it) */
      int groesse1;
      int groesse2;

      stat(fname, &statbuf);
      groesse1=statbuf.st_size;
      sleep(1);
      stat(fname, &statbuf);
      groesse2=statbuf.st_size;
      if (groesse1 != groesse2)
        found = 0;
    }

    closedir(dirdata);
  }

  if (!found)
    *filename = 0;
  else
    strcpy(filename, fname);
#ifdef DEBUGMSG
  printf("## result for dir %s: %s\n\n", dir, filename);
#endif
  return found;
}

int my_system( char*  command, char *info)
{
  int pid,status;

#ifdef DEBUGMSG
  printf("!! my_system(%s, %s)\n",command, info);
#endif
  if (command==0)
    return 1;
  pid=fork();
  if (pid==-1)
    return -1;
  if (pid==0)  // only executed in the child
  {
    char* argv[4];

#ifdef DEBUGMSG
    printf("!! pid=%i, child running external command\n",pid);
#endif
    argv[0]="sh";
    argv[1]="-c";
    argv[2]=(char*) command;
    argv[3]=0;
    execv("/bin/sh",argv);  // replace child with the external command
#ifdef DEBUGMSG
    printf("!! pid=%i, execv() failed, child exits now\n",pid);
#endif
    exit(1);                // exit with error when the execv call failed
  }
  errno=0;
#ifdef DEBUGMSG
    printf("!! father waiting for child %i\n",pid);
#endif
  *run_info = 0;
  if (info)
    if (*info)
      strcpy(run_info, info);
  do
  {
    if (waitpid(pid,&status,0)==-1)
    {
      if (errno!=EINTR)
      {
        *run_info = 0;
        return -1;
      }
    }
    else
    {
      *run_info = 0;
      return WEXITSTATUS(status);
    }
  }
  while (1);    
}

int write_pid( char* filename)
{
  char pid[20];
  int pidfile;

  sprintf(pid,"%i\n", (int)getpid());
  pidfile = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0644);
  if (pidfile >= 0)
  {
    write(pidfile, pid, strlen(pid));
    close(pidfile);
    return 1;
  }
  return 0;
}

void remove_pid( char* filename)
{
  unlink(filename);
}

int parse_validity(char *value, int defaultvalue)
{
  int result = defaultvalue;
  char buffer[100];
  int i;
  char tmp[100];
  int got_numbers = 0;
  int got_letters = 0;
  int idx;
  char *p;

  if (value && *value)
  {
    // n min, hour, day, week, month, year
    // 3.0.9: if only keyword is given, insert number 1.
    // Fixed number without keyword handling.
    // Convert to lowercase so upcase is also accepted.

    *buffer = 0;
    strncpy(tmp, value, sizeof(tmp) -1);
    tmp[sizeof(tmp) -1] = 0;
    cutspaces(tmp);
    for (idx = 0; tmp[idx]; idx++)
    {
      tmp[idx] = tolower(tmp[idx]);
      if (tmp[idx] == '\t')
        tmp[idx] = ' ';
      if (isdigit(tmp[idx]))
        got_numbers = 1;
      else
        got_letters = 1;
    }

    if (got_numbers && !got_letters)
    {
      i = atoi(tmp);
      if (i >= 0 && i <= 255)
        result = i;
      return result;
    }

    if ((p = strchr(tmp, ' ')))
      *p = 0;

    if (strstr("min hour day week month year", tmp))
      sprintf(buffer, "1 %.*s", (int)sizeof(buffer) -3, tmp);
    else
      sprintf(buffer, "%.*s", (int)sizeof(buffer) -1, value);

    while ((i = atoi(buffer)) > 0)
    {
      // 0 ... 143     (value + 1) * 5 minutes (i.e. 5 minutes intervals up to 12 hours)
      if (strstr(buffer, "min"))
      {
        if (i <= 720)
        {
          result = (i < 5)? 0 : i /5 -1;
          break;
        }
        sprintf(buffer, "%i hour", i /= 60);
      }

      // 144 ... 167   12 hours + ((value - 143) * 30 minutes) (i.e. 30 min intervals up to 24 hours)
      if (strstr(buffer, "hour"))
      {
        if (i <= 12)
        {
          sprintf(buffer, "%i min", i *60);
          continue;
        }
        if (i <= 24)
        {
          result = (i -12) *2 +143;
          break;
        }
        sprintf(buffer, "%i day", i /= 24);
      }

      // 168 ... 196   (value - 166) * 1 day (i.e. 1 day intervals up to 30 days)
      if (strstr(buffer, "day"))
      {
        if (i < 2)
        {
          sprintf(buffer, "24 hour");
          continue;
        }
        if (i <= 34)
        {
          result = (i <= 30)? i +166 : 30 +166;
          break;
        }
        sprintf(buffer, "%i week", i /= 7);
      }

      // 197 ... 255   (value - 192) * 1 week (i.e. 1 week intervals up to 63 weeks)
      if (strstr(buffer, "week"))
      {
        if (i < 5)
        {
          sprintf(buffer, "%i day", i *7);
          continue;
        }
        result = (i <= 63)? i +192 : 255;
        break;
      }

      if (strstr(buffer, "month"))
      {
        sprintf(buffer, "%i day", (i == 12)? 365 : i *30);
        continue;
      }

      if (strstr(buffer, "year"))
      {
        if (i == 1)
        {
          sprintf(buffer, "52 week");
          continue;
        }
        result = 255;
      }

      break;
    }
  }

  return result;
}

// 0=invalid, 1=valid
int report_validity(char *buffer, int validity_period)
{
  int result = 0;
  int n;
  char *p;

  if (validity_period < 0 || validity_period > 255)
    sprintf(buffer, "invalid (%i)", validity_period);
  else
  {
    if (validity_period <= 143)
    {
      // 0 ... 143    (value + 1) * 5 minutes (i.e. 5 minutes intervals up to 12 hours)
      n = (validity_period +1) *5;
      p = "min";
    }
    else if (validity_period <= 167)
    {
      // 144 ... 167  12 hours + ((value - 143) * 30 minutes) (i.e. 30 min intervals up to 24 hours)
      n =  12 +(validity_period -143) /2;
      p = "hour";
    }
    else if (validity_period <= 196)
    {
      // 168 ... 196  (value - 166) * 1 day (i.e. 1 day intervals up to 30 days)
      n = validity_period -166;
      p = "day";
    }
    else
    {
      // 197 ... 255  (value - 192) * 1 week (i.e. 1 week intervals up to 63 weeks)
      n = validity_period -192;
      p = "week";
    }

    sprintf(buffer, "%i %s%s (%i)", n, p, (n > 1)? "s" : "", validity_period);
    result = 1;
  }

  return result;
}

int getrand(int toprange)
{
  srand(time(NULL));
  return (rand() % toprange) +1;
}

int is_executable(char *filename)
{
  // access() migth do this easier, but in Gygwin it returns 0 even when requested permissions are NOT granted.
  int result = 0;
  struct stat statbuf;
  mode_t mode;
  int n, i;
  gid_t *g;

  if (stat(filename, &statbuf) >= 0)
  {
    mode = statbuf.st_mode & 0755;

    if (getuid())
    {
      if (statbuf.st_uid != getuid())
      {
        if ((n = getgroups(0, NULL)) > 0)
        {
          if ((g = (gid_t *)malloc(n * sizeof(gid_t))) != NULL)
          {
            if ((n = getgroups(n, g)) > 0)
            {
              for (i = 0; (i < n) & (!result); i++)
                if (g[i] == statbuf.st_gid)
                  result = 1;
            }
            free(g);
          }
        }

        if (result)
        {
          if ((mode & 050) != 050)
            result = 0;
        }
        else if ((mode & 05) == 05)
          result = 1;
      }
      else if ((mode & 0500) == 0500)
        result = 1;
    }
    else if ((mode & 0100) || (mode & 010) || (mode & 01))
      result = 1;
  }

  return result;
}

int check_access(char *filename)
{
  // access() migth do this easier, but in Gygwin it returns 0 even when requested permissions are NOT granted.
  // TODO: should be combined with is_executable.
  int result = 0;
  struct stat statbuf;
  mode_t mode;
  int n, i;
  gid_t *g;

  if (stat(filename, &statbuf) >= 0)
  {
    mode = statbuf.st_mode; // & 0777;

    if (getuid())
    {
      if (statbuf.st_uid != getuid())
      {
        if ((n = getgroups(0, NULL)) > 0)
        {
          if ((g = (gid_t *)malloc(n * sizeof(gid_t))) != NULL)
          {
            if ((n = getgroups(n, g)) > 0)
            {
              for (i = 0; (i < n) & (!result); i++)
                if (g[i] == statbuf.st_gid)
                  result = 1;
            }
            free(g);
          }
        }

        if (result)
        {
          if ((mode & 060) != 060)
            result = 0;
        }
        else if ((mode & 06) == 06)
          result = 1;
      }
      else if ((mode & 0600) == 0600)
        result = 1;
    }
    else if ((mode & 0200) || (mode & 020) || (mode & 02))
      result = 1;
  }

  return result;
}

Generated by  Doxygen 1.6.0   Back to index