home *** CD-ROM | disk | FTP | other *** search
-
- static char rcsid[] = "@(#)$Id: reply.c,v 5.12 1993/04/12 03:02:05 syd Exp $";
-
- /*******************************************************************************
- * The Elm Mail System - $Revision: 5.12 $ $State: Exp $
- *
- * Copyright (c) 1988-1992 USENET Community Trust
- * Copyright (c) 1986,1987 Dave Taylor
- *******************************************************************************
- * Bug reports, patches, comments, suggestions should be sent to:
- *
- * Syd Weinstein, Elm Coordinator
- * elm@DSI.COM dsinc!elm
- *
- *******************************************************************************
- * $Log: reply.c,v $
- * Revision 5.12 1993/04/12 03:02:05 syd
- * If a To: or Cc: line is split in a comment, that is between ( and ),
- * get_and_expand_everyone won't parse that correctly.
- * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
- *
- * Revision 5.11 1993/04/12 02:34:36 syd
- * I have now added a parameter which controls whether want_to clears the
- * line and centers the question or behaves like it did before. I also
- * added a 0 at the end of the parameter list to all the other calls to
- * want_to where a centered question on a clean line is not desirable.
- * From: Jukka Ukkonen <ukkonen@csc.fi>
- *
- * Revision 5.10 1993/02/06 16:46:06 syd
- * remove outdate include of utsname, no longer needed in reply.c
- * From: Syd
- *
- * Revision 5.9 1993/02/03 19:06:31 syd
- * Remove extra strchr/strcat/strcpy et al declarations
- * From: Syd
- *
- * Revision 5.8 1993/02/03 16:25:45 syd
- * Adresses with double quoted strings that contains comma was parsed
- * wrongly by break_down_tolist() and figure_out_addressee().
- * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
- *
- * Revision 5.7 1992/12/24 21:42:01 syd
- * Fix messages and nls messages to match. Plus use want_to
- * where appropriate.
- * From: Syd, via prompting from Jan Djarv <Jan.Djarv@sa.erisoft.se>
- *
- * Revision 5.6 1992/12/11 01:45:04 syd
- * remove sys/types.h include, it is now included by defs.h
- * and this routine includes defs.h or indirectly includes defs.h
- * From: Syd
- *
- * Revision 5.5 1992/11/26 00:46:50 syd
- * Fix how errno is used so err is inited and used instead
- * as errno gets overwritten by print system call
- * From: Syd
- *
- * Revision 5.4 1992/11/07 20:05:52 syd
- * change to use header_cmp to allow for linear white space around the colon
- * From: Syd
- *
- * Revision 5.3 1992/10/24 13:35:39 syd
- * changes found by using codecenter on Elm 2.4.3
- * From: Graham Hudspith <gwh@inmos.co.uk>
- *
- * Revision 5.2 1992/10/11 01:25:58 syd
- * Add undefs of tolower so BSD macro isnt used from ctype.h
- * From: Syd
- *
- * Revision 5.1 1992/10/03 22:58:40 syd
- * Initial checkin as of 2.4 Release at PL0
- *
- *
- ******************************************************************************/
-
- /*** routine allows replying to the sender of the current message
-
- ***/
-
- #include "headers.h"
- #include "s_elm.h"
- #include <errno.h>
- #include <ctype.h>
-
- #ifdef BSD
- #undef tolower
- #endif
-
- /** Note that this routine generates automatic header information
- for the subject and (obviously) to lines, but that these can
- be altered while in the editor composing the reply message!
- **/
-
- char *strip_parens(), *get_token();
-
- extern int errno;
-
- char *error_description();
-
-
- /* Determine the subject to use for a reply. */
- void
- get_reply_subj(out_subj,in_subj,dflt_subj)
- char *out_subj; /* store the resulting subject here */
- char *in_subj; /* subject of the original message */
- char *dflt_subj; /* default to use if "in_subj" is empty */
- {
- if ( *in_subj == '\0' ) {
- strcpy(out_subj,dflt_subj);
- return;
- }
- if (
- ( in_subj[0] == 'r' || in_subj[0] == 'R' ) &&
- ( in_subj[1] == 'e' || in_subj[1] == 'E' ) &&
- ( in_subj[2] == ':' )
- ) {
- for ( in_subj += 3 ; whitespace(*in_subj) ; ++in_subj ) ;
- }
- strcat( strcpy( out_subj, "Re: " ), in_subj);
- }
-
- int
- optimize_and_add(new_address, full_address)
- char *new_address, *full_address;
- {
- /** This routine will add the new address to the list of addresses
- in the full address buffer IFF it doesn't already occur. It
- will also try to fix dumb hops if possible, specifically hops
- of the form ...a!b...!a... and hops of the form a@b@b etc
- **/
-
- register int len, host_count = 0, i;
- char hosts[MAX_HOPS][SLEN]; /* array of machine names */
- char *host, *addrptr;
-
- if (in_list(full_address, new_address))
- return(1); /* duplicate address */
-
- /** optimize **/
- /* break down into a list of machine names, checking as we go along */
-
- addrptr = (char *) new_address;
-
- while ((host = get_token(addrptr, "!", 1)) != NULL) {
- for (i = 0; i < host_count && ! equal(hosts[i], host); i++)
- ;
-
- if (i == host_count) {
- strcpy(hosts[host_count++], host);
- if (host_count == MAX_HOPS) {
- dprint(2, (debugfile,
- "Error: hit max_hops limit trying to build return address (%s)\n",
- "optimize_and_add"));
- error(catgets(elm_msg_cat, ElmSet, ElmBuildRAHitMaxHops,
- "Can't build return address. Hit MAX_HOPS limit!"));
- return(1);
- }
- }
- else
- host_count = i + 1;
- addrptr = NULL;
- }
-
- /** fix the ARPA addresses, if needed **/
-
- if (qchloc(hosts[host_count-1], '@') > -1)
- fix_arpa_address(hosts[host_count-1]);
-
- /** rebuild the address.. **/
-
- new_address[0] = '\0';
-
- for (i = 0; i < host_count; i++)
- sprintf(new_address, "%s%s%s", new_address,
- new_address[0] == '\0'? "" : "!",
- hosts[i]);
-
- if (full_address[0] == '\0')
- strcpy(full_address, new_address);
- else {
- len = strlen(full_address);
- full_address[len ] = ',';
- full_address[len+1] = ' ';
- full_address[len+2] = '\0';
- strcat(full_address, new_address);
- }
-
- return(0);
- }
-
- void
- get_and_expand_everyone(return_address, full_address)
- char *return_address, *full_address;
- {
- /** Read the current message, extracting addresses from the 'To:'
- and 'Cc:' lines. As each address is taken, ensure that it
- isn't to the author of the message NOR to us. If neither,
- prepend with current return address and append to the
- 'full_address' string.
- **/
-
- char ret_address[SLEN], buf[VERY_LONG_STRING], new_address[SLEN],
- address[SLEN], comment[SLEN], next_line[SLEN];
- int lines, iindex, line_pending = 0, line_len, err;
-
- /** First off, get to the first line of the message desired **/
-
- if (fseek(mailfile, headers[current-1]->offset, 0) == -1) {
- err = errno;
- dprint(1,(debugfile,"Error: seek %ld resulted in errno %s (%s)\n",
- headers[current-1]->offset, error_description(err),
- "get_and_expand_everyone"));
- error2(catgets(elm_msg_cat, ElmSet, ElmSeekFailedFile,
- "ELM [seek] couldn't read %d bytes into file (%s)."),
- headers[current-1]->offset, error_description(err));
- return;
- }
-
- /** okay! Now we're there! **/
-
- /** let's fix the ret_address to reflect the return address of this
- message with '%s' instead of the persons login name... **/
-
- translate_return(return_address, ret_address);
-
- /** now let's parse the actual message! **/
-
- for (lines = headers[current-1]->lines;;) {
-
- /* read in another line if required - break out if end of mbox reached */
- if ( !line_pending && (line_len = mail_gets(buf, SLEN, mailfile)) == 0 )
- return;
- line_pending = 0;
-
- /* break out at end of header or start of next message */
- if ( line_len < 2 )
- return;
- if (buf[line_len - 1] == '\n')
- lines--;
- if (lines <= 0)
- return;
-
- /* we only want lines with addresses */
- if (!header_cmp(buf, "To", NULL) && !header_cmp(buf, "cc", NULL))
- continue;
-
- /* extract the addresses from this line and possible continuation lines */
- next_line[0] = '\0';
-
- /* read in another line - continuation lines start with whitespace */
- while ( (line_len = mail_gets(next_line, SLEN, mailfile)) != 0 &&
- whitespace(next_line[0]) ) {
- no_ret(buf);
- strcat(buf, next_line); /* Append continuation line */
-
- if (next_line[line_len - 1] == '\n')
- lines--;
- next_line[0] = '\0';
- }
-
- no_ret(buf);
- iindex = chloc(buf, ':')+1; /* point beyond header name */
- dprint(2,(debugfile,"> %s\n",buf));
-
- /* go through all addresses in this line */
- while (break_down_tolist(buf, &iindex, address, comment)) {
- if (okay_address(address, return_address)) {
-
- /**
- Some mailers can emit unqualified addresses in the
- headers, e.g. a Cc to a local user might appear as
- just "user" and not "user@dom.ain". We do a real
- low-rent check here. If it looks like a domain
- address then we will pass it through. Otherwise we
- send it back through the originating host for routing.
- **/
- if (qchloc(address, '@') >= 0)
- strcpy(new_address, address);
- else
- sprintf(new_address, ret_address, address);
- optimize_and_add(new_address, full_address);
-
- }
- }
- if (next_line[0] != '\0') {
- strcpy(buf, next_line);
- line_pending++;
- }
- }
- }
-
- int
- reply()
- {
- /** Reply to the current message. Returns non-zero iff
- the screen has to be rewritten. **/
-
- char return_address[SLEN], subject[SLEN];
- int return_value, form_letter;
-
- form_letter = (headers[current-1]->status & FORM_LETTER);
-
- if (get_return(return_address, current-1)) {
- strcpy(subject, headers[current-1]->subject);
- } else {
- get_reply_subj( subject, headers[current-1]->subject,
- ( form_letter ?
- catgets(elm_msg_cat, ElmSet, ElmFilledInForm, "Filled in form") :
- catgets(elm_msg_cat, ElmSet, ElmReYourMail, "Re: your mail") ) );
- }
- if (form_letter)
- return_value = mail_filled_in_form(return_address, subject);
- else
- return_value = send_msg(return_address, "", subject, TRUE, NO, TRUE);
- return(return_value);
- }
-
- int
- reply_to_everyone()
- {
- /** Reply to everyone who received the current message.
- This includes other people in the 'To:' line and people
- in the 'Cc:' line too. Returns non-zero iff the screen
- has to be rewritten. **/
-
- char return_address[SLEN], subject[SLEN];
- char full_address[VERY_LONG_STRING];
- int return_value;
-
- get_return(return_address, current-1);
-
- full_address[0] = '\0'; /* no copies yet */
- get_and_expand_everyone(return_address, full_address);
- dprint(2,(debugfile,
- "reply_to_everyone() - return_addr=\"%s\" full_addr=\"%s\"\n",
- return_address,full_address));
-
- get_reply_subj( subject, headers[current-1]->subject,
- catgets(elm_msg_cat, ElmSet, ElmReYourMail, "Re: your mail"));
-
- return_value = send_msg(return_address, full_address, subject,
- TRUE, NO, TRUE);
- return(return_value);
-
- }
-
- int
- forward()
- {
- /** Forward the current message. What this actually does is
- to temporarily set forwarding to true, then call 'send' to
- get the address and route the mail. Modified to also set
- 'noheader' to FALSE also, so that the original headers
- of the message sent are included in the message body also.
- Return TRUE if the main part of the screen has been changed
- (useful for knowing whether a redraw is needed.
- **/
-
- char subject[SLEN], address[VERY_LONG_STRING];
- int results, edit_msg = FALSE;
-
- forwarding = TRUE;
-
- address[0] = '\0';
-
- if (headers[current-1]->status & FORM_LETTER)
- PutLine0(LINES-3,COLUMNS-40, catgets(elm_msg_cat, ElmSet, ElmNoEditingAllowed,
- "<No editing allowed.>"));
- else {
- MCsprintf(subject, catgets(elm_msg_cat, ElmSet, ElmEditOutgoingMessage,
- "Edit outgoing message? (%c/%c) "), *def_ans_yes, *def_ans_no);
- edit_msg = (want_to(subject,
- *def_ans_yes, LINES-3, 0) != *def_ans_no);
- }
-
- if (strlen(headers[current-1]->subject) > 0) {
-
- strcpy(subject, headers[current-1]->subject);
-
- /* this next strange compare is to see if the last few chars are
- already '(fwd)' before we tack another on */
-
- if (strlen(subject) < 6 || (strcmp((char *) subject+strlen(subject)-5,
- "(fwd)") != 0))
- strcat(subject, " (fwd)");
-
- results = send_msg(address, "", subject, edit_msg,
- headers[current-1]->status & FORM_LETTER?
- PREFORMATTED : allow_forms, FALSE);
- }
- else
- results = send_msg(address, "",
- catgets(elm_msg_cat, ElmSet, ElmForwardedMail, "Forwarded mail..."), edit_msg,
- headers[current-1]->status & FORM_LETTER ? PREFORMATTED : allow_forms, FALSE);
-
- forwarding = FALSE;
-
- return(results);
- }
-
- int
- get_return_name(address, name, trans_to_lowercase)
- char *address, *name;
- int trans_to_lowercase;
- {
- /** Given the address (either a single address or a combined list
- of addresses) extract the login name of the first person on
- the list and return it as 'name'. Modified to stop at
- any non-alphanumeric character. **/
-
- /** An important note to remember is that it isn't vital that this
- always returns just the login name, but rather that it always
- returns the SAME name. If the persons' login happens to be,
- for example, joe.richards, then it's arguable if the name
- should be joe, or the full login. It's really immaterial, as
- indicated before, so long as we ALWAYS return the same name! **/
-
- /** Another note: modified to return the argument as all lowercase
- always, unless trans_to_lowercase is FALSE... **/
-
- /**
- * Yet another note: Modified to return a reasonable name
- * even when double quoted addresses and DecNet addresses
- * are embedded in a domain style address.
- **/
-
- char single_address[SLEN], *sa;
- register int i, loc, iindex = 0,
- end, first = 0;
- register char *c;
-
- dprint(6, (debugfile,"get_return_name called with (%s, <>, shift=%s)\n",
- address, onoff(trans_to_lowercase)));
-
- /* First step - copy address up to a comma, space, or EOLN */
-
- for (sa = single_address; *address; ) {
- i = len_next_part(address);
- if (i > 1) {
- while (--i >= 0)
- *sa++ = *address++;
- } else if (*address == ',' || whitespace(*address))
- break;
- else
- *sa++ = *address++;
- }
- *sa = '\0';
-
- /* Now is it an Internet address?? */
-
- if ((loc = qchloc(single_address, '@')) != -1) { /* Yes */
-
- /*
- * Is it a double quoted address?
- */
-
- if (single_address[0] == '"') {
- first = 1;
- /*
- * Notice `end' will really be the index of
- * the last char in a double quoted address.
- */
- loc = ((end = chloc (&single_address[1], '"')) == -1)
- ? loc
- : end;
- }
- else {
- first = 0;
- }
-
- /*
- * Hope it is not one of those weird X.400
- * addresses formatted like
- * /G=Jukka/S=Ukkonen/O=CSC/@fumail.fi
- */
-
- if (single_address[first] == '/') {
- /* OK then, let's assume it is one of them. */
-
- iindex = 0;
-
- if ((c = strstr (&single_address[first], "/G"))
- || (c = strstr (&single_address[first], "/g"))) {
-
- for (c += 2; *c && (*c++ != '='); );
- for ( ;*c && (*c != '/'); c++) {
- name[iindex++] = trans_to_lowercase
- ? tolower (*c) : *c;
- }
- if (iindex > 0) {
- name[iindex++] = '.';
- }
- }
- if ((c = strstr (&single_address[first], "/S"))
- || (c = strstr (&single_address[first], "/s"))) {
-
- for (c += 2; *c && (*c++ != '='); );
- for ( ;*c && (*c != '/'); c++) {
- name[iindex++] = trans_to_lowercase
- ? tolower (*c) : *c;
- }
- }
- name[iindex] = '\0';
-
- for (c = name; *c; c++) {
- *c = ((*c == '.') || (*c == '-') || isalnum (*c))
- ? *c : '_';
- }
-
- if (iindex == 0) {
- strcpy (name, "X.400.John.Doe");
- }
- return 0;
- }
-
- /*
- * Is it an embedded DecNet address?
- */
-
- while (c = strstr (&single_address[first], "::")) {
- first = c - single_address + 2;
- }
-
-
- /*
- * At this point the algorithm is to keep shifting our
- * copy window left until we hit a '!'. The login name
- * is then located between the '!' and the first meta-
- * character to it's right (ie '%', ':', '/' or '@').
- */
-
- for (i=loc; single_address[i] != '!' && i > first-1; i--)
- if (single_address[i] == '%' ||
- single_address[i] == ':' ||
- single_address[i] == '/' ||
- single_address[i] == '@') loc = i-1;
-
- if (i < first || single_address[i] == '!') i++;
-
- for (iindex = 0; iindex < loc - i + 1; iindex++)
- if (trans_to_lowercase)
- name[iindex] = tolower(single_address[iindex+i]);
- else
- name[iindex] = single_address[iindex+i];
- name[iindex] = '\0';
-
- }
- else { /* easier - standard USENET address */
-
- /*
- * This really is easier - we just cruise left from
- * the end of the string until we hit either a '!'
- * or the beginning of the line. No sweat.
- */
-
- loc = strlen(single_address)-1; /* last char */
-
- for (i = loc; i > -1 && single_address[i] != '!'
- && single_address[i] != '.'; i--) {
- if (trans_to_lowercase)
- name[iindex++] = tolower(single_address[i]);
- else
- name[iindex++] = single_address[i];
- }
- name[iindex] = '\0';
- reverse(name);
- }
- return 0;
- }
-
- int
- break_down_tolist(buf, iindex, address, comment)
- char *buf, *address, *comment;
- int *iindex;
- {
- /** This routine steps through "buf" and extracts a single address
- entry. This entry can be of any of the following forms;
-
- address (name)
- name <address>
- address
-
- Once it's extracted a single entry, it will then return it as
- two tokens, with 'name' (e.g. comment) surrounded by parens.
- Returns ZERO if done with the string...
- **/
-
- char buffer[LONG_STRING];
- register int i, loc = 0, hold_index, len;
-
- if (*iindex > strlen(buf)) return(FALSE);
-
- while (whitespace(buf[*iindex])) (*iindex)++;
-
- if (*iindex > strlen(buf)) return(FALSE);
-
- /** Now we're pointing at the first character of the token! **/
-
- hold_index = *iindex;
-
- if (buf[*iindex] == '"') { /* A quoted string */
- buffer[loc++] = buf[(*iindex)++];
- while (buf[*iindex] != '"' && buf[*iindex] != '\0') {
- if (buf[*iindex] == '\\' && buf[(*iindex)+1] != '\0')
- buffer[loc++] = buf[(*iindex)++]; /* Copy backslash */
- buffer[loc++] = buf[(*iindex)++];
- }
-
- if (buf[*iindex] == '"')
- buffer[loc++] = buf[(*iindex)++]; /* Copy final " */
- }
-
-
- while (buf[*iindex] != ',' && buf[*iindex] != '\0')
- buffer[loc++] = buf[(*iindex)++];
-
- (*iindex)++;
- buffer[loc] = '\0';
-
- while (whitespace(buffer[loc])) /* remove trailing whitespace */
- buffer[--loc] = '\0';
-
- if (strlen(buffer) == 0) return(FALSE);
-
- dprint(5, (debugfile, "\n* got \"%s\"\n", buffer));
-
- if (buffer[loc-1] == ')') { /* address (name) format */
- for (loc = 0, len = strlen(buffer);buffer[loc] != '(' && loc < len; loc++)
- /* get to the opening comment character... */ ;
-
- loc--; /* back up to just before the paren */
- while (whitespace(buffer[loc])) loc--; /* back up */
-
- /** get the address field... **/
-
- for (i=0; i <= loc; i++)
- address[i] = buffer[i];
- address[i] = '\0';
-
- /** now get the comment field, en toto! **/
-
- loc = 0;
-
- for (i = chloc(buffer, '('), len = strlen(buffer); i < len; i++)
- comment[loc++] = buffer[i];
- comment[loc] = '\0';
- }
- else if (buffer[loc-1] == '>') { /* name <address> format */
- dprint(7, (debugfile, "\tcomment <address>\n"));
- for (loc = 0, len = strlen(buffer);buffer[loc] != '<' && loc < len; loc++)
- /* get to the opening comment character... */ ;
- while (whitespace(buffer[loc])) loc--; /* back up */
-
- /** get the comment field... **/
-
- comment[0] = '(';
- for (i=1; i < loc; i++)
- comment[i] = buffer[i-1];
- comment[i++] = ')';
- comment[i] = '\0';
-
- /** now get the address field, en toto! **/
-
- loc = 0;
-
- for (i = chloc(buffer,'<') + 1, len = strlen(buffer); i < len - 1; i++)
- address[loc++] = buffer[i];
-
- address[loc] = '\0';
- }
- else {
- /** the next section is added so that all To: lines have commas
- in them accordingly **/
-
- for (i=0; buffer[i] != '\0'; i++)
- if (whitespace(buffer[i])) break;
- if (i < strlen(buffer)) { /* shouldn't be whitespace */
- buffer[i] = '\0';
- *iindex = hold_index + strlen(buffer) + 1;
- }
- strcpy(address, buffer);
- comment[0] = '\0';
- }
-
- dprint(5, (debugfile, "-- returning '%s' '%s'\n", address, comment));
-
- return(TRUE);
- }
-