/*
 * xmail - X window system interface to the mail program
 *
 * Copyright 1990,1991,1992 by National Semiconductor Corporation
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of National Semiconductor Corporation not
 * be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * NATIONAL SEMICONDUCTOR CORPORATION MAKES NO REPRESENTATIONS ABOUT THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS"
 * WITHOUT EXPRESS OR IMPLIED WARRANTY.  NATIONAL SEMICONDUCTOR CORPORATION
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO
 * EVENT SHALL NATIONAL SEMICONDUCTOR CORPORATION BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Michael C. Wagnitz - National Semiconductor Corporation
 *
 */
#include "global.h"

extern	char	*mailrcFile(void);
String		list = NULL;		/* list of aliases */
int		list_length;		/* size of alias list */



alias_compare(AliasRec **a1, AliasRec **a2)
{
 return(strcmp((*a1)->name, (*a2)->name));
}


/*
** @(#)addtobuf() - add text to recipient or file and folder names buffer
**			drop any leading backslash from a recipient name
*/
void
addtobuf(char *text, char *buffer, char *otherBuffer)
{
 int	current_line_length;
 char	*last_line;


 if (strchr("./+|", *text) != NULL) {
    if (*otherBuffer)
       (void) strcat(otherBuffer, ",");
    (void) strcat(otherBuffer, text);
   } else {
    if (*buffer) (void) strcat(buffer, ", ");
    if (last_line = strrchr(buffer, '\t')) last_line++;
       else last_line = buffer;
    current_line_length = strlen(last_line);
    if (current_line_length && current_line_length + (int)strlen(text) > 71) {
       (void) strcat(buffer, "\n\t");	/* make it a continuation of header */
      }
    (void) strcat(buffer, (*text == '\\') ? &text[1] : text);
   }
} /* addtobuf */


/*
** @(#) de_alias() - move (possibly) comma separated aliases into buffers
*/
void
de_alias(char *text, char *buffer, char *otherBuffer)
{
 char	*cp, *next_alias, buf[BUFSIZ];


 if (strchr(text, ',') == NULL)
    addtobuf(text, buffer, otherBuffer);
 else {
    (void) strncpy(buf, text, BUFSIZ);
    for (next_alias = buf; *next_alias; next_alias = cp) {
        if (cp = strchr(next_alias, ',')) *cp++ = '\0';
        else cp = next_alias + strlen(next_alias);
        addtobuf(next_alias, buffer, otherBuffer);
       }
   }
} /* de_alias */


/*
** @(#)alias() - return alias value(s) from mail, or name if no alias found
*/
char *
alias(char *name)
{
 int		i, n, testing_for_compound_address;
 char		tmp[BUFSIZ], buf[BUFSIZ];
 String		ep, s, p, value;


 tmp[0] = '\0';
 if (name)
    (void) strncpy(tmp, name, BUFSIZ);
/*
** If not already done, extract the mail alias list and build the alias table.
** Provide support for the possibility of multiple names for an alias with or
** without comma separations.
*/
 if (! aliases) {
    if (mailpid) list = XtNewString(QueryMail("alias"));
    else  if (! (list = XtNewString(GetMailrc("alias")))) {
                 list = (String) XtMalloc((unsigned) 2);
                 list[0] = '\0';
               }
/*
** count up the number of aliases in the list and allocate the list size
*/
    ep = &list[strlen(list)];
    for (i = 1, p = list; p < ep; p++) if (*p == '\n') i++;
    aliases = (AliasRec **) XtMalloc((unsigned) (i + 1) * sizeof(AliasRec *));
/*
** Copy the pointers for the alias names and values into an array, marking
** the ends of each with a null and separating any multiple values with a
** comma.  Ensure there is no trailing comma in the value list.
*/
    for (n = 0, p = list; n < i && p < ep; n++, p++) {
        aliases[n] = (AliasRec *) XtMalloc((unsigned) sizeof(AliasRec));
        for (; *p && (*p == ' ' || *p == '\t'); p++);
        for (aliases[n]->name = p; *p && *p != ' ' && *p != '\t'; p++);
        for (*p++ = '\0'; *p && strchr(" \t\"\'", *p); p++);
        testing_for_compound_address = True;
        for (aliases[n]->alias = p; *p && *p != '\n'; p++) {
/*
** If it contains a chevron, parenthesis, or pipe symbol, then just treat
** the whole line as one alias entry.
*/
            if (*p == '|') {
               if (s = strchr(p, '\n'))
                  p = s;
               else p += strlen(p);
               break;
              }
            if (testing_for_compound_address) {
               if ((s = strchr(p, '<')) || (s = strchr(p, '('))) {
                  if (value = strchr(p, '\n')) {	/* could be more than one */
                     if (s < value) {		/* if its within this alias */
                        p = value;		/* point to end of this alias */
                        if (*(p - 1) == '"' ||	/* could be true (no mailpid) */
                            *(p - 1) == '\'')
                            *(p - 1) = '\0';
                        break;
                       } else {
                        if (*s == '<' && (s = strchr(p, '(')) && s < value) {
                           p = value;		/* point to end of this alias */
                           if (*(p - 1) == '"' || /* possibly not in mail */
                               *(p - 1) == '\'')
                               *(p - 1) = '\0';
                           break;
                          } else testing_for_compound_address = False;
                       }
                    } else {			/* last entry of this record */
                     p += strlen(p);		/* point to the end of line */
                     break;
                    }
                 } else testing_for_compound_address = False;
              } /* end - if testing_for_compound_address */
            if ((*p == ' ' || *p == '\t') && *(p+1) && *(p+1) != '\n' &&
               *(p-1) != *p && *(p-1) != ',') *p = ',';
           }
        for (s = p - 1; strchr(", \t", *s); s--);
        if (strchr(", \t", *++s)) *s = '\0';
        if (*p) *p = '\0';
       }
    aliases[n] = NULL;

    if (n)
       qsort((char *)aliases, n, sizeof(char *), alias_compare);
   } /* end - if no alias list */
/*
** In deference to those who didn't install the app-defaults file, accept a
** newline character as a word separator as well as the comma normally found.
** If input is made up of more than one word, check each word for aliasing.
** If it contains a chevron or parenthesis, it is a 'compound' address type.
** If line length approaches 80 characters, add a newline-tab and continue.
*/
 if (*(value = tmp)) {
    buf[0] = '\0';
    for (p = value; *p;) {		/* 'value' points to current 'word' */
        for (s = p; *s && strchr(", \t\n", *s) == NULL; s++);
        if (*s) {
           /*
           ** First, check for case of possible compound address.
           ** Could be a form of either: "(user[ ]name) [<]addr[>]",
           **                            "<addr> [(]user name[)]",
           ** or                         "[<]addr[>] (user name)".
           ** If any of these, skip to end of that address/name.
           */
           if (strchr(" \t", *s)) {
              if (*value == '(') {
                 s = strchr(value, ')');
                 if (*s) {		/* if form is '(user) addr', skip to */
                    for (s++; *s && strchr(" \t", *s); s++); /* start of addr */
                    for (; *s && ! strchr(", \t\n", *s); s++); /* then to end */
                   }
                } else if (*value == '<') {
                 s = strchr(value, '>');
                 if (*s) {
                    /*
                    ** if form is '<addr> [(]user[)]', skip to end of user str
                    */
                    for (s++; *s && strchr(" \t", *s); s++);
                    for (; *s && ! strchr(",\t\n", *s); s++);
                   }
                } else {		/* 'addr (usr)|usr <addr>'? find end */
                 for (; *s && strchr(" \t", *s); s++);
                 if (! strchr("(<", *s)) s--;	/* NOT one of these, back up */
                 else {
                    if (*s == '<')
                       s = strchr(s, '>');	/* find end of <address> or */
                    else
                       s = strchr(s, ')');	/* find end of (user name) */
                    if (s && *s) s++;
                   }
                }
             }
           if (s) *s++ = '\0';
           p = s;
          } else p = &value[strlen(value)];

        for (n = 0; aliases[n]; n++) {
            if (strcmp(value, aliases[n]->name) == 0) {
               de_alias(aliases[n]->alias, buf, otherBuf);
               break;
              }
           }

        if (! aliases[n])	/* If not an alias, use the name supplied. */
           de_alias(value, buf, otherBuf);

        for (; *p && strchr(", \t\n", *p); p++);
        value = p;
       }
    value = buf;
   }
 return ((char *)value);
} /* alias */

/*
** @(#)GetMailEnv() - Get environment value from mail or shell
**                    Accommodate the case of trailing blanks on the item.
**		      Expand environment variables.
*/
char *
GetMailEnv(String item)
{
 int	length;
 String	ep, mailenv, s, c, value, getenv(const char *);
 char		buf[BUFSIZ];


 value = NULL;
 (void) strcpy(buf, item);
 for (length = 0; buf[length] && buf[length] != ' '; length++);
 buf[length] = '\0';

 if (! mailpid) {
    if (! (value = GetMailrc(buf))) {
       if ((s = getenv(buf)) != NULL)
          value = XtNewString(s);
      }
   } else {
    mailenv = QueryMail("set");

    ep = &mailenv[strlen(mailenv)];
    for (s = mailenv; s < ep && strncmp(s, buf, length) != 0; s++)
        for (; s < ep && *s != '\n'; s++);

    if (! *s) {
       if (s = getenv(buf))
          value = XtNewString(s);
      } else {
       /*
       ** Try to accomodate Berkeley mail (versus SunOS) whose ``set''
       ** command returns a sorted list with no equal sign between the
       ** item and its value.
       */
       if (c = strchr(s, '\n')) *c = '\0';
       c = &s[strlen(buf)];			/* point just past the item */
       if (*c != '=') {				/* We're NOT running Sunmail */
          for (;(*c == ' ' || *c == '\t'); c++);
          if (! *c)
             value = XtNewString("True");	/* use boolean if no value */
          else {
             if (! (ep = strchr(c+1, '"')) && ! (ep = strchr(c+1, "'"[0])))
                value = XtNewString(c);		/* most things will be okay */
             else {				/* if value is fully quoted */
                c++;
                *ep = '\0';
                value = XtNewString(c);
               }
            }
         } else {				/* We're are running Sunmail */
          if (! (c = strchr(s, '"'))) c = strchr(s, "'"[0]);
          s = c;
          if (! s)			/* variable is flag only, no value */
             value = XtNewString("True");	/* use boolean if no value */
          else {
             for (c = ++s; *c && *c != *(s - 1); c++);
             length = c - s;
             value = (String) XtMalloc((unsigned) length + 1);
             (void) strncpy(value, s, length);
             value[length] = '\0';
	    }
	 }
      }
   }
  /*
   * Expand shell variables in value
   */
  if (value) {
     String v, e, nvalue;

     while (s = strchr(value, '$')) {
        for (c = s + 1; *c && !strchr(" \t\n$/\"\'", *c); c++);
        length = c - s - 1;
        (void) strncpy(buf, s + 1, length);
        buf[length] = '\0';
        if (*buf == '{') {			/* if variable is braced... */
           for (v = buf, e = buf + 1; *e && *e != '}';) *v++ = *e++;
           *v = '\0';
          }
        if (!(e = getenv(buf)))
	   e = "";
        if (nvalue = (String) XtMalloc((unsigned) strlen(value) - length + strlen(e) + 2)) {
           for (c = nvalue, v = value; v != s;) *c++ = *v++;
           for (s = e; *s;) *c++ = *s++;
           for (v += length + 1; *v;) *c++ = *v++;
           *c = '\0';
           XtFree((String) value);
           value = nvalue;
          }
       }
    }
 return(value);
} /* GetMailEnv */


/*
** @(#)mailrcFile() - Return a path to environment or default .mailrc file
*/
char *
mailrcFile(void)
{
 char	*s, *getenv(const char *);
 static char	buf[BUFSIZ];

 if (s = getenv("MAILRC"))
    (void) strcpy(buf, s);
 else
    (void) sprintf(buf, "%s/.mailrc", HOME);

 return((char *)buf);
} /* mailrcFile */


/*
** @(#)add_to_list - save buffer of aliases in the global alias list
*/
void
add_to_list(String s)
{
 if (! list) {
    list_length = BUFSIZ;
    list = (String) XtMalloc((unsigned) list_length);
    list[0] = '\0';
   }

 for (; *s == ' ' || *s == '\t'; s++);		/* skip leading whitespace */
 if ((int)strlen(list) + (int)strlen(s) + 1 >= list_length) {
    list_length += BUFSIZ;
    list = (String) XtRealloc(list, list_length);
   }
 (void) strcat(list, s);			/* append the entire line */
} /* end - add_to_list */


/*
** @(#)get_mailrc - item and return it's value
**                  Handle continuation lines, source of additional files, more
**                  than one occurance of the item, and aliases/groups.
*/
int
get_mailrc(char *item, char **value, char *filename)
{
 int	unset, n, status = 0;
 int	negated = strncmp(item, "no", 2) == 0 ? 1 : 0;
 int	size = strlen(item) - 2 * negated;
 int	we_want_aliases = (strcmp(item, "alias") == 0);
 String	c, d, getenv(const char *), s = NULL;
 char	e[1], tmp[BUFSIZ], buf[BUFSIZ];
 FILE	*fp = NULL;


 if ((fp = fopen(filename, "r")) != NULL) {
    s = fgets(buf, BUFSIZ, fp);
    while (s) {
       if (strncmp(buf, "source", 6) == 0) {
          bcopy(buf + 7, buf, strlen(buf) - 6);
          LASTCH(buf) = '\0';			/* drop the newline character */
          if (buf[0] == '$' || (buf[0] == '~' && buf[1] == '/')) {
             for (unset = 0, n = 1; buf[n] && buf[n] != '/'; n++)
                 if (! strchr("{()}", buf[n]))
                    tmp[unset++] = buf[n];
             tmp[unset] = '\0';
             if (tmp[0])
                d = getenv(tmp);
             else
                d = HOME;
             (void) sprintf(tmp, "%s%s", d, &buf[n]);
             (void) strcpy(buf, tmp);
            }
          if ((status = get_mailrc(item, &s, buf))) {
             if (we_want_aliases) *value = list;
             else {
                XtFree((String) *value);
                *value = s;
               }
            } /* end - if something was found in another source */
         } else {
          if (we_want_aliases) {
             if (strncmp(buf,"alias",5) == 0 || strncmp(buf,"group",5) == 0) {
                status = 1;		/* we have found at least one alias */
                add_to_list(&buf[5]);
                if (buf[strlen(buf) - 2] == '\\') {	/* alias is continued */
                   list[strlen(list) - 2] = '\0';	/* drop the "\\n" */
                   while (s = fgets(buf, BUFSIZ, fp)) {
                         if ((int)strlen(s) > 1 && s[(int)strlen(s) - 2] == '\\') {
                            (void) strcpy(&buf[strlen(buf) - 2], " "); /* drop "\\n" */
                            add_to_list(s);		/* add continuations */
                           } else break;
                        }
                   if (s) add_to_list(s);	/* don't forget the last line */
                  } /* end - if this is a continued alias definition line */
               } /* end - if we found a match */
             *value = list;
            } else {		/* I'm looking for 'set' or 'unset' records */
             if (! (strncmp(buf, "set", 3) && strncmp(buf, "unset", 5))) {
                unset = (buf[0] == 'u')? 1:0;	/* find out which one matched */
                s = &buf[(unset?5:3)];		/* starting just beyond 'set' */
                while (*s) {			/* could be multiple assigns */
                   for (; *s && strchr(" \t\\\n", *s); s++);	/* next word */
                   if (*s) {
                      if ((strncmp(s, item, size) != 0) && (!unset ||
                         !negated || strncmp(s, &item[2], size) != 0))
                         for (;*s&&!strchr(" \t\\\n",*s);s++); /* skip item */
                      else {
                         status = 1;		/* we have at least one match */
                         s = s + size;		/* look for a value to it */
                         if (*s++ != '=') {	/* variable only, no value */
                            XtFree((String) *value);
                            *value = (unset && ! negated)? NULL: XtNewString("True");
                           } else {
                            if (*s == '"' || *s == "'"[0])	/* if quoted */
                               for (c = s + 1; *c && *c != *s; c++);
                            else
                               for (c = s--; *c && !strchr(" \t\\\n", *c); c++);
                            e[0] = *c;		/* save the current character */
                            *c = '\0';		/* mark the end of the value */
                            d = c + 1;		/* save point after value */
                            c = s + 1;		/* point to start of value */
                            XtFree((String) *value);	/* toss any previous */
                            *value = XtNewString(c);	/* keep latest value */
                            s = d--;		/* look for any more matches */
                            *d = e[0];		/* restore saved character */
                           } /* end - if boolean variable or valued item */
                        } /* end - we have a match */
                     } /* end - if some word(s) still exist on the line */
                   if (! *s && buf[strlen(buf) - 2] == '\\') /* if continued */
                      s = fgets(buf, BUFSIZ, fp);	/* then keep looking */
                  } /* end - while examining this set|unset record */
               } /* end - if we have a set|unset record */
            } /* end - looking for alias or set/unset records */
         } /* end - if not a ``source'' record */
       s = fgets(buf, BUFSIZ, fp);	/* now read the next line of the file */
      } /* end - while something to read in the mailrc file */
    (void) fclose(fp);
   }
 return(status);
}
/* end - get_mailrc */


/*
** @(#)GetMailrc() - Get mail environment variable value from the mailrc file
**		     Added support for source'd files within the .mailrc file
*/
char *
GetMailrc(char *item)
{
 char	*s = NULL;

 (void) get_mailrc(item, &s, mailrcFile());

 return((char *)s);
} /* GetMailrc */
