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

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