Skip to content
Snippets Groups Projects
smsq.c 24.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- An open source telephony toolkit.
     *
     * Copyright (C) 2004 - 2005
     *
     * SMS queuing application for use with asterisk app_sms
     * by Adrian Kennard
     *
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    
    #include "asterisk.h"
    
    
    #include <popt.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <dirent.h>
    #include <ctype.h>
    #include <time.h>
    
    #ifdef SOLARIS
    #define     POPT_ARGFLAG_SHOW_DEFAULT 0x00800000
    #endif
    
    #if !defined(POPT_ARGFLAG_SHOW_DEFAULT)
    #define     POPT_ARGFLAG_SHOW_DEFAULT 0x00800000
    #endif
    
    /*!
     * \brief reads next USC character from null terminated UTF-8 string and advanced pointer
     * for non valid UTF-8 sequences.
     * \return character as is Does \b NOT advance pointer for null termination 
    */
    
    static int utf8decode (unsigned char **pp)
    {
       unsigned char *p = *pp;
       if (!*p)
          return 0;                 /* null termination of string */
       (*pp)++;
       if (*p < 0xC0)
          return *p;                /* ascii or continuation character */
       if (*p < 0xE0)
       {
          if (*p < 0xC2 || (p[1] & 0xC0) != 0x80)
             return *p;             /* not valid UTF-8 */
          (*pp)++;
          return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
       }
       if (*p < 0xF0)
       {
          if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80)
             return *p;             /* not valid UTF-8 */
          (*pp) += 2;
          return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
       }
       if (*p < 0xF8)
       {
          if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80)
             return *p;             /* not valid UTF-8 */
          (*pp) += 3;
          return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
       }
       if (*p < 0xFC)
       {
          if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
              || (p[4] & 0xC0) != 0x80)
             return *p;             /* not valid UTF-8 */
          (*pp) += 4;
          return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
       }
       if (*p < 0xFE)
       {
          if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
              || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80)
             return *p;             /* not valid UTF-8 */
          (*pp) += 5;
          return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) +
             (p[5] & 0x3F);
       }
       return *p;                   /* not sensible */
    }
    
    
    /*! 
     * \brief check for any queued messages in specific queue (queue="" means any queue)
     * \param dir,queue,subaddress,channel,callerid,wait,delay,retries,concurrent
     * \retval 0 if nothing queued
     * \retval 1 if queued and outgoing set up OK
     * \retval 2 of outgoing exists 
    */
    
    static char txqcheck (char *dir, char *queue, char subaddress, char *channel, char *callerid, int wait, int delay, int retries, int concurrent)
    {
       char ogname[100],
         temp[100],
         dirname[100],
        *p=NULL;
       FILE *f;
       DIR *d;
    
       int ql = strlen (queue), qfl = ql;
    
    Mark Spencer's avatar
    Mark Spencer committed
       snprintf (dirname, sizeof(dirname), "sms/%s", dir);
    
       d = opendir (dirname);
       if (!d)
          return 0;
       while ((fn = readdir (d))
              && !(*fn->d_name != '.'
                   && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.'))));
       if (!fn)
       {
          closedir (d);
          return 0;
       }
       if (!ql)
    
       {                            /* not searching any specific queue, so use whatr we found as the queue */
    
       }
       p = strchr (queue, '-');
       if (p && p < queue + ql)
       {
          ql = p - queue;
          subaddress = p[1];
       }
    
       snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
    
       f = fopen (temp, "w");
       if (!f)
       {
          perror (temp);
          closedir (d);
          return 0;
       }
       fprintf (f, "Channel: ");
       if (!channel)
          fprintf (f, "Local/%.*s\n", ql, queue);
       else
       {
          p = strchr (channel, '/');
          if (!p)
             p = channel;
          p = strchr (p, 'X');
          if (p)
    
             fprintf (f, "%.*s%c%s\n", (int)(p - channel), channel, subaddress, p + 1);
    
          else
             fprintf (f, "%s\n", channel);
       }
       fprintf (f, "Callerid: SMS <");
       if (!callerid)
          fprintf (f, "%.*s", ql, queue);
       else
       {
          p = strchr (callerid, 'X');
          if (p)
    
             fprintf (f, "%.*s%c%s", (int)(p - callerid), callerid, subaddress, p + 1);
    
          else
             fprintf (f, "%s", callerid);
       }
       fprintf (f, ">\n");
       fprintf (f, "Application: SMS\n");
    
       fprintf (f, "Data: %.*s", qfl, queue);
    
       if (dir[1] == 't')
          fprintf (f, "|s");
       fprintf (f, "\nMaxRetries: %d\n", retries);
       fprintf (f, "RetryTime: %d\n", delay);
       fprintf (f, "WaitTime: %d\n", wait);
       fclose (f);
       closedir (d);
       {
          int try = 0;
          while (try < concurrent)
          {
             try++;
    
    Mark Spencer's avatar
    Mark Spencer committed
             snprintf(ogname, sizeof(ogname), "outgoing/smsq.%s.%s.%d", dir, queue, try);
    
             if (!link (temp, ogname))
    
             {                      /* queued OK */
    
       /* failed to create call queue */
    
    /*! 
     * \brief Process received queue entries
     * Run through a process, setting environment variables
    */
    
    static void rxqcheck (char *dir, char *queue, char *process)
    {
    
       char *p;
    
       void *pp = &p;
    
       char dirname[100],
         temp[100];
       DIR *d;
       int ql = strlen (queue);
       struct dirent *fn;
    
       snprintf(temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
    
    Mark Spencer's avatar
    Mark Spencer committed
       snprintf(dirname, sizeof(dirname), "sms/%s", dir);
    
       d = opendir (dirname);
       if (!d)
          return;
       while ((fn = readdir (d)))
          if ((*fn->d_name != '.'
               && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.'))))
          {                         /* process file */
    
    Mark Spencer's avatar
    Mark Spencer committed
             char filename[1010];
    
             char line[1000];
             unsigned short ud[160];
             unsigned char udl = 0;
             FILE *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
             snprintf (filename, sizeof(filename), "sms/%s/%s", dir, fn->d_name);
    
             if (rename (filename, temp))
    
                continue;           /* cannot access file */
    
             f = fopen (temp, "r");
             unlink (temp);
             if (!f)
             {
                perror (temp);
                continue;
             }
             unsetenv ("oa");
             unsetenv ("da");
             unsetenv ("scts");
             unsetenv ("pid");
             unsetenv ("dcs");
             unsetenv ("mr");
             unsetenv ("srr");
             unsetenv ("rp");
             unsetenv ("vp");
             unsetenv ("udh");
             unsetenv ("ud");
             unsetenv ("ude");
             unsetenv ("ud8");
             unsetenv ("ud16");
             unsetenv ("morx");
             unsetenv ("motx");
             unsetenv ("queue");
             if (*queue)
                setenv ("queue", queue, 1);
             setenv (dir, "", 1);
             while (fgets (line, sizeof (line), f))
             {
                for (p = line; *p && *p != '\n' && *p != '\r'; p++);
                *p = 0;             /* strip eoln */
                p = line;
                if (!*p || *p == ';')
                   continue;        /* blank line or comment, ignore */
                while (isalnum (*p))
                {
                   *p = tolower (*p);
                   p++;
                }
                while (isspace (*p))
                   *p++ = 0;
                if (*p == '=')
    
                   *p++ = 0;
                   if (!strcmp (line, "oa") || !strcmp (line, "da") || !strcmp (line, "scts") || !strcmp (line, "pid")
                       || !strcmp (line, "dcs") || !strcmp (line, "mr") || !strcmp (line, "vp"))
                      setenv (line, p, 1);
                   else if ((!strcmp (line, "srr") || !strcmp (line, "rp")) && atoi (p))
                      setenv (line, "", 1);
                   else if (!strcmp (line, "ud"))
    
                   {                /* read the user data as UTF-8 */
    
                      while ((v = utf8decode (pp)) && udl < 160)
    
                         if (v && v <= 0xFFFF)
                            ud[udl++] = v;
                   }
                } else if (*p == '#')
                {
                   *p++ = 0;
                   if (*p == '#')
    
                      p++;
                      if (!strcmp (line, "udh"))
                         setenv (line, p, 1);
                      else if (!strcmp (line, "ud"))
    
                      {             /* read user data UCS-2 */
    
                         udl = 0;
                         while (*p && udl < 160)
                         {
                            if (isxdigit (*p) && isxdigit (p[1]) && isxdigit (p[2]) && isxdigit (p[3]))
                            {
                               ud[udl++] =
                                  (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 12) +
                                  (((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
                                  (((isalpha (p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha (p[3]) ? 9 : 0) + (p[3] & 0xF));
                               p += 4;
                            } else
                               break;
                         }
                      }
                   } else
    
                      if (!strcmp (line, "ud"))
    
                      {             /* read user data UCS-1 */
    
                         udl = 0;
                         while (*p && udl < 160)
                         {
                            if (isxdigit (*p) && isxdigit (p[1]))
                            {
                               ud[udl++] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF));
                               p += 2;
                            } else
                               break;
                         }
                      }
                   }
                }
             }
             fclose (f);
    
             /* set up user data variables */
    
                char tmp[481];
                int n, x;
                for (n = 0, x = 0; x < udl; x++)
    
                   unsigned short v = ud[x];
    
                         tmp[n++] = v;
    
                         tmp[n++] = (0xC0 + (v >> 6));
                         tmp[n++] = (0x80 + (v & 0x3F));
    
                         tmp[n++] = (0xE0 + (v >> 12));
                         tmp[n++] = (0x80 + ((v >> 6) & 0x3F));
                         tmp[n++] = (0x80 + (v & 0x3F));
    
                tmp[n] = 0;
                setenv ("ud", tmp, 1);
                for (n = 0, x = 0; x < udl; x++)
    
                   unsigned short v = ud[x];
    
                   if (v < ' ' || v == '\\')
                   {
    
                      tmp[n++] = '\\';
    
                         tmp[n++] = '\\';
    
                         tmp[n++] = 'n';
    
                         tmp[n++] = 'r';
    
                         tmp[n++] = 't';
    
                         tmp[n++] = 'f';
    
                         tmp[n++] = '0' + (v >> 6);
                         tmp[n++] = '0' + ((v >> 3) & 7);
                         tmp[n++] = '0' + (v & 7);
    
                      tmp[n++] = v;
    
                      tmp[n++] = (0xC0 + (v >> 6));
                      tmp[n++] = (0x80 + (v & 0x3F));
    
                      tmp[n++] = (0xE0 + (v >> 12));
                      tmp[n++] = (0x80 + ((v >> 6) & 0x3F));
                      tmp[n++] = (0x80 + (v & 0x3F));
    
                tmp[n] = 0;
                setenv ("ude", tmp, 1);
                for (x = 0; x < udl && ud[x] < 0x100; x++);
                if (x == udl)
    
                   for (n = 0, x = 0; x < udl; x++)
    
                      sprintf (tmp + n, "%02X", ud[x]);
    
                   setenv ("ud8", tmp, 1);
    
                for (n = 0, x = 0; x < udl; x++)
    
                   sprintf (tmp + n, "%04X", ud[x]);
    
                setenv ("ud16", tmp, 1);
    
             /* run the command */
    
             if (system (process) == -1) {
                fprintf(stderr, "Failed to fork process '%s'\n", process);
             }
    
    /* Main app */
    
    int
    main (int argc, const char *argv[])
    {
       char c;
       int mt = 0,
          mo = 0,
          tx = 0,
          rx = 0,
          nodial = 0,
          nowait = 0,
          concurrent = 1,
          motxwait = 10,
          motxdelay = 1,
          motxretries = 10,
          mttxwait = 10,
          mttxdelay = 30,
          mttxretries = 100,
          mr = -1,
          pid = -1,
          dcs = -1,
          srr = 0,
          rp = 0,
          vp = 0,
          udl = 0,
          utf8 = 0,
          ucs1 = 0,
          ucs2 = 0;
       unsigned short ud[160];
       unsigned char *uds = 0,
          *udh = 0;
       char *da = 0,
          *oa = 0,
          *queue = "",
          *udfile = 0,
          *process = 0,
          *spooldir = "/var/spool/asterisk",
          *motxchannel = "Local/1709400X",
          *motxcallerid = 0,
          *mttxchannel = 0,
          *mttxcallerid = "080058752X0",
          *defaultsubaddress = "9",
          subaddress = 0,
          *scts = 0;
    
       poptContext optCon;          /* context for parsing command-line options */
    
       const struct poptOption optionsTable[] = {
          {"queue", 'q', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &queue, 0, "Queue [inc sub address]", "number[-X]"},
          {"da", 'd', POPT_ARG_STRING, &da, 0, "Destination address", "number"},
          {"oa", 'o', POPT_ARG_STRING, &oa, 0, "Origination address", "number"},
          {"ud", 'm', POPT_ARG_STRING, &uds, 0, "Message", "text"},
          {"ud-file", 'f', POPT_ARG_STRING, &udfile, 0, "Message file", "filename"},
          {"UTF-8", 0, POPT_ARG_NONE, &utf8, 0, "File treated as null terminated UTF-8 (default)", 0},
          {"UCS-1", 0, POPT_ARG_NONE, &ucs1, 0, "File treated as UCS-1", 0},
          {"UCS-2", 0, POPT_ARG_NONE, &ucs2, 0, "File treated as UCS-2", 0},
          {"mt", 't', POPT_ARG_NONE, &mt, 0, "Mobile Terminated", 0},
          {"mo", 0, POPT_ARG_NONE, &mo, 0, "Mobile Originated", 0},
          {"tx", 0, POPT_ARG_NONE, &tx, 0, "Send message", 0},
          {"rx", 'r', POPT_ARG_NONE, &rx, 0, "Queue for receipt", 0},
          {"process", 'e', POPT_ARG_STRING, &process, 0, "Rx queue process command", "command"},
          {"no-dial", 'x', POPT_ARG_NONE, &nodial, 0, "Do not dial", 0},
          {"no-wait", 0, POPT_ARG_NONE, &nowait, 0, "Do not wait if already calling", 0},
          {"concurrent", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &concurrent, 0, "Number of concurrent calls to allow", "n"},
          {"motx-channel", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &motxchannel, 0, "Channel for motx calls", "channel"},
          {"motx-callerid", 0, POPT_ARG_STRING, &motxcallerid, 0,
           "Caller ID for motx calls (default is queue name without sub address)", "number"},
          {"motx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxwait, 0, "Time to wait for motx call to answer",
           "seconds"},
          {"motx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxdelay, 0, "Time between motx call retries", "seconds"},
          {"motx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxretries, 0, "Number of retries for motx call", "n"},
          {"mttx-channel", 0, POPT_ARG_STRING, &mttxchannel, 0,
           "Channel for mttx calls (default is Local/ and queue name without sub address)", "channel"},
          {"mttx-callerid", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &mttxcallerid, 0,
           "Caller ID for mttx calls (default is queue name without sub address)", "number"},
          {"mttx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxwait, 0, "Time to wait for mttx call to answer",
           "seconds"},
          {"mttx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxdelay, 0, "Time between mttx call retries", "seconds"},
          {"mttx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxretries, 0, "Number of retries for mttx call", "n"},
          {"mr", 'n', POPT_ARG_INT, &mr, 0, "Message reference", "n"},
          {"pid", 'p', POPT_ARG_INT, &pid, 0, "Protocol ID", "n"},
          {"dcs", 'c', POPT_ARG_INT, &dcs, 0, "Data Coding Scheme", "n"},
          {"udh", 0, POPT_ARG_STRING, &udh, 0, "User data header", "hex"},
          {"srr", 0, POPT_ARG_NONE, &srr, 0, "Status Report Request", 0},
          {"rp", 0, POPT_ARG_NONE, &rp, 0, "Return Path request", 0},
          {"v", 0, POPT_ARG_INT, &vp, 0, "Validity Period", "seconds"},
          {"scts", 0, POPT_ARG_STRING, &scts, 0, "Timestamp", "YYYY-MM-SSTHH:MM:SS"},
          {"default-sub-address", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &defaultsubaddress, 0, "Default sub address", "X"},
          {"spool-dir", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &spooldir, 0, "Asterisk spool dir", "dirname"},
          POPT_AUTOHELP {NULL, 0, 0, NULL, 0}
       };
    
       optCon = poptGetContext (NULL, argc, argv, optionsTable, 0);
       poptSetOtherOptionHelp (optCon, "<oa/da> <message>");
    
       /* Now do options processing, get portname */
       if ((c = poptGetNextOpt (optCon)) < -1)
       {
          /* an error occurred during option processing */
          fprintf (stderr, "%s: %s\n", poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (c));
          return 1;
       }
       if (!ucs1 && !ucs2)
          utf8 = 1;
       if (utf8 + ucs1 + ucs2 > 1)
       {
          fprintf (stderr, "Pick one of UTF-8, UCS-1 or UCS-2 only\n");
          return 1;
       }
       if (!udfile && (ucs1 || ucs2))
       {
          fprintf (stderr, "Command line arguments always treated as UTF-8\n");
          return 1;
       }
    
       /*  if (!where && poptPeekArg (optCon)) where = (char *) poptGetArg (optCon); */
    
       if (!mt && !mo && process)
          mt = 1;
       if (!mt && !mo && oa)
          mt = 1;
       if (!mt)
          mo = 1;
       if (mt && mo)
       {
          fprintf (stderr, "Cannot be --mt and --mo\n");
          return 1;
       }
       if (!rx && !tx && process)
          rx = 1;
       if (!rx)
          tx = 1;
       if (tx && rx)
       {
          fprintf (stderr, "Cannot be --tx and --rx\n");
          return 1;
       }
       if (rx)
          nodial = 1;
       if (uds && udfile)
       {
          fprintf (stderr, "Cannot have --ud and --ud-file\n");
          return 1;
       }
       if (mo && !da && poptPeekArg (optCon))
          da = (char *) poptGetArg (optCon);
       if (mt && !oa && poptPeekArg (optCon))
          oa = (char *) poptGetArg (optCon);
       if (tx && oa && mo)
       {
          fprintf (stderr, "--oa makes no sense with --mo as CLI is used (i.e. queue name)\n");
          return 1;
       }
       if (tx && da && mt)
       {
          fprintf (stderr, "--da makes no sense with --mt as called number is used (i.e. queue name)\n");
          return 1;
       }
       if (da && strlen (da) > 20)
       {
          fprintf (stderr, "--da too long\n");
          return 1;
       }
       if (oa && strlen (oa) > 20)
       {
          fprintf (stderr, "--oa too long\n");
          return 1;
       }
       if (queue && strlen (queue) > 20)
       {
          fprintf (stderr, "--queue name too long\n");
          return 1;
       }
       if (mo && scts)
       {
          fprintf (stderr, "scts is set my service centre\n");
          return 1;
       }
       if (uds)
       {                            /* simple user data command line option in \UTF-8 */
          while (udl < 160 && *uds)
          {
             int v = utf8decode (&uds);
             if (v > 0xFFFF)
             {
                fprintf (stderr, "Invalid character U+%X at %d\n", v, udl);
                return 1;
             }
             ud[udl++] = v;
          }
       }
       if (!uds && !udfile && poptPeekArg (optCon))
       {                            /* multiple command line arguments in UTF-8 */
          while (poptPeekArg (optCon) && udl < 160)
          {
    
             unsigned char *a = (unsigned char *) poptGetArg (optCon);
    
             if (udl && udl < 160)
                ud[udl++] = ' ';    /* space between arguments */
             while (udl < 160 && *a)
             {
                int v = utf8decode (&a);
                if (v > 0xFFFF)
                {
                   fprintf (stderr, "Invalid character U+%X at %d\n", v, udl);
                   return 1;
                }
                ud[udl++] = v;
             }
          }
       }
       if (poptPeekArg (optCon))
       {
          fprintf (stderr, "Unknown argument %s\n", poptGetArg (optCon));
          return 1;
       }
       if (udfile)
    
       {                            /* get message from file */
    
          unsigned char dat[1204],
           *p = dat,
             *e;
          int f,
            n;
          if (*udfile)
             f = open (udfile, O_RDONLY);
          else
             f = fileno (stdin);
          if (f < 0)
          {
             perror (udfile);
             return 1;
          }
          n = read (f, dat, sizeof (dat));
          if (n < 0)
          {
             perror (udfile);
             return 1;
          }
          if (*udfile)
             close (f);
          e = dat + n;
          if (utf8)
          {                         /* UTF-8 */
             while (p < e && udl < 160 && *p)
                ud[udl++] = utf8decode (&p);
          } else if (ucs1)
          {                         /* UCS-1 */
             while (p < e && udl < 160)
                ud[udl++] = *p++;
          } else
          {                         /* UCS-2 */
             while (p + 1 < e && udl < 160)
             {
                ud[udl++] = (*p << 8) + p[1];
                p += 2;
             }
          }
       }
       if (queue)
       {
          char *d = strrchr (queue, '-');
          if (d && d[1])
             subaddress = d[1];
          else
             subaddress = *defaultsubaddress;
       }
    
       if (chdir (spooldir))
       {
          perror (spooldir);
          return 1;
       }
    
       if (oa || da)
    
       {                            /* send message */
    
          char temp[100],
            queuename[100],
           *dir = (mo ? rx ? "sms/morx" : "sms/motx" : rx ? "sms/mtrx" : "sms/mttx");
          FILE *f;
    
          snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
    
          mkdir ("sms", 0777);      /* ensure directory exists */
          mkdir (dir, 0777);        /* ensure directory exists */
    
          snprintf (queuename, sizeof(queuename), "%s/%s.%ld-%d", dir, *queue ? queue : "0", (long)time (0), (int)getpid ());
    
          f = fopen (temp, "w");
          if (!f)
          {
             perror (temp);
             return 1;
          }
          if (oa)
             fprintf (f, "oa=%s\n", oa);
          if (da)
             fprintf (f, "da=%s\n", da);
          if (scts)
             fprintf (f, "scts=%s\n", scts);
          if (pid >= 0)
             fprintf (f, "pid=%d\n", pid);
          if (dcs >= 0)
             fprintf (f, "dcs=%d\n", dcs);
          if (mr >= 0)
             fprintf (f, "mr=%d\n", mr);
          if (srr)
             fprintf (f, "srr=1\n");
          if (rp)
             fprintf (f, "rp=1\n");
          if (udh)
             fprintf (f, "udh#%s\n", udh);
          if (vp > 0)
             fprintf (f, "vp=%d\n", vp);
          if (udl)
          {
             int p;
             for (p = 0; p < udl && ud[p] < 0x100; p++);
             if (p == udl)
             {
                for (p = 0; p < udl && ud[p] < 0x80 && ud[p] >= 0x20; p++);
                if (p == udl)
                {                   /* use text */
                   fprintf (f, "ud=");
                   for (p = 0; p < udl; p++)
                      fputc (ud[p], f);
                } else
                {                   /* use one byte hex */
                   fprintf (f, "ud#");
                   for (p = 0; p < udl; p++)
                      fprintf (f, "%02X", ud[p]);
                }
             } else
             {                      /* use two byte hex */
                fprintf (f, "ud##");
                for (p = 0; p < udl; p++)
                   fprintf (f, "%04X", ud[p]);
             }
             fprintf (f, "\n");
          }
          fclose (f);
          if (rename (temp, queuename))
          {
             perror (queuename);
             unlink (temp);
             return 1;
          }
       }
    
       if (!nodial && tx && !process)
    
       {                            /* dial to send messages */
    
          char ret=0,
            try = 3;
          if (nowait)
             try = 1;
          while (try--)
          {
             if (mo)
                ret = txqcheck ("motx", queue, subaddress, motxchannel, motxcallerid, motxwait, motxdelay, motxretries, concurrent);
             else
                ret = txqcheck ("mttx", queue, subaddress, mttxchannel, mttxcallerid, mttxwait, mttxdelay, mttxretries, concurrent);
             if (ret < 2)
    
                break;              /* sent, or queued OK */
    
             if (try)
                sleep (1);
          }
          if (ret == 2 && !nowait)
             fprintf (stderr, "No call scheduled as already sending\n");
       }
       if (process)
          rxqcheck (mo ? rx ? "morx" : "motx" : rx ? "mtrx" : "mttx", queue, process);
    
       return 0;
    }