/* -*- mode: c; mode: fold -*- */ /*{{{ includes */ # include "config.h" # include # include # include # include # include # include # include # include # include # include # include # if HAVE_LOCALE_H # include # endif /* HAVE_LOCALE_H */ # if HAVE_GETOPT_H # include # endif /* HAVE_GETOPT_H */ # include "pager.h" # include "valid.h" /*}}}*/ /*{{{ general definitions */ # define VERSION "0.96.c2 (alpha software, CAPI support V2)" # ifndef LIBDIR # define LIBDIR NULL # endif /* LIBDIR */ # if HAVE_SIGSETJMP # undef jmp_buf # define jmp_buf sigjmp_buf # undef setjmp # define setjmp(xx) sigsetjmp ((xx), 1) # undef longjmp # define longjmp(xx,vv) siglongjmp ((xx), (vv)) # endif /* HAVE_SIGSETJMP */ # define SPLIT_TERM " ..." # define SPLIT_INIT "... " # define OOM fprintf (stderr, "Out of memory in line %d, aborted\n", __LINE__) /*}}}*/ /*{{{ configuration file entry names and default values */ # define CONFIG_SEP "," # define SEC_ALIAS "alias" # define CFG_SERVICES "services" # define CFG_CALLID "call-id" # define CFG_SIG "signature" # define CFG_USESIG "use-signature" # define CFG_VERBOSE "verbose" # define CFG_LOGFILE "logfile" # define CFG_LOGSTR "logstring" # define CFG_MODEMS "modems" # define CFG_FREPORT "final-report" # define CFG_SPEED "speed" # define CFG_BPB "bits-per-byte" # define CFG_PARITY "parity" # define CFG_SB "stopbits" # define CFG_PHONE "phone" # define CFG_PROTOCOL "protocol" # define CFG_MAXSIZE "max-size" # define CFG_MAYSPLIT "may-split" # define CFG_MAXMSGS "max-messages" # define CFG_TRUNCATE "truncate" # define CFG_USECID "use-call-id" # define CFG_RMCID "rm-invalids-cid,remove-invalid-character-call-id" # define CFG_RMPID "rm-invalids-pid,remove-invalid-character-pager-id" # define CFG_INSPID "insert-pager-id" # define CFG_CHPID "change-pid,change-pager-id" # define CFG_VALPID "valid-pid,valid-pager-id" # define CFG_CHCID "change-cid,change-call-id" # define CFG_VALCID "valid-cid,valid-call-id" # define CFG_CONVTAB "conv-table" # define CFG_CONVERT "convert" # define CFG_COST "cost" # define CFG_FORCE "force" # define CFG_CANDELAY "can-delay" # define CFG_CANEXP "can-expire" # define CFG_CANRDS "can-rds,can-request-delivery-status" # define CFG_DORDS "rds,request-delivery-status" # define CFG_CHKCID "check-call-id" # define CFG_CHKPID "check-pager-id" # define CFG_DEVICE "device" # define CFG_LCKPRE "lock-prefix" # define CFG_LCKMETHOD "lock-method" # define CFG_INIT "init" # define CFG_LINIT "local-init" # define CFG_DIAL "dial" # define CFG_TIMEOUT "timeout" # define CFG_RESET "reset" # define DEF_SPEED 38400 # define DEF_BPB 8 # define DEF_PARITY "n" # define DEF_SB 1 # define DEF_USECID False # define DEF_INSPID False # define DEF_CONVTAB NULL # define DEF_CONVERT NULL # define DEF_DEVICE "/dev/modem" # define DEF_LCKPRE "/usr/spool/uucp/LCK.." # define DEF_LCKMETHOD NULL # define DEF_INIT "\\r !d ATZ\\r tm_mday, tt -> tm_mon + 1, tt -> tm_year + 1900, tt -> tm_hour, tt -> tm_min, tt -> tm_sec, typ); vfprintf (fp, fmt, par); fprintf (fp, "\n"); fclose (fp); } umask (omask); va_end (par); } /*}}}*/ /*{{{ dialing */ static char * mkrealphone (char *phone, char *pagerid) { char *dst; int siz, n, len; char *ptr; siz = strlen (phone) + 16; if (dst = malloc (siz + 2)) { for (n = 0; *phone; ) if (*phone == '%') { ++phone; if (*phone) { ptr = NULL; switch (*phone) { case 'P': ptr = pagerid; break; } if (ptr) { len = strlen (ptr); if (n + len >= siz) { siz = n + len + 64; if (! (dst = Realloc (dst, siz + 2))) break; } strcpy (dst + n, ptr); n += len; } ++phone; } } else { if (n >= siz) { siz += 32; if (! (dst = Realloc (dst, siz + 2))) break; } dst[n++] = *phone++; } if (dst) dst[n] = '\0'; } return dst; } static void * do_dial (void *cfg, char *service, char *pagerid, char **modem) { void *sp; char *tmp, *sav, *ptr; char *t2, *s2, *p2; char *device, *lckpre, *lckmethod; int speed, rspeed, bpb, parity, sb; int tout; char *minit, *linit, *dial, *reset, *phone; int siz, len; int n, m; sp = NULL; *modem = NULL; if ((! (tmp = cfg_get (cfg, service, CFG_MODEMS, NULL))) || (! (tmp = strdup (tmp)))) return NULL; speed = cfg_iget (cfg, service, CFG_SPEED, DEF_SPEED); bpb = cfg_iget (cfg, service, CFG_BPB, DEF_BPB); if (ptr = cfg_get (cfg, service, CFG_PARITY, DEF_PARITY)) parity = *ptr; else parity = '\0'; sb = cfg_iget (cfg, service, CFG_SB, DEF_SB); dial = NULL; reset = NULL; tout = 0; for (ptr = tmp; *ptr; ) { sav = ptr; ptr = skipch (ptr, ','); device = cfg_get (cfg, sav, CFG_DEVICE, DEF_DEVICE); lckpre = cfg_get (cfg, sav, CFG_LCKPRE, DEF_LCKPRE); lckmethod = cfg_get (cfg, sav, CFG_LCKMETHOD, DEF_LCKMETHOD); minit = cfg_get (cfg, sav, CFG_INIT, DEF_INIT); linit = cfg_get (cfg, sav, CFG_LINIT, DEF_LINIT); dial = cfg_get (cfg, sav, CFG_DIAL, DEF_DIAL); reset = cfg_get (cfg, sav, CFG_RESET, NULL); tout = cfg_iget (cfg, sav, CFG_TIMEOUT, DEF_TIMEOUT); rspeed = cfg_block_iget (cfg, sav, CFG_SPEED, speed); if (device && (t2 = strdup (device))) { for (p2 = t2; *p2; ) { s2 = p2; p2 = skipch (p2, ','); V (3, ("Trying to open %s for modem %s\n", s2, sav)); if (device_is_capi(s2)) { if (sp = hyla_capi_init(s2)) break; } else if (sp = tty_open (s2, lckpre, lckmethod)) { if (tty_setup (sp, True, True, rspeed, bpb, sb, parity) != -1) { if (! (n = setjmp (env))) { dojump = True; tty_hangup (sp, 500); if (((! minit) || (tty_send_expect (sp, tout, minit, NULL) != -1)) && ((! linit) || (tty_send_expect (sp, tout, linit, NULL) != -1))) { dojump = False; V (3, ("Using modem %s at %d bps, %d%c%d over %s\n", sav, rspeed, bpb, parity, sb, s2)); *modem = strdup (sav); break; } } else { V (2, ("\n")); if ((n == SIGTERM) || (n == SIGQUIT)) longjmp (termenv, n); } dojump = False; } sp = tty_close (sp); } } free (t2); } if (sp) break; } free (tmp); if (sp && dial && (phone = cfg_get (cfg, service, CFG_PHONE, NULL)) && (phone = mkrealphone (phone, pagerid))) { if (! (n = setjmp (env))) { dojump = True; for (p2 = phone; *p2; ) { s2 = p2; p2 = skipch (p2, ','); siz = strlen (dial) + strlen (phone) + 64; if (hyla_is_capi(sp)) { if ((!dial) || (!isdigit(*dial))) dial = ""; V (3, ("Trying do dial %s%s\n", (dial) ? dial : "", s2)); if (hyla_capi_connect(sp, dial, s2)) { V (1, ("Dial successful\n")); while (*p2) ++p2; } else { if (!*p2) sp = hyla_capi_close(sp); } } else if (tmp = malloc (siz + 2)) { for (n = 0, m = 0; dial[n]; ) { if (dial[n] == '%') { ++n; if (dial[n]) { ptr = NULL; switch (dial[n]) { case 'L': ptr = phone; break; } if (ptr) { len = strlen (ptr); if (m + len >= siz) { siz += len + 64; if (! (tmp = Realloc (tmp, siz + 2))) break; } strcpy (tmp + m, ptr); m += len; } ++n; } } else { if (m + 1 >= siz) { siz += 128; if (! (tmp = Realloc (tmp, siz + 2))) break; } switch (dial[n]) { case '-': tmp[m++] = ','; break; default: tmp[m++] = dial[n]; break; } ++n; } } if (tmp) { tmp[m] = '\0'; V (3, ("Trying do dial %s\n", s2)); if (tty_send_expect (sp, tout, tmp, NULL) != -1) { V (1, ("Dial successful\n")); tty_drain (sp, 1); while (*p2) ++p2; } else { if (*p2) tty_hangup (sp, 500); else sp = tty_close (sp); } free (tmp); } } } } else { V (2, ("\n")); if (sp) { tty_send (sp, "x\r", 2); tty_hangup (sp, 500); if (reset) tty_send_expect (sp, tout, reset, NULL); sp = tty_close (sp); } if ((n == SIGTERM) || (n == SIGQUIT)) longjmp (termenv, n); } dojump = False; free (phone); } return sp; } /*}}}*/ /*{{{ send ASCII */ static int asc_send (void *sp, void *ctab, string_t *callid, message *mg, int mcnt, void *cfg, char *service, date_t *delay, date_t *expire, Bool rds) { int ret; void *ap; int n; int err, nerr; int atout; char *alin, *alout, *apid, *amsg, *anext, *async; char *tmp; ret = 0; if (ap = asc_new (sp)) { atout = cfg_iget (cfg, service, CFG_ATOUT, DEF_ATOUT); alin = cfg_get (cfg, service, CFG_ALOGIN, DEF_ALOGIN); alout = cfg_get (cfg, service, CFG_ALOGOUT, DEF_ALOGOUT); apid = cfg_get (cfg, service, CFG_APID, DEF_APID); amsg = cfg_get (cfg, service, CFG_AMSG, DEF_AMSG); anext = cfg_get (cfg, service, CFG_ANEXT, DEF_ANEXT); async = cfg_get (cfg, service, CFG_ASYNC, DEF_ASYNC); asc_config (ap, do_log, atout, alin, alout, apid, amsg, anext, async, delay, expire, rds); if (ctab) { asc_add_convtable (ap, ctab); ctab = cv_free (ctab); } if ((err = asc_login (ap, callid)) == NO_ERR) { for (n = 0; n < mcnt; ++n) { tmp = schar (mg[n].msg); if (tmp) err = asc_transmit (ap, mg[n].pid, tmp); else err = ERR_FATAL; if (err == NO_ERR) mg[n].st.success = True; else mg[n].st.reason = CantSend; do_log ((err == NO_ERR ? LGS_SENT : LGF_SENT), "ASCII Message %ssent to %s via %s: %s", (err == NO_ERR ? "" : "NOT "), (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), service, (tmp ? tmp : "")); if (ESTOP (err)) break; if (n + 1 < mcnt) { if (err == NO_ERR) err = asc_next (ap); else err = asc_sync (ap); if (ESTOP (err)) break; else if (err != NO_ERR) if ((nerr = asc_sync (ap)) != NO_ERR) { if (nerr < err) err = nerr; break; } } } if (n < mcnt) ret = 1; if ((err != ERR_ABORT) && (asc_logout (ap) != NO_ERR)) ret = 1; } else ret = 1; asc_free (ap); } else ret = 1; return ret; } /*}}}*/ /*{{{ send script */ static int scr_send (void *sp, void *ctab, string_t *callid, message *mg, int mcnt, void *cfg, char *service, date_t *delay, date_t *expire, Bool rds) { int ret; void *s; char *typ; char *name; char *scr; char *slogin, *slogout, *spid, *smsg, *snext, *ssync; char *tmp; int n, err, nerr; ret = 1; typ = cfg_get (cfg, service, CFG_STYP, DEF_STYP); if (s = scr_new (sp, typ, LIBDIR)) { scr_config (s, do_log, delay, expire, rds); name = cfg_get (cfg, service, CFG_SNAME, DEF_SNAME); if (name) if ((*name == '/') || (*name == '+')) { if (*name == '+') ++name; if (scr_load_file (s, name) == NO_ERR) ret = 0; } else if (scr = cfg_get (cfg, service, name, NULL)) if (scr_load_string (s, scr) == NO_ERR) ret = 0; if (! ret) { slogin = cfg_get (cfg, service, CFG_SLOGIN, DEF_SLOGIN); slogout = cfg_get (cfg, service, CFG_SLOGOUT, DEF_SLOGOUT); spid = cfg_get (cfg, service, CFG_SPID, DEF_SPID); smsg = cfg_get (cfg, service, CFG_SMSG, DEF_SMSG); snext = cfg_get (cfg, service, CFG_SNEXT, DEF_SNEXT); ssync = cfg_get (cfg, service, CFG_SSYNC, DEF_SSYNC); if (ctab) { scr_add_convtable (s, ctab); ctab = cv_free (ctab); } if (scr_execute (s, slogin, schar (callid)) == NO_ERR) { err = NO_ERR; for (n = 0; n < mcnt; ++n) { err = scr_execute (s, spid, mg[n].pid); do_log ((err == NO_ERR ? LGS_INF : LGF_INF), "SCRIPT pagerid %s %ssent via %s", (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), (err == NO_ERR ? "" : "NOT "), service); if (ESTOP (err)) break; else if (err == NO_ERR) { tmp = schar (mg[n].msg); if (tmp) err = scr_execute (s, smsg, tmp); else err = ERR_FATAL; if (err == NO_ERR) mg[n].st.success = True; else mg[n].st.reason = CantSend; do_log ((err == NO_ERR ? LGS_INF : LGF_INF), "SCRIPT message %s %ssent via %s", (tmp ? tmp : ""), (err == NO_ERR ? "" : "NOT "), service); do_log ((err == NO_ERR ? LGS_SENT : LGF_SENT), "SCRIPT Message %ssent to %s via %s: %s", (err == NO_ERR ? "" : "NOT "), (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), service, (tmp ? tmp : "")); if (ESTOP (err)) break; } if (n + 1 < mcnt) { if (err == NO_ERR) err = scr_execute (s, snext, NULL); else err = scr_execute (s, ssync, NULL); if (ESTOP (err)) break; else if (err != NO_ERR) if ((nerr = scr_execute (s, ssync, NULL)) != NO_ERR) { if (nerr < err) err = nerr; break; } } } if (n < mcnt) ret = 1; if ((err != ERR_ABORT) && (scr_execute (s, slogout, NULL) != NO_ERR)) ret = 1; } else ret = 1; } scr_free (s); } return ret; } /*}}}*/ /*{{{ send TAP */ static int tap_send (void *sp, void *ctab, string_t *callid, message *mg, int mcnt, void *cfg, char *service, date_t *delay, date_t *expire, Bool rds) { void *tp; string_t *field[4]; int fld; int n; char *tmp; int err, nerr; if (! (tp = tap_new (sp))) return 1; tap_timeouts (tp, cfg_iget (cfg, service, CFG_T1, DEF_T1), cfg_iget (cfg, service, CFG_T2, DEF_T2), cfg_iget (cfg, service, CFG_T3, DEF_T3), cfg_iget (cfg, service, CFG_T4, DEF_T4), cfg_iget (cfg, service, CFG_T5, DEF_T5)); tap_retries (tp, cfg_iget (cfg, service, CFG_N1, DEF_N1), cfg_iget (cfg, service, CFG_N2, DEF_N2), cfg_iget (cfg, service, CFG_N3, DEF_N3), cfg_iget (cfg, service, CFG_TLICNT, DEF_TLICNT), cfg_iget (cfg, service, CFG_TLOCNT, DEF_TLOCNT)); tap_config (tp, do_log, cfg_bget (cfg, service, CFG_TOLD, DEF_TOLD)); if (ctab) { tap_add_convtable (tp, ctab); ctab = cv_free (ctab); } if (tap_login (tp, NULL, '\0', NULL, callid) != NO_ERR) { tap_free (tp); return 1; } err = NO_ERR; for (n = 0; n < mcnt; ++n) { fld = 0; field[fld++] = snewc (mg[n].pid); field[fld++] = snew (mg[n].msg -> str, mg[n].msg -> len); field[fld] = NULL; nerr = tap_transmit (tp, field, (n + 1 < mcnt ? False : True)); if (nerr == NO_ERR) mg[n].st.success = True; else mg[n].st.reason = CantSend; tmp = schar (mg[n].msg); do_log ((nerr == NO_ERR ? LGS_SENT : LGF_SENT), "TAP Message %ssent to %s via %s: %s", (nerr == NO_ERR ? "" : "NOT "), (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), service, (tmp ? tmp : "")); if (nerr != NO_ERR) err = nerr; for (fld = 0; field[fld]; ++fld) sfree (field[fld]); if (ESTOP (err)) break; } if ((err != ERR_ABORT) && ((n = tap_logout (tp)) != NO_ERR)) if (err == NO_ERR) err = n; tap_free (tp); return (err != NO_ERR) ? 1 : 0; } /*}}}*/ /*{{{ send UCP */ static int ucp_send (void *sp, void *ctab, string_t *callid, message *mg, int mcnt, void *cfg, char *service, date_t *delay, date_t *expire, Bool rds) { void *up; int n; int err, nerr; string_t *pid; char *tmp; if (! (up = ucp_new (sp))) return 1; err = ERR_FATAL; if (ctab) { ucp_add_convtable (up, ctab); ctab = cv_free (ctab); } ucp_config (up, do_log, cfg_bget (cfg, service, CFG_UXTEND, DEF_UXTEND), cfg_iget (cfg, service, CFG_USTOUT, DEF_USTOUT), cfg_iget (cfg, service, CFG_URETRY, DEF_URETRY), cfg_iget (cfg, service, CFG_URTOUT, DEF_URTOUT), delay, expire, rds); if ((err = ucp_login (up, callid)) == NO_ERR) { for (n = 0; n < mcnt; ++n) { pid = snewc (mg[n].pid); if (pid) { nerr = ucp_transmit (up, pid, mg[n].msg, (n + 1 < mcnt ? False : True)); if (nerr == NO_ERR) mg[n].st.success = True; else mg[n].st.reason = CantSend; } else nerr = ERR_FATAL; sfree (pid); tmp = schar (mg[n].msg); do_log ((nerr == NO_ERR ? LGS_SENT : LGF_SENT), "UCP Message %ssent to %s via %s: %s", (nerr == NO_ERR ? "" : "NOT "), (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), service, (tmp ? tmp : "")); if (nerr != NO_ERR) err = nerr; if (ESTOP (err)) break; } if ((err != ERR_ABORT) && ((n = ucp_logout (up)) != NO_ERR)) if (err == NO_ERR) err = n; } ucp_free (up); return (err != NO_ERR) ? 1 : 0; } /*}}}*/ /*{{{ support routines */ static Protocol getproto (char *str) { if (str) if (! strcmp (str, "ascii")) return Ascii; else if (! strcmp (str, "script")) return Script; else if (! strcmp (str, "tap")) return Tap; else if (! strcmp (str, "ucp")) return Ucp; return Unknown; } static string_t * readfile (char *fname) { FILE *fp; string_t *str; int size; struct stat st; str = NULL; if (fp = fopen (fname, "r")) { size = 0; if (fstat (fileno (fp), & st) != -1) size = st.st_size; if ((size > 0) && (str = snew (NULL, size + 1))) if (fread (str -> str, sizeof (char), size, fp) == size) str -> len = size; else str = sfree (str); fclose (fp); } return str; } static string_t * read_stdin (void) { string_t *str; int ch; if (str = snew (NULL, 256)) while ((ch = getchar ()) != EOF) { if (str -> len >= str -> size) sexpand (str, str -> size + 64); str -> str[str -> len++] = (char_t) ch; } return str; } static void extend_convtable (void *ctab, void *cfg, char *service) { char *str; char *cv, *cvs; char *ptr, *sav; char *src, *dst; int n; char *defconv[] = { "no-control", "control", "no-8bit", "8bit", "numeric", NULL }; if ((str = cfg_get (cfg, service, CFG_CONVERT, DEF_CONVERT)) && (str = strdup (str))) { for (cv = str; *cv; ) { cvs = cv; cv = skipch (cv, ','); if (*cvs == '*') { ++cvs; for (n = 0; defconv[n]; ++n) if (! strcmp (cvs, defconv[n])) break; switch (n) { case 0: /* no-control */ for (n = 0; n < 32; ++n) { cv_invalid (ctab, (char_t) n); cv_invalid (ctab, (char_t) (n | 0x80)); } cv_invalid (ctab, (char_t) '\x7f'); break; case 1: /* control */ for (n = 0; n < 32; ++n) { cv_undefine (ctab, (char_t) n); cv_undefine (ctab, (char_t) (n | 0x80)); } cv_undefine (ctab, (char_t) '\x7f'); break; case 2: /* no-8bit */ for (n = 128; n < 256; ++n) cv_invalid (ctab, (char_t) n); break; case 3: /* 8bit */ for (n = 128; n < 256; ++n) cv_undefine (ctab, (char_t) n); break; case 4: /* numeric */ for (n = 0; n < 256; ++n) if (n && strchr ("0123456789", (char) n)) cv_undefine (ctab, (char_t) n); else cv_invalid (ctab, (char_t) n); break; } } else if ((cvs = cfg_get (cfg, service, cvs, NULL)) && (cvs = strdup (cvs))) { for (ptr = cvs; ptr; ) { sav = ptr; if (ptr = strchr (ptr, '\n')) *ptr++ = '\0'; for (src = sav; isspace (*src); ++src) ; if (*src && (*src != '#')) { dst = skip (src); if (*src && *dst) cv_sdefine (ctab, src, dst); } } free (cvs); } } free (str); } } static int check (char *str, char *pat) { Bool done, incs; char *ptr, *sav, *val; int slen; int n, m, chk; char *p; char *chtab[] = { "type", "length", "minimum", "maximum", NULL }, *tytab[] = { "numeric", "sedecimal", "lower", "upper", "alpha", "alphanumeric", "print", "ascii", NULL }; if (! pat) return 0; done = False; if (*pat == '+') { ++pat; if (pat = strdup (pat)) { done = True; slen = strlen (str); for (ptr = pat; *ptr && done; ) { sav = ptr; ptr = skipch (ptr, ','); val = skipch (sav, '='); for (n = 0; chtab[n]; ++n) if (! strcmp (sav, chtab[n])) break; switch (n) { case 0: /* type */ for (m = 0; tytab[m]; ++m) if (! strcmp (val, tytab[m])) break; for (p = str; *p; ++p) { switch (m) { default: chk = 1; break; case 0: /* numeric */ chk = isdigit (*p); break; case 1: /* sedecimal */ chk = isxdigit (*p); break; case 2: /* lower */ chk = islower (*p); break; case 3: /* upper */ chk = isupper (*p); break; case 4: /* alpha */ chk = isalpha (*p); break; case 5: /* alphanumeric */ chk = isalnum (*p); break; case 6: /* print */ chk = isprint (*p); break; case 7: /* ascii */ chk = isascii (*p); break; } if (! chk) return -1; } break; case 1: /* length */ if (slen != atoi (val)) return -1; break; case 2: /* minimum */ if (slen < atoi (val)) return -1; break; case 3: /* maximum */ if (slen > atoi (val)) return -1; break; } } } if (! done) return -1; } else { while (*str && *pat) { incs = True; switch (tolower (*pat)) { case '>': done = True; incs = False; break; case '<': return -1; break; case '1': if (! isdigit (*str)) return -1; break; case 'h': if (! isxdigit (*str)) return -1; break; case 'l': if (! islower (*str)) return -1; break; case 'u': if (! isupper (*str)) return -1; break; case 'a': if (! isalpha (*str)) return -1; break; case 'n': if (! isalnum (*str)) return 1; break; case 'p': if (! isprint (*str)) return -1; break; case 'x': if (! isascii (*str)) return -1; break; } if ((! done) || *(pat + 1)) ++pat; if (incs) ++str; } if ((! done) && (*str || *pat)) return -1; } return 0; } /*}}}*/ /*{{{ preparse receiver and messages */ static int ncompare (const void *a, const void *b) { return ((message *) a) -> nr - ((message *) b) -> nr; } static int mcompare (const void *a, const void *b) { const message *am = (message *) a, *bm = (message *) b; int n; n = strcmp (am -> service, bm -> service); if (! n) n = strcmp (am -> pid, bm -> pid); return n; } static char * do_replace (char *str, int start, int len, char *rplc, int rlen) { int n, m; int slen, diff; if (rlen > len) { slen = strlen (str); diff = rlen - len; if (! (str = Realloc (str, slen + diff + 2))) return NULL; for (n = slen; n >= start + len; --n) str[n + diff] = str[n]; str[slen + diff] = '\0'; } else if (rlen < len) { for (n = start + rlen, m = start + len; str[m]; ++n, ++m) str[n] = str[m]; str[n] = '\0'; } if (rlen > 0) memcpy (str + start, rplc, rlen); return str; } static void remove_invalids (char *str, char *rmv) { int m, n; char *ptr; for (n = 0, m = 0; str[n]; ++n) { for (ptr = rmv; *ptr; ++ptr) if (*ptr == str[n]) break; if (! *ptr) if (n != m) str[m++] = str[n]; else ++m; } str[m] = '\0'; } static message * create_messages (void *cfg, char *service, serv *sbase, char **argv, int argc, char *mfile, int *mcnt, char *callid, char *sig, Bool ytrunc, Bool force, date_t *delay, date_t *expire, Bool rds) { char **recv; int rcnt, rsiz; FILE *fp; message *mg; int cnt, siz; Bool lforce; serv *srun; int n, m, len; char *str, *ptr, *sav, *tstr, *tptr; string_t *fstdin; Bool isalias; Bool doins; int rsize; string_t *tmp; char *rmv; char *rplc; int start, end; Bool ttrunc; char *tsig; int msize; Bool msplit; char *sr; rcnt = 0; rsiz = 0; recv = NULL; for (n = 0; n < argc; ++n) { if (rcnt + 2 >= rsiz) { rsiz += 16; if (! (recv = (char **) Realloc (recv, (rsiz + 2) * sizeof (char *)))) { OOM; return NULL; } } if (! (recv[rcnt] = strdup (argv[n]))) { OOM; return NULL; } ++rcnt; } if (mfile) { if (! (fp = fopen (mfile, "r"))) { fprintf (stderr, "Unable to open message file %s for reading\n", mfile); return NULL; } while (ptr = getline (fp, False)) { sav = skip (ptr); if (*ptr && *sav) { if (rcnt + 2 >= rsiz) { rsiz += 16; if (! (recv = (char **) Realloc (recv, (rsiz + 2) * sizeof (char *)))) { OOM; return NULL; } } if ((! (recv[rcnt] = strdup (ptr))) || (! (recv[rcnt + 1] = strdup (sav)))) { OOM; return NULL; } rcnt += 2; } } fclose (fp); } if (! recv) { fprintf (stderr, "No receiver found\n"); return NULL; } recv[rcnt] = NULL; for (n = 0; n < rcnt; n += 2) { ptr = recv[n]; if (((! isdigit (*ptr)) || (*ptr == ':')) && (*ptr != '\\')) { if (*ptr == ':') ++ptr; if (! (str = strdup (ptr))) { OOM; return NULL; } m = 0; tstr = 0; for (ptr = str; *ptr; ) { sav = ptr; ptr = skipch (ptr, ','); if ((! (tptr = cfg_block_get (cfg, SEC_ALIAS, sav, NULL))) || (! strchr (tptr, ','))) tptr = sav; len = strlen (tptr); if (tstr = Realloc (tstr, m + len + 2)) { if (m) tstr[m++] = ','; strcpy (tstr + m, tptr); m += len; } } free (str); free (recv[n]); if (! (recv[n] = tstr)) { OOM; return NULL; } } } mg = NULL; cnt = 0; siz = 0; fstdin = NULL; for (n = 0; n < rcnt; n += 2) if (str = recv[n]) { for (ptr = str; *ptr; ) { if (cnt >= siz) { siz += 4; if (! (mg = (message *) Realloc (mg, (siz + 1) * sizeof (message)))) { OOM; return NULL; } } mg[cnt].nr = cnt; sav = ptr; ptr = skipch (ptr, ','); if (*sav == '/') { ++sav; isalias = False; } else if (*sav == ':') { ++sav; isalias = True; } else if (! isdigit (*sav)) { rmv = cfg_get (cfg, NULL, CFG_RMPID, NULL); if (rmv && *sav && strchr (rmv, *sav)) isalias = False; else isalias = True; } else isalias = False; if (isalias) { if (! (mg[cnt].alias = strdup (sav))) { OOM; return NULL; } if (! (tstr = cfg_block_get (cfg, SEC_ALIAS, sav, NULL))) { fprintf (stderr, "No alias %s found\n", sav); return NULL; } else sav = tstr; } else mg[cnt].alias = NULL; mg[cnt].pid = strdup (sav); sav = recv[n + 1]; if (*sav == '+') { if (! (mg[cnt].msg = readfile (sav + 1))) { fprintf (stderr, "Unable to read %s, aborted\n", sav + 1); return NULL; } } else if ((*sav == '-') && (! *(sav + 1))) { if (! fstdin) if (! (fstdin = read_stdin ())) { fprintf (stderr, "Unable to read from stdin, aborted\n"); return NULL; } mg[cnt].msg = snew (fstdin -> str, fstdin -> len); } else if ((*sav == '.') && (! *(sav + 1))) mg[cnt].msg = snew (NULL, 0); else { if (*sav == '\\') ++sav; mg[cnt].msg = snewc (sav); } if (! (mg[cnt].pid && mg[cnt].msg)) { OOM; return NULL; } mg[cnt].callid = NULL; mg[cnt].service = NULL; mg[cnt].prot = Unknown; mg[cnt].s = NULL; memset (& mg[cnt].st, 0, sizeof (mg[cnt].st)); rmv = cfg_get (cfg, mg[cnt].service, CFG_RMPID, NULL); for (srun = sbase; srun; srun = srun -> next) if ((! service) || (! strcmp (srun -> name, service))) { if (rmv) remove_invalids (mg[cnt].pid, rmv); if (v_alidate (srun -> pchk, mg[cnt].pid, & start, & end)) break; } if (! srun) { fprintf (stderr, "No service for pager id %s found\n", mg[cnt].pid); return NULL; } if (! (mg[cnt].service = strdup (srun -> name))) { OOM; return NULL; } if (rplc = cfg_get (cfg, mg[cnt].service, CFG_CHPID, NULL)) { if (! strcmp (rplc, "-")) rplc = ""; else if (*rplc == '\\') ++rplc; if (! (mg[cnt].pid = do_replace (mg[cnt].pid, start, end - start, rplc, strlen (rplc)))) { OOM; return NULL; } } mg[cnt].s = srun; V (4, ("Found service %s for %s\n", mg[cnt].service, mg[cnt].pid)); ++cnt; } free (str); } free (recv); if (fstdin) sfree (fstdin); if (cnt > 1) qsort (mg, cnt, sizeof (message), mcompare); for (n = 0; n < cnt; ++n) { sr = mg[n].service; if ((mg[n].prot = getproto (cfg_get (cfg, sr, CFG_PROTOCOL, NULL))) == Unknown) { fprintf (stderr, "Unknown protocol for service %s\n", sr); return NULL; } if (! callid) ptr = cfg_get (cfg, sr, CFG_CALLID, NULL); else ptr = callid; if (ptr) if (! (mg[n].callid = strdup (ptr))) { OOM; return NULL; } else if (rmv = cfg_get (cfg, sr, CFG_RMCID, NULL)) remove_invalids (mg[n].callid, rmv); if (cfg_bget (cfg, sr, CFG_USECID, DEF_USECID)) { if (! mg[n].callid) { fprintf (stderr, "Need caller id for service %s\n", sr); return NULL; } } else if (mg[n].callid) { free (mg[n].callid); mg[n].callid = NULL; } if (mg[n].callid) { if (mg[n].s && mg[n].s -> cchk) if (! v_alidate (mg[n].s -> cchk, mg[n].callid, & start, & end)) { fprintf (stderr, "Call ID %s is invalid for service %s\n", mg[n].callid, mg[n].service); return NULL; } if (rplc = cfg_get (cfg, mg[n].service, CFG_CHCID, NULL)) { if (! strcmp (rplc, "-")) rplc = ""; else if (*rplc == '\\') ++rplc; if (! (mg[n].callid = do_replace (mg[n].callid, start, end - start, rplc, strlen (rplc)))) { OOM; return NULL; } } if (check (mg[n].callid, cfg_get (cfg, sr, CFG_CHKCID, NULL)) < 0) { fprintf (stderr, "Invalid caller id %s for service %s\n", mg[n].callid, sr); return NULL; } } if (check (mg[n].pid, cfg_get (cfg, sr, CFG_CHKPID, NULL)) < 0) { fprintf (stderr, "Invalid pager id %s for service %s\n", mg[n].pid, sr); return NULL; } if (force) lforce = force; else lforce = cfg_bget (cfg, sr, CFG_FORCE, False); if (delay && (! cfg_bget (cfg, sr, CFG_CANDELAY, False)) && (! lforce)) { fprintf (stderr, "Service %s is unable to delay a message\n", sr); return NULL; } if (expire && (! cfg_bget (cfg, sr, CFG_CANEXP, False)) && (! lforce)) { fprintf (stderr, "Service %s is unable to set expiration\n", sr); return NULL; } if (! rds) rds = cfg_bget (cfg, sr, CFG_DORDS, False); if (rds && (! cfg_bget (cfg, sr, CFG_CANRDS, False)) && (! lforce)) { fprintf (stderr, "Service %s is unable to report delivery status\n", sr); return NULL; } if (! sig) tsig = cfg_get (cfg, sr, CFG_SIG, NULL); else tsig = sig; if (tsig && (! cfg_bget (cfg, sr, CFG_USESIG, False))) tsig = NULL; if (tsig && *tsig) if (! (scatc (mg[n].msg, " ") && scatc (mg[n].msg, tsig))) { OOM; return NULL; } mg[n].s = NULL; } for (n = 0; n < cnt; ++n) { sr = mg[n].service; if (! ytrunc) ttrunc = cfg_bget (cfg, sr, CFG_TRUNCATE, False); else ttrunc = ytrunc; msize = cfg_iget (cfg, sr, CFG_MAXSIZE, 0); msplit = cfg_bget (cfg, sr, CFG_MAYSPLIT, False); if ((msize > 0) && (mg[n].msg -> len > msize)) if (ytrunc) mg[n].msg -> len = msize; else if (! msplit) { fprintf (stderr, "Unable to split message for service %s\n", sr); return NULL; } else { doins = (msize > 40) ? True : False; if (cnt >= siz) { siz += 4; if (! (mg = (message *) Realloc (mg, (siz + 1) * sizeof (message)))) { OOM; return NULL; } } for (m = cnt; m > n + 1; --m) mg[m] = mg[m - 1]; mg[n + 1].nr = mg[n].nr; mg[n + 1].callid = mg[n].callid ? strdup (mg[n].callid) : NULL; mg[n + 1].alias = mg[n].alias ? strdup (mg[n].alias) : NULL; mg[n + 1].pid = strdup (mg[n].pid); mg[n + 1].service = strdup (mg[n].service); mg[n + 1].prot = mg[n].prot; if (doins) rsize = msize - (sizeof (SPLIT_INIT) - 1); else rsize = msize; for (m = 1; (m < 15) && (m < rsize); ++m) if (sisspace (mg[n].msg, rsize - m)) { rsize -= m; sdel (mg[n].msg, rsize, 1); break; } if (doins) { if (mg[n + 1].msg = snewc (SPLIT_INIT)) if (tmp = scut (mg[n].msg, rsize, mg[n].msg -> len - rsize)) { if (! scat (mg[n + 1].msg, tmp)) mg[n + 1].msg = sfree (mg[n + 1].msg); else { mg[n].msg -> len = rsize; if (! scatc (mg[n].msg, SPLIT_TERM)) mg[n].msg = sfree (mg[n].msg); } sfree (tmp); } else mg[n + 1].msg = sfree (mg[n + 1].msg); } else { mg[n + 1].msg = scut (mg[n].msg, rsize, mg[n].msg -> len - rsize); mg[n].msg -> len = rsize; } mg[n + 1].s = NULL; memset (& mg[n + 1].st, 0, sizeof (mg[n + 1].st)); srelease (mg[n].msg); if (! (mg[n + 1].pid && mg[n + 1].msg && mg[n + 1].service && mg[n].msg)) { OOM; return NULL; } ++cnt; } } # ifndef NDEBUG if (verbose >= 4) { (*verbout) ("Sending following message%s:\n", (cnt == 1 ? "" : "s")); for (n = 0; n < cnt; ++n) if (mg[n].pid && mg[n].msg) (*verbout) ("%-12s (%s, %s): %s\n", (mg[n].alias ? mg[n].alias : mg[n].pid), mg[n].service, mg[n].pid, schar (mg[n].msg)); } # endif /* NDEBUG */ *mcnt = cnt; return mg; } /*}}}*/ /*{{{ calculate costs */ # define WDAY(cc1,cc2) ((((unsigned char) (cc1)) << 8) | ((unsigned char) (cc2))) # define DAY (60 * 24) typedef struct _ttable { int wday; int start, end; double elen; struct _ttable *next; } ttable; static char *vtab[] = { "fixed", "entity-length", "max-entities", "dial-overhead", "cost", "unit", "timetable", "remainder", NULL }; static int tonum (char ch) { switch (ch) { default: case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; } } static ttable * parse_timetable (char *str) { ttable *base, *prev, *tmp; char *ptr, *sav, *cptr; int n; base = NULL; prev = NULL; for (ptr = str; *ptr; ) { sav = ptr; ptr = skipch (ptr, ';'); cptr = skipch (sav, '='); if (tmp = (ttable *) malloc (sizeof (ttable))) { tmp -> wday = 0; tmp -> start = 0; tmp -> end = DAY - 1; tmp -> elen = atof (cptr); tmp -> next = NULL; while (*sav) if (isalpha (*sav) && isalpha (*(sav + 1))) { *sav = tolower (*sav); *(sav + 1) = tolower (*(sav + 1)); switch (WDAY (*sav, *(sav + 1))) { case WDAY ('s', 'o'): tmp -> wday |= (1 << 0); break; case WDAY ('m', 'o'): tmp -> wday |= (1 << 1); break; case WDAY ('t', 'u'): tmp -> wday |= (1 << 2); break; case WDAY ('w', 'e'): tmp -> wday |= (1 << 3); break; case WDAY ('t', 'h'): tmp -> wday |= (1 << 4); break; case WDAY ('f', 'r'): tmp -> wday |= (1 << 5); break; case WDAY ('s', 'a'): tmp -> wday |= (1 << 6); break; case WDAY ('w', 'k'): tmp -> wday |= (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5); break; case WDAY ('s', 's'): tmp -> wday |= (1 << 0) | (1 << 6); break; case WDAY ('a', 'l'): tmp -> wday |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); break; } sav += 2; } else break; while (*sav && (! isdigit (*sav))) if (*sav == '-') break; if (isdigit (*sav)) { n = 0; while (isdigit (*sav)) { n *= 10; n += tonum (*sav++); } tmp -> start = (n / 100) * 60 + n % 100; } while (*sav && (! isdigit (*sav))) ++sav; if (isdigit (*sav)) { n = 0; while (isdigit (*sav)) { n *= 10; n += tonum (*sav++); } tmp -> end = (n / 100) * 60 + n % 100; } if (prev) prev -> next = tmp; else base = tmp; prev = tmp; } } return base; } static inline Bool timetable_valid (ttable *t, int wday, int tim) { if (t -> wday & (1 << wday)) if (t -> end > t -> start) { if ((tim >= t -> start) && (tim <= t -> end)) return True; } else if ((tim <= t -> end) || (tim >= t -> start)) return True; return False; } static char * calc_cost (void *cfg, char *service, time_t start, int dur) { char *ret; char *tstr; char *ptr, *var, *val; int n; Bool fixed; double elen; int ment; int dover; double cost; char *unit; char *ttab; int remainder; ttable *tbase, *trun; struct tm *tt; double fdur; int cur, wday; double sec; int ents; double fcost; if ((! (tstr = cfg_get (cfg, service, CFG_COST, NULL))) || (! (tstr = strdup (tstr)))) return NULL; ret = NULL; fixed = False; elen = 0.0; ment = -1; dover = 0; cost = 0.0; unit = NULL; ttab = NULL; remainder = 2; for (ptr = tstr; *ptr; ) { var = ptr; ptr = skipch (ptr, ','); val = skipch (var, '='); for (n = 0; vtab[n]; ++n) if (! strcmp (vtab[n], var)) break; switch (n) { case 0: /* fixed */ fixed = True; break; case 1: /* entity-length */ elen = atof (val); break; case 2: /* max-entities */ ment = atoi (val); break; case 3: /* dial-overhead */ dover = atoi (val); break; case 4: /* cost */ cost = atof (val); break; case 5: /* unit */ unit = val; break; case 6: /* timetable */ ttab = val; break; case 7: /* remainder */ remainder = atoi (val); break; } } if (dover > 0) { start += dover; dur -= dover; if (dur < 1) dur = 1; } if (ttab && (tbase = parse_timetable (ttab))) { if (tt = localtime (& start)) { cur = tt -> tm_hour * 60 + tt -> tm_min; sec = (double) tt -> tm_sec; wday = tt -> tm_wday; ents = 0; fdur = 0; trun = NULL; while (fdur <= (double) dur) { if ((! trun) || (! timetable_valid (trun, wday, cur))) { for (trun = tbase; trun; trun = trun -> next) if (timetable_valid (trun, wday, cur)) break; if (! trun) trun = tbase; } if (fixed) { cost = trun -> elen; break; } if (trun -> elen < 0.1) break; ++ents; fdur += trun -> elen; sec += trun -> elen; while (sec >= 60.0) { sec -= 60.0; ++cur; while (cur >= DAY) { cur -= DAY; wday++; if (wday > 6) wday = 0; } } } } while (tbase) { trun = tbase; tbase = tbase -> next; free (trun); } } else ents = (int) ((double) dur / elen) + 1; if ((ment > 0) && (ents > ment)) ents = ment; if (fixed) fcost = cost; else fcost = cost * (double) ents; if (fcost > 0.01) { do_log (LG_COST, "Session costs aprox. %.*f %s", remainder, fcost, (unit ? unit : "")); if (ret = malloc (64 + (unit ? strlen (unit) : 0))) { sprintf (ret, "%.*f", remainder, fcost); if (unit) { for (ptr = ret; *ptr; ++ptr) ; *ptr++ = ' '; strcpy (ptr, unit); } } } free (tstr); return ret; } /*}}}*/ /*{{{ send pages */ static int sendit (void *cfg, int cur, message *mg, int mcnt, int *rerr, date_t *delay, date_t *expire, Bool rds) { char *sr; Bool err; int sig; int n, m; char *ptr; int cnt; int mmsgs; Bool inspid; char *convtab; void *ctab; message *send; int count; void *sp; char *modem, *reset; int tout; int (*transmit) (void *, void *, string_t *, message *, int, void *, char *, date_t *, date_t *, Bool); time_t start, end; int diff; string_t *cid; sr = mg[cur].service; mmsgs = cfg_iget (cfg, sr, CFG_MAXMSGS, 0); inspid = cfg_bget (cfg, sr, CFG_INSPID, DEF_INSPID); for (n = cur, cnt = 0; n < mcnt; ++n, ++cnt) { if (n != cur) if (strcmp (mg[n].service, sr)) break; else if ((mmsgs > 0) && (cnt == mmsgs)) break; else if (inspid && strcmp (mg[cur].pid, mg[n].pid)) break; mg[n].st.success = False; mg[n].st.reason = NotTried; mg[n].st.rmsg = NULL; } ctab = NULL; if (ctab = cv_new ()) { if (convtab = cfg_get (cfg, sr, CFG_CONVTAB, DEF_CONVTAB)) cv_read_table (ctab, convtab); extend_convtable (ctab, cfg, sr); } send = mg + cur; count = cnt; do_log (LG_SSESSION, "Starting up for service %s", sr); time (& start); err = 0; if (sp = do_dial (cfg, sr, (inspid ? mg[0].pid : NULL), & modem)) { for (n = 0; n < cnt; ++n) mg[cur + n].st.reason = NotSend; if (! (sig = setjmp (env))) { dojump = True; switch (mg[cur].prot) { default: case Unknown: transmit = NULL; break; case Ascii: transmit = asc_send; break; case Script: transmit = scr_send; break; case Tap: transmit = tap_send; break; case Ucp: transmit = ucp_send; break; } if (transmit) { cid = mg[0].callid ? snewc (mg[0].callid) : NULL; err = (*transmit) (sp, ctab, cid, send, count, cfg, sr, delay, expire, rds); sfree (cid); } else err = 1; tty_drain (sp, 1); } else V (2, ("\n")); dojump = False; for (n = 0, m = -1; n < cnt; ++n) if (mg[n + cur].st.success == True) m = n; if (m != -1) cnt = m + 1; tty_hangup (sp, 500); time (& end); diff = (int) (end - start); if (ptr = calc_cost (cfg, sr, start, diff)) if (! mg[cur].st.rmsg) mg[cur].st.rmsg = ptr; else free (ptr); do_log (LG_ESESSION, "Session %s on service %s (duration %d:%02d)", (err ? "failed" : "succeeded"), sr, diff / 60, diff % 60); reset = cfg_get (cfg, modem, CFG_RESET, DEF_RESET); if (reset) { tout = cfg_iget (cfg, modem, CFG_TIMEOUT, DEF_TIMEOUT); tty_send_expect (sp, tout, reset, NULL); } tty_close (sp); if (modem) free (modem); if ((sig == SIGTERM) || (sig == SIGQUIT)) longjmp (termenv, sig); } else { for (n = cur + 1; n < mcnt; ++n) if (strcmp (sr, mg[n].service)) break; cnt = n - cur; for (n = 0; n < cnt; ++n) mg[cur + n].st.reason = CantDial; do_log (LG_ESESSION, "Unable to dial service %s", sr); fprintf (stderr, "Unable to dial %s\n", sr); err = 1; } if (err) *rerr = err; return cur + cnt; } /*}}}*/ /*{{{ print configuration values */ static void print_config (void *cfg, char *service, char **vars) { int n; char *val; while (*vars) { printf ("%s ->", *vars); for (n = service ? 0 : 1; n < 2; ++n) { if (n && service) printf (" ("); else printf (" "); if (val = n ? cfg_get (cfg, NULL, *vars, NULL) : cfg_block_get (cfg, service, *vars, NULL)) printf ("%s", val); else printf ("*none*"); if (n && service) printf (")"); if (! n) printf (" ->"); else printf ("\n"); } ++vars; } } /*}}}*/ /*{{{ getopt */ # if ! HAVE_GETOPT && ! HAVE_GETOPT_LONG # if NEED_OPTIND_OPTARG # undef NEED_OPTIND_OPTARG # endif /* NEED_OPTIND_OPTARG */ static int optind = 1; static int optidx = 0; static char *optarg = NULL; static int getopt (int argc, char **argv, char *what) { char ch, *ptr; optarg = NULL; if (optind >= argc) return -1; do { if (! argv[optind][optidx]) { optidx = 0; ++optind; if (optind >= argc) return -1; } if (optidx == 0) if (argv[optind][0] != '-') return -1; else optidx = 1; } while (! optidx); if ((optidx == 1) && (argv[optind][optidx] == '-') && (! argv[optind][optidx + 1])) { ++optind; return -1; } ch = argv[optind][optidx++]; if (! (ptr = strchr (what, ch))) { fprintf (stderr, "%s: Unknown option -%c\n", argv[0] ? argv[0] : "", ch); return '?'; } ++ptr; if (*ptr == ':') if (argv[optind][optidx]) { optarg = (argv[optind]) + optidx; ++optind; optidx = 0; } else { ++optind; if (optind < argc) optarg = argv[optind++]; else { fprintf (stderr, "%s: Missiong parameter for option -%c\n", argv[0] ? argv[0] : "", ch); ch = ':'; } optidx = 0; } return ch; } # endif /* ! HAVE_GETOPT && ! HAVE_GETOPT_LONG */ /*}}}*/ /*{{{ usage/version */ static void usage (char *pgm) { # if HAVE_GETOPT_LONG # define PLONG(xxx) fprintf xxx # else /* HAVE_GETOPT_LONG */ # define PLONG(xxx) putc ('\t', stderr) # endif /* HAVE_GETOPT_LONG */ fprintf (stderr, "Usgae: %s [] { }+\n", pgm); fprintf (stderr, "Function: sends messages to paging devices\n"); fprintf (stderr, "Options:\n"); PLONG ((stderr, "\t--config=, ")); fprintf (stderr, "-C uses alternate global configurtion file\n"); PLONG ((stderr, "\t--service=, ")); fprintf (stderr, "-s use this service, if applicated\n"); PLONG ((stderr, "\t--truncate, ")); fprintf (stderr, "-t truncates too long messages\n"); PLONG ((stderr, "\t--call-id=, ")); fprintf (stderr, "-c if possible, use this caller id\n"); PLONG ((stderr, "\t--signature=, ")); fprintf (stderr, "-S add this signature to the message\n"); PLONG ((stderr, "\t--logfile=, ")); fprintf (stderr, "-l log activities to a file\n"); PLONG ((stderr, "\t--logstring=, ")), fprintf (stderr, "-L log only these activities\n"); PLONG ((stderr, "\t--force, ")); fprintf (stderr, "-f force sending, even if -d/-e/-r is not supported\n"); PLONG ((stderr, "\t--delay=, ")); fprintf (stderr, "-d when to send the message (if supported)\n"); PLONG ((stderr, "\t--expire=, ")); fprintf (stderr, "-e when should the mesage expire (if supported)\n"); PLONG ((stderr, "\t--request-delivery-status, ")); fprintf (stderr, "-r request delivery report (if supported)\n"); PLONG ((stderr, "\t--final-report=, ")); fprintf (stderr, "-R store the final report to \n"); PLONG ((stderr, "\t--message=file=, ")); fprintf (stderr, "-z read pager-id/message pairs from \n"); PLONG ((stderr, "\t--verbose[=], ")); fprintf (stderr, "-v increase verbosity\n"); fprintf (stderr, "\t this is the receiver of the message\n"); fprintf (stderr, "\t the message, if it is prefixed by a plus sign (+), then the remaining part\n"); fprintf (stderr, "\t is taken as a filename from where the message is read\n"); fprintf (stderr, "\n"); PLONG ((stderr, "\t--print-config, ")); fprintf (stderr, "-p print configuration variables/values\n"); fprintf (stderr, "\t the arguments are the variable names to be displayed\n"); PLONG ((stderr, "\t--version, ")); fprintf (stderr, "-V show version and compile informations\n"); # ifdef VERSION fprintf (stderr, "\nVersion: %s\n", VERSION); # endif /* VERSION */ # undef PLONG } static void show_version (char *pgm) { printf ("%s: V. %s\n", pgm ? pgm : "yaps", VERSION); printf ("Definitions:"); # if POSIX_SIGNAL printf (" POSIX_SIGNAL"); # endif /* POSIX_SIGNAL */ # if BSD_SIGNAL printf (" BSD_SIGNAL"); # endif /* BSD_SIGNAL */ # if SYSV_SIGNAL printf (" SYSV_SIGNAL"); # endif /* SYSV_SIGNAL */ # if SIG_VOID_RETURN printf (" SIG_VOID_RETURN"); # endif /* SIG_VOID_RETURN */ # if SIG_INT_RETURN printf (" SIG_INT_RETURN"); # endif /* SIG_INT_RETURN */ # if HAVE_SYS_SELECT_H printf (" HAVE_SYS_SELECT_H"); # endif /* HAVE_SYS_SELECT_H */ # if HAVE_LOCALE_H printf (" HAVE_LOCALE_H"); # endif /* HAVE_LOCALE_H */ # if HAVE_REGEX_H printf (" HAVE_REGEX_H"); # endif /* HAVE_REGEX_H */ # if HAVE_SYS_SYSMACROS_H printf (" HAVE_SYS_SYSMACROS_H"); # endif /* HAVE_SYS_SYSMACROS_H */ # if HAVE_SYS_MKDEV_H printf (" HAVE_SYS_MKDEV_H"); # endif /* HAVE_SYS_MKDEV_H */ # if HAVE_GETOPT_H printf (" HAVE_GETOPT_H"); # endif /* HAVE_GETOPT_H */ # if HAVE_TZSET printf (" HAVE_TZSET"); # endif /* HAVE_TZSET */ # if HAVE_FCHMOD printf (" HAVE_FCHMOD"); # endif /* HAVE_FCHMOD */ # if HAVE_FCHOWN printf (" HAVE_FCHOWN"); # endif /* HAVE_FCHOWN */ # if HAVE_SIGSETJMP printf (" HAVE_SIGSETJMP"); # endif /* HAVE_SIGSETJMP */ # if HAVE_MEMCPY printf (" HAVE_MEMCPY"); # endif /* HAVE_MEMCPY */ # if HAVE_BCOPY printf (" HAVE_BCOPY"); # endif /* HAVE_BCOPY */ # if HAVE_MEMSET printf (" HAVE_MEMSET"); # endif /* HAVE_MEMSET */ # if HAVE_BZERO printf (" HAVE_BZERO"); # endif /* HAVE_BZERO */ # if HAVE_GETOPT printf (" HAVE_GETOPT"); # endif /* HAVE_GETOPT */ # if HAVE_GETOPT_LONG printf (" HAVE_GETOPT_LONG"); # endif /* HAVE_GETOPT_LONG */ # if NEED_OPTIND_OPTARG printf (" NEED_OPTIND_OPTARG"); # endif /* NEED_OPTIND_OPTARG */ # if BROKEN_REALLOC printf (" BROKEN_REALLOC"); # endif /* BROKEN_REALLOC */ printf ("\n"); } /*}}}*/ /*{{{ main */ # if HAVE_GETOPT_LONG static struct option opttab[] = { { "config", required_argument, NULL, 'C' }, { "service", required_argument, NULL, 's' }, { "truncate", no_argument, NULL, 't' }, { "call-id", required_argument, NULL, 'c' }, { "signature", required_argument, NULL, 'S' }, { "logfile", required_argument, NULL, 'l' }, { "logstring", required_argument, NULL, 'L' }, { "force", no_argument, NULL, 'f' }, { "delay", required_argument, NULL, 'd' }, { "expire", required_argument, NULL, 'e' }, { "request-delivery-status", no_argument, NULL, 'r' }, { "final-report", required_argument, NULL, 'R' }, { "message-file", required_argument, NULL, 'z' }, { "verbose", optional_argument, NULL, 'v' }, { "print-config", no_argument, NULL, 'p' }, { "version", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0 } }; # define GETOPT(what) getopt_long (argc, argv, (what), opttab, NULL) # else /* HAVE_GETOPT_LONG */ # define GETOPT(what) getopt (argc, argv, (what)) # endif /* HAVE_GETOPT_LONG */ int main (int argc, char **argv) { # if NEED_OPTIND_OPTARG extern int optind; extern char *optarg; # endif /* NEED_OPTIND_OPTARG */ char *cfgfile; void *cfg; char *home, *lfn; int n, m; int ret; char *svcs; serv *sbase, *sprev, *stmp; char *chk; char *service; Bool ytrunc; char *callid; char *sig; Bool force; date_t *delay, *expire; Bool rds; char *freport; FILE *fp; char *mfile; Bool pcfg; date_t *cur; message *mg; int mcnt; int onr; Bool succ; char *ptr, *sav; # if POSIX_SIGNAL struct sigaction sact, sign; # endif /* POSIX_SIGNAL */ cfgfile = CFGFILE; service = NULL; ytrunc = False; callid = NULL; sig = NULL; force = False; delay = NULL; expire = NULL; rds = False; freport = NULL; mfile = NULL; pcfg = False; logfile = NULL; logstr = NULL; verbose = -1; # if HAVE_TZSET tzset (); # endif /* HAVE_TZSET */ # if HAVE_LOCALE_H setlocale (LC_ALL, ""); # endif /* HAVE_LOCALE_H */ while ((n = GETOPT ("C:s:tc:S:l:L:fd:e:rR:z:vpVh")) != -1) switch (n) { case 'C': cfgfile = optarg; break; case 's': service = optarg; break; case 't': ytrunc = True; break; case 'c': callid = optarg; break; case 'S': sig = optarg; break; case 'l': logfile = optarg; break; case 'L': logstr = optarg; break; case 'f': force = True; break; case 'd': if (delay) delay = dat_free (delay); if (! (delay = dat_parse (optarg))) { fprintf (stderr, "Invalid date format for delay\n"); return 1; } break; case 'e': if (expire) expire = dat_free (expire); if (! (expire = dat_parse (optarg))) { fprintf (stderr, "Invalid date format for expire\n"); return 1; } break; case 'r': rds = True; break; case 'R': freport = optarg; break; case 'z': mfile = optarg; break; case 'v': # if HAVE_GETOPT_LONG if (optarg) verbose = atoi (optarg); else # endif /* HAVE_GETOPT_LONG */ if (verbose < 0) verbose = 1; else ++verbose; break; case 'p': pcfg = True; break; case 'V': show_version (argv[0]); return 0; case 'h': default: usage (argv[0]); return n == 'h' ? 0 : 1; } if (delay || expire) { if (cur = dat_parse (NULL)) { if (delay && (dat_diff (cur, delay) < 0)) { fprintf (stderr, "Delay is before now, invalid\n"); return 1; } if (expire && (dat_diff (cur, expire) < 0)) { fprintf (stderr, "Expiration already reached\n"); return 1; } dat_free (cur); } if (delay && expire) if (dat_diff (delay, expire) < 0) { fprintf (stderr, "Message expires before it should be send\n"); return 1; } } cfg = cfg_read (cfgfile, NULL, CONFIG_SEP); if ((home = getenv ("HOME")) && (lfn = malloc (strlen (home) + sizeof (LCFGFILE) + 4))) { sprintf (lfn, "%s/%s", home, LCFGFILE); cfg = cfg_read (lfn, cfg, CONFIG_SEP); free (lfn); } if (pcfg) { print_config (cfg, service, argv + optind); return 0; } if (((optind == argc) && (! mfile)) || ((argc - optind) & 1)) { if ((optind == argc) && (! mfile)) fprintf (stderr, "No message\n"); if ((argc - optind) & 1) fprintf (stderr, "Unbalanced pagerid/message\n"); usage (argv[0]); return 1; } if (mfile && (access (mfile, R_OK) < 0)) { fprintf (stderr, "Unable to read message file %s\n", mfile); return 1; } if (verbose < 0) verbose = cfg_iget (cfg, NULL, CFG_VERBOSE, 0); if (! logfile) logfile = cfg_get (cfg, NULL, CFG_LOGFILE, NULL); if (! logstr) logstr = cfg_get (cfg, NULL, CFG_LOGSTR, NULL); if (! (svcs = cfg_get (cfg, NULL, CFG_SERVICES, NULL))) { fprintf (stderr, "No services definied\n"); return 1; } if (! (svcs = strdup (svcs))) { OOM; return 1; } sbase = NULL; sprev = NULL; for (ptr = svcs; *ptr; ) { sav = ptr; ptr = skipch (ptr, ','); if (! (chk = cfg_block_get (cfg, sav, CFG_VALPID, NULL))) { fprintf (stderr, "Service %s has no pattern for matching pager ids\n", sav); return 1; } if ((stmp = (serv *) malloc (sizeof (serv))) && (stmp -> name = strdup (sav))) { if (! (stmp -> pchk = v_new (chk))) { fprintf (stderr, "Pattern %s in service %s is not valid\n", chk, sav); return 1; } stmp -> cchk = NULL; if (chk = cfg_get (cfg, sav, CFG_VALCID, NULL)) if (! (stmp -> cchk = v_new (chk))) { fprintf (stderr, "Pattern %s in service %s not valid\n", chk, sav); return 1; } stmp -> next = NULL; if (sprev) sprev -> next = stmp; else sbase = stmp; sprev = stmp; } else { OOM; return 1; } } if (! sbase) { fprintf (stderr, "No services found\n"); return 1; } if (! (mg = create_messages (cfg, service, sbase, argv + optind, argc - optind, mfile, & mcnt, callid, sig, ytrunc, force, delay, expire, rds))) return 1; while (sbase) { stmp = sbase; sbase = sbase -> next; free (stmp -> name); v_free (stmp -> pchk); if (stmp -> cchk) v_free (stmp -> cchk); free (stmp); } jumpdone = False; if (! (ret = setjmp (termenv))) { dojump = False; # if POSIX_SIGNAL sact.sa_handler = handler; sign.sa_handler = SIG_IGN; sigemptyset (& sact.sa_mask); sigemptyset (& sign.sa_mask); sigaddset (& sact.sa_mask, SIGHUP); sigaddset (& sact.sa_mask, SIGINT); sigaddset (& sact.sa_mask, SIGQUIT); sigaddset (& sact.sa_mask, SIGTERM); sigaddset (& sact.sa_mask, SIGPIPE); sigaddset (& sign.sa_mask, SIGHUP); sigaddset (& sign.sa_mask, SIGINT); sigaddset (& sign.sa_mask, SIGQUIT); sigaddset (& sign.sa_mask, SIGTERM); sigaddset (& sign.sa_mask, SIGPIPE); sact.sa_flags = 0; sign.sa_flags = 0; sigaction (SIGHUP, & sact, NULL); sigaction (SIGINT, & sact, NULL); sigaction (SIGQUIT, & sact, NULL); sigaction (SIGTERM, & sact, NULL); sigaction (SIGPIPE, & sign, NULL); # else /* POSIX_SIGNAL */ signal (SIGHUP, handler); signal (SIGINT, handler); signal (SIGQUIT, handler); signal (SIGTERM, handler); # ifdef SIGPIPE signal (SIGPIPE, SIG_IGN); # endif /* SIGPIPE */ # endif /* POSIX_SIGNAL */ for (n = 0; n < mcnt; ) n = sendit (cfg, n, mg, mcnt, & ret, delay, expire, rds); } if (mg) { if (! freport) freport = cfg_get (cfg, NULL, CFG_FREPORT, NULL); fp = NULL; if (freport) if (! strcmp (freport, "-")) fp = stdout; else if (! (fp = fopen (freport, "w"))) { fprintf (stderr, "Unable to open %s for final report, sending it to stdout\n", freport); fp = stdout; } if (fp && (mcnt > 1)) qsort (mg, mcnt, sizeof (message), ncompare); for (m = 0, onr = -1; m < mcnt; ++m) { if (fp && (onr != mg[m].nr)) { onr = mg[m].nr; succ = True; for (n = m; (n < mcnt) && (mg[n].nr == onr); ++n) if (! (mg[n].st.success)) { succ = False; break; } ptr = NULL; if (! succ) { switch (mg[n].st.reason) { case NotTried: ptr = "unable to connect"; break; case CantDial: ptr = "could not dial service provider"; break; case NotSend: ptr = "unable to send"; break; case CantSend: ptr = "could not transmit the message"; break; } } fprintf (fp, "%c%d %s: %s %s\n", (succ ? '+' : '-'), mg[m].nr, (mg[m].alias ? mg[m].alias : (mg[m].pid ? mg[m].pid : "")), (mg[m].st.rmsg ? mg[m].st.rmsg : ""), (ptr ? ptr : "")); } if (mg[m].callid) free (mg[m].callid); if (mg[m].alias) free (mg[m].alias); if (mg[m].pid) free (mg[m].pid); if (mg[m].service) free (mg[m].service); if (mg[m].msg) sfree (mg[m].msg); if (mg[m].st.rmsg) free (mg[m].st.rmsg); } free (mg); if (fp && (fp != stdout)) fclose (fp); } cfg_end (cfg); return ret; } /*}}}*/