/* -*- mode: c; mode: fold -*- */ # include "config.h" # include # include # include # include "pager.h" /*{{{ typedefs */ typedef struct { # ifndef NDEBUG # define MAGIC MKMAGIC ('u', 'c', 'p', '\0') long magic; # endif /* NDEBUG */ void *sp; /* the serial connection */ void *ctab; /* the conversion table */ void (*logger) (char, char *, ...); char *uline; /* received input line */ string_t *callid; /* caller id */ Bool xtend; /* use extended UCP version */ int send_tout; /* timeout during sending */ int send_retry; /* # of retries to send a message */ int rds_tout; /* timeout for status report */ date_t delay; /* delay message */ date_t expire; /* expire message */ Bool rds; /* request delivery status */ int cnr; /* current transaction number */ } ucp; typedef struct { char *AdC; char *OAdC; char *PID; char *na; char *MT; char *Msg; char *adc; char *oadc; int pid; int mt; string_t *msg; } bsimple; typedef struct { char *AdC; char *OAdC; char *AC; char *NRq; char *NAdC; char *NT; char *NPID; char *na1; char *na2; char *na3; char *DD; char *DDT; char *VP; char *RPID; char *SCTS; char *DSt; char *Rsn; char *DSCTS; char *MT; char *NB; char *Msg; char *MMS; char *na4; char *DCS; char *MCL; char *RPI; char *na5; char *na6; char *res1; char *res2; char *res3; char *res4; char *res5; char *adc; char *oadc; char *ac; Bool nrq; char *nadc; int nt; int npid; Bool dd; date_t ddt; date_t vp; int rpid; date_t scts; int dst; int rsn; date_t dscts; int mt; int nb; string_t *msg; Bool mms; Bool dcs; int mcl; int rpi; } bextend; typedef struct { char *Res; char *MVP; char *EC; char *MSG; Bool ack; date_t mvp; int ec; char *adc; date_t scts; } banswer; typedef struct { int trn; int len; char ttyp; int ot; char *data; int cnt; union { bsimple s; bextend e; banswer a; } b; int chksum; } frame; /*}}}*/ /*{{{ support routines */ static char_t hex (int val) { switch (val) { default: case 0: return (char_t) '0'; case 1: return (char_t) '1'; case 2: return (char_t) '2'; case 3: return (char_t) '3'; case 4: return (char_t) '4'; case 5: return (char_t) '5'; case 6: return (char_t) '6'; case 7: return (char_t) '7'; case 8: return (char_t) '8'; case 9: return (char_t) '9'; case 10: return (char_t) 'A'; case 11: return (char_t) 'B'; case 12: return (char_t) 'C'; case 13: return (char_t) 'D'; case 14: return (char_t) 'E'; case 15: return (char_t) 'F'; } } static int unhex (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; case 'A': case 'a': return 10; case 'B': case 'b': return 11; case 'C': case 'c': return 12; case 'D': case 'd': return 13; case 'E': case 'e': return 14; case 'F': case 'f': return 15; } } /*}}}*/ /*{{{ frame basics */ static frame * new_frame (char ttyp, int ot) { frame *f; bsimple *s; bextend *e; banswer *a; if (f = (frame *) malloc (sizeof (frame))) { f -> trn = 0; f -> len = 0; f -> ttyp = ttyp; f -> ot = ot; f -> data = NULL; f -> cnt = 0; memset (& f -> b, 0, sizeof (f -> b)); f -> chksum = 0; if (f -> ttyp == 'R') switch (f -> ot) { case 1: case 31: case 51: case 52: case 53: case 55: case 56: case 57: case 58: a = & f -> b.a; a -> Res = NULL; a -> MVP = NULL; a -> EC = NULL; a -> MSG = NULL; a -> ack = False; dat_clear (& a -> mvp); a -> ec = -1; a -> adc = NULL; dat_clear (& a -> scts); break; } else if (f -> ttyp == 'O') switch (f -> ot) { case 1: case 31: s = & f -> b.s; s -> AdC = NULL; s -> OAdC = NULL; s -> PID = NULL; s -> na = NULL; s -> MT = NULL; s -> Msg = NULL; s -> adc = NULL; s -> oadc = NULL; s -> pid = -1; s -> mt = -1; s -> msg = NULL; break; case 51: case 52: case 53: case 55: case 56: case 57: case 58: e = & f -> b.e; e -> AdC = NULL; e -> OAdC = NULL; e -> AC = NULL; e -> NRq = NULL; e -> NAdC = NULL; e -> NT = NULL; e -> NPID = NULL; e -> na1 = NULL; e -> na2 = NULL; e -> na3 = NULL; e -> DD = NULL; e -> DDT = NULL; e -> VP = NULL; e -> RPID = NULL; e -> SCTS = NULL; e -> DSt = NULL; e -> Rsn = NULL; e -> DSCTS = NULL; e -> MT = NULL; e -> NB = NULL; e -> Msg = NULL; e -> MMS = NULL; e -> na4 = NULL; e -> DCS = NULL; e -> MCL = NULL; e -> RPI = NULL; e -> na5 = NULL; e -> na6 = NULL; e -> res1 = NULL; e -> res2 = NULL; e -> res3 = NULL; e -> res4 = NULL; e -> res5 = NULL; e -> adc = NULL; e -> oadc = NULL; e -> ac = NULL; e -> nrq = False; e -> nadc = NULL; e -> nt = -1; e -> npid = -1; e -> dd = False; dat_clear (& e -> ddt); dat_clear (& e -> vp); e -> rpid = -1; dat_clear (& e -> scts); e -> dst = -1; e -> rsn = -1; dat_clear (& e -> dscts); e -> mt = -1; e -> nb = -1; e -> msg = NULL; e -> mms = False; e -> dcs = False; e -> mcl = -1; e -> rpi = -1; break; } } return f; } static void free_body (frame *f) { # define dfr(xxx) if (xxx) free (xxx) bsimple *s; bextend *e; banswer *a; s = NULL; e = NULL; a = NULL; if (f -> ttyp == 'O') switch (f -> ot) { case 1: case 31: s = & f -> b.s; break; case 51: case 52: case 53: case 55: case 56: case 57: case 58: e = & f -> b.e; break; } else if (f -> ttyp == 'R') switch (f -> ot) { case 1: case 31: case 51: case 52: case 53: case 55: case 56: case 57: case 58: a = & f -> b.a; break; } if (s) { dfr (s -> AdC); dfr (s -> OAdC); dfr (s -> PID); dfr (s -> na); dfr (s -> MT); dfr (s -> Msg); dfr (s -> adc); dfr (s -> oadc); sfree (s -> msg); } else if (e) { dfr (e -> AdC); dfr (e -> OAdC); dfr (e -> AC); dfr (e -> NRq); dfr (e -> NAdC); dfr (e -> NT); dfr (e -> NPID); dfr (e -> na1); dfr (e -> na2); dfr (e -> na3); dfr (e -> DD); dfr (e -> DDT); dfr (e -> VP); dfr (e -> RPID); dfr (e -> SCTS); dfr (e -> DSt); dfr (e -> Rsn); dfr (e -> DSCTS); dfr (e -> MT); dfr (e -> NB); dfr (e -> Msg); dfr (e -> MMS); dfr (e -> na4); dfr (e -> DCS); dfr (e -> MCL); dfr (e -> RPI); dfr (e -> na5); dfr (e -> na6); dfr (e -> res1); dfr (e -> res2); dfr (e -> res3); dfr (e -> res4); dfr (e -> res5); dfr (e -> adc); dfr (e -> oadc); dfr (e -> ac); dfr (e -> nadc); sfree (e -> msg); } else if (a) { dfr (a -> Res); dfr (a -> MVP); dfr (a -> EC); dfr (a -> MSG); dfr (a -> adc); } # undef dfr } static void free_frame (frame *f) { if (f) { if (f -> data) free (f -> data); free_body (f); free (f); } } /*}}}*/ /*{{{ assemble */ static char * encode (ucp *u, string_t *s, Bool docv) { char *ret; char_t ch; int c; int len; int n, m; len = s ? (s -> len * 2) : 0; if (ret = malloc (len + 2)) { for (n = 0, m = 0; n < len; ++m) { if (docv) { if ((c = cv_conv (u -> ctab, s -> str[m])) < 0) continue; ch = (char_t) c; } else ch = s -> str[m]; ret[n++] = hex ((ch >> 4) & 0xf); ret[n++] = hex (ch & 0xf); } ret[n] = '\0'; } return ret; } static inline void gbool (char **ptr, Bool what, char *t, char *f) { if (*ptr) free (*ptr); if (what) *ptr = t ? strdup (t) : NULL; else *ptr = f ? strdup (f) : NULL; } static inline void gdate (char **ptr, date_t *d, int len) { if (*ptr) free (*ptr); if (d -> day > 0) { if (*ptr = malloc (14)) { sprintf (*ptr, "%02d%02d%02d%02d%02d%02d", d -> day, d -> mon, d -> year % 100, d -> hour, d -> min, d -> sec); if (len < 12) (*ptr)[len] = '\0'; } } else *ptr = NULL; } static inline void gint (char **ptr, int val, char *fmt, int def) { if (*ptr) free (*ptr); if (val == -1) val = def; if (val != -1) { if (*ptr = malloc (32)) sprintf (*ptr, (fmt ? fmt : "%d"), val); } else *ptr = NULL; } static inline void gstr (char **ptr, char *str) { if (*ptr) free (*ptr); *ptr = str ? strdup (str) : NULL; } static string_t * assemble_frame (ucp *u, frame *f) { bsimple *s; bextend *e; banswer *a; string_t *ret; Bool fail; char buf[64], *ptr; int len, n; unsigned long chk; if (! (ret = snew (NULL, 128))) return NULL; if (! (scopyc (ret, "\x02") && scatc (ret, "00/00000/"))) return sfree (ret); sprintf (buf, "%c/%02d/", f -> ttyp, f -> ot); if (! scatc (ret, buf)) return sfree (ret); fail = False; if (f -> ttyp == 'R') { a = & f -> b.a; switch (f -> ot) { default: fail = True; break; case 1: case 31: case 51: case 52: case 53: case 55: case 56: case 57: case 58: gbool (& a -> Res, a -> ack, "A", "N"); if (a -> MSG) { free (a -> MSG); a -> MSG = NULL; } if (a -> ack) { gdate (& a -> MVP, & a -> mvp, 10); ptr = NULL; gdate (& ptr, & a -> scts, 12); if (ptr) { if (a -> adc) { if (a -> MSG = malloc (strlen (ptr) + strlen (a -> adc) + 4)) sprintf (a -> MSG, "%s:%s", a -> adc, ptr); free (ptr); } else a -> MSG = ptr; } else if (a -> adc) a -> MSG = strdup (a -> adc); } else gint (& a -> EC, a -> ec, NULL, 3); if (((f -> ot == 1) || (f -> ot == 31)) && a -> ack) { if (! (scatc (ret, a -> Res) && scatc (ret, "/") && scatc (ret, a -> MSG) && scatc (ret, "/"))) fail = True; } else if (! (scatc (ret, a -> Res) && scatc (ret, "/") && scatc (ret, (a -> ack ? a -> MVP : a -> EC)) && scatc (ret, "/") && scatc (ret, a -> MSG) && scatc (ret, "/"))) fail = True; break; } } else if (f -> ttyp == 'O') { s = & f -> b.s; e = & f -> b.e; switch (f -> ot) { default: fail = True; break; case 1: gstr (& s -> AdC, s -> adc); gstr (& s -> OAdC, s -> oadc); gstr (& s -> na, NULL); gint (& s -> MT, s -> mt, NULL, 3); if (s -> Msg) free (s -> Msg); s -> Msg = encode (u, s -> msg, True); if (! (scatc (ret, s -> AdC) && scatc (ret, "/") && scatc (ret, s -> OAdC) && scatc (ret, "/") && scatc (ret, s -> na) && scatc (ret, "/") && scatc (ret, s -> MT) && scatc (ret, "/") && scatc (ret, s -> Msg) && scatc (ret, "/"))) fail = True; break; case 31: gstr (& s -> AdC, s -> adc); gint (& s -> PID, s -> pid, "%04d", -1); if (! (scatc (ret, s -> AdC) && scatc (ret, "/") && scatc (ret, s -> PID) && scatc (ret, "/"))) fail = True; break; case 51: case 52: case 53: case 55: case 56: case 57: case 58: gstr (& e -> AdC, e -> adc); gstr (& e -> OAdC, e -> oadc); gstr (& e -> AC, e -> ac); gbool (& e -> NRq, e -> nrq, "1", NULL); gstr (& e -> NAdC, (e -> nrq ? e -> nadc : NULL)); gint (& e -> NT, (e -> nrq ? e -> nt : -1), NULL, -1); gint (& e -> NPID, (e -> nrq ? e -> npid : -1), "%04d", -1); gstr (& e -> na1, NULL); gstr (& e -> na2, NULL); gstr (& e -> na3, NULL); gbool (& e -> DD, e -> dd, "1", NULL); if (e -> dd) gdate (& e -> DDT, & e -> ddt, 10); else gstr (& e -> DDT, NULL); gdate (& e -> VP, & e -> vp, 10); gint (& e -> RPID, e -> rpid, "%04d", -1); gdate (& e -> SCTS, & e -> scts, 12); gint (& e -> DSt, e -> dst, NULL, -1); gint (& e -> Rsn, e -> rsn, "%03d", -1); gdate (& e -> DSCTS, & e -> dscts, 12); gint (& e -> MT, e -> mt, NULL, 3); if (e -> mt == 4) if (e -> nb != -1) len = e -> nb; else len = e -> msg ? e -> msg -> len * 8 : 0; else len = -1; gint (& e -> NB, len, NULL, -1); if (e -> Msg) free (e -> Msg); e -> Msg = encode (u, e -> msg, (e -> mt == 4 ? False : True)); gbool (& e -> MMS, e -> mms, "1", NULL); gstr (& e -> na4, NULL); gbool (& e -> DCS, e -> dcs, "1", NULL); gint (& e -> MCL, e -> mcl, NULL, -1); gint (& e -> RPI, e -> rpi, NULL, -1); gstr (& e -> na5, NULL); gstr (& e -> na6, NULL); gstr (& e -> res1, NULL); gstr (& e -> res2, NULL); gstr (& e -> res3, NULL); gstr (& e -> res4, NULL); gstr (& e -> res5, NULL); if (! (scatc (ret, e -> AdC) && scatc (ret, "/") && scatc (ret, e -> OAdC) && scatc (ret, "/") && scatc (ret, e -> AC) && scatc (ret, "/") && scatc (ret, e -> NRq) && scatc (ret, "/") && scatc (ret, e -> NAdC) && scatc (ret, "/") && scatc (ret, e -> NT) && scatc (ret, "/") && scatc (ret, e -> NPID) && scatc (ret, "/") && scatc (ret, e -> na1) && scatc (ret, "/") && scatc (ret, e -> na2) && scatc (ret, "/") && scatc (ret, e -> na3) && scatc (ret, "/") && scatc (ret, e -> DD) && scatc (ret, "/") && scatc (ret, e -> DDT) && scatc (ret, "/") && scatc (ret, e -> VP) && scatc (ret, "/") && scatc (ret, e -> RPID) && scatc (ret, "/") && scatc (ret, e -> SCTS) && scatc (ret, "/") && scatc (ret, e -> DSt) && scatc (ret, "/") && scatc (ret, e -> Rsn) && scatc (ret, "/") && scatc (ret, e -> DSCTS) && scatc (ret, "/") && scatc (ret, e -> MT) && scatc (ret, "/") && scatc (ret, e -> NB) && scatc (ret, "/") && scatc (ret, e -> Msg) && scatc (ret, "/") && scatc (ret, e -> MMS) && scatc (ret, "/") && scatc (ret, e -> na4) && scatc (ret, "/") && scatc (ret, e -> DCS) && scatc (ret, "/") && scatc (ret, e -> MCL) && scatc (ret, "/") && scatc (ret, e -> RPI) && scatc (ret, "/") && scatc (ret, e -> na5) && scatc (ret, "/") && scatc (ret, e -> na6) && scatc (ret, "/") && scatc (ret, e -> res1) && scatc (ret, "/") && scatc (ret, e -> res2) && scatc (ret, "/") && scatc (ret, e -> res3) && scatc (ret, "/") && scatc (ret, e -> res4) && scatc (ret, "/") && scatc (ret, e -> res5) && scatc (ret, "/"))) fail = True; break; } } else fail = True; if (! fail) { len = ret -> len - 1 + 2; sprintf (buf, "%02d/%05d", f -> trn, len); if (sputc (ret, buf, 1, -1)) { for (n = 1, chk = 0; n < ret -> len; ++n) chk += ret -> str[n] & 0xff; if (sexpand (ret, ret -> len + 4)) { ret -> str[ret -> len++] = hex ((chk >> 4) & 0xf); ret -> str[ret -> len++] = hex (chk & 0xf); ret -> str[ret -> len++] = '\x03'; } } else fail = True; } if (fail) ret = sfree (ret); return ret; } /*}}}*/ /*{{{ parse */ static date_t parse_date (char *str) { date_t ret; int n; int val; time_t tim; struct tm *tt; ret.day = 0; ret.mon = 0; ret.year = 0; ret.hour = 0; ret.min = 0; ret.sec = 0; if (str) for (n = 0; (n < 6) && *str && *(str + 1); ++n) { val = unhex (*str) * 10 + unhex (*(str + 1)); str += 2; switch (n) { case 0: ret.day = val; break; case 1: ret.mon = val; break; case 2: ret.year = val; break; case 3: ret.hour = val; break; case 4: ret.min = val; break; case 5: ret.sec = val; break; } } time (& tim); if (tt = localtime (& tim)) ret.year += ((tt -> tm_year + 1900) / 100) * 100; return ret; } static string_t * parse_message (ucp *u, int mt, char *msg) { string_t *ret; int len; int n; char_t ch; void *ct; if ((mt != 4) && (u -> ctab)) ct = cv_reverse (u -> ctab); else ct = NULL; len = strlen (msg); if (ret = snew (NULL, len / 2 + 2)) for (n = 0; n + 1 < len; n += 2) { ch = (unhex (msg[n]) << 4) | unhex (msg[n + 1]); ret -> str[ret -> len++] = cv_conv (ct, ch); } if (ct) cv_free (ct); return ret; } static Bool parse_body (ucp *u, frame *f) { Bool ret; char *dat, *ptr, *sav; char **rev; Bool done, fail; int n; if (! (dat = strdup (f -> data))) return False; ret = False; done = False; fail = False; for (ptr = dat, n = 0; (! done) && (! fail) && ptr; ++n) { sav = ptr; if (ptr = strchr (ptr, '/')) *ptr++ = '\0'; rev = NULL; if (f -> ttyp == 'O') { switch (f -> ot) { case 1: switch (n) { case 0: rev = & f -> b.s.AdC; break; case 1: rev = & f -> b.s.OAdC; f -> b.s.adc = f -> b.s.AdC ? strdup (f -> b.s.AdC) : NULL; break; case 2: rev = & f -> b.s.na; f -> b.s.oadc = f -> b.s.OAdC ? strdup (f -> b.s.OAdC) : NULL; break; case 3: rev = & f -> b.s.MT; break; case 4: rev = & f -> b.s.Msg; f -> b.s.mt = f -> b.s.MT ? atoi (f -> b.s.MT) : -1; done = True; break; } break; case 31: switch (n) { case 0: rev = & f -> b.s.AdC; break; case 1: rev = & f -> b.s.PID; f -> b.s.adc = f -> b.s.AdC ? strdup (f -> b.s.AdC) : NULL; done = True; break; } break; case 51: case 52: case 53: case 55: case 56: case 57: case 58: switch (n) { case 0: rev = & f -> b.e.AdC; break; case 1: rev = & f -> b.e.OAdC; f -> b.e.adc = f -> b.e.AdC ? strdup (f -> b.e.AdC) : NULL; break; case 2: rev = & f -> b.e.AC; f -> b.e.oadc = f -> b.e.OAdC ? strdup (f -> b.e.OAdC) : NULL; break; case 3: rev = & f -> b.e.NRq; f -> b.e.ac = f -> b.e.AC ? strdup (f -> b.e.AC) : NULL; break; case 4: rev = & f -> b.e.NAdC; if (f -> b.e.NRq) f -> b.e.nrq = (atoi (f -> b.e.NRq) == 1 ? True : False); else f -> b.e.nrq = False; break; case 5: rev = & f -> b.e.NT; f -> b.e.nadc = f -> b.e.NAdC ? strdup (f -> b.e.NAdC) : NULL; break; case 6: rev = & f -> b.e.NPID; f -> b.e.nt = f -> b.e.NT ? atoi (f -> b.e.NT) : 0; break; case 7: rev = & f -> b.e.na1; f -> b.e.npid = f -> b.e.NPID ? atoi (f -> b.e.NPID) : -1; break; case 8: rev = & f -> b.e.na2; break; case 9: rev = & f -> b.e.na3; break; case 10: rev = & f -> b.e.DD; break; case 11: rev = & f -> b.e.DDT; if (f -> b.e.DD) f -> b.e.dd = (atoi (f -> b.e.DD) == 1 ? True : False); else f -> b.e.dd = False; break; case 12: rev = & f -> b.e.VP; f -> b.e.ddt = parse_date (f -> b.e.DDT); break; case 13: rev = & f -> b.e.RPID; f -> b.e.vp = parse_date (f -> b.e.VP); break; case 14: rev = & f -> b.e.SCTS; f -> b.e.rpid = f -> b.e.RPID ? atoi (f -> b.e.RPID) : -1; break; case 15: rev = & f -> b.e.DSt; f -> b.e.scts = parse_date (f -> b.e.SCTS); break; case 16: rev = & f -> b.e.Rsn; f -> b.e.dst = f -> b.e.DSt ? atoi (f -> b.e.DSt) : -1; break; case 17: rev = & f -> b.e.DSCTS; f -> b.e.rsn = f -> b.e.Rsn ? atoi (f -> b.e.Rsn) : 0; break; case 18: rev = & f -> b.e.MT; f -> b.e.dscts = parse_date (f -> b.e.DSCTS); break; case 19: rev = & f -> b.e.NB; f -> b.e.mt = f -> b.e.MT ? atoi (f -> b.e.MT) : -1; break; case 20: rev = & f -> b.e.Msg; f -> b.e.nb = f -> b.e.NB ? atoi (f -> b.e.NB) : 0; break; case 21: rev = & f -> b.e.MMS; if (f -> b.e.Msg) if (! (f -> b.e.msg = parse_message (u, f -> b.e.mt, f -> b.e.Msg))) fail = True; break; case 22: rev = & f -> b.e.na4; if (f -> b.e.MMS) f -> b.e.mms = (atoi (f -> b.e.MMS) == 1 ? True : False); else f -> b.e.mms = False; break; case 23: rev = & f -> b.e.DCS; break; case 24: rev = & f -> b.e.MCL; if (f -> b.e.DCS) f -> b.e.dcs = (atoi (f -> b.e.DCS) == 1 ? True : False); else f -> b.e.dcs = False; break; case 25: rev = & f -> b.e.RPI; f -> b.e.mcl = f -> b.e.MCL ? atoi (f -> b.e.MCL) : -1; break; case 26: rev = & f -> b.e.na5; f -> b.e.rpi = f -> b.e.RPI ? atoi (f -> b.e.RPI) : -1; break; case 27: rev = & f -> b.e.na6; break; case 28: rev = & f -> b.e.res1; break; case 29: rev = & f -> b.e.res2; break; case 30: rev = & f -> b.e.res3; break; case 31: rev = & f -> b.e.res4; break; case 32: rev = & f -> b.e.res5; done = True; break; } break; } } else if (f -> ttyp == 'R') { switch (f -> ot) { case 1: case 31: switch (n) { case 0: rev = & f -> b.a.Res; break; case 1: if (f -> b.a.Res) { if (f -> b.a.Res[0] == 'A') { f -> b.a.ack = True; rev = & f -> b.a.MSG; done = True; } else if (f -> b.a.Res[0] == 'N') { f -> b.a.ack = False; rev = & f -> b.a.EC; } else fail = True; } break; case 2: if (! f -> b.a.ack) { f -> b.a.ec = f -> b.a.EC ? atoi (f -> b.a.EC) : -1; rev = & f -> b.a.MSG; done = True; } break; } break; case 51: case 52: case 53: case 55: case 56: case 57: case 58: switch (n) { case 0: rev = & f -> b.a.Res; break; case 1: if (f -> b.a.Res) { if (f -> b.a.Res[0] == 'A') { f -> b.a.ack = True; rev = & f -> b.a.MVP; } else if (f -> b.a.Res[0] == 'N') { f -> b.a.ack = False; rev = & f -> b.a.EC; } else fail = True; } else fail = True; break; case 2: if (f -> b.a.ack) f -> b.a.mvp = parse_date (f -> b.a.MVP); else f -> b.a.ec = f -> b.a.EC ? atoi (f -> b.a.EC) : -1; rev = & f -> b.a.MSG; done = True; break; } break; } } else fail = True; if (! fail) { if (! rev) fail = True; else { if (*sav) { if (! (*rev = strdup (sav))) fail = True; } else *rev = NULL; } if ((! ptr) && (! done)) fail = True; } } if (done && (! fail) && (! ptr)) { if (f -> ttyp == 'O') { switch (f -> ot) { case 1: case 31: if (f -> b.s.Msg) if (! (f -> b.s.msg = parse_message (u, f -> b.s.mt, f -> b.s.Msg))) fail = True; break; } } else if (f -> ttyp == 'R') { switch (f -> ot) { case 1: case 31: case 51: case 52: case 53: case 55: case 56: case 57: case 58: if (f -> b.a.MSG) { if (! (f -> b.a.adc = strdup (f -> b.a.MSG))) fail = True; else { if (ptr = strchr (f -> b.a.adc, ':')) *ptr++ = '\0'; f -> b.a.scts = parse_date (ptr); } } break; } } else fail = True; if (! fail) ret = True; } free (dat); return ret; } static frame * parse_frame (ucp *u, char *str) { frame *f; char *start, *end; char *ptr, *sav; int n, len; unsigned int chksum; f = NULL; if (str = strdup (str)) { start = strchr (str, '\x02'); end = strchr (str, '\x03'); ptr = strrchr (str, '/'); if (start && end && (end > start) && ptr && (start + 1 < ptr) && (ptr + 3 == end) && (f = new_frame ('\0', 0))) { memset (f, 0, sizeof (frame)); len = (int) ((unsigned long) end - (unsigned long) start) - 1; for (chksum = 0, n = 1; n < len - 1; ++n) chksum += (unsigned char) start[n]; chksum &= 0xff; ptr = start + 1; for (n = 0; n < 4; ++n) { if (! (sav = ptr)) break; if (ptr = strchr (ptr, '/')) *ptr++ = '\0'; switch (n) { case 0: f -> trn = atoi (sav); break; case 1: f -> len = atoi (sav); break; case 2: f -> ttyp = *sav; break; case 3: f -> ot = atoi (sav); break; } } sav = ptr; if ((n < 4) || (f -> len != len) || (! sav) || (! (ptr = strrchr (sav, '/')))) { free (f); f = NULL; } else { f -> cnt = (int) ((unsigned long) ptr - (unsigned long) sav); ++ptr; if (*ptr && *(ptr + 1)) f -> chksum = (unhex (*ptr) << 4) | unhex (*(ptr + 1)); else f -> chksum = -1; if ((chksum == f -> chksum) && (f -> data = malloc (f -> cnt + 1))) { memcpy (f -> data, sav, f -> cnt); f -> data[f -> cnt] = '\0'; if (! parse_body (u, f)) { free_frame (f); f = NULL; } } else { free (f); f = NULL; } } } free (str); } return f; } /*}}}*/ /*{{{ convert */ static string_t * convert_ucp (ucp *u, Bool last, string_t *pagerid, string_t *msg) { string_t *ret; frame *f; int ot; int n; int ch; char *adc, *oadc; ret = NULL; if (! u -> xtend) ot = 1; else ot = 51; if (f = new_frame ('O', ot)) { f -> trn = u -> cnr++; adc = sextract (pagerid); oadc = u -> callid ? sextract (u -> callid) : NULL; if (msg) msg = snew (msg -> str, msg -> len); if (u -> xtend) { f -> b.e.adc = adc; f -> b.e.oadc = oadc; if (u -> rds && (u -> delay.day <= 0)) { f -> b.e.nrq = True; f -> b.e.nt = 7; } else f -> b.e.nrq = False; if (u -> delay.day > 0) { f -> b.e.dd = True; f -> b.e.ddt = u -> delay; } else f -> b.e.dd = False; if (u -> expire.day > 0) f -> b.e.vp = u -> expire; for (n = 0; n < msg -> len; ++n) if ((ch = cv_conv (u -> ctab, msg -> str[n])) != -1) if (ch & 0x80) break; if (n < msg -> len) { f -> b.e.mt = 4; f -> b.e.nb = msg -> len * 8; } else f -> b.e.mt = 3; f -> b.e.msg = msg; } else { f -> b.s.adc = adc; f -> b.s.oadc = oadc; f -> b.s.mt = 3; f -> b.s.msg = msg; } ret = assemble_frame (u, f); free_frame (f); } return ret; } /*}}}*/ /*{{{ callback interface */ static void grabline (void *sp, string_t *s, char_t sep, void *data) { ucp *u = (ucp *) data; char *str; MCHK (u); if (u) { if (u -> uline) { free (u -> uline); u -> uline = NULL; } if (str = sextract (s)) { if (u -> uline = malloc (strlen (str) + 4)) sprintf (u -> uline, "%s%c", str, (char) sep); free (str); } } } /*}}}*/ /*{{{ interpret originate message */ static void interpret_originate (ucp *u, frame *f) { string_t *msg; msg = NULL; switch (f -> ot) { case 1: case 31: msg = f -> b.s.msg; break; case 51: case 52: case 53: case 55: case 56: case 57: case 58: msg = f -> b.e.msg; break; } if (msg) V (1, ("Got message type %d: `%s'\n", f -> ot, schar (msg))); } /*}}}*/ /*{{{ send a message */ static Bool send_msg (ucp *u, string_t *msg, Bool wds, int *err) { Bool ret; Bool dosend; int n; int ep; frame *f, *ans; banswer *an; string_t *astr; char *ptr; ret = False; dosend = True; for (n = 0; n < 2; ++n) { if ((! n) && dosend) { dosend = False; if (tty_send (u -> sp, msg -> str, msg -> len) != msg -> len) { V (1, ("Unable to send message\n")); *err = ERR_FAIL; break; } } if (n && (! wds)) continue; ep = tty_expect (u -> sp, (n ? u -> rds_tout : u -> send_tout), "\x03", 1, NULL); if ((ep != 1) || (! u -> uline)) { if (ep < 0) *err = ERR_FAIL; break; } if (f = parse_frame (u, u -> uline)) { switch (f -> ttyp) { case 'R': if (f -> b.a.ack) { V (1, ("Message spooled\n")); ret = True; break; } else { switch (f -> b.a.ec) { default: ptr = "unknown error"; break; case 1: ptr = "checksum error"; break; case 2: ptr = "syntax error"; break; case 3: ptr = "operation not supported by system"; break; case 4: ptr = "operation not allowed (at this point)"; break; case 5: ptr = "call barring active"; break; case 6: ptr = "AdC invalid"; break; case 7: ptr = "authentication failure"; break; case 8: ptr = "legitimisation code for all calls, failure"; break; case 9: ptr = "GA not valid"; break; case 10: ptr = "repetition not allowed"; break; case 11: ptr = "legitimisation code for repetition, failure"; break; case 12: ptr = "priority call not allowed"; break; case 13: ptr = "legitimisation code for priority, failure"; break; case 14: ptr = "urgent message not allowed"; break; case 15: ptr = "legitimisation code for urgent message, failure"; break; case 16: ptr = "reveerse charging not allowed"; break; case 17: ptr = "legitimisation code for reverse charging, failure"; break; case 18: ptr = "deferred delivery not allowed"; break; case 19: ptr = "new AC not valid"; break; case 20: ptr = "new legitimisation code not valid"; break; case 21: ptr = "standard text not valid"; break; case 22: ptr = "time period not valid"; break; case 23: ptr = "message type not supported by system"; break; case 24: ptr = "message too long"; break; case 25: ptr = "requested standard text not valid"; break; case 26: ptr = "message type not valid for pager type"; break; case 27: ptr = "message not found in SMSC"; break; case 30: ptr = "subscriber hang-up"; break; case 31: ptr = "fax group not supported"; break; case 32: ptr = "fax message type not supported"; break; case 33: ptr = "address already in list (60 series)"; break; case 34: ptr = "address not in list (60 series)"; break; case 35: ptr = "list full (60 series)"; break; } V (1, ("Invalid message (error %d) %s\n", f -> b.a.ec, (ptr ? ptr : ""))); switch (f -> b.a.ec) { case -1: case 1: case 2: case 3: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 31: case 32: case 33: case 34: case 35: *err = ERR_FAIL; break; default: *err = ERR_FATAL; break; } } break; case 'O': if (n && wds && (f -> ot == 53)) { if (u -> logger) { switch (f -> b.e.dst) { default: ptr = NULL; break; case 0: ptr = "delivered"; break; case 1: ptr = "buffered"; break; } if (ptr) (*u -> logger) (LG_PROTO, "UCP: message %s", ptr); } printf ("Message "); switch (f -> b.e.dst) { case 0: printf ("delivered"); break; case 1: printf ("buffered"); break; case 2: printf ("not delivered"); break; default: printf ("failed"); break; } printf (" (%d)", f -> b.e.rsn); switch (f -> b.e.rsn) { default: ptr = "unknown error"; break; case 0x01: ptr = "successful delivered"; break; case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: ptr = "temporary no service"; break; case 0x09: ptr = "unknown service"; break; case 0x0a: ptr = "network timeout"; break; case 0x32: ptr = "storing time expired"; break; case 0x64: ptr = "service not supported"; break; case 0x65: ptr = "receiver unknown"; break; case 0x66: ptr = "service not available"; break; case 0x67: ptr = "call locked"; break; case 0x68: ptr = "operation locked"; break; case 0x69: ptr = "service center overrun"; break; case 0x6a: ptr = "service not supported"; break; case 0x6b: ptr = "receiver temporary not reachable"; break; case 0x6c: ptr = "delivering error"; break; case 0x6d: ptr = "receiver run out of memory"; break; case 0x6e: ptr = "protocol error"; break; case 0x6f: ptr = "receiver does not support service"; break; case 0x70: ptr = "unknown serice center"; break; case 0x71: ptr = "service center overrun"; break; case 0x72: ptr = "illegal receiving device"; break; case 0x73: ptr = "receiver no customer"; break; case 0x74: ptr = "error in receiving device"; break; case 0x75: ptr = "lower protocol not available"; break; case 0x76: ptr = "system error"; break; case 0x77: ptr = "PLMN system error"; break; case 0x78: ptr = "HLR system error"; break; case 0x79: ptr = "VLR system error"; break; case 0x7a: ptr = "previous VLR system error"; break; case 0x7b: ptr = "error on delivering (check receiver ID)"; break; case 0x7c: ptr = "VMSC system error"; break; case 0x7d: ptr = "EIR system error"; break; case 0x7e: ptr = "system error"; break; case 0x7f: ptr = "unexpected data"; break; case 0xc8: ptr = "addressing error for service center"; break; case 0xc9: ptr = "invalid absolute storing time"; break; case 0xca: ptr = "message too large"; break; case 0xcb: ptr = "GDM message cannot be extracted"; break; case 0xcc: ptr = "translation into IA5 not possible"; break; case 0xcd: ptr = "invalid format of storing time"; break; case 0xce: ptr = "invalid receiver address"; break; case 0xcf: ptr = "message sent twice"; break; case 0xd0: ptr = "invalid message type"; break; } if (ptr) printf (" reason is %s", ptr); if (f -> b.e.msg) printf (": %s", schar (f -> b.e.msg)); printf ("\n"); } else { if (! n) --n; interpret_originate (u, f); } if (ans = new_frame ('R', f -> ot)) { ans -> trn = f -> trn; an = & ans -> b.a; an -> adc = u -> callid ? sextract (u -> callid) : NULL; dat_localtime (& an -> scts); switch (f -> ot) { case 1: an -> ack = False; an -> ec = 23; break; case 31: an -> ack = True; dosend = True; break; case 51: an -> ack = False; an -> ec = 23; break; case 52: an -> ack = True; break; case 53: an -> ack = True; break; case 55: an -> ack = False; an -> ec = 23; break; case 56: an -> ack = False; an -> ec = 23; break; case 57: an -> ack = True; break; case 58: an -> ack = True; break; } if (astr = assemble_frame (u, ans)) { if (tty_send (u -> sp, astr -> str, astr -> len) != astr -> len) { V (1, ("Unable to send answer\n")); *err = ERR_FAIL; } sfree (astr); } free_frame (ans); } break; } free_frame (f); if (*err != NO_ERR) break; } } return ret; } /*}}}*/ /*{{{ login/logout/transmit */ int ucp_login (void *up, string_t *callid) { ucp *u = (ucp *) up; MCHK (u); if ((! u) || (! u -> sp)) return ERR_ABORT; u -> cnr = 0; u -> callid = sfree (u -> callid); if (callid && (! (u -> callid = snew (callid -> str, callid -> len)))) return ERR_ABORT; return NO_ERR; } int ucp_logout (void *up) { MCHK ((ucp *) up); return NO_ERR; } int ucp_transmit (void *up, string_t *pagerid, string_t *msg, Bool last) { ucp *u = (ucp *) up; int n, err; Bool done; string_t *smsg; MCHK (u); if ((! u) || (! u -> sp)) return ERR_FATAL; err = NO_ERR; tty_set_line_callback (u -> sp, grabline, "\x03", (void *) u); for (n = 0; n < u -> send_retry; ++n) if (smsg = convert_ucp (u, last, pagerid, msg)) { done = send_msg (u, smsg, (u -> delay.day > 0 ? False : u -> rds), & err); sfree (smsg); if (done || (err != NO_ERR)) break; } if ((n == u -> send_retry) || (err != NO_ERR)) { if (err == NO_ERR) err = ERR_FAIL; V (1, ("Unable to send message\n")); } tty_set_line_callback (u -> sp, NULL, NULL, NULL); if (u -> uline) { free (u -> uline); u -> uline = NULL; } return err; } /*}}}*/ /*{{{ configuration */ void ucp_config (void *up, void (*logger) (char, char *, ...), Bool xtend, int stout, int retry, int rtout, date_t *delay, date_t *expire, Bool rds) { ucp *u = (ucp *) up; MCHK (u); if (u) { u -> logger = logger; u -> xtend = xtend; if (u -> xtend) u -> rds = rds; else { u -> rds = False; dat_clear (& u -> delay); dat_clear (& u -> expire); } if (stout >= 0) u -> send_tout = stout; if (retry >= 0) u -> send_retry = retry; if (rtout >= 0) u -> rds_tout = rtout; if (u -> xtend) { if (delay) u -> delay = *delay; if (expire) u -> expire = *expire; } } } void ucp_set_convtable (void *up, void *ctab) { ucp *u = (ucp *) up; MCHK (u); if (u) { if (u -> ctab) cv_free (u -> ctab); u -> ctab = ctab; } } void ucp_add_convtable (void *up, void *ctab) { ucp *u = (ucp *) up; MCHK (u); if (u) { if (! u -> ctab) u -> ctab = cv_new (); if (u -> ctab) cv_merge (u -> ctab, ctab, True); } } /*}}}*/ /*{{{ new/free/etc */ void * ucp_new (void *sp) { ucp *u; if (u = (ucp *) malloc (sizeof (ucp))) { # ifndef NDEBUG u -> magic = MAGIC; # endif /* NDEBUG */ u -> sp = sp; u -> ctab = NULL; u -> logger = NULL; u -> uline = NULL; u -> callid = NULL; u -> xtend = False; u -> send_tout = 60; u -> send_retry = 3; u -> rds_tout = 40; dat_clear (& u -> delay); dat_clear (& u -> expire); u -> rds = False; u -> cnr = 0; } return (void *) u; } void * ucp_free (void *up) { ucp *u = (ucp *) up; MCHK (u); if (u) { if (u -> ctab) cv_free (u -> ctab); if (u -> uline) free (u -> uline); if (u -> callid) sfree (u -> callid); free (u); } return NULL; } int ucp_preinit (void) { return 0; } void ucp_postdeinit (void) { } /*}}}*/