home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume14 / mush6.0 / part10 / hdrs.c
Encoding:
C/C++ Source or Header  |  1988-04-12  |  30.0 KB  |  1,058 lines

  1. /* hdrs.c     (c) copyright 1986 (Dan Heller) */
  2.  
  3. /* Routines that deal with message headers inside messages */
  4. #include "mush.h"
  5.  
  6. /*
  7.  * get which message via the offset and search for the headers which
  8.  * match the string "str". there may be more than one of a field (like Cc:)
  9.  * so get them all and "cat" them together into the static buffer
  10.  * "buf" and return its address.
  11.  */
  12. char *
  13. header_field(n, str)
  14. char *str;
  15. {
  16.     static char    buf[BUFSIZ];
  17.     char        tmp[BUFSIZ];
  18.     register char  *p, *p2, *b = buf;
  19.     int contd_hdr;  /* true if next line is a continuation of the hdr we want */
  20.  
  21.     if (fseek(tmpf, msg[n].m_offset, L_SET) == -1) {
  22.     error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
  23.     turnon(glob_flags, READ_ONLY);
  24.     return NULL;
  25.     }
  26.     *b = 0;
  27.     while((p = fgets(tmp, sizeof(buf), tmpf)) && *p != '\n') {
  28.     if (*p != ' ' && *p != '\t') {
  29.         contd_hdr = 0;
  30.         /* strcmp ignoring case */
  31.         for(p2 = str; *p && *p2 && *p2 == lower(*p); ++p, ++p2);
  32.         /* MATCH is true if p2 is at the end of str and *p is ':' */
  33.         if (*p2 || *p++ != ':')
  34.         continue;
  35.         else
  36.         contd_hdr = 1;
  37.     } else if (!contd_hdr)
  38.         continue;
  39.     skipspaces(0);
  40.     p2 = no_newln(p);
  41.     *++p2 = ' ', *++p2 = 0;
  42.     if (strlen(p) + (b-buf) < sizeof (buf))
  43.         b += Strcpy(b, p);
  44.     }
  45.     if (b > buf) /* now get rid of the trailing blank */
  46.     *--b = 0;
  47.     return (*buf)? buf: NULL;
  48. }
  49.  
  50. do_hdrs(argc, argv, list)
  51. register char **argv, list[];
  52. {
  53.     register int   pageful = 0, fnd;
  54.     int        (*oldint)(), (*oldquit)(), show_deleted;
  55.     static int     cnt;
  56.     register char  *p;
  57.     char        first_char = (argc) ? **argv: 'h';
  58.  
  59.     if (argc > 1 && !strcmp(argv[1], "-?"))
  60.     return help(0, "headers", cmd_help);
  61.  
  62.     if (!msg_cnt) {
  63.     if (ison(glob_flags, DO_PIPE))
  64.         return 0;
  65. #ifdef CURSES
  66.     if (iscurses)
  67.         clear();
  68. #endif /* CURSES */
  69. #ifdef SUNTOOL
  70.     if (istool)
  71.         mail_status(0);
  72. #endif /* SUNTOOL */
  73.     return 0;
  74.     }
  75.     if (first_char == ':' || (argc > 1 && argv[1][0] == ':')) {
  76.     if (first_char != ':')
  77.         argv++;
  78.     return specl_hdrs(argv, list);
  79.     }
  80.  
  81.     on_intr();
  82.  
  83.     if (argc && (argv[0][1] == '+' || argc > 1 && !strcmp(argv[1], "+")) ||
  84.         first_char == 'z' && !argv[1])
  85.     if (msg_cnt > screen)
  86.         cnt = min(msg_cnt - screen, n_array[0] + screen);
  87.     else
  88.         cnt = 0;
  89.     else if (argc && (argv[0][1] == '-' || argc > 1 && !strcmp(argv[1], "-")))
  90.     cnt = max((cnt - 2*screen), 0);
  91.     else if (argc && *++argv &&
  92.     (isdigit(**argv) || **argv == '^' || **argv == '$' || **argv == '.')
  93.      || ison(glob_flags, IS_PIPE)) {
  94.     /* if we're coming from a pipe, start display at the first msg bit
  95.      * set in the msg_list
  96.      */
  97.     if (ison(glob_flags, IS_PIPE)) {
  98.         if (isoff(glob_flags, DO_PIPE))
  99.         for (fnd = 0; fnd < msg_cnt; fnd++)
  100.             if (msg_bit(list, fnd))
  101.             wprint("%s\n", compose_hdr(fnd));
  102.         off_intr();
  103.         return 0;
  104.     }
  105.     /* if a number was given, use it */
  106.     if (!(fnd = chk_msg(*argv))) {
  107.         off_intr();
  108.         return -1;
  109.     }
  110.     for (cnt = fnd - 1; cnt > 0 && cnt + screen > msg_cnt; cnt--);
  111.     } else if (current_msg < n_array[0] || current_msg > n_array[screen-1])
  112.     cnt = current_msg; /* adjust if user reads passed screen bounds */
  113.     else if (cnt >= msg_cnt || !argc || !*argv)
  114.     cnt = max((cnt - screen), 0); /* adjust window to maintian position */
  115.  
  116.     show_deleted = !!do_set(set_options, "show_deleted");
  117.  
  118.     for (;pageful<screen && cnt<msg_cnt && isoff(glob_flags, WAS_INTR); cnt++) {
  119.     if (!iscurses && !show_deleted && first_char == 'h'
  120.         && ison(msg[cnt].m_flags, DELETE))
  121.         continue;
  122.     n_array[pageful++] = cnt;
  123.     /* this message was displayed -- set the bit */
  124.     if (list)
  125.         set_msg_bit(list, cnt);
  126.     /* if do_pipe, don't output anything */
  127.     if (ison(glob_flags, DO_PIPE))
  128.         continue;
  129.     p = compose_hdr(cnt);
  130.     if (!istool && (!iscurses || ison(glob_flags, IS_GETTING)))
  131.         puts(p);
  132. #ifdef SUNTOOL
  133.     else if (istool) {
  134.         if (cnt == current_msg) /* embolden or reverse_video */
  135.         highlight(hdr_win, 0,pageful*l_height(DEFAULT), DEFAULT,p);
  136.         else
  137.         pw_text(hdr_win, 0, pageful * l_height(DEFAULT), PIX_SRC,
  138.                             fonts[DEFAULT], p);
  139.         Clrtoeol(hdr_win, strlen(p)*l_width(DEFAULT),
  140.              pageful*l_height(DEFAULT), DEFAULT);
  141.     }
  142. #endif /* SUNTOOL */
  143. #ifdef CURSES
  144.         else if (iscurses)
  145.         mvprintw(pageful, 0, "%-.*s", COLS-2, p), clrtoeol();
  146. #endif /* CURSES */
  147.     }
  148.     /* just in case a signal stopped us */
  149.     off_intr();
  150.     pageful++;
  151. #ifdef CURSES
  152.     if (iscurses && pageful < screen)
  153.     move(pageful, 0), clrtobot();
  154. #endif /* CURSES */
  155.     if (cnt == msg_cnt) {
  156.     while (pageful <= screen) {
  157.         n_array[pageful-1] = msg_cnt+1; /* assign out-of-range values */
  158. #ifdef SUNTOOL
  159.         if (istool)
  160.         Clrtoeol(hdr_win, 0, pageful * l_height(DEFAULT), DEFAULT);
  161. #endif /* SUNTOOL */
  162.         ++pageful;
  163.     }
  164.     }
  165. #ifdef SUNTOOL
  166.     if (istool) {
  167.     if (msg_cnt > screen) {
  168.         panel_set(next_scr, PANEL_SHOW_ITEM, TRUE, 0);
  169.         panel_set(prev_scr, PANEL_SHOW_ITEM, TRUE, 0);
  170.     }
  171.     mail_status(0);
  172.     }
  173. #endif /* SUNTOOL */
  174.     return 0;
  175. }
  176.  
  177. #define NEW 1
  178. #define ALL 2
  179.  
  180. specl_hdrs(argv, list)
  181. char **argv, list[];
  182. {
  183.     u_long    special = 0;
  184.     int     n = 0;
  185.  
  186.     while (argv[0][++n])
  187.     switch(argv[0][n]) {
  188.         case 'a': special = ALL;
  189.         when 'n': special = NEW;
  190.         when 'u': special = UNREAD;
  191.         when 'o': special = OLD;
  192.         when 'd': special = DELETE;
  193.         when 'r': special = REPLIED;
  194.         otherwise: print("choose from n,u,o,d,r, or a"); return -1;
  195.     }
  196.     if (debug)
  197.     (void) check_flags(special);
  198.  
  199.     for (n = 0; n < msg_cnt; n++) {
  200.     /*
  201.      * First, see if we're looking for NEW messages.
  202.      * If so, then check to see if the msg is unread and not old.
  203.      * If special > ALL, then special has a mask of bits describing
  204.      * the state of the message.
  205.      */
  206.     if (ison(glob_flags, IS_PIPE)&& !msg_bit(list, n))
  207.         continue;
  208.     if (special == ALL || special == NEW &&
  209.            (ison(msg[n].m_flags, UNREAD) && isoff(msg[n].m_flags, OLD))) {
  210.         if (isoff(glob_flags, DO_PIPE))
  211.         print("%s\n", compose_hdr(n));
  212.         if (list)
  213.         set_msg_bit(list, n);
  214. #ifndef SYSV
  215.     /*
  216.      * XENIX compiler can't handle "special" in ison() macro.
  217.      * It only works if the second argument is a constant!
  218.      */
  219.     } else if (special > ALL && ison(msg[n].m_flags, special)) {
  220.         if (isoff(glob_flags, DO_PIPE))
  221.         print("%s\n", compose_hdr(n));
  222.         if (list)
  223.         set_msg_bit(list, n);
  224. #endif /* SYSV */
  225.     } else {
  226.         if (list)
  227.         unset_msg_bit(list, n);
  228.         if (debug) {
  229.         printf("msg[%d].m_flags: %d", n, msg[n].m_flags);
  230.         (void) check_flags(msg[n].m_flags);
  231.         }
  232.     }
  233.     }
  234.     return 0;
  235. }
  236.  
  237. #define Strncpy(buf,p) (void) strncpy(buf,p, sizeof(buf)),buf[sizeof(buf)-1]=0
  238.  
  239. /*
  240.  * compose a header from the information about a message (from, to, date,
  241.  * subject, etc..).  The header for message number "cnt" is built and is
  242.  * returned in the static buffer "buf".  There will be *at least* 9 chars
  243.  * in the buffer which will be something like: " 123 >N " The breakdown
  244.  * is as follows: 4 chars for the message number, 1 space, 1 char for '>'
  245.  * (if current message) and two spaces for message status (new, unread, etc)
  246.  * followed by 1 terminating space.
  247.  * Read other comments in the routine for more info.
  248.  */
  249. char *
  250. compose_hdr(cnt)
  251. {
  252.     static char buf[256];
  253.     register char *p, *b;
  254.     char from[256], subject[256], date[17], lines[16], chars[16], line[256];
  255.     char to[256], addr[256], name[256], status[2];
  256.     char Day[3], Mon[4], Tm[8], Yr[5], Wkday[4];
  257.  
  258.     /* status of the message */
  259.     if (ison(msg[cnt].m_flags, DELETE))
  260.     status[0] = '*';
  261.     else if (ison(msg[cnt].m_flags, OLD) && ison(msg[cnt].m_flags, UNREAD))
  262.     status[0] = 'U';
  263.     else if (ison(msg[cnt].m_flags, PRESERVE))
  264.     status[0] = 'P';
  265.     else if (isoff(msg[cnt].m_flags, UNREAD))
  266.     status[0] = ' ';
  267.     else
  268.     status[0] = 'N';
  269.  
  270.     if (ison(msg[cnt].m_flags, REPLIED))
  271.     status[1] = 'r';
  272.     else
  273.     status[1] = ' ';
  274.  
  275.     to[0] = from[0] = subject[0] = date[0] = lines[0] = chars[0] = addr[0] =
  276.     name[0] = line[0] = Day[0] = Mon[0] = Tm[0] = Yr[0] = Wkday[0] = 0;
  277.  
  278.     /* who's the message to */
  279.     if ((p = header_field(cnt, "to")) ||
  280.     (p = header_field(cnt, "resent-to")) ||
  281.     (p = header_field(cnt, "apparently-to")))
  282.     Strncpy(to, p);
  283.  
  284.     /* who the messages is from--
  285.      * %f        From field
  286.      * %a        From address
  287.      * %n        From name
  288.      */
  289.     if (!(p = header_field(cnt, "from"))) {
  290.     /* if all else fails, then get the first token in "From" line */
  291.     register char *p2;
  292.     p = ""; /* just in case */
  293.     if (fseek(tmpf, msg[cnt].m_offset, L_SET) == -1 ||
  294.         !(p2 = fgets(line, sizeof(line), tmpf))) {
  295.         error("fseek in %s (msg %d, folder=%s)", tempfile, cnt+1, mailfile);
  296.         turnon(glob_flags, READ_ONLY);
  297.     } else if (!(p = index(p2, ' ')))
  298.         print("Fudged \"From\" line: %s", p2);
  299.     else if (p2 = any(++p, " \t"))
  300.         *p2 = 0;
  301.     }
  302.     skipspaces(0);
  303.     (void) no_newln(p);
  304.     /* if the "from" line produced the user's login name, then the message is
  305.      * from the user -- attempt to give more useful information by telling
  306.      * to whom the message was sent.  This is not possible if the "to" header
  307.      * failed to get info (which is probably impossible).
  308.      */
  309.     if (!strcmp(p, login) && *to) {
  310.     (void) strcpy(from, "TO: ");
  311.     (void) strncpy(from+4, to, sizeof(from)-4), from[sizeof(from)-4] = 0;
  312.     (void) get_name_n_addr(from+4, name+4, addr+4);
  313.     if (name[4])
  314.         (void) strncpy(name,"TO: ",4); /* strncpy doesn't null terminate */
  315.     if (addr[4])
  316.         (void) strncpy(addr,"TO: ",4); /* don't overwrite name there */
  317.     } else {
  318.     Strncpy(from, p);
  319.     (void) get_name_n_addr(from, name, addr);
  320.     }
  321.  
  322.     if (p = msg_date(cnt))
  323.     date_to_string(p, Yr, Mon, Day, Wkday, Tm, date);
  324.  
  325.     (void) sprintf(lines, "%d", msg[cnt].m_lines);
  326.     (void) sprintf(chars, "%ld", msg[cnt].m_size);
  327.  
  328.     /* and the subject */
  329.     if (p = header_field(cnt, "subject"))
  330.     Strncpy(subject, p);
  331.  
  332.     /* now, construct a header out of a format string */
  333.     if (!hdr_format)
  334.     hdr_format = DEF_HDR_FMT;
  335.  
  336.     (void) sprintf(buf, "%4.d ", cnt+1);
  337.     b = buf+5;
  338.     *b++ = ((cnt == current_msg && !iscurses)? '>': ' ');
  339.     *b++ = status[0], *b++ = status[1];
  340.     *b++ = ' ';
  341.     /* use the cnt variable to count chars since beginning of buf
  342.      * initialize to 9 (strlen(buf) so far).  This magic number is
  343.      * used in other places in msgs.c and mail.c
  344.      */
  345.     cnt = 9;
  346.     for (p = hdr_format; *p; p++)
  347.     if (*p == '\\')
  348.         switch (*++p) {
  349.         case 't':
  350.             while (cnt % 8)
  351.             cnt++, *b++ = ' ';
  352.         when 'n':
  353.             cnt = 1, *b++ = '\n';
  354.         otherwise: cnt++, *b++ = *p;
  355.         }
  356.     else if (*p == '%') {
  357.         char fmt[64];
  358.         register char *p2 = fmt;
  359.         int len, got_dot = FALSE;
  360.  
  361.         *p2++ = '%';
  362.         if (p[1] != '-')
  363.         *p2++ = '-';
  364.         else
  365.         *++p;
  366.         while (isdigit(*++p) || !got_dot && *p == '.') {
  367.         if (*p == '.')
  368.             got_dot = TRUE;
  369.         *p2++ = *p;
  370.         }
  371.         if (!got_dot && isdigit(p[-1])) {
  372.         int val;
  373.         *p2 = 0; /* assure null termination */
  374.         val = atoi(fmt+1);
  375.         p2 += strlen(sprintf(p2, ".%d", (val >= 0 ? val : -val)));
  376.         }
  377.         *p2++ = 's', *p2 = 0;
  378.         switch (*p) {
  379.         case 'f': p2 = from;
  380.         when 'a':
  381.             if (!*(p2 = addr))
  382.             p2 = from;
  383.         when 'n':
  384.             if (!*(p2 = name))
  385.             p2 = from;
  386.         when '%': p2 = "%";
  387.         when 't': p2 = to;
  388.         when 's': p2 = subject;
  389.         when 'l': p2 = lines;
  390.         when 'c': p2 = chars;
  391.         /* date formatting chars */
  392.         when 'd': p2 = date; /* the full date */
  393.         when 'T': p2 = Tm;
  394.         when 'M': p2 = Mon;
  395.         when 'Y': p2 = Yr;
  396.         when 'N': p2 = Day;
  397.         when 'D': p2 = Wkday;
  398.         otherwise: continue; /* unknown formatting char */
  399.         }
  400.         len = strlen(sprintf(b, fmt, p2));
  401.         cnt += len, b += len;
  402.         /* Get around a bug in 5.5 IBM RT which pads with NULL's not ' ' */
  403.         while (cnt && !*(b-1))
  404.         b--, cnt--;
  405.     } else
  406.         cnt++, *b++ = *p;
  407.     for (*b-- = 0; isspace(*b); --b)
  408.     *b = 0;
  409.     return buf;
  410. }
  411.  
  412. /*
  413.  * Using message "n", build a list of recipients that you would mail to if
  414.  * you were to reply to this message.  If "all" is true, then it will take
  415.  * everyone from the To line in addition to the original sender.
  416.  * fix_address() is caled from mail.c, not from here.  There are too many
  417.  * other uses for reply_to to always require reconstruction of return paths.
  418.  * Note that we do NOT deal with Cc paths here either.
  419.  * Check to make sure that we in fact return a legit address (i.e. not blanks
  420.  * or null). If such a case occurs, return login name.  Always pad end w/blank.
  421.  */
  422. char *
  423. reply_to(n, all, buf)
  424. register char *buf;
  425. {
  426.     register char *p = NULL, *p2, *b = buf, *field;
  427.     char line[256];
  428.  
  429.     if (field = do_set(set_options, "reply_to_hdr")) {
  430. #ifndef MSG_SEPARATOR
  431.     if (!*field)
  432.         goto From; /* special case -- get the colon-less From line */
  433. #endif /* MSG_SEPARATOR */
  434.     field = lcase_strcpy(line, field);
  435.     while (*field) {
  436.         if (p2 = any(field, " \t,:"))
  437.         *p2 = 0;
  438.         if ((p = header_field(n, field)) || !p2)
  439.         break;
  440.         else {
  441.         field = p2+1;
  442.         while (isspace(*field) || *field == ':' || *field == ',')
  443.             field++;
  444.         }
  445.     }
  446.     if (!p)
  447.         print("Warning: message contains no `reply_to_hdr' headers.\n");
  448.     }
  449.     if (p || (!p && ((p = header_field(n, "from")) ||
  450.         (p = header_field(n, "reply-to")) ||
  451.         (p = header_field(n, "return-path")))))
  452.     skipspaces(0);
  453.     else if (!p) {
  454. #ifndef MSG_SEPARATOR
  455. From:
  456.     /* if all else fails, then get the first token in "From" line */
  457.     if (fseek(tmpf, msg[n].m_offset, L_SET) == -1 ||
  458.         !(p2 = fgets(line, sizeof(line), tmpf))) {
  459.         error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
  460.         turnon(glob_flags, READ_ONLY);
  461.         return "";
  462.     }
  463.     p = index(p2, ' ');
  464.     skipspaces(1);
  465.     if (p2 = index(p, ' '))
  466.         *p2 = 0;
  467. #else /* MSG_SEPARATOR */
  468.     wprint("Warning: unable to find who msg %d is from!\n", n+1);
  469. #endif /* MSG_SEPARATOR */
  470.     }
  471.     b += Strcpy(buf, p);
  472.  
  473.     /*
  474.      * if `all' is true, append everyone on the "To:" line.
  475.      * cc_to(), called separately, will catch the cc's
  476.      */
  477.     if (all && (p = header_field(n, "to")) && *p) {
  478.     b += Strcpy(b, ", ");
  479.     /* The assumption that BUFSIZ is correct is unwise, but I know it
  480.      * to be true for Mush.  Be forewarned if you call this routine.
  481.      */
  482.     (void) strncpy(b, p, BUFSIZ - (b - buf) - 2);
  483.     buf[BUFSIZ-3] = 0;
  484.     }
  485.     fix_up_addr(buf);
  486.     take_me_off(buf);
  487.     for (p = buf; *p == ',' || isspace(*p); p++);
  488.     if (!*p)
  489.     (void) strcpy(buf, login);
  490.     return strcat(buf, " ");
  491. }
  492.  
  493. char *
  494. subject_to(n, buf)
  495. register char *buf;
  496. {
  497.     register char *p;
  498.     buf[0] = 0; /* make sure it's already null terminated */
  499.     if (!(p = header_field(n, "subject")))
  500.     return NULL;
  501.     if (strncmp(p, "Re:", 3))
  502.     (void) strcpy(buf, "Re: ");
  503.     return strcat(buf, p);
  504. }
  505.  
  506. char *
  507. cc_to(n, buf)
  508. register char *buf;
  509. {
  510.     register char *p;
  511.     buf[0] = 0; /* make sure it's already null terminated */
  512.     if (!(p = header_field(n, "cc")))
  513.     return NULL;
  514.     fix_up_addr(buf);
  515.     take_me_off(p);
  516.     return strcpy(buf, p);
  517. }
  518.  
  519. /*
  520.  * fix addresses according to the sender's address.  If he's on a remote
  521.  * machine, chances are that the addresses of everyone else he mailed to
  522.  * are addresses from his machine.  Reconstruct those addresses to route
  523.  * thru the senders machine first.
  524.  */
  525. fix_addresses(to, cc)
  526. char *to, *cc;
  527. {
  528.     char pre_path[256], addr[256], name[256], buf[BUFSIZ], c, *p2;
  529.     register char *next, *p, *b = buf, *str;
  530.     int pre_len = 0;
  531.  
  532.     pre_path[0] = 0;
  533.     /* Get the address of the sender (which is always listed first) */
  534.     if (!(next = get_name_n_addr(to, name, addr)))
  535.     return;
  536.  
  537.     /* fix up the sender's address; improve_uucp_path to optimize pre_path */
  538.     improve_uucp_paths(addr);
  539.  
  540.     /* if user didn't route via uucp, pre_path will be blank */
  541.     if (p = rindex(addr, '!')) {
  542.     c = *++p, *p = 0;
  543.     (void) strcpy(pre_path, addr); /* the uucp route he used */
  544.     pre_len = strlen(pre_path);
  545.     *p = c;
  546.     Debug("Routing thru \"%s\"\n", pre_path);
  547.     }
  548.  
  549.     b += Strcpy(b, addr);
  550.     if (*name)
  551.     b += strlen(sprintf(b, " (%s)", name));
  552.     while (*next == ',' || isspace(*next)) /* move next to the next address */
  553.     next++;
  554.     if (*next) /* there's more to come on the To line */
  555.     b += Strcpy(b, ", ");
  556.     else {
  557.     (void) strcpy(to, buf);
  558.     if (!cc || !*cc)
  559.         return;
  560.     }
  561.     for (str = next, c = 0; c < 2; str = cc, c++) {
  562.     if (str == cc)
  563.         b = buf;
  564.     *b = 0; /* null terminate beginning in case there's nothing to do */
  565.     if (!str || !*str)
  566.         continue;
  567.     do  {
  568.         /* get_name returns a pointer to the next address */
  569.         if (!(p = get_name_n_addr(str, name, addr)))
  570.         break;
  571.         /* check to see if there's enough buffer space to add this addr */
  572.         if ((b - buf) + pre_len + strlen(addr) + strlen(name) + 5 >= BUFSIZ)
  573.         break;
  574.         while (p2 = index(addr, '@'))
  575.         *p2++ = '%'; /* '@' has too high precedence for uucp paths */
  576.         /* don't prepend the sender's path unless required */
  577.         if (pre_len && strncmp(addr, pre_path, pre_len))
  578.         b += Strcpy(b, pre_path);
  579.         b += Strcpy(b, addr);
  580.         if (*name)
  581.         b += strlen(sprintf(b, " (%s)", name));
  582.         while (*p == ',' || isspace(*p))
  583.         p++;
  584.         if (*p)
  585.         b += Strcpy(b, ", ");
  586.     } while (*(str = p));
  587.     for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  588.         *b = 0;
  589.     improve_uucp_paths(buf);
  590.     if (c == 0)
  591.         (void) strcpy(to, buf);
  592.     else
  593.         (void) strcpy(cc, buf);
  594.     }
  595. }
  596.  
  597. /*
  598.  * pass a string describing header like, "Subject: ", current value, and
  599.  * whether or not to prompt for it or to just post the information.
  600.  * If do_prompt is true, "type in" the current value so user can either
  601.  * modify it, erase it, or add to it.
  602.  */
  603. char *
  604. set_header(str, curstr, do_prompt)
  605. register char *str, *curstr;
  606. {
  607.     static char       buf[BUFSIZ];
  608.     int        offset = 0;
  609.     register char  *p = curstr;
  610.  
  611.     buf[0] = 0;
  612.     wprint(str);
  613.     fflush(stdout);         /* force str curstr */
  614.     if (do_prompt) {
  615.     if (curstr)
  616.         for (p = curstr; *p; p++)
  617. #ifdef SUNTOOL
  618.         if (istool)
  619.             rite(*p); /* mimics typing for the tool */
  620.         else
  621. #endif /* SUNTOOL */
  622.         if (isoff(glob_flags, ECHO_FLAG))
  623.             fputc((buf[offset++] = *p), stdout);
  624.         else
  625. #ifdef TIOCSTI
  626.             if (ioctl(0, TIOCSTI, p) == -1) {
  627.             error("ioctl: TIOCSTI");
  628.             wprint("You must retype the entire line.\n%s", str);
  629.             break;
  630.             }
  631. #else
  632.                 {
  633.             wprint("WARNING: -e flag! Type the line over.\n%s", str);
  634.             break;
  635.         }
  636. #endif /* TIOCSTI */
  637.  
  638.     if (istool)
  639.         return NULL;
  640.     /* simulate the fact that we're getting input for the letter even tho
  641.      * we may not be.  set_header is called before IS_GETTING is true,
  642.      * but if we set it to true temporarily, then signals will return to
  643.      * the right place (stop/continue).
  644.      */
  645.     {
  646.         u_long getting = ison(glob_flags, IS_GETTING);
  647.         if (!getting)
  648.         turnon(glob_flags, IS_GETTING);
  649.         if (Getstr(buf, sizeof(buf), offset) == -1)
  650.         buf[0] = 0;
  651.         if (!getting)
  652.         turnoff(glob_flags, IS_GETTING);
  653.     }
  654.     } else
  655.     puts(strcpy(buf, curstr));
  656.     if (debug > 1)
  657.     print("returning (%s) from set_header\n", buf);
  658.     return buf;
  659. }
  660.  
  661. /*
  662.  * improve uucp paths by looking at the name of each host listed in the
  663.  * path given.
  664.  *    sun!island!pixar!island!argv
  665.  * It's a legal address, but redundant. Also, if we know we talk to particular
  666.  * hosts via uucp, then we can just start with that host and disregard the path
  667.  * preceding it.  So, first get the known hosts and save them. Then start
  668.  * at the end of the original path (at the last ! found), and move backwards
  669.  * saving each hostname.  If we get to a host that we know about, stop there
  670.  * and use that address.  If we get to a host we've already seen, then
  671.  * delete it and all the hosts since then until the first occurance of that
  672.  * hostname.  When we get to the beginning, the address will be complete.
  673.  *
  674.  * Return all results into the original buffer passed to us.  Since we can
  675.  * at worst not touch the path (shorten it if anything), we know we're not
  676.  * going to overrun the buffer.
  677.  */
  678. improve_uucp_paths(original)
  679. register char *original;
  680. {
  681.     char      *hostnames[128];
  682.     char       name[BUFSIZ], addr[BUFSIZ], buf[BUFSIZ], *knowns, *end;
  683.     register char *p, *recipient, *start = original, *b = buf;
  684.     int           saved_hosts, i;
  685.  
  686.     if (!original || !*original)
  687.     return;
  688.  
  689.     knowns = do_set(set_options, "known_hosts");
  690.  
  691.     while (end = get_name_n_addr(start, name, addr)) {
  692.     saved_hosts = 0;
  693.     /* no uucp path, just user name [@host] with optional name attached */
  694.     if (!(p = rindex(addr, '!'))) {
  695.         char c = *end;
  696.         *end = 0;
  697.         b += Strcpy(b, start); /* copy the entire address with comments */
  698.         *end = c;
  699.         recipient = NULL;
  700.     } else {
  701.         recipient = p+1;
  702.         while (p > addr) {
  703.         /* null the '!' separating the rest of the path from the part
  704.          * of the path preceding it and move p back to the previous
  705.          * '!' (or beginning to addr) for hostname to point to.
  706.          */
  707.         for (*p-- = 0; p > addr && *p != '!'; p--)
  708.             ;
  709.         /* if p is not at the addr, move it forward past the '!' */
  710.         if (p != addr)
  711.             ++p; /* now points to a null terminated hostname */
  712. #ifndef SYSV
  713.         /* if host is ourselves, ignore this and preceding hosts */
  714.         for (i = 0; i < MAX_HOST_NAMES && ourname[i]; i++)
  715.             if (!lcase_strcmp(p, ourname[i]))
  716.             break;
  717.         if (i < MAX_HOST_NAMES && ourname[i])
  718.             break; /* our own host is not included in the path */
  719. #endif /* SYSV */
  720.         /* check already saved hostnames. If host is one of them,
  721.          * delete remaining hostnames since there is a redundant path.
  722.          */
  723.         for (i = 0; i < saved_hosts; i++)
  724.             if (!lcase_strcmp(hostnames[i], p))
  725.                 saved_hosts = i;
  726.  
  727.         hostnames[saved_hosts++] = p;
  728.         /* If we know that we call this host, break */
  729.         if (p == addr || knowns && chk_two_lists(p, knowns, " ,\t"))
  730.             break;
  731.         --p; /* move p back onto the '!'; it will not equal addr */
  732.         }
  733.         while (saved_hosts-- > 0) {
  734.         b += Strcpy(b, hostnames[saved_hosts]);
  735.         *b++ = '!';
  736.         }
  737.         if (recipient)
  738.         b += Strcpy(b, recipient);
  739.         if (*name)
  740.         b += strlen(sprintf(b, " (%s)", name));
  741.     }
  742.     for (start = end; *start == ',' || isspace(*start); start++)
  743.         ;
  744.     if (!*start)
  745.         break;
  746.     b += Strcpy(b, ", ");
  747.     }
  748.     (void) strcpy(original, buf);
  749. }
  750.  
  751. /*
  752.  * rm_cmts_in_addr() removes the comment lines in addresses that result from
  753.  * sendmail or other mailers which append the user's "real name" on the
  754.  * from lines.  See get_name_n_addr().
  755.  */
  756. rm_cmts_in_addr(str)
  757. register char *str;
  758. {
  759.     char addr[BUFSIZ], buf[BUFSIZ], *start = str;
  760.     register char *b = buf;
  761.  
  762.     *b = 0;
  763.     do  {
  764.     if (!(str = get_name_n_addr(str, NULL, addr)))
  765.         break;
  766.     b += Strcpy(b, addr);
  767.     while (*str == ',' || isspace(*str))
  768.         str++;
  769.     if (*str)
  770.         b += Strcpy(b, ", ");
  771.     } while (*str);
  772.     for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  773.     *b = 0;
  774.     (void) strcpy(start, buf);
  775. }
  776.  
  777. /*
  778.  * take_me_off() is intended to search for the user's login name in an
  779.  * address string and remove it.  Note that string should be legal addresses
  780.  * only -- no (comments) allowed here.  See rm_cmts_in_addr().
  781.  * If "metoo" is set, don't touch addr. This implementation is very bug prone
  782.  * because the user's name may be a hostname or the path specified may
  783.  * be incomplete.
  784.  */
  785. take_me_off(str)
  786. char *str;
  787. {
  788.     int i = 0, rm_me;
  789.     char addr[BUFSIZ], buf[BUFSIZ], *start = str;
  790.     char *Alts;
  791.     register char *p, *b = buf;
  792.  
  793.     if (!str || !*str || do_set(set_options, "metoo"))
  794.     return;
  795.  
  796.     Alts = do_set(set_options, "alternates");
  797.  
  798.     *b = 0;
  799.     do  {
  800.     if (!(p = get_name_n_addr(str, NULL, addr)))
  801.         break;
  802.     rm_me = FALSE;
  803.     /* see if user's login is in the address */
  804.     if (!strcmp(login, addr))
  805.         rm_me = TRUE;
  806.     /* if Alts is not set and the above strcmp failed, don't remove him */
  807.     else if (*addr && Alts && *Alts && chk_two_lists(login,addr, "!@%=")) {
  808.         /* To be in this block, there must be a remote address */
  809.         i = 0; /* initialize 'i' in case while loop is skipped */
  810. #ifndef SYSV
  811.         /* see if the hostnames match our hostname. */
  812.         while (i < MAX_HOST_NAMES && ourname[i])
  813.         if (chk_two_lists(addr, ourname[i++], "!@%="))
  814.             break;
  815. #endif /* SYSV */
  816.         /* If one of the hostnames in the address is one of user's
  817.          * hostnames, remove this address. If the alternates
  818.          * hostnames listed contains a hostname in the address, remove
  819.          * from the list.
  820.          */
  821.         if (
  822. #ifndef SYSV
  823.         i < MAX_HOST_NAMES && ourname[i] ||
  824. #endif /* SYSV */
  825.         *Alts == '*' || !chk_two_lists(addr, Alts, "!@%= \t,"))
  826.             rm_me = TRUE;
  827.     }
  828.     if (!rm_me) {
  829.         char c = *p;
  830.         *p = 0;
  831.         b += Strcpy(b, str);
  832.         *p = c;
  833.     }
  834.     while (*p == ',' || isspace(*p))
  835.         p++;
  836.     if (*p && !rm_me)
  837.         b += Strcpy(b, ", ");
  838.     } while (*(str = p));
  839.     for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  840.     *b = 0;
  841.     (void) strcpy(start, buf);
  842. }
  843.  
  844. /*
  845.  * Place commas in between all addresses that don't already have
  846.  * them.  Addresses which use comments which are in parens or _not_
  847.  * within angle brackets *must* already have commas around them or
  848.  * you can't determine what is a comment and what is an address.
  849.  */
  850. fix_up_addr(str)
  851. register char *str;
  852. {
  853.     char buf[BUFSIZ], *start = str, c;
  854.     register char *p, *b = buf;
  855.  
  856.     *b = 0;
  857.     do  {
  858.     /* get_name returns a pointer to the next address */
  859.     if (!(p = get_name_n_addr(str, NULL, NULL)))
  860.         break;
  861.     c = *p, *p = 0;
  862.     if (strlen(str) + (b - buf) >= BUFSIZ - 2) {
  863.         /* print("Address too long! Lost address: \"%s\"\n", str); */
  864.         *p = c;
  865.         break;
  866.     }
  867.     for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  868.         *b = 0;
  869.     for (*p = c; *p == ',' || isspace(*p); p++)
  870.         ;
  871.     if (*p)
  872.         b += Strcpy(b, ", ");
  873.     } while (*(str = p));
  874.     for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  875.     *b = 0;
  876.     (void) strcpy(start, buf);
  877. }
  878.  
  879. /*
  880.  * Get address and name from a string (str) which came from an address header
  881.  * in a message or typed by the user.  The string may contain one or more
  882.  * well-formed addresses.  Each must be separated by a comma.
  883.  *
  884.  * address, address, address
  885.  * address (comment or name here)
  886.  * comment or name <address>
  887.  * "Comment, even those with comma's!" <address>
  888.  * address (comma, (more parens), etc...)
  889.  *
  890.  * This does *not* handle cases like:
  891.  *    comment <address (comment)>
  892.  *
  893.  * find the *first* address here and return a pointer to the end of the
  894.  * address (usually a comma).  Return NULL on error: non-matching parens,
  895.  * brackets, quotes...
  896.  */
  897. char *
  898. get_name_n_addr(str, name, addr)
  899. register char *str, *name, *addr;
  900. {
  901.     register char *p, *p2, *beg_addr = addr, *beg_name = name, c;
  902.  
  903.     if (addr)
  904.     *addr = 0;
  905.     if (name)
  906.     *name = 0;
  907.     if (!str || !*str)
  908.     return NULL;
  909.  
  910.     /* first check to see if there's something to look for */
  911.     if (!(p = any(str, ",(<\""))) {
  912.     /* no comma or indication of a quote character. Find a space and
  913.      * return that.  If nothing, the entire string is a complete address
  914.      */
  915.     if (p = any(str, " \t"))
  916.         c = *p, *p = 0;
  917.     if (addr)
  918.         (void) strcpy(addr, str);
  919.     if (p)
  920.         *p = c;
  921.     return p? p : str + strlen(str);
  922.     }
  923.  
  924.     /* comma terminated before any comment stuff.  If so, check for whitespace
  925.      * before-hand cuz it's possible that strings aren't comma separated yet
  926.      * and they need to be.
  927.      *
  928.      * address address address, address
  929.      *                        ^p  <- p points here.
  930.      *        ^p2 <- should point here.
  931.      */
  932.     if (*p == ',') {
  933.     c = *p, *p = 0;
  934.     if (p2 = any(str, " \t"))
  935.         *p = ',', c = *p2, p = p2;
  936.     if (addr)
  937.         (void) strcpy(addr, str);
  938.     *p = c;
  939.     return p;
  940.     }
  941.  
  942.     /* starting to get hairy -- we found an angle bracket. This means that
  943.      * everything outside of those brackets are comments until we find that
  944.      * all important comma.  A comment AFTER the <addr> :
  945.      *  <address> John Doe
  946.      * can't call this function recursively or it'll think that "John Doe"
  947.      * is a string with two legal address on it (each name being an address).
  948.      */
  949.     if (*p == '<') { /* note that "str" stil points to comment stuff! */
  950.     if (name && *str) {
  951.         *p = 0;
  952.         name += Strcpy(name, str);
  953.         *p = '<';
  954.     }
  955.     if (!(p2 = index(p+1, '>'))) {
  956.         wprint("Warning! Malformed address: \"%s\"\n", str);
  957.         return NULL;
  958.     }
  959.     if (addr) {
  960.         /* to support <addr (comment)> style addresses, add code here */
  961.         *p2 = 0;
  962.         skipspaces(1);
  963.         addr += Strcpy(addr, p);
  964.         while (addr > beg_addr && isspace(*(addr-1)))
  965.         *--addr = 0;
  966.         *p2 = '>';
  967.     }
  968.     /* take care of the case "... <addr> com (ment)" */
  969.     {
  970.         int p_cnt = 0; /* parenthesis counter */
  971.         p = p2;
  972.         /* don't recurse yet -- scan till null, comma or '<'(add to name) */
  973.         for (p = p2; p[1] && (p_cnt || p[1] != ',' && p[1] != '<'); p++) {
  974.         if (p[1] == '(')
  975.             p_cnt++;
  976.         else if (p[1] == ')')
  977.             p_cnt--;
  978.         if (name)
  979.             *name++ = p[1];
  980.         }
  981.         if (p_cnt) {
  982.         wprint("Warning! Malformed name: \"%s\"\n", name);
  983.         return NULL;
  984.         }
  985.     }
  986.     if (name && name > beg_name) {
  987.         while (isspace(*(name-1)))
  988.         --name;
  989.         *name = 0;
  990.     }
  991.     }
  992.  
  993.     /* this is the worst -- now we have parentheses/quotes.  These guys can
  994.      * recurse pretty badly and contain commas within them.
  995.      */
  996.     if (*p == '(' || *p == '"') {
  997.     char *start = p;
  998.     int comment = 1;
  999.     c = *p;
  1000.     /* "str" points to address while p points to comments */
  1001.     if (addr && *str) {
  1002.         *p = 0;
  1003.         while (isspace(*str))
  1004.         str++;
  1005.         addr += Strcpy(addr, str);
  1006.         while (addr > beg_addr && isspace(*(addr-1)))
  1007.         *--addr = 0;
  1008.         *p = c;
  1009.     }
  1010.     while (comment) {
  1011.         if (c == '"' && !(p = index(p+1, '"')) ||
  1012.         c == '(' && !(p = any(p+1, "()"))) {
  1013.         wprint("Warning! Malformed address: \"%s\"\n", str);
  1014.         return NULL;
  1015.         }
  1016.         if (*p == '(') /* loop again on parenthesis. quote ends loop */
  1017.         comment++;
  1018.         else
  1019.         comment--;
  1020.     }
  1021.     /* Something like ``Comment (Comment) <addr>''.  In this case
  1022.      * the name should include both comment parts with the
  1023.      * parenthesis.   We have to redo addr.
  1024.      */
  1025.     if ((p2 = any(p+1, "<,")) && *p2 == '<') {
  1026.         if (!(p = index(p2, '>'))) {
  1027.         wprint("Warning! Malformed address: \"%s\"\n", str);
  1028.         return NULL;
  1029.         }
  1030.         if (addr = beg_addr) { /* reassign addr and compare to null */
  1031.         c = *p; *p = 0;
  1032.         addr += Strcpy(addr, p2+1);
  1033.         while (addr > beg_addr && isspace(*(addr-1)))
  1034.             *--addr = 0;
  1035.         *p = c;
  1036.         }
  1037.         if (name) {
  1038.         c = *p2; *p2 = 0;
  1039.         name += Strcpy(name, str);
  1040.         while (name > beg_name && isspace(*(name-1)))
  1041.             *--name = 0;
  1042.         *p2 = c;
  1043.         }
  1044.     } else if (name && start[1]) {
  1045.         c = *p, *p = 0; /* c may be ')' instead of '(' now */
  1046.         name += Strcpy(name, start+1);
  1047.         while (name > beg_name && isspace(*(name-1)))
  1048.         *--name = 0;
  1049.         *p = c;
  1050.     }
  1051.     }
  1052.     skipspaces(1);
  1053.     /* this is so common, save time by returning now */
  1054.     if (!*p || *p == ',')
  1055.     return p;
  1056.     return get_name_n_addr(p, name, addr);
  1057. }
  1058.