changed def paclen in client.c to 236
[spider.git] / src / client.c
1 /*
2  * C Client for the DX Spider cluster program
3  *
4  * Eventually this program will be a complete replacement
5  * for the perl version.
6  *
7  * This program provides the glue necessary to talk between
8  * an input (eg from telnet or ax25) and the perl DXSpider
9  * node.
10  *
11  * Currently, this program connects STDIN/STDOUT to the
12  * message system used by cluster.pl
13  *
14  * Copyright (c) 2000 Dirk Koopman G1TLH
15  *
16  * $Id$
17  */
18
19 #include <stdio.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <netdb.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <string.h>
32 #include <termios.h>
33 #include <regex.h>
34
35 #include "sel.h"
36 #include "cmsg.h"
37 #include "debug.h"
38
39 #define TEXT 1
40 #define MSG 2
41 #define MAXBUFL 1024
42
43 #ifndef MAXPATHLEN 
44 #define MAXPATHLEN 256
45 #endif
46
47 #define DEFPACLEN 236
48 #define MAXPACLEN 236
49 #define MAXCALLSIGN 9
50
51 #define DBUF 1
52 #define DMSG 2
53 #define DSTS 4
54
55 #define UC unsigned char
56
57 typedef struct 
58 {
59         int cnum;                                       /* the connection number */
60         int sort;                                       /* the type of connection either text or msg */
61         cmsg_t *in;                                     /* current input message being built up */
62         cmsg_t *out;                            /* current output message being sent */
63         cmsg_t *obuf;                           /* current output being buffered */
64         reft *inq;                                      /* input queue */
65         reft *outq;                                     /* output queue */
66         sel_t *sp;                                      /* my select fcb address */
67         struct termios t;                       /* any termios associated with this cnum */
68         char echo;                                      /* echo characters back to this cnum */
69         char t_set;                                     /* the termios structure is valid */
70         char buffer_it;                         /* buffer outgoing packets for paclen */
71 } fcb_t;
72
73 typedef struct 
74 {
75         char *in;
76         regex_t *regex;
77 } myregex_t;
78
79
80 char *node_addr = "localhost";  /* the node tcp address, can be overridden by DXSPIDER_HOST */
81 int node_port = 27754;                  /* the tcp port of the node at the above address can be overidden by DXSPIDER_PORT*/
82 char *call;                                             /* the caller's callsign */
83 char *connsort;                                 /* the type of connection */
84 fcb_t *in;                                              /* the fcb of 'stdin' that I shall use */
85 fcb_t *node;                                    /* the fcb of the msg system */
86 char nl = '\n';                                 /* line end character */
87 char mode = 1;                  /* 0 - ax25, 1 - normal telnet, 2 - nlonly telnet */
88 char ending = 0;                                /* set this to end the program */
89 char echo = 1;                                  /* echo characters on stdout from stdin */
90 char int_tabs = 0;                              /* interpret tabs -> spaces */
91 char *root = "/spider";         /* root of data tree, can be overridden by DXSPIDER_ROOT  */
92 int timeout = 60;                               /* default timeout for logins and things */
93 int paclen = DEFPACLEN;                 /* default buffer size for outgoing packets */
94 int tabsize = 8;                                /* default tabsize for text messages */
95 char *connsort = "local";               /* the connection variety */
96 int state = 0;                                  /* the current state of the connection */
97 int laststate = 0;                              /* the last state we were in */
98
99
100
101 #define CONNECTED 100
102 #define WAITLOGIN 1
103 #define WAITPASSWD 2
104 #define WAITINPUT 10
105
106
107 myregex_t iscallreg[] = {               /* regexes to determine whether this is a reasonable callsign */
108         {
109                 "^[A-Z]+[0-9]+[A-Z]+[1-9]?$", 0                /* G1TLH G1TLH1 */
110         },
111         {
112                 "^[0-9]+[A-Z]+[0-9]+[A-Z]+[1-9]?$", 0          /* 2E0AAA 2E0AAA1 */
113         },
114         {
115                 "^[A-Z]+[0-9]+[A-Z]+-[1-9]$", 0                /* G1TLH-2 */
116         },
117         {
118                 "^[0-9]+[A-Z]+[0-9]+[A-Z]+-[1-9]$", 0          /* 2E0AAA-2 */
119         },
120         {
121                 "^[A-Z]+[0-9]+[A-Z]+-1[0-5]$", 0               /* G1TLH-11 */
122         },
123         {
124                 "^[0-9]+[A-Z]+[0-9]+[A-Z]+-1[0-5]$", 0         /* 2E0AAA-11 */
125         },
126         {
127                 0, 0
128         }
129 };
130
131 void terminate(int);
132
133 /*
134  * utility routines - various
135  */
136
137 void chgstate(int new)
138 {
139         laststate = state;
140         state = new;
141         dbg(DSTS, "chg state %d->%d", laststate, state);
142 }
143
144 void die(char *s, ...)
145 {
146         char buf[2000];
147         
148         va_list ap;
149         va_start(ap, s);
150         vsnprintf(buf, sizeof(buf)-1, s, ap);
151         va_end(ap);
152         fprintf(stderr,"%s\n", buf);
153         terminate(-1);
154 }
155
156 char *strupper(char *s)
157 {
158         char *d = malloc(strlen(s)+1);
159         char *p = d;
160         
161         if (!d)
162                 die("out of room in strupper");
163         while (*p++ = toupper(*s++)) ;
164         return d;
165 }
166
167 char *strlower(char *s)
168 {
169         char *d = malloc(strlen(s)+1);
170         char *p = d;
171         
172         if (!d)
173                 die("out of room in strlower");
174         while (*p++ = tolower(*s++)) ;
175         return d;
176 }
177
178 int eq(char *a, char *b)
179 {
180         return (strcmp(a, b) == 0);
181 }
182
183 FILE *xopen(char *dir, char *name, char *mode)
184 {
185         char fn[MAXPATHLEN+1];
186         snprintf(fn, MAXPATHLEN, "%s/%s/%s", root, dir, name);
187         return fopen(fn, mode);
188 }
189
190 int iscallsign(char *s)
191 {
192         myregex_t *rp;
193
194         if (strlen(s) > MAXCALLSIGN)
195                 return 0;
196         
197         for (rp = iscallreg; rp->in; ++rp) {
198                 if (regexec(rp->regex, s, 0, 0, 0) == 0)
199                         return 1;
200         }
201         return 0;
202 }
203
204 /*
205  * higher level send and receive routines
206  */
207
208 fcb_t *fcb_new(int cnum, int sort)
209 {
210         fcb_t *f = malloc(sizeof(fcb_t));
211         if (!f)
212                 die("no room in fcb_new");
213         memset (f, 0, sizeof(fcb_t));
214         f->cnum = cnum;
215         f->sort = sort;
216         f->inq = chain_new();
217         f->outq = chain_new();
218         return f;
219 }
220
221 void flush_text(fcb_t *f)
222 {
223         if (f->obuf) {
224                 cmsg_send(f->outq, f->obuf, 0);
225                 f->sp->flags |= SEL_OUTPUT;
226                 f->obuf = 0;
227         }
228 }
229
230 void send_text(fcb_t *f, char *s, int l, int nlreq)
231 {
232         cmsg_t *mp;
233         char *p;
234         
235         if (f->buffer_it && f->obuf) {
236                 mp = f->obuf;
237         } else {
238                 f->obuf = mp = cmsg_new(paclen+1, f->sort, f);
239         }
240
241         /* remove trailing spaces  */
242         while (l > 0 &&isspace(s[l-1]))
243                 --l;
244
245         for (p = s; p < s+l; ) {
246                 if (mp->inp >= mp->data + paclen) {
247                         flush_text(f);
248                         f->obuf = mp = cmsg_new(paclen+1, f->sort, f);
249                 }
250                 *mp->inp++ = *p++;
251         }
252         if (mp->inp >= mp->data + paclen) {
253                 flush_text(f);
254                 f->obuf = mp = cmsg_new(paclen+1, f->sort, f);
255         }
256         if (nlreq) {
257                 if (nl == '\r')
258                         *mp->inp++ = nl;
259                 else {
260                         if (mode != 2)
261                                 *mp->inp++ = '\r';
262                         *mp->inp++ = '\n';
263                 }
264         }
265         if (!f->buffer_it)
266                 flush_text(f);
267 }
268
269 void send_msg(fcb_t *f, char let, UC *s, int l)
270 {
271         cmsg_t *mp;
272         int ln;
273         int myl = strlen(call)+2+l;
274
275         mp = cmsg_new(myl+4+1, f->sort, f);
276         *mp->inp++ = let;
277         strcpy(mp->inp, call);
278         mp->inp += strlen(call);
279         *mp->inp++ = '|';
280         if (l > 0) {
281                 UC *p;
282                 for (p = s; p < s+l; ++p) {
283                         if (mp->inp >= mp->data + (myl - 4)) {
284                                 int off = mp->inp - mp->data;
285                                 myl += 256;
286                                 mp = realloc(mp, myl);
287                                 mp->inp = mp->data + off;
288                         }
289                         
290                         if (*p < 0x20 || *p > 0x7e || *p == '%') {
291                                 sprintf(mp->inp, "%%%02X", *p & 0xff);
292                                 mp->inp += strlen(mp->inp);
293                         } else 
294                                 *mp->inp++ = *p;
295                 }
296         } 
297         *mp->inp++ = '\n';
298         *mp->inp = 0;
299         cmsg_send(f->outq, mp, 0);
300         f->sp->flags |= SEL_OUTPUT;
301 }
302
303 /*
304  * the callback (called by sel_run) that handles all the inputs and outputs
305  */
306
307 int fcb_handler(sel_t *sp, int in, int out, int err)
308 {
309         fcb_t *f = sp->fcb;
310         cmsg_t *mp, *omp;
311         UC c;
312         
313         /* input modes */
314         if (ending == 0 && in) {
315                 char *p, buf[MAXBUFL];
316                 int r;
317
318                 /* read what we have into a buffer */
319                 r = read(f->cnum, buf, MAXBUFL);
320                 if (r < 0) {
321                         switch (errno) {
322                         case EINTR:
323                         case EINPROGRESS:
324                         case EAGAIN:
325                                 goto lout;
326                         default:
327                                 dbg(DBUF,"got errno %d in input", errno);
328                                 ending++;
329                                 return 0;
330                         }
331                 } else if (r == 0) {
332                         dbg(DBUF, "ending normally");
333                         ending++;
334                         return 0;
335                 }
336
337                 dbgdump(DBUF, "in ->", buf, r);
338                 
339                 /* create a new message buffer if required */
340                 if (!f->in)
341                         f->in = cmsg_new(MAXBUFL+1, f->sort, f);
342                 mp = f->in;
343
344                 switch (f->sort) {
345                 case TEXT:
346                         p = buf;
347                         if (f->echo)
348                                 omp = cmsg_new(3*r+1, f->sort, f);
349                         while (r > 0 && p < &buf[r]) {
350
351                                 /* echo processing */
352                                 if (f->echo) {
353                                         switch (*p) {
354                                         case '\b':
355                                         case 0x7f:
356                                                 strcpy(omp->inp, "\b \b");
357                                                 omp->inp += strlen(omp->inp);
358                                                 break;
359                                         case '\r':
360                                                 break;
361                                         case '\n':
362                                                 strcpy(omp->inp, "\r\n");
363                                                 omp->inp += 2;
364                                                 break;
365                                         default:
366                                                 *omp->inp++ = *p;
367                                         }
368                                 }
369                                 
370                                 /* character processing */
371                                 switch (*p) {
372                                 case '\t':
373                                         if (int_tabs) {
374                                                 memset(mp->inp, ' ', tabsize);
375                                                 mp->inp += tabsize;
376                                                 ++p;
377                                         } else {
378                                                 *mp->inp++ = *p++;
379                                         }
380                                         break;
381                                 case 0x08:
382                                 case 0x7f:
383                                         if (mp->inp > mp->data)
384                                                 mp->inp--;
385                                         ++p;
386                                         break;
387                                 default:
388                                         if (nl == '\n' && *p == '\r') {   /* ignore \r in telnet mode (ugh) */
389                                                 p++;
390                                         } else if (nl == '\r' && *p == '\n') {  /* and ignore \n in ax25 mode (double ugh) */
391                                                 p++;
392                                         } else if (*p == nl) {
393                                                 if (mp->inp == mp->data)
394                                                         *mp->inp++ = ' ';
395                                                 *mp->inp = 0;              /* zero terminate it, but don't include it in the length */
396                                                 dbgdump(DMSG, "QUEUE TEXT", mp->data, mp->inp-mp->data);
397                                                 cmsg_send(f->inq, mp, 0);
398                                                 f->in = mp = cmsg_new(MAXBUFL+1, f->sort, f);
399                                                 ++p;
400                                         } else {
401                                                 if (mp->inp < &mp->data[MAXBUFL-8])
402                                                         *mp->inp++ = *p++;
403                                                 else {
404                                                         mp->inp = mp->data;
405                                                 }
406                                         }
407                                 }
408                         }
409                         
410                         /* queue any echo text */
411                         if (f->echo) {
412                                 dbgdump(DMSG, "QUEUE ECHO TEXT", omp->data, omp->inp - omp->data);
413                                 cmsg_send(f->outq, omp, 0);
414                                 f->sp->flags |= SEL_OUTPUT;
415                         }
416                         
417                         break;
418
419                 case MSG:
420                         p = buf;
421                         while (r > 0 && p < &buf[r]) {
422                                 UC ch = *p++;
423                                 
424                                 if (mp->inp >= mp->data + (MAXBUFL-1)) {
425                                         mp->state = 0;
426                                         mp->inp = mp->data;
427                                         dbg(DMSG, "Message longer than %d received", MAXBUFL);
428                                 }
429
430                                 switch (mp->state) {
431                                 case 0: 
432                                         if (ch == '%') {
433                                                 c = 0;
434                                                 mp->state = 1;
435                                         } else if (ch == '\n') {
436                                                 /* kick it upstairs */
437                                                 *mp->inp = 0;
438                                                 dbgdump(DMSG, "QUEUE MSG", mp->data, mp->inp - mp->data);
439                                                 cmsg_send(f->inq, mp, 0);
440                                                 mp = f->in = cmsg_new(MAXBUFL+1, f->sort, f);
441                                         } else if (ch < 0x20 || ch > 0x7e) {
442                                                 dbg(DMSG, "Illegal character (0x%02X) received", *p);
443                                                 mp->inp = mp->data;
444                                         } else {
445                                                 *mp->inp++ = ch;
446                                         }
447                                         break;
448
449                                 case 1:
450                                         mp->state = 2;
451                                         if (ch >= '0' && ch <= '9') 
452                                                 c = (ch - '0') << 4;
453                                         else if (ch >= 'A' && ch <= 'F')
454                                                 c = (ch - 'A' + 10) << 4;
455                                         else if (ch >= 'a' && ch <= 'a')
456                                                 c = (ch - 'a' + 10) << 4;
457                                         else {
458                                                 dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state);
459                                                 mp->inp = mp->data;
460                                                 mp->state = 0;
461                                         }
462                                         break;
463                                         
464                                 case 2:
465                                         if (ch >= '0' && ch <= '9') 
466                                                 *mp->inp++ = c | (ch - '0');
467                                         else if (ch >= 'A' && ch <= 'F')
468                                                 *mp->inp++ = c | (ch - 'A' + 10);
469                                         else if (ch >= 'a' && ch <= 'a')
470                                                 *mp->inp++ = c | (ch - 'a' + 10);
471                                         else {
472                                                 dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state);
473                                                 mp->inp = mp->data;
474                                         }
475                                         mp->state = 0;
476                                 }
477                         }
478                         break;
479                         
480                 default:
481                         die("invalid sort (%d) in input handler", f->sort);
482                 }
483         }
484         
485         /* output modes */
486 lout:;
487         if (out) {
488                 int l, r;
489                 
490                 if (!f->out) {
491                         mp = f->out = cmsg_next(f->outq);
492                         if (!mp) {
493                                 sp->flags &= ~SEL_OUTPUT;
494                                 return 0;
495                         }
496                         mp->inp = mp->data;
497                 }
498                 l = mp->size - (mp->inp - mp->data);
499                 if (l > 0) {
500                         
501                         dbgdump(DBUF, "<-out", mp->inp, l);
502                         
503                         r = write(f->cnum, mp->inp, l);
504                         if (r < 0) {
505                                 switch (errno) {
506                                 case EINTR:
507                                 case EINPROGRESS:
508                                 case EAGAIN:
509                                         goto lend;
510                                 default:
511                                         dbg(DBUF,"got errno %d in output", errno);
512                                         ending++;
513                                         return;
514                                 }
515                         } else if (r > 0) {
516                                 mp->inp += r;
517                         }
518                 } else if (l < 0) 
519                         die("got negative length in handler on node");
520                 if (mp->inp - mp->data >= mp->size) {
521                         cmsg_callback(mp, 0);
522                         f->out = 0;
523                 }
524         }
525 lend:;
526         return 0;
527 }
528
529 /* 
530  * set up the various mode flags, NL endings and things
531  */
532 void setmode(char *m)
533 {
534         connsort = strlower(m);
535         if (eq(connsort, "telnet") || eq(connsort, "local") || eq(connsort, "nlonly")) {
536                 nl = '\n';
537                 echo = 1;
538                 mode = eq(connsort, "nlonly") ? 2 : 1;
539         } else if (eq(connsort, "ax25")) {
540                 nl = '\r';
541                 echo = 0;
542                 mode = 0;
543         } else if (eq(connsort, "connect")) {
544                 nl = '\n';
545                 echo = 0;
546                 mode = 3;
547         } else {
548                 die("Connection type must be \"telnet\", \"nlonly\", \"ax25\", \"login\" or \"local\"");
549         }
550 }
551
552
553 /*
554  * things to do with ongoing processing of inputs
555  */
556
557 void process_stdin()
558 {
559         cmsg_t *mp = cmsg_next(in->inq);
560         char *p, hasa, hasn, i;
561         char callsign[MAXCALLSIGN+1];
562         
563         if (mp) {
564                 dbg(DMSG, "MSG size: %d", mp->size);
565
566                 switch (state) {
567                 case CONNECTED:
568                         if (mp->size > 0) {
569                                 send_msg(node, 'I', mp->data, mp->size);
570                         }
571                         break;
572                 case WAITLOGIN:
573                         for (i = 0; i < mp->size; ++i) {
574                                 UC ch = mp->data[i];
575                                 if (i < MAXCALLSIGN) {
576                                         if (isalpha(ch))
577                                                 ++hasa;
578                                         if (isdigit(ch))
579                                                 ++hasn;
580                                     if (isalnum(ch) || ch == '-')
581                                                 callsign[i] = ch;
582                                         else
583                                                 die("invalid callsign");
584                                 } else 
585                                         die("invalid callsign");
586                         }
587                         callsign[i]= 0;
588                         if (strlen(callsign) < 3)
589                                 die("invalid callsign");
590                         if (hasa && hasn)
591                                 ;
592                         else
593                                 die("invalid callsign");
594                         call = strupper(callsign);
595                         
596                         /* check the callsign against the regexes */
597                         if (!iscallsign(call)) {
598                                 die("Sorry, %s isn't a valid callsign", call);
599                         }
600
601                         alarm(0);
602                         signal(SIGALRM, SIG_IGN);
603                         
604                         /* tell the cluster who I am */
605                         send_msg(node, 'A', connsort, strlen(connsort));
606                         
607                         chgstate(CONNECTED);
608                 }
609
610                 cmsg_callback(mp, 0);
611
612         }
613 }
614
615 void process_node()
616 {
617         cmsg_t *mp = cmsg_next(node->inq);
618         if (mp) {
619                 dbg(DMSG, "MSG size: %d", mp->size);
620         
621                 if (mp->size > 0 && mp->inp > mp->data) {
622                         char *p = strchr(mp->data, '|');
623                         if (p)
624                                 p++;
625                         switch (mp->data[0]) {
626                         case 'Z':
627                                 ending++;
628                                 return;
629                         case 'E':
630                                 if (isdigit(*p))
631                                         in->echo = *p - '0';
632                                 break;
633                         case 'B':
634                                 if (isdigit(*p))
635                                         in->buffer_it = *p - '0';
636                                 break;
637                         case 'D':
638                                 if (p) {
639                                         int l = mp->inp - (UC *) p;
640                                         send_text(in, p, l, 1);
641                                 }
642                                 break;
643                         default:
644                                 break;
645                         }
646                 }
647                 cmsg_callback(mp, 0);
648         } else {
649                 flush_text(in);
650         }
651 }
652
653 /*
654  * things to do with going away
655  */
656
657 void term_timeout(int i)
658 {
659         /* none of this is going to be reused so don't bother cleaning up properly */
660         if (in && in->t_set)
661                 tcsetattr(0, TCSANOW, &in->t);
662         if (node) {
663                 shutdown(node->cnum, 3);
664                 close(node->cnum);
665         }
666         exit(i);
667 }
668
669 void terminate(int i)
670 {
671         signal(SIGALRM, term_timeout);
672         alarm(10);
673         
674         while ((in && !is_chain_empty(in->outq)) ||
675                    (node && !is_chain_empty(node->outq))) {
676                 sel_run();
677         }
678         if (in && in->t_set)
679                 tcsetattr(0, TCSADRAIN, &in->t);
680         if (node) {
681                 shutdown(node->cnum, 3);
682                 close(node->cnum);
683         }
684         exit(i);
685 }
686
687 void login_timeout(int i)
688 {
689         write(0, "Timed Out", 10);
690         write(0, &nl, 1);
691         sel_run();                                      /* force a coordination */
692         if (in && in->t_set)
693                 tcsetattr(0, TCSANOW, &in->t);
694         exit(i);
695 }
696
697 /*
698  * things to do with initialisation
699  */
700
701 void initargs(int argc, char *argv[])
702 {
703         int i, c, err = 0;
704
705         while ((c = getopt(argc, argv, "h:p:x:")) > 0) {
706                 switch (c) {
707                 case 'h':
708                         node_addr = optarg;
709                         break;
710                 case 'l':
711                         paclen = atoi(optarg);
712                         if (paclen < 80)
713                                 paclen = 80;
714                         if (paclen > MAXPACLEN)
715                                 paclen = MAXPACLEN;
716                         break;
717                 case 'p':
718                         node_port = atoi(optarg);
719                         break;
720                 case 'x':
721                         dbginit("client");
722                         dbgset(atoi(optarg));
723                         break;
724                 default:
725                         ++err;
726                         goto lerr;
727                 }
728         }
729
730 lerr:
731         if (err) {
732                 die("usage: client [-x n|-h<host>|-p<port>|-l<paclen>] <call>|login [local|telnet|ax25]");
733         }
734         
735         if (optind < argc) {
736                 call = strupper(argv[optind]);
737                 ++optind;
738         }
739         if (!call)
740                 die("Must have at least a callsign (for now)");
741
742         if (optind < argc) {
743                 setmode(argv[optind]);          
744         } else {
745                 setmode("local");
746         }
747
748         /* this is kludgy, but hey so is the rest of this! */
749         if (mode != 0 && paclen == DEFPACLEN) {
750                 paclen = MAXPACLEN;
751         }
752 }
753
754 void connect_to_node()
755 {
756         struct hostent *hp, *gethostbyname();
757         struct sockaddr_in server;
758         int nodef;
759         sel_t *sp;
760                                 
761         if ((hp = gethostbyname(node_addr)) == 0) 
762                 die("Unknown host tcp host %s for printer", node_addr);
763
764         memset(&server, 0, sizeof server);
765         server.sin_family = AF_INET;
766         memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
767         server.sin_port = htons(node_port);
768                                                 
769         nodef = socket(AF_INET, SOCK_STREAM, 0);
770         if (nodef < 0) 
771                 die("Can't open socket to %s port %d (%d)", node_addr, node_port, errno);
772
773         if (connect(nodef, (struct sockaddr *) &server, sizeof server) < 0) {
774                 die("Error on connect to %s port %d (%d)", node_addr, node_port, errno);
775         }
776         node = fcb_new(nodef, MSG);
777         node->sp = sel_open(nodef, node, "Msg System", fcb_handler, MSG, SEL_INPUT);
778         
779 }
780
781 /*
782  * the program itself....
783  */
784
785 main(int argc, char *argv[])
786 {
787         /* compile regexes for iscallsign */
788         {
789                 myregex_t *rp;
790                 for (rp = iscallreg; rp->in; ++rp) {
791                         regex_t reg;
792                         int r = regcomp(&reg, rp->in, REG_EXTENDED|REG_ICASE|REG_NOSUB);
793                         if (r)
794                                 die("regcomp returned %d for '%s'", r, rp->in);
795                         rp->regex = malloc(sizeof(regex_t));
796                         if (!rp->regex)
797                                 die("out of room - compiling regexes");
798                         *rp->regex = reg;
799                 }
800         }
801         
802         /* set up environment */
803         {
804                 char *p = getenv("DXSPIDER_ROOT");
805                 if (p)
806                         root = p;
807                 p = getenv("DXSPIDER_HOST");
808                 if (p)
809                         node_addr = p;
810                 p = getenv("DXSPIDER_PORT");
811                 if (p)
812                         node_port = atoi(p);
813                 p = getenv("DXSPIDER_PACLEN");
814                 if (p) {
815                         paclen = atoi(p);
816                         if (paclen < 80)
817                                 paclen = 80;
818                         if (paclen > MAXPACLEN)
819                                 paclen = MAXPACLEN;
820                 }
821         }
822         
823         /* get program arguments, initialise stuff */
824         initargs(argc, argv);
825         sel_init(10, 0, 10000);
826
827         /* trap signals */
828         signal(SIGHUP, SIG_IGN);
829         signal(SIGINT, terminate);
830         signal(SIGQUIT, terminate);
831         signal(SIGTERM, terminate);
832 #ifdef SIGPWR
833         signal(SIGPWR, terminate);
834 #endif
835
836         /* connect up stdin */
837         in = fcb_new(0, TEXT);
838         in->sp = sel_open(0, in, "STDIN", fcb_handler, TEXT, SEL_INPUT);
839         if (tcgetattr(0, &in->t) < 0) {
840 /*              echo = 0; */
841                 in->echo = echo;
842                 in->t_set = 0;
843         } else {
844                 struct termios t = in->t;
845                 t.c_lflag &= ~(ECHO|ECHONL|ICANON);
846                 t.c_oflag = 0;
847                 if (tcsetattr(0, TCSANOW, &t) < 0) 
848                         die("tcsetattr (%d)", errno);
849                 in->echo = echo;
850                 in->t_set = 1;
851         }
852         in->buffer_it = 1;
853
854         /* connect up node */
855         connect_to_node();
856
857         /* is this a login? */
858         if (eq(call, "LOGIN") || eq(call, "login")) {
859         
860                 char buf[MAXPACLEN+1];
861                 int r, i;
862                 FILE *f = xopen("data", "issue", "r");
863                 if (f) {
864                         while (fgets(buf, paclen, f)) {
865                                 i = strlen(buf);
866                                 if (i && buf[i-1] == '\n') 
867                                         buf[--i] = 0;
868                                 send_text(in, buf, i, 1);
869                         }
870                         fclose(f);
871                 }
872                 signal(SIGALRM, login_timeout);
873                 alarm(timeout);
874                 send_text(in, "login: ", 7, 0);
875                 chgstate(WAITLOGIN);
876         } else {
877
878                 /* check the callsign against the regexes */
879                 if (!iscallsign(call)) {
880                         die("Sorry, %s isn't a valid callsign", call);
881                 }
882
883                 /* tell the cluster who I am */
884                 send_msg(node, 'A', connsort, strlen(connsort));
885         
886                 chgstate(CONNECTED);
887         }
888         
889
890         /* main processing loop */
891         while (ending == 0) {
892                 sel_run();
893                 if (ending == 0) {
894                         process_stdin();
895                         process_node();
896                 }
897         }
898         terminate(0);
899 }
900
901
902
903
904
905