source: trunk/athena/bin/discuss/mclient/dsmail.c @ 22404

Revision 22404, 17.6 KB checked in by ghudson, 19 years ago (diff)
Eliminate declarations of system functions which cause warnings or errors. Fix some broken ss library calls.
Line 
1 /*
2 *
3 *      Copyright (C) 1988, 1989 by the Massachusetts Institute of Technology
4 *      Developed by the MIT Student Information Processing Board (SIPB).
5 *      For copying information, see the file mit-copyright.h in this release.
6 *
7 */
8/*
9 *
10 * dsmail.c - Modifies/parses mail to discuss
11 *
12 *        $Id: dsmail.c,v 1.11 2006-03-10 07:11:39 ghudson Exp $
13 *
14 */
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <ctype.h>
20#include <sys/types.h>
21#include <sys/file.h>
22#include <sysexits.h>
23#include <regex.h>
24#include <sys/fcntl.h>
25
26
27#include <discuss/discuss.h>
28#include <rpc.h>
29#include "config.h"
30
31#define DEFAULT_SUBJECT "No subject found in mail header"
32#define BUFFLEN 400
33#define LISTLEN 40
34
35/* Ultrix sux */
36#ifndef EX_CONFIG
37#define EX_CONFIG EX_SOFTWARE
38#endif
39
40#ifndef lint
41static char rcsid[] =
42    "$Id: dsmail.c,v 1.11 2006-03-10 07:11:39 ghudson Exp $";
43#endif
44
45extern char *optarg;                /* External variables for getopt */
46extern int optind;
47
48char *deflist[] = {
49        "^to$","^from$","^cc$","^[^d].*-to$",".*-from$","^date$",
50        "^message-id$", "^mime-version$", "^content-.*$", NULL
51};
52               
53char *subjlist[] = {
54        "^subject$",NULL
55};
56
57char *inreplyto[] = {
58        "^in-reply-to$",NULL
59};
60
61char *fromlist[] = {
62        "^from$",NULL
63};     
64
65char *progname;
66char *save[LISTLEN],*reject[LISTLEN];
67char *usr_mtg = "";
68int dodefs, allfields, debug, have_host, subject_match=0;
69char *optarg;
70int optind;
71extern tfile unix_tfile();
72int reply_to;
73
74void PRS();
75void lower();
76void trim();
77void SendToMeeting();
78int parse_reply_to();
79int have_trn_num();
80int subject_compare();
81
82main(argc,argv)
83        int argc;
84        char *argv[];
85{
86     FILE *f;
87     char line[BUFFLEN+1],*colonp,*ep;
88     char *subject=NULL,*reply_to_str=NULL;
89     int i,ok_prev=0,iscont = 0, have_signature = 0, len, d;
90     char filename[60], signature[35], field_name[100];
91     
92     init_dsc_err_tbl();
93
94     PRS(argc,argv);                            /* Parse args */
95
96     line[BUFFLEN]='\0';
97
98     if (debug)
99          f=stdout;
100     else {
101          (void) mktemp(strcpy(filename, "/tmp/DSXXXXXX"));
102          if ((d = open(filename, O_CREAT|O_EXCL|O_RDWR, 0600)) == -1)
103            {
104              fprintf (stderr, "250-Can't create temporary file ");
105              fflush (stderr);
106              perror(filename);
107              return EX_TEMPFAIL;
108            }
109          if ((f = fdopen(d, "w+")) == NULL)
110            {
111              perror("250-Can't fdopen temporary file");
112              return EX_TEMPFAIL;
113            }
114     }
115
116     if (debug) printf("start of text->");
117     
118     if (fgets(line,BUFFLEN,stdin)==NULL)
119          exit(EX_NOINPUT);
120
121     while (*line != '\n') {                    /* Do headers */
122          if (!iscont) {
123               char *cp;
124
125               ep = line + strlen(line);
126               colonp = strchr(line, ':');
127               if (colonp == NULL)
128                    colonp = ep;
129               len = colonp - line;
130               if (len > sizeof(field_name))
131                    len = sizeof(field_name)-1;
132               strncpy(field_name, line, len);
133               field_name[len] = '\0';
134               lower(field_name);
135
136               if (list_compare(field_name,subjlist)) { /* Subject */
137                    cp = colonp;
138                    if (*cp == ':')
139                         cp++;
140                    while (isspace(*cp))
141                         cp++;
142
143                    /* ignore second subject line */
144                    if (subject==NULL) {
145                         subject=malloc(ep-cp+1);
146                         if (!subject)
147                             exit (EX_TEMPFAIL);                               
148                         (void) strcpy(subject,cp);
149
150                         /* Trim subject */
151                         trim(subject);
152                    }   
153               } else if (list_compare(field_name, inreplyto)) {
154                    /* ignore second subject line */
155                    cp = colonp;
156                    if (*cp == ':')
157                         cp++;
158
159                    if (reply_to_str==NULL) {
160                         reply_to_str=malloc(ep-cp+1);
161                         if (!reply_to_str)
162                             exit (EX_TEMPFAIL);                               
163                         (void) strcpy(reply_to_str,cp);
164                    }   
165               }
166               else if (list_compare(field_name,fromlist)) {
167                    cp = colonp;
168                    if (*cp == ':') {
169                         cp++;
170                         extract_full_name(cp, signature, sizeof(signature));
171                         have_signature = TRUE;
172                         if (debug)
173                              printf ("got sig: %s\n",
174                                      signature);
175                    }
176               }
177          } else {
178               if (list_compare(field_name, inreplyto) && reply_to_str != NULL) {
179                    /* Concat reply-to */
180                    reply_to_str = realloc(reply_to_str,strlen(reply_to_str) + strlen(line) + 1);
181                    if (!reply_to_str)
182                        exit (EX_TEMPFAIL);                             
183                    strcpy(reply_to_str+strlen(reply_to_str),line);
184               }
185          }
186
187          if ((ok_prev && iscont) || (!iscont && CheckField(field_name))) {
188               ok_prev=1;
189               fprintf(f,"%s",line);
190          } else
191               ok_prev=0;
192
193          iscont = line[strlen(line)-1] != '\n';
194          if (fgets(line,BUFFLEN,stdin)==NULL)
195               goto bye;
196          if (!iscont)
197               iscont = (line[0] == ' ' || line[0] == '\t');
198     }
199     fprintf (f, "\n");
200
201     /* Copy over body of message */
202     while (fgets(line,BUFFLEN,stdin)!=NULL) {
203          fprintf(f,"%s",line);
204     }
205     if (fflush(f) == EOF) {
206          fprintf (stderr, "250-Can't write to ");
207          fflush (stderr);
208          perror (debug ? "standard output" : filename);
209          exit (EX_TEMPFAIL);
210     }
211     
212     SendToMeeting(f,(subject==NULL) ? DEFAULT_SUBJECT : subject,
213                   reply_to_str,
214                   (have_signature) ? signature : NULL);
215 bye:
216     if (!debug)
217          (void) unlink(filename);
218     exit (EX_OK);
219     
220}
221
222void SendToMeeting(f, subject, reply_to_str, signature)
223char *subject;
224char *reply_to_str;
225char *signature;     
226FILE *f;
227{
228     int len,have_mtg_info;
229     static char module[100] = "discuss@";
230     char *mtg_name,*cp,*short_name;
231     int fatal_err, result, smtp_code, exit_code, reply_to_trn;
232     tfile transaction;
233     mtg_info minfo;
234     trn_info tinfo;
235     int trn_no, i;
236     
237     gethostname(&module[8], sizeof(module)-8);
238     init_rpc();
239     set_module(module, &fatal_err, &result);
240
241     switch (result) {
242     case 0:
243          break;
244     case RPC_NS_TIMEOUT:
245          smtp_code = 250;
246          exit_code = EX_TEMPFAIL;
247          break;
248     default:
249          smtp_code = 554;
250          exit_code = EX_CONFIG;
251          break;
252     }
253     if (result) {
254          fprintf(stderr, "%03d-Can't connect to discuss server: %s\n",
255                  smtp_code, error_message(result));
256          exit (exit_code);
257     }
258     
259     rewind(f); lseek(fileno(f), 0, L_SET);
260     transaction = unix_tfile(fileno(f));
261
262     cp = strchr(subject, '\n');
263     if (cp)
264          *cp = '\0';
265
266     /* Parse reply_to_string */
267     have_mtg_info = FALSE;
268     reply_to_trn = 0;
269     if (reply_to_str != NULL && have_trn_num(reply_to_str)) {
270          short_name = usr_mtg + strlen(usr_mtg);
271          while (short_name > usr_mtg && *short_name != '/')
272               short_name--;
273
274          if (*short_name == '/')
275               short_name++;
276
277          reply_to_trn = parse_reply_to(reply_to_str, short_name);
278          if (reply_to_trn == 0) {
279               /* Get mtg info, to check out long name */
280               get_mtg_info(usr_mtg, &minfo, &result);
281               if (result == 0) {
282                    have_mtg_info = TRUE;
283                    reply_to_trn = parse_reply_to(reply_to_str, minfo.long_name);
284               } else
285                    reply_to_trn = parse_reply_to(reply_to_str, NULL);
286          }
287     }
288
289     if (reply_to_trn == 0 && subject_match && strcmp(subject, DEFAULT_SUBJECT) && *subject != '\0') {
290          if (!have_mtg_info) {
291               /* Get mtg info, to check out long name */
292               get_mtg_info(usr_mtg, &minfo, &result);
293               if (result == 0)
294                    have_mtg_info = TRUE;
295          }
296          if (have_mtg_info) {
297               /* Walk through meeting, doing subject comparisons */
298               for (i = 0; i < subject_match; i++) {
299                    if (minfo.last-i < minfo.first)
300                         break;
301
302                    get_trn_info(usr_mtg, minfo.last-i, &tinfo, &result);
303                    if (result == 0) {
304                         if (subject_compare(tinfo.subject, subject)) {
305                              reply_to_trn = minfo.last-i;
306                              dsc_destroy_trn_info(&tinfo);
307                              break;
308                         }
309                         dsc_destroy_trn_info(&tinfo);
310                    }
311               }
312          }
313     }
314
315     if (debug) {
316          printf("subject is ``%s''\nreply to %d in %s\n",
317                 subject, reply_to_trn, usr_mtg);
318     }
319
320     if (signature == NULL || *signature == '\0') {
321          add_trn(usr_mtg, transaction, subject, reply_to_trn, &trn_no, &result);
322     } else {
323          if (get_server_version() < SERVER_2) {
324               add_trn(usr_mtg, transaction, subject, reply_to_trn, &trn_no, &result);
325          } else {
326               add_trn2(usr_mtg, transaction, subject, signature, reply_to_trn, &trn_no, &result);
327          }
328     }
329
330     /* If error, try it as a non-reply */
331     if ((result == NO_SUCH_TRN || result == DELETED_TRN || result == NO_ACCESS)  && reply_to_trn != 0) {
332          tdestroy(transaction);
333          rewind(f); lseek(fileno(f), 0, L_SET);
334          transaction = unix_tfile(fileno(f));
335          if (signature == NULL || *signature == '\0' || get_server_version() < SERVER_2) {
336               add_trn(usr_mtg, transaction, subject, 0, &trn_no, &result);
337          } else {
338               add_trn2(usr_mtg, transaction, subject, signature, 0, &trn_no, &result);
339          }
340     }
341     
342     switch (result) {
343     case 0:
344          break;
345     default:
346          smtp_code = 550;
347          exit_code = EX_CANTCREAT;
348     }
349     if (result) {
350          fprintf (stderr, "%03d-Can't enter transaction", smtp_code);
351          if (reply_to)
352               fprintf (stderr, " as reply to %d", reply_to);
353          fprintf (stderr, "\n%03d- into meeting %s: %s\n",
354                   smtp_code, usr_mtg, error_message (result));
355          exit(exit_code);
356     }
357     (void) fclose(f);
358}
359
360void
361lower(s)
362char *s;
363{
364     while (*s) {
365          if (isupper(*s))
366               *s = tolower(*s);
367          s++;
368     }
369}
370
371void
372trim(s)
373char *s;
374{
375     char *cp;
376
377     cp = &s[strlen(s)]-1;
378     while (cp >= s && isspace(*cp)) {
379          *cp-- = '\0';
380     }
381}
382
383int CheckField(key)
384        char *key;
385{
386        int keepfield;
387
388        keepfield=allfields;
389        if (!keepfield && dodefs && list_compare(key,deflist))
390                keepfield=1;
391        if (!keepfield && list_compare(key,save))
392                keepfield=1;
393        if (keepfield && list_compare(key,reject))
394                keepfield=0;
395        return(keepfield);
396}
397
398/* Parse command line arguments */
399void PRS(argc,argv)
400        int argc;
401        char **argv;
402{
403        int c,rp,sp;
404       
405        progname=argv[0];
406        sp=rp=0;
407        optind=1;               /* Initialize for getopt */
408        while ((c = getopt(argc,argv,"AZDs:da:r:h")) != EOF)
409                switch(c) {
410                case 'd':
411                        dodefs=!dodefs;
412                        break;
413
414                case 's':
415                        subject_match = atoi(optarg);
416                        break;
417
418                case 'D':
419                        debug=!debug;
420                        break;
421
422                case 'A':
423                        allfields=!allfields;
424                        break;
425
426                case 'a':
427                        lower(optarg);
428                        save[sp++]=optarg;
429                        if (sp>=LISTLEN) {
430                                fprintf(stderr,"500-Too many accept fields\n");
431                                exit (EX_USAGE);
432                        }
433                        break;
434
435                case 'h':
436                        have_host = 1;
437                        break;
438
439                case 'r':
440                        lower(optarg);
441                        reject[rp++]=optarg;
442                        if (sp>=LISTLEN) {
443                                fprintf(stderr,"500-Too many reject fields\n");
444                                exit (EX_USAGE);
445                        }
446                }   
447        if (optind>=argc)
448                goto lusage;
449        usr_mtg=argv[optind];
450        if (have_host) {
451                usr_mtg = strchr (usr_mtg, ':');
452                if (!usr_mtg)
453                        goto lusage;
454                usr_mtg++;
455        }
456        save[sp]=NULL;          /* Insert terminators */
457        reject[rp]=NULL;
458        return;
459 lusage:
460        printf("500-Usage: %s [-dADZ] [-s subject-match-count] [-a field] [-r field] meeting-path-name\n",
461               progname);
462        exit (EX_USAGE);
463}
464
465int list_compare(s,list)
466        char *s,**list;
467{
468        char buf[BUFSIZ];
469        regex_t reg;
470        int status;
471
472        while (*list!=NULL) {
473                status = regcomp(&reg, *list, REG_NOSUB);
474                if (status != 0) {
475                        regerror(status, &reg, buf, sizeof(buf));
476                        fprintf(stderr,"554-%s - %s: %s\n",
477                                progname, *list, buf);
478                }
479                if (regexec(&reg, s, 0, NULL, 0) == 0) {
480                        regfree(&reg);
481                        return 1;
482                }
483                regfree(&reg);
484                list++;
485        }
486        return(0);
487}
488
489strip_addr(addr, dest, dest_size)
490char *addr,*dest;
491int dest_size;
492{
493     char *dest_end,*dp,*sp,*quote_start;
494     int paren_level,found_angle;
495
496     dest_end = &dest[dest_size-1];
497     dp = dest;
498     sp = addr;
499     paren_level = 0;
500
501eat_white:
502     while (isspace(*sp) && *sp != '\n')
503          sp++;
504
505     if (*sp == '(')
506          goto eat_comment;
507
508     if (*sp == '"')
509          goto eat_string;
510
511     if (*sp == '<') {
512          dp = dest;
513          sp++;
514          found_angle = TRUE;
515          goto eat_white;
516     }
517
518     if (*sp == '>' && found_angle) {
519          *sp++;
520          goto eat_white;
521     }
522
523     if (*sp == '\0' || *sp == '\n') {
524          *dp++ = '\0';
525          goto post_proc;
526     }
527
528     *dp++ = *sp++;
529     if (dp == dest_end) {
530          *dp++ = '\0';
531          goto post_proc;
532     }
533
534     goto eat_white;
535
536eat_comment:
537     paren_level++;
538     sp++;
539
540cont_comment:
541     while (*sp != ')' && *sp != '(' && *sp) {
542          sp++;
543     }
544
545     if (*sp == '\0') {
546          *dp = '\0';
547          goto post_proc;
548     }
549
550     if (*sp == '(')
551          goto eat_comment;
552
553     sp++;              /* ) */
554     paren_level--;
555     if (paren_level <= 0)
556          goto eat_white;
557
558     goto cont_comment;
559
560eat_string:
561     quote_start = sp;
562     sp++;
563
564     while(*sp != '"' && *sp)
565          sp++;
566
567     if (!*sp) {
568          *dp = '\0';
569          goto post_proc;
570     }
571
572     if (*++sp == '@') {                /* "foo"@bar */
573          sp = quote_start;
574          *dp++ = *sp++;
575          while (dp < dest_end && *sp != '"')
576               *dp++ = *sp++;
577
578          if (dp == dest_end) {
579               *dp = '\0';
580               goto post_proc;
581          }
582          *dp++ = *sp++;
583          if (dp == dest_end) {
584               *dp++ = '\0';
585               goto post_proc;
586          }
587     }
588     goto eat_white;
589
590     /* No post processing */
591post_proc:
592     return;
593}
594
595/*
596 *
597 *   Routine to extract a full name from an address.  If no full name
598 *   can be found, then we simply return the stripped address.
599 *
600 */
601extract_full_name(addr, dest, dest_size)
602char *addr,*dest;
603int dest_size;
604{
605     char *dest_end,*dp,*sp,*bracket,*close_paren;
606     int paren_level,non_white;
607
608     dest_end = &dest[dest_size-1];
609     dp = dest;
610     sp = addr;
611
612     /* Find angle bracket (if possible) */
613     while (*sp && *sp != '<' && *sp != '\n')
614          sp++;
615
616     bracket = NULL;
617     if (*sp == '<')
618          bracket = sp;
619
620     non_white = 0;
621     if (bracket != NULL) {
622          for (sp = addr; sp < bracket; sp++) {
623               if (!isspace(*sp) && *sp != '"')
624                    non_white++;
625          }
626     }
627
628     if (non_white > 1) {               /* We have a name */
629          sp = addr;
630          while (isspace(*sp) || *sp == '"')    /* Skip leading spaces */
631               sp++;
632
633          while (isspace(*(bracket-1)) || *(bracket-1) == '"')  /* Skip trailing spaces */
634               bracket--;
635
636          /* Copy it over */
637          while (sp < bracket && dp < dest_end)
638               *dp++ = *sp++;
639
640          *dp++ = '\0';
641          return;
642     }
643
644     /* Now, let's see if we have name in a comment (look back from the
645        end for a parenthesis. */
646     for (sp = addr; *sp && *sp != '\n'; sp++)
647          ;
648
649     sp--;
650     while (sp > addr && isspace(*sp))
651          sp--;
652
653     if (*sp == ')') {                  /* Name in comment */
654          close_paren = sp;
655          paren_level = 1;
656          sp--;
657
658          for (;sp > addr; sp--) {
659               if (*sp == ')')
660                    paren_level++;
661               else if (*sp == '(') {
662                    paren_level--;
663                    if (paren_level == 0)
664                         break;
665               }
666          }
667
668          if (*sp == '(') {             /* Copy it over */
669               sp++;
670
671               while(isspace(*sp))
672                    sp++;
673
674               while (sp < close_paren && dp < dest_end)
675                    *dp++ = *sp++;
676
677               *dp = '\0';
678               return;
679          }
680     }
681
682     strip_addr(addr, dest, dest_size);
683     return;
684}         
685
686/*
687 *
688 *   parse_reply_to() - Parse an in-reply-to message for a given message.
689 *
690 */
691int
692parse_reply_to(str, mtg_name)
693char *str;
694char *mtg_name;
695{
696     char *bracketp,*end_bracketp,*startp,*cp;
697     int trn_num,mtg_name_len;
698
699     startp = str;
700     while (1) {
701          bracketp = strchr(startp, '[');
702          if (bracketp == NULL)
703               return(0);
704
705          end_bracketp = strchr(bracketp, ']');
706          if (end_bracketp == NULL || end_bracketp - bracketp > 10)
707               return(0);
708
709          trn_num = 0;
710          cp = bracketp+1;
711          while (isdigit(*cp)) {
712               trn_num = trn_num*10 + *cp++ - '0';
713          }
714         
715          if (*cp == '\\')                              /* Skip quoting char */
716               cp++;
717         
718          if (*cp != ']')
719               trn_num = 0;
720         
721          if (trn_num == 0)
722               return(0);
723         
724          cp++;
725          while (isspace(*cp) || *cp == '"')
726               cp++;
727         
728          if (mtg_name == NULL)                         /* Match anything */
729               return(trn_num);
730
731          /* Look for "in" */
732          if (*cp != 'i' && *cp != 'I')
733               return(trn_num);
734         
735          cp++;
736          if (*cp != 'n' && *cp != 'N')
737               return(trn_num);
738
739          cp++;
740          while (isspace(*cp) || *cp == '"')
741               cp++;
742
743          /* Check for meeting name.  If it doesn't match, we don't have
744             a transaction number */
745          mtg_name_len = strlen(mtg_name);
746          if (mtg_name == NULL || strlen(cp) < mtg_name_len) {
747               startp = cp;
748               continue;
749          }
750
751          /* Check if match, and ends fine (random punctuation or space) */
752          if (!strncasecmp(cp, mtg_name, mtg_name_len) &&
753              !isalnum(*(cp+mtg_name_len)) && *(cp+mtg_name_len) != '_' &&
754              *(cp+mtg_name_len) != '-')
755               return(trn_num);
756
757          startp = cp;
758     }
759}
760
761int
762have_trn_num(str)
763char *str;
764{
765     char *bracketp,*end_bracketp,*cp;
766     int trn_num;
767
768     bracketp = strchr(str, '[');
769     if (bracketp == NULL)
770          return(FALSE);
771
772     end_bracketp = strchr(bracketp, ']');
773     if (end_bracketp == NULL || end_bracketp - bracketp > 10)
774          return;
775
776     trn_num = 0;
777     cp = bracketp+1;
778     while (isdigit(*cp)) {
779          trn_num = trn_num*10 + *cp++ - '0';
780     }
781
782     if (*cp == '\\')                           /* Skip quoting char */
783          cp++;
784
785     if (*cp != ']')
786          trn_num = 0;
787
788     if (trn_num == 0)
789          return(FALSE);
790
791     return(TRUE);
792}
793
794/*
795 *
796 *   Compare subjects, ignoring 're: *' at the beginning and trailing
797 *      white space
798 *
799 */
800int
801subject_compare(subj1, subj2)
802char *subj1, *subj2;
803{
804     char *cp;
805     int min_len, len1, len2;
806
807     while (*subj1) {
808          while (isspace(*subj1))
809               subj1++;
810
811          cp = subj1;
812
813          if (*cp != 'r' && *cp != 'R')
814               break;
815
816          cp++;
817          if (*cp != 'e' && *cp != 'E')
818               break;
819          cp++;
820
821          if (*cp != ':')
822               break;
823
824          subj1 = cp+1;
825     }
826
827     while (*subj2) {
828          while (isspace(*subj2))
829               subj2++;
830
831          cp = subj2;
832
833          if (*cp != 'r' && *cp != 'R')
834               break;
835
836          cp++;
837          if (*cp != 'e' && *cp != 'E')
838               break;
839          cp++;
840
841          if (*cp != ':')
842               break;
843
844          subj2 = cp+1;
845     }
846
847     /* Compare subjects, ignoring trailing white-space */
848     len1 = strlen(subj1);
849     len2 = strlen(subj2);
850     if (len1 < len2)
851          min_len = len1;
852     else
853          min_len = len2;
854
855     if (min_len <= 0)
856          return(0);
857
858     if (strncasecmp(subj1, subj2, min_len))
859          return(0);
860
861     if (len1 == len2)
862          return(1);
863
864     if (len1 < len2)
865          cp = &subj2[len1];
866     else
867          cp = &subj1[len2];
868
869     /* If rest if white-space, we match */
870     while (*cp) {
871          if (*cp != ' ')
872               return(0);
873          cp++;
874     }
875     return(1);
876}
Note: See TracBrowser for help on using the repository browser.