Merged revisions 77869 via svnmerge from
[asterisk/asterisk.git] / utils / smsq.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2004 - 2005
5  *
6  * SMS queuing application for use with asterisk app_sms
7  * by Adrian Kennard
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 #include <stdio.h>
21 #include <popt.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <dirent.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <time.h>
31
32 #include <asterisk/compat.h>
33 #ifdef SOLARIS
34 #define     POPT_ARGFLAG_SHOW_DEFAULT 0x00800000
35 #endif
36 #if !defined(POPT_ARGFLAG_SHOW_DEFAULT)
37 #define     POPT_ARGFLAG_SHOW_DEFAULT 0x00800000
38 #endif
39
40
41 /* reads next USC character from null terminated UTF-8 string and advanced pointer */
42 /* for non valid UTF-8 sequences, returns character as is */
43 /* Does not advance pointer for null termination */
44 static int utf8decode (unsigned char **pp)
45 {
46    unsigned char *p = *pp;
47    if (!*p)
48       return 0;                 /* null termination of string */
49    (*pp)++;
50    if (*p < 0xC0)
51       return *p;                /* ascii or continuation character */
52    if (*p < 0xE0)
53    {
54       if (*p < 0xC2 || (p[1] & 0xC0) != 0x80)
55          return *p;             /* not valid UTF-8 */
56       (*pp)++;
57       return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
58    }
59    if (*p < 0xF0)
60    {
61       if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80)
62          return *p;             /* not valid UTF-8 */
63       (*pp) += 2;
64       return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
65    }
66    if (*p < 0xF8)
67    {
68       if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80)
69          return *p;             /* not valid UTF-8 */
70       (*pp) += 3;
71       return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
72    }
73    if (*p < 0xFC)
74    {
75       if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
76           || (p[4] & 0xC0) != 0x80)
77          return *p;             /* not valid UTF-8 */
78       (*pp) += 4;
79       return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
80    }
81    if (*p < 0xFE)
82    {
83       if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
84           || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80)
85          return *p;             /* not valid UTF-8 */
86       (*pp) += 5;
87       return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) +
88          (p[5] & 0x3F);
89    }
90    return *p;                   /* not sensible */
91 }
92
93 /* check for any queued messages in specific queue (queue="" means any queue) */
94 /* returns 0 if nothing queued, 1 if queued and outgoing set up OK, 2 of outgoing exists */
95 static char txqcheck (char *dir, char *queue, char subaddress, char *channel, char *callerid, int wait, int delay, int retries, int concurrent)
96 {
97    char ogname[100],
98      temp[100],
99      dirname[100],
100     *p=NULL;
101    FILE *f;
102    DIR *d;
103    int ql = strlen (queue), qfl = ql;
104    struct dirent *fn;
105    snprintf (dirname, sizeof(dirname), "sms/%s", dir);
106    d = opendir (dirname);
107    if (!d)
108       return 0;
109    while ((fn = readdir (d))
110           && !(*fn->d_name != '.'
111                && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.'))));
112    if (!fn)
113    {
114       closedir (d);
115       return 0;
116    }
117    if (!ql)
118    {                            /* not searching any specific queue, so use whatr we found as the queue */
119       queue = fn->d_name;
120       qfl = ql = p - queue;
121    }
122    p = strchr (queue, '-');
123    if (p && p < queue + ql)
124    {
125       ql = p - queue;
126       subaddress = p[1];
127    }
128    snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
129    f = fopen (temp, "w");
130    if (!f)
131    {
132       perror (temp);
133       closedir (d);
134       return 0;
135    }
136    fprintf (f, "Channel: ");
137    if (!channel)
138       fprintf (f, "Local/%.*s\n", ql, queue);
139    else
140    {
141       p = strchr (channel, '/');
142       if (!p)
143          p = channel;
144       p = strchr (p, 'X');
145       if (p)
146          fprintf (f, "%.*s%c%s\n", (int)(p - channel), channel, subaddress, p + 1);
147       else
148          fprintf (f, "%s\n", channel);
149    }
150    fprintf (f, "Callerid: SMS <");
151    if (!callerid)
152       fprintf (f, "%.*s", ql, queue);
153    else
154    {
155       p = strchr (callerid, 'X');
156       if (p)
157          fprintf (f, "%.*s%c%s", (int)(p - callerid), callerid, subaddress, p + 1);
158       else
159          fprintf (f, "%s", callerid);
160    }
161    fprintf (f, ">\n");
162    fprintf (f, "Application: SMS\n");
163    fprintf (f, "Data: %.*s", qfl, queue);
164    if (dir[1] == 't')
165       fprintf (f, "|s");
166    fprintf (f, "\nMaxRetries: %d\n", retries);
167    fprintf (f, "RetryTime: %d\n", delay);
168    fprintf (f, "WaitTime: %d\n", wait);
169    fclose (f);
170    closedir (d);
171    {
172       int try = 0;
173       while (try < concurrent)
174       {
175          try++;
176          snprintf(ogname, sizeof(ogname), "outgoing/smsq.%s.%s.%d", dir, queue, try);
177          if (!link (temp, ogname))
178          {                      /* queued OK */
179             unlink (temp);
180             return 1;
181          }
182       }
183    }
184    /* failed to create call queue */
185    unlink (temp);
186    return 2;
187 }
188
189 /* Process received queue entries and run through a process, setting environment variables */
190 static void rxqcheck (char *dir, char *queue, char *process)
191 {
192    char *p;
193    void *pp = &p;
194    char dirname[100],
195      temp[100];
196    DIR *d;
197    int ql = strlen (queue);
198    struct dirent *fn;
199    snprintf(temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
200    snprintf(dirname, sizeof(dirname), "sms/%s", dir);
201    d = opendir (dirname);
202    if (!d)
203       return;
204    while ((fn = readdir (d)))
205       if ((*fn->d_name != '.'
206            && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.'))))
207       {                         /* process file */
208          char filename[1010];
209          char line[1000];
210          unsigned short ud[160];
211          unsigned char udl = 0;
212          FILE *f;
213          snprintf (filename, sizeof(filename), "sms/%s/%s", dir, fn->d_name);
214          if (rename (filename, temp))
215             continue;           /* cannot access file */
216          f = fopen (temp, "r");
217          unlink (temp);
218          if (!f)
219          {
220             perror (temp);
221             continue;
222          }
223          unsetenv ("oa");
224          unsetenv ("da");
225          unsetenv ("scts");
226          unsetenv ("pid");
227          unsetenv ("dcs");
228          unsetenv ("mr");
229          unsetenv ("srr");
230          unsetenv ("rp");
231          unsetenv ("vp");
232          unsetenv ("udh");
233          unsetenv ("ud");
234          unsetenv ("ude");
235          unsetenv ("ud8");
236          unsetenv ("ud16");
237          unsetenv ("morx");
238          unsetenv ("motx");
239          unsetenv ("queue");
240          if (*queue)
241             setenv ("queue", queue, 1);
242          setenv (dir, "", 1);
243          while (fgets (line, sizeof (line), f))
244          {
245             for (p = line; *p && *p != '\n' && *p != '\r'; p++);
246             *p = 0;             /* strip eoln */
247             p = line;
248             if (!*p || *p == ';')
249                continue;        /* blank line or comment, ignore */
250             while (isalnum (*p))
251             {
252                *p = tolower (*p);
253                p++;
254             }
255             while (isspace (*p))
256                *p++ = 0;
257             if (*p == '=')
258             {                   /* = */
259                *p++ = 0;
260                if (!strcmp (line, "oa") || !strcmp (line, "da") || !strcmp (line, "scts") || !strcmp (line, "pid")
261                    || !strcmp (line, "dcs") || !strcmp (line, "mr") || !strcmp (line, "vp"))
262                   setenv (line, p, 1);
263                else if ((!strcmp (line, "srr") || !strcmp (line, "rp")) && atoi (p))
264                   setenv (line, "", 1);
265                else if (!strcmp (line, "ud"))
266                {                /* read the user data as UTF-8 */
267                   long v;
268                   udl = 0;
269                   while ((v = utf8decode (pp)) && udl < 160)
270                      if (v && v <= 0xFFFF)
271                         ud[udl++] = v;
272                }
273             } else if (*p == '#')
274             {
275                *p++ = 0;
276                if (*p == '#')
277                {                /* ##  */
278                   p++;
279                   if (!strcmp (line, "udh"))
280                      setenv (line, p, 1);
281                   else if (!strcmp (line, "ud"))
282                   {             /* read user data UCS-2 */
283                      udl = 0;
284                      while (*p && udl < 160)
285                      {
286                         if (isxdigit (*p) && isxdigit (p[1]) && isxdigit (p[2]) && isxdigit (p[3]))
287                         {
288                            ud[udl++] =
289                               (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 12) +
290                               (((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
291                               (((isalpha (p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha (p[3]) ? 9 : 0) + (p[3] & 0xF));
292                            p += 4;
293                         } else
294                            break;
295                      }
296                   }
297                } else
298                {                /* # */
299                   if (!strcmp (line, "ud"))
300                   {             /* read user data UCS-1 */
301                      udl = 0;
302                      while (*p && udl < 160)
303                      {
304                         if (isxdigit (*p) && isxdigit (p[1]))
305                         {
306                            ud[udl++] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF));
307                            p += 2;
308                         } else
309                            break;
310                      }
311                   }
312                }
313             }
314          }
315          fclose (f);
316          /* set up user data variables */
317          {
318             char temp[481];
319             int n,
320               p;
321             for (n = 0, p = 0; p < udl; p++)
322             {
323                unsigned short v = ud[p];
324                if (v)
325                {
326                   if (v < 0x80)
327                      temp[n++] = v;
328                   else if (v < 0x800)
329                   {
330                      temp[n++] = (0xC0 + (v >> 6));
331                      temp[n++] = (0x80 + (v & 0x3F));
332                   } else
333                   {
334                      temp[n++] = (0xE0 + (v >> 12));
335                      temp[n++] = (0x80 + ((v >> 6) & 0x3F));
336                      temp[n++] = (0x80 + (v & 0x3F));
337                   }
338                }
339             }
340             temp[n] = 0;
341             setenv ("ud", temp, 1);
342             for (n = 0, p = 0; p < udl; p++)
343             {
344                unsigned short v = ud[p];
345                if (v < ' ' || v == '\\')
346                {
347                   temp[n++] = '\\';
348                   if (v == '\\')
349                      temp[n++] = '\\';
350                   else if (v == '\n')
351                      temp[n++] = 'n';
352                   else if (v == '\r')
353                      temp[n++] = 'r';
354                   else if (v == '\t')
355                      temp[n++] = 't';
356                   else if (v == '\f')
357                      temp[n++] = 'f';
358                   else
359                   {
360                      temp[n++] = '0' + (v >> 6);
361                      temp[n++] = '0' + ((v >> 3) & 7);
362                      temp[n++] = '0' + (v & 7);
363                   }
364                } else if (v < 0x80)
365                   temp[n++] = v;
366                else if (v < 0x800)
367                {
368                   temp[n++] = (0xC0 + (v >> 6));
369                   temp[n++] = (0x80 + (v & 0x3F));
370                } else
371                {
372                   temp[n++] = (0xE0 + (v >> 12));
373                   temp[n++] = (0x80 + ((v >> 6) & 0x3F));
374                   temp[n++] = (0x80 + (v & 0x3F));
375                }
376             }
377             temp[n] = 0;
378             setenv ("ude", temp, 1);
379             for (p = 0; p < udl && ud[p] < 0x100; p++);
380             if (p == udl)
381             {
382                for (n = 0, p = 0; p < udl; p++)
383                {
384                   sprintf (temp + n, "%02X", ud[p]);
385                   n += 2;
386                }
387                setenv ("ud8", temp, 1);
388             }
389             for (n = 0, p = 0; p < udl; p++)
390             {
391                sprintf (temp + n, "%04X", ud[p]);
392                n += 4;
393             }
394             setenv ("ud16", temp, 1);
395          }
396          /* run the command */
397          system (process);
398       }
399    closedir (d);
400 }
401
402 /* Main app */
403 int
404 main (int argc, const char *argv[])
405 {
406    char c;
407    int mt = 0,
408       mo = 0,
409       tx = 0,
410       rx = 0,
411       nodial = 0,
412       nowait = 0,
413       concurrent = 1,
414       motxwait = 10,
415       motxdelay = 1,
416       motxretries = 10,
417       mttxwait = 10,
418       mttxdelay = 30,
419       mttxretries = 100,
420       mr = -1,
421       pid = -1,
422       dcs = -1,
423       srr = 0,
424       rp = 0,
425       vp = 0,
426       udl = 0,
427       utf8 = 0,
428       ucs1 = 0,
429       ucs2 = 0;
430    unsigned short ud[160];
431    unsigned char *uds = 0,
432       *udh = 0;
433    char *da = 0,
434       *oa = 0,
435       *queue = "",
436       *udfile = 0,
437       *process = 0,
438       *spooldir = "/var/spool/asterisk",
439       *motxchannel = "Local/1709400X",
440       *motxcallerid = 0,
441       *mttxchannel = 0,
442       *mttxcallerid = "080058752X0",
443       *defaultsubaddress = "9",
444       subaddress = 0,
445       *scts = 0;
446    poptContext optCon;          /* context for parsing command-line options */
447    const struct poptOption optionsTable[] = {
448       {"queue", 'q', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &queue, 0, "Queue [inc sub address]", "number[-X]"},
449       {"da", 'd', POPT_ARG_STRING, &da, 0, "Destination address", "number"},
450       {"oa", 'o', POPT_ARG_STRING, &oa, 0, "Origination address", "number"},
451       {"ud", 'm', POPT_ARG_STRING, &uds, 0, "Message", "text"},
452       {"ud-file", 'f', POPT_ARG_STRING, &udfile, 0, "Message file", "filename"},
453       {"UTF-8", 0, POPT_ARG_NONE, &utf8, 0, "File treated as null terminated UTF-8 (default)", 0},
454       {"UCS-1", 0, POPT_ARG_NONE, &ucs1, 0, "File treated as UCS-1", 0},
455       {"UCS-2", 0, POPT_ARG_NONE, &ucs2, 0, "File treated as UCS-2", 0},
456       {"mt", 't', POPT_ARG_NONE, &mt, 0, "Mobile Terminated", 0},
457       {"mo", 0, POPT_ARG_NONE, &mo, 0, "Mobile Originated", 0},
458       {"tx", 0, POPT_ARG_NONE, &tx, 0, "Send message", 0},
459       {"rx", 'r', POPT_ARG_NONE, &rx, 0, "Queue for receipt", 0},
460       {"process", 'e', POPT_ARG_STRING, &process, 0, "Rx queue process command", "command"},
461       {"no-dial", 'x', POPT_ARG_NONE, &nodial, 0, "Do not dial", 0},
462       {"no-wait", 0, POPT_ARG_NONE, &nowait, 0, "Do not wait if already calling", 0},
463       {"concurrent", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &concurrent, 0, "Number of concurrent calls to allow", "n"},
464       {"motx-channel", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &motxchannel, 0, "Channel for motx calls", "channel"},
465       {"motx-callerid", 0, POPT_ARG_STRING, &motxcallerid, 0,
466        "Caller ID for motx calls (default is queue name without sub address)", "number"},
467       {"motx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxwait, 0, "Time to wait for motx call to answer",
468        "seconds"},
469       {"motx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxdelay, 0, "Time between motx call retries", "seconds"},
470       {"motx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxretries, 0, "Number of retries for motx call", "n"},
471       {"mttx-channel", 0, POPT_ARG_STRING, &mttxchannel, 0,
472        "Channel for mttx calls (default is Local/ and queue name without sub address)", "channel"},
473       {"mttx-callerid", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &mttxcallerid, 0,
474        "Caller ID for mttx calls (default is queue name without sub address)", "number"},
475       {"mttx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxwait, 0, "Time to wait for mttx call to answer",
476        "seconds"},
477       {"mttx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxdelay, 0, "Time between mttx call retries", "seconds"},
478       {"mttx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxretries, 0, "Number of retries for mttx call", "n"},
479       {"mr", 'n', POPT_ARG_INT, &mr, 0, "Message reference", "n"},
480       {"pid", 'p', POPT_ARG_INT, &pid, 0, "Protocol ID", "n"},
481       {"dcs", 'c', POPT_ARG_INT, &dcs, 0, "Data Coding Scheme", "n"},
482       {"udh", 0, POPT_ARG_STRING, &udh, 0, "User data header", "hex"},
483       {"srr", 0, POPT_ARG_NONE, &srr, 0, "Status Report Request", 0},
484       {"rp", 0, POPT_ARG_NONE, &rp, 0, "Return Path request", 0},
485       {"v", 0, POPT_ARG_INT, &vp, 0, "Validity Period", "seconds"},
486       {"scts", 0, POPT_ARG_STRING, &scts, 0, "Timestamp", "YYYY-MM-SSTHH:MM:SS"},
487       {"default-sub-address", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &defaultsubaddress, 0, "Default sub address", "X"},
488       {"spool-dir", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &spooldir, 0, "Asterisk spool dir", "dirname"},
489       POPT_AUTOHELP {NULL, 0, 0, NULL, 0}
490    };
491
492    optCon = poptGetContext (NULL, argc, argv, optionsTable, 0);
493    poptSetOtherOptionHelp (optCon, "<oa/da> <message>");
494
495    /* Now do options processing, get portname */
496    if ((c = poptGetNextOpt (optCon)) < -1)
497    {
498       /* an error occurred during option processing */
499       fprintf (stderr, "%s: %s\n", poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (c));
500       return 1;
501    }
502    if (!ucs1 && !ucs2)
503       utf8 = 1;
504    if (utf8 + ucs1 + ucs2 > 1)
505    {
506       fprintf (stderr, "Pick one of UTF-8, UCS-1 or UCS-2 only\n");
507       return 1;
508    }
509    if (!udfile && (ucs1 || ucs2))
510    {
511       fprintf (stderr, "Command line arguments always treated as UTF-8\n");
512       return 1;
513    }
514    /*  if (!where && poptPeekArg (optCon)) where = (char *) poptGetArg (optCon); */
515    if (!mt && !mo && process)
516       mt = 1;
517    if (!mt && !mo && oa)
518       mt = 1;
519    if (!mt)
520       mo = 1;
521    if (mt && mo)
522    {
523       fprintf (stderr, "Cannot be --mt and --mo\n");
524       return 1;
525    }
526    if (!rx && !tx && process)
527       rx = 1;
528    if (!rx)
529       tx = 1;
530    if (tx && rx)
531    {
532       fprintf (stderr, "Cannot be --tx and --rx\n");
533       return 1;
534    }
535    if (rx)
536       nodial = 1;
537    if (uds && udfile)
538    {
539       fprintf (stderr, "Cannot have --ud and --ud-file\n");
540       return 1;
541    }
542    if (mo && !da && poptPeekArg (optCon))
543       da = (char *) poptGetArg (optCon);
544    if (mt && !oa && poptPeekArg (optCon))
545       oa = (char *) poptGetArg (optCon);
546    if (tx && oa && mo)
547    {
548       fprintf (stderr, "--oa makes no sense with --mo as CLI is used (i.e. queue name)\n");
549       return 1;
550    }
551    if (tx && da && mt)
552    {
553       fprintf (stderr, "--da makes no sense with --mt as called number is used (i.e. queue name)\n");
554       return 1;
555    }
556    if (da && strlen (da) > 20)
557    {
558       fprintf (stderr, "--da too long\n");
559       return 1;
560    }
561    if (oa && strlen (oa) > 20)
562    {
563       fprintf (stderr, "--oa too long\n");
564       return 1;
565    }
566    if (queue && strlen (queue) > 20)
567    {
568       fprintf (stderr, "--queue name too long\n");
569       return 1;
570    }
571    if (mo && scts)
572    {
573       fprintf (stderr, "scts is set my service centre\n");
574       return 1;
575    }
576    if (uds)
577    {                            /* simple user data command line option in \UTF-8 */
578       while (udl < 160 && *uds)
579       {
580          int v = utf8decode (&uds);
581          if (v > 0xFFFF)
582          {
583             fprintf (stderr, "Invalid character U+%X at %d\n", v, udl);
584             return 1;
585          }
586          ud[udl++] = v;
587       }
588    }
589    if (!uds && !udfile && poptPeekArg (optCon))
590    {                            /* multiple command line arguments in UTF-8 */
591       while (poptPeekArg (optCon) && udl < 160)
592       {
593          unsigned char *a = (unsigned char *) poptGetArg (optCon);
594          if (udl && udl < 160)
595             ud[udl++] = ' ';    /* space between arguments */
596          while (udl < 160 && *a)
597          {
598             int v = utf8decode (&a);
599             if (v > 0xFFFF)
600             {
601                fprintf (stderr, "Invalid character U+%X at %d\n", v, udl);
602                return 1;
603             }
604             ud[udl++] = v;
605          }
606       }
607    }
608    if (poptPeekArg (optCon))
609    {
610       fprintf (stderr, "Unknown argument %s\n", poptGetArg (optCon));
611       return 1;
612    }
613    if (udfile)
614    {                            /* get message from file */
615       unsigned char dat[1204],
616        *p = dat,
617          *e;
618       int f,
619         n;
620       if (*udfile)
621          f = open (udfile, O_RDONLY);
622       else
623          f = fileno (stdin);
624       if (f < 0)
625       {
626          perror (udfile);
627          return 1;
628       }
629       n = read (f, dat, sizeof (dat));
630       if (n < 0)
631       {
632          perror (udfile);
633          return 1;
634       }
635       if (*udfile)
636          close (f);
637       e = dat + n;
638       if (utf8)
639       {                         /* UTF-8 */
640          while (p < e && udl < 160 && *p)
641             ud[udl++] = utf8decode (&p);
642       } else if (ucs1)
643       {                         /* UCS-1 */
644          while (p < e && udl < 160)
645             ud[udl++] = *p++;
646       } else
647       {                         /* UCS-2 */
648          while (p + 1 < e && udl < 160)
649          {
650             ud[udl++] = (*p << 8) + p[1];
651             p += 2;
652          }
653       }
654    }
655    if (queue)
656    {
657       char *d = strrchr (queue, '-');
658       if (d && d[1])
659          subaddress = d[1];
660       else
661          subaddress = *defaultsubaddress;
662    }
663
664    if (chdir (spooldir))
665    {
666       perror (spooldir);
667       return 1;
668    }
669
670    if (oa || da)
671    {                            /* send message */
672       char temp[100],
673         queuename[100],
674        *dir = (mo ? rx ? "sms/morx" : "sms/motx" : rx ? "sms/mtrx" : "sms/mttx");
675       FILE *f;
676       snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
677       mkdir ("sms", 0777);      /* ensure directory exists */
678       mkdir (dir, 0777);        /* ensure directory exists */
679       snprintf (queuename, sizeof(queuename), "%s/%s.%ld-%d", dir, *queue ? queue : "0", (long)time (0), (int)getpid ());
680       f = fopen (temp, "w");
681       if (!f)
682       {
683          perror (temp);
684          return 1;
685       }
686       if (oa)
687          fprintf (f, "oa=%s\n", oa);
688       if (da)
689          fprintf (f, "da=%s\n", da);
690       if (scts)
691          fprintf (f, "scts=%s\n", scts);
692       if (pid >= 0)
693          fprintf (f, "pid=%d\n", pid);
694       if (dcs >= 0)
695          fprintf (f, "dcs=%d\n", dcs);
696       if (mr >= 0)
697          fprintf (f, "mr=%d\n", mr);
698       if (srr)
699          fprintf (f, "srr=1\n");
700       if (rp)
701          fprintf (f, "rp=1\n");
702       if (udh)
703          fprintf (f, "udh#%s\n", udh);
704       if (vp > 0)
705          fprintf (f, "vp=%d\n", vp);
706       if (udl)
707       {
708          int p;
709          for (p = 0; p < udl && ud[p] < 0x100; p++);
710          if (p == udl)
711          {
712             for (p = 0; p < udl && ud[p] < 0x80 && ud[p] >= 0x20; p++);
713             if (p == udl)
714             {                   /* use text */
715                fprintf (f, "ud=");
716                for (p = 0; p < udl; p++)
717                   fputc (ud[p], f);
718             } else
719             {                   /* use one byte hex */
720                fprintf (f, "ud#");
721                for (p = 0; p < udl; p++)
722                   fprintf (f, "%02X", ud[p]);
723             }
724          } else
725          {                      /* use two byte hex */
726             fprintf (f, "ud##");
727             for (p = 0; p < udl; p++)
728                fprintf (f, "%04X", ud[p]);
729          }
730          fprintf (f, "\n");
731       }
732       fclose (f);
733       if (rename (temp, queuename))
734       {
735          perror (queuename);
736          unlink (temp);
737          return 1;
738       }
739    }
740
741    if (!nodial && tx && !process)
742    {                            /* dial to send messages */
743       char ret=0,
744         try = 3;
745       if (nowait)
746          try = 1;
747       while (try--)
748       {
749          if (mo)
750             ret = txqcheck ("motx", queue, subaddress, motxchannel, motxcallerid, motxwait, motxdelay, motxretries, concurrent);
751          else
752             ret = txqcheck ("mttx", queue, subaddress, mttxchannel, mttxcallerid, mttxwait, mttxdelay, mttxretries, concurrent);
753          if (ret < 2)
754             break;              /* sent, or queued OK */
755          if (try)
756             sleep (1);
757       }
758       if (ret == 2 && !nowait)
759          fprintf (stderr, "No call scheduled as already sending\n");
760    }
761    if (process)
762       rxqcheck (mo ? rx ? "morx" : "motx" : rx ? "mtrx" : "mttx", queue, process);
763
764    return 0;
765 }