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