/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Building, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989, 1990, 1991, 1992  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON 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, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  $Revision: 2.13 $   $State: Exp $          *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/

/*======================================================================
     init.c
     Routines for pine start up and initialization
       init_vars
       init_username
       init_hostname
       read_pinerc
       write_pinerc
       init_mail_dir

       sent-mail expiration
 
       open debug files 

      - open and set up the debug files
      - fetch username, password, and home directory
      - get host and domain name
      - read and write the users .pinerc config file
      - create the mail subdierctory
      - expire old sent-mail

  ====*/


#include "headers.h"


extern char version_buff[];

typedef enum {ParseLocal, ParseGlobal} ParsePinerc;

#ifdef ANSI
static int  read_pinerc(char *, struct variable *, ParsePinerc);
static void init_v(struct variable *, int);
/* AIX gives warning here 'cause it can't quite cope with enums */
#else
static int read_pinerc();
static void init_v();
#endif




/*----------------------------------------------------------------------
These are the variables that control a number of pine functions.  They
come out of the .pinerc and the /usr/local/lib/pinerc files. Some can
be se by the user while in Pine. Eventually all the local ones should
be so and maybe the global ones too.

Each variable can have a user, global and current value. All of these
values are malloced. The user value is the one read ouf of the users
.pinerc, the global value is the one from the system pine
configuration file.  Perhaps someday there will be group values. The
current value is the one that is actually in use. The current value
can come from the user value, the global value or neither in which
case it is NULL or the default system value is used (the default
system is different from the global value; the system value may come
from the password file or such).

  ----*/

static struct variable variables[] = {
#ifdef DOS /* Have to have this on DOS, PC's, Macs, etc... */
"user-id",                    1, 0, 1, 0, NULL, NULL, NULL,
   "Your login/e-mail user name",
#else /* Don't allow this on UNIX T/S machines for some security */
"user-id",                    0, 0, 1, 0, NULL, NULL, NULL,
   "Your login/e-mail user name",
#endif
"personal-name",              1, 0, 1, 0, NULL, NULL, NULL,
   "Your full name",
"printer",                    1, 0, 1, 1, NULL, NULL, NULL,
   "Your printer selection",
"personal-print-command",     1, 0, 1, 0, NULL, NULL, NULL, 
   "Special print command if it isn't one of the standard printers",
"standard-printer",           1, 0, 0, 1, NULL, NULL, NULL,
   "The system wide standard printer",
"last-time-prune-questioned" ,1, 0, 1, 0, NULL, NULL, NULL, 
   "Date last time you were asked about deleting old sent-mail",
"user-domain",                1, 0, 1, 1, NULL, NULL, NULL, 
   "Domain name you are in  e.g. nwnet.net, cac.washington.edu, bwc.org",
"use-only-domain-name",       1, 0, 1, 1, NULL, NULL, NULL,
   "Eliminate host part from hostname, using only domain part for domain name",
"inbox-path",                 1, 0, 1, 1, NULL, NULL, NULL, 
   "Name/path of inbox. (Folder path name or \"{host}inbox\" for remote IMAP inbox)",
"elm-style-save",             1, 0, 1, 1, NULL, NULL, NULL,
   "If \"yes\" default folder name to sender when saving mail",
"header-in-reply",            1, 0, 1, 1, NULL, NULL, NULL,
   "Include message header in inclusion of original mail when replying",
"default-fcc",                1, 0, 1, 1, NULL, NULL, NULL,
   "The default folder where a copy of outgoing mail is saved",
"bugs-nickname",              1, 0, 0, 1, NULL, NULL, NULL,
   "Nickname of bugs entry to be automatically placed in all address books",
"bugs-fullname",              1, 0, 0, 1, NULL, NULL, NULL,
   "Full name of bugs entry to be automatically placed in all address books",
"bugs-address",               1, 0, 0, 1, NULL, NULL, NULL, 
   "Address of bugs entry to be automatically placed in all address books",
"smtp-server",                1, 0, 1, 1, NULL, NULL, NULL,
   "Name of SMTP server for sending mail. If blank, sendmail will be used",
"editor",                     1, 0, 1, 1, NULL, NULL, NULL,
   "Editor to use in place of Pine's internal composer, Pico",
"image-viewer",               1, 0, 1, 1, NULL, NULL, NULL,
   "Program to view images if format such as GIF and TIFF",
"feature-level",              1, 0, 1, 1, NULL, NULL, NULL,
   "\"seedling\", \"sapling\" or \"old-growth\" for novice, intermediate and advanced",
"old-style-reply",            1, 0, 1, 1, NULL, NULL, NULL,
   "Use old style forward/reply with new text and signature below included text",
"signature-file",             1, 0, 1, 1, NULL, NULL, NULL,
   "Name of file to read signature out of for inclusion in outgoing mail",
"mail-directory",             1, 0, 1, 1, NULL, NULL, NULL,
   "Sub directory in users home directory where mail folders/files are kept",
"compose-mime",               1, 0, 0, 1, NULL, NULL, NULL,
 "Allow composition of MIME message (attachments) when this is set to \"yes\"",
"character-set",              1, 0, 1, 1, NULL, NULL, NULL,
  "Character set used by your terminal e.g. US-ASCII, ISO-8859-1, ISO-8859-4",
"show-all-characters",        1, 0, 1, 1, NULL, NULL, NULL,
  "Will display all text without checking whether or not it is displayable",
"new-version",                1, 0, 0, 1, NULL, NULL, NULL,
  "If set to \"no\" messages about new versions of Pine will be supressed",
 NULL,                        1, 0, 0, 0, NULL, NULL, NULL,

};


static struct pinerc_line {
  union {
      char *stuff;
      struct variable *var;
  } line;
  unsigned int  is_var:1;
} *pinerc_lines = NULL;


/*----------------------------------------------------------------------
     Initialize the variables

 Args:   ps   -- The usual pine structure

 Result: 

  This reads the system pine configuration file and the users pine
configuration file ".pinerc" and places the results in the variables 
structure. It sorts out what was read and sets a few other variables 
based on the contents.
  ----*/
void 
init_vars(ps)
     struct pine *ps;
{
    char buf[MAXPATH +1];
    struct variable *vars;

    ps->vars = vars = variables;
    /*--- The defaults here are defined in os-xxx.h so they can vary
          per machine ---*/
    vars[V_PRINTER].global_val          = cpystr(DF_DEFAULT_PRINTER);
    vars[V_STANDARD_PRINTER].global_val = cpystr(DF_STANDARD_PRINTER);
    vars[V_ELM_STYLE_SAVE].global_val   = cpystr(DF_ELM_STYLE_SAVE);
    vars[V_HEADER_IN_REPLY].global_val  = cpystr(DF_HEADER_IN_REPLY);
    vars[V_INBOX_PATH].global_val       = cpystr("inbox");
    vars[V_DEFAULT_FCC].global_val      = cpystr(DF_DEFAULT_FCC);
    vars[V_USE_ONLY_DOMAIN_NAME].global_val = cpystr(DF_USE_ONLY_DOMAIN_NAME);
    vars[V_FEATURE_LEVEL].global_val    = cpystr(DF_FEATURE_LEVEL);
    vars[V_OLD_STYLE_REPLY].global_val  = cpystr(DF_OLD_STYLE_REPLY);
    vars[V_SIGNATURE_FILE].global_val   = cpystr(DF_SIGNATURE_FILE);
    vars[V_MAIL_DIRECTORY].global_val   = cpystr(DF_MAIL_DIRECTORY);

#ifdef	DOS
    if(getenv("PINERC"))
      read_pinerc(getenv("PINERC"), vars, ParseLocal);
    else
      read_pinerc(SYSTEM_PINERC, vars, ParseLocal);
#else
    read_pinerc(SYSTEM_PINERC, vars, ParseGlobal);

    build_path(buf, ps->home_dir, ".pinerc");
    read_pinerc(buf, vars, ParseLocal);
#endif

    init_v(vars, V_INBOX_PATH);

    init_v(vars, V_ELM_STYLE_SAVE);
    ps->elm_style_save = !strucmp(vars[V_ELM_STYLE_SAVE].current_val, "yes");

    init_v(vars, V_USER_DOMAIN);
    init_v(vars,V_USE_ONLY_DOMAIN_NAME);

    init_v(vars,V_HEADER_IN_REPLY);
    ps->header_in_reply = !strucmp(vars[V_HEADER_IN_REPLY].current_val, "yes");

    init_v(vars,V_PRINTER);

    vars[V_PERSONAL_PRINT_COMMAND].current_val =
         cpystr(vars[V_PERSONAL_PRINT_COMMAND].user_val);

    vars[V_STANDARD_PRINTER].current_val = 
         cpystr(vars[V_STANDARD_PRINTER].global_val);


    if(vars[V_LAST_TIME_PRUNE_QUESTION].user_val != NULL) {
        vars[V_LAST_TIME_PRUNE_QUESTION].current_val =
          cpystr(vars[V_LAST_TIME_PRUNE_QUESTION].user_val);
        /* The month value in the file runs from 1-12, the variable here
           runs from 0-11; the value in the file used to be 0-11, but we're 
           fixing it in January */
        ps->last_expire_year  = atoi(ps->VAR_LAST_TIME_PRUNE_QUESTION);
        ps->last_expire_month = atoi(strindex(ps->VAR_LAST_TIME_PRUNE_QUESTION,
                                              '.') + 1);
        if(ps->last_expire_month == 0) {
            /* Fix for 0 because of old bug */
            char buf[10];
            sprintf(buf, "%d.%d", ps_global->last_expire_year,
              ps_global->last_expire_month + 1);
            set_variable(V_LAST_TIME_PRUNE_QUESTION, buf);
        } else {
            ps->last_expire_month--; 
        } 
    } else {
        ps->last_expire_year  = -1;
        ps->last_expire_month = -1;
    }

    ps->VAR_BUGS_NICKNAME = cpystr(vars[V_BUGS_NICKNAME].global_val);
    ps->VAR_BUGS_FULLNAME = cpystr(vars[V_BUGS_FULLNAME].global_val);
    ps->VAR_BUGS_ADDRESS  = cpystr(vars[V_BUGS_ADDRESS].global_val);

    init_v(vars,V_DEFAULT_FCC);
    init_v(vars,V_SMTP_SERVER);
    init_v(vars,V_EDITOR);
    init_v(vars,V_IMAGE_VIEWER);

    init_v(vars, V_FEATURE_LEVEL);
    if(strucmp(vars[V_FEATURE_LEVEL].current_val, "seedling") == 0)
      ps->feature_level = Seedling;
    else if(strucmp(vars[V_FEATURE_LEVEL].current_val, "old-growth") == 0)
      ps->feature_level = Seasoned;
    else
      ps->feature_level = Sapling;

    init_v(vars, V_OLD_STYLE_REPLY);
    init_v(vars, V_SIGNATURE_FILE);
    init_v(vars, V_MAIL_DIRECTORY);
    
    init_v(vars, V_COMPOSE_MIME);
    ps->compose_mime = strucmp(vars[V_COMPOSE_MIME].current_val, "no");

    init_v(vars, V_CHAR_SET);
    init_v(vars, V_SHOW_ALL_CHARACTERS);
    if(vars[V_SHOW_ALL_CHARACTERS].current_val != NULL)
      ps->show_all_characters = strucmp(vars[V_SHOW_ALL_CHARACTERS].current_val,
                                      "yes") == 0;
    else
      ps->show_all_characters = 0;

    init_v(vars, V_NEW_VERSION);
    if(vars[V_NEW_VERSION].current_val != NULL)
      ps->show_new_version = strucmp(vars[V_NEW_VERSION].current_val,
                                      "yes") == 0;
    else
      ps->show_new_version = 1;
    

    /* The other actual current values are processed on a case by
       case basis below in other functions*/

#ifdef DEBUG
#define STRING(vv) ((vv) == NULL ? "<unset>" : (vv))
    for(vars = variables; vars->name != NULL; vars++) {
        dprint(2, (debugfile, "%17.17s : %18.18s %18.18s %18.18s\n",
                   vars->name, STRING(vars->user_val),
                   STRING(vars->global_val),STRING(vars->current_val)));
    }
#endif
}




static void    
init_v(vars, v_no)
  struct variable *vars;
  int              v_no;
{
    vars[v_no].current_val =
      cpystr(expand_variables(tmp_20k_buf,
        vars[v_no].user_val != NULL ? vars[v_no].user_val :
                                      vars[v_no].global_val
                             ));
}
                                                     


/*----------------------------------------------------------------------

	Expand Metacharacters/variables in file-names

   Read input line and expand shell-variables/meta-characters

	<input>		<replaced by>
	${variable}	getenv("variable")
	$variable	getenv("variable")
	~		getenv("HOME")
	\c		c
	<others>	<just copied>

NOTE handling of braces in ${name} doesn't check much or do error recovery
	
  ----*/

char *
expand_variables(lineout, linein)
char *linein, *lineout;
{
    char *src=linein, *dest=lineout, *p;
    char word[128];

    if(linein == NULL)
      return(NULL);

    while ( *src ) { /* something in input string */
        if (*src == '\\' ) {        /* backslah to escape char */
            *dest++ = *++src;        /* copy next as is */
        } else if (*src == '~') {
            if ((p=getenv("HOME")) !=NULL)     /* HOME directory*/
                while (*p)
                    *dest++ = *p++;
        } else if (*src == '$') {    /* shell variable */
            int found_brace=0;
            
            src++;            /* skip dollar */
            p=word;
            if (*src == '{') {    /* starts with brace? */
                src++;        
                found_brace=1;
            }
            while (isalnum(*src))    /* copy to word */
                *p++ = *src++;
            if ( found_brace ) { /* look foor clkosing  brace */
                while (*src && *src != '}')
                    src++;    /* skip until brace */
                if(*src == '}')
                  src++;
            }
            *p='\0';
                    if ((p=getenv(word)) !=NULL) /* HOME directory*/
                while (*p)
                    *dest++ = *p++;
            continue;
        } else { /* other cases: just copy */
            *dest++ = *src;
        }
        if (*src)    /* next character (if any) */
            src++;
    }
    *dest = '\0';
    return(lineout);
}




/*----------------------------------------------------------------------
    Sets  login, full_username and home_dir

   Args: ps -- The Pine structure to put the user name, etc in

  Result: sets the fullname, login and home_dir field of the pine structure
          returns 0 on success, -1 if not.
  ----*/

init_username(ps)
     struct pine *ps;
{
    char fld_dir[MAXPATH+1];
    int  rv;

    rv = 0;


    /* Look up password first because it forces a real read of 
       passwd file
     */
    if(get_system_passwd() != NULL) {
        ps->passwd = cpystr(get_system_passwd());
    } else {
        ps->passwd = NULL;
    }

#ifdef DOS
    if(ps->vars[V_USER_ID].user_val != NULL) {
        ps->VAR_USER_ID =  cpystr(ps->vars[V_USER_ID].user_val);
    } else {
/* on a PC, it's probably okto ask for the user's name since they
 * could just as easily change it in some config file
 */
/* what's here needs work! */
printf("Who are you?  Input login name: ");
ps->VAR_USER_ID=cpystr(gets(fld_dir));
    }
#else
    if(get_system_login() != NULL) {
        ps->VAR_USER_ID = cpystr(get_system_login());
    } else {
        fprintf(stderr, "Who are you? (Unable to look up login name)\n");
        ps->VAR_USER_ID = cpystr("");
        rv = -1;
    }
#endif

    if(ps->vars[V_PERSONAL_NAME].user_val != NULL) {
        ps->VAR_PERSONAL_NAME = cpystr(expand_variables(tmp_20k_buf,
                                        ps->vars[V_PERSONAL_NAME].user_val));
    } else if(get_system_fullname() != NULL) {
        ps->VAR_PERSONAL_NAME = cpystr(get_system_fullname());
    } else {
        ps->VAR_PERSONAL_NAME = cpystr("");
    }


    if(strlen(ps->home_dir) + strlen(ps->VAR_MAIL_DIRECTORY)+2 > MAXPATH) {
        printf("Folders directory name is longer than %d\n", MAXPATH);
        printf("Directory name: \"%s/%s\"\n",ps->home_dir,
               ps->VAR_MAIL_DIRECTORY);
        return(-1);
    }
    build_path(fld_dir, ps->home_dir, ps->VAR_MAIL_DIRECTORY);
    ps->folders_dir = cpystr(fld_dir);

    dprint(1, (debugfile, "Userid: %s\nFullname: \"%s\"\nPassword: %s\n",
               ps->VAR_USER_ID, ps->VAR_PERSONAL_NAME, ps->passwd));
    return(rv);
}




/*----------------------------------------------------------------------
        Fetch the hostname of the current system and put it in pine struct

   Args: ps -- The pine structure to put the hostname, etc in

  Result: hostname, localdomain, userdomain and maildomain are set


** Pine uses the following set of names:
  hostname - The fully qualified hostname. Obtained with
             gethostbyname() which reads /etc/hosts or does a DNS
             lookup. This may be blank.
  localdomain - The domain name without the host. Obtained from the
             above hostname if it has a "." in it. Removes first
             segment. If hostname has no "." in it then the hostname
             is used. This may be blank.
  userdomain - Explicitly configured domainname. This is read out of
             the global or  users .pinerc. The users entry in the .pinerc
             overrides.


** Pine has the following uses for such names:

  1. On outgoing messages in the From: line
         Uses userdomain if there is one. If not uses localdomain
         unless Pine has been configured to use full hostnames


  2. When expanding/fully-qualifying unqualified addresses during
     composition
         (same as 1)
         Uses userdomain if there is one. If not uses localdomain
         unless Pine has been configured to use full hostnames


  3. When expanding/fully-qualifying unqualified addresses during
     composition when a local entry in the password file exists for
     name.
         If no userdomain is given, then this lookup is always done.
         Localdomain or hostname is used to qualify this address.
         If userdomain matches localdomain then the local lookup is
         done and localdomain is used on these addresses. Pine may
         also be configured to do local lookup even if the user
         has placed himself in another domain. In that case the lookup
         is kind of a strange thing to do, but if so desirest...

  4. In determining if an address is that of the current pine user for
     formatting index and filtering addresses when replying
         If a userdomain is specified the address must match the
         userdomain exactly. If a userdomain is not specified or the
         userdomain is the same as the hostname or domainname, then
         an address will be considered the users if it matches either
         the domainname or the hostname. Of course the userid must match
         too. 
        
  5. In Message ID's
         The fully qualified hostname is always users here.


** Setting the domain names
  To set the domain name for all Pine users on the system to be
different from what Pine figures out from DNS, put the domain name in
/usr/local/lib/domain. To set the domain name for an individual user,
set the "user-domain" variable in his .pinerc. The .pinerc setting
overrides any other setting.

  Pine can use either just the domain name or the full hostname.
Currently this is a compile time option. #define USE_FULL_HOSTNAME in
sysdefs.h to use the full hostname, otherwise the domain name with out
the host will be used.
 
  Currently, the override to do local name lookups even if the
userdomain is set to place the user out of the local domain can not be
set (except by modifying the source code).
 ----*/
init_hostname(ps)
     struct pine *ps;
{
    char            hostname[MAX_ADDRESS+1], domainname[MAX_ADDRESS+1];


    ps->use_full_hostname = strucmp(ps->VAR_USE_ONLY_DOMAIN_NAME,"yes");

    getdomainnames(hostname, MAX_ADDRESS, domainname, MAX_ADDRESS);
    ps->hostname    = cpystr(hostname);
    ps->localdomain = cpystr(domainname);
    ps->userdomain  = NULL;

    if(ps->VAR_USER_DOMAIN != NULL) 
      ps->userdomain = cpystr(ps->VAR_USER_DOMAIN);

    if(ps->userdomain != NULL) {
        ps->maildomain = ps->userdomain;
        if(strcmp(ps->userdomain, ps->localdomain) == 0 ||
           strcmp(ps->userdomain, ps->hostname) == 0)
          ps->do_local_lookup = 1;
        else
          ps->do_local_lookup = 0;
    } else {
        ps->do_local_lookup = 1;
        ps->maildomain = ps->use_full_hostname ? ps->hostname :
                                                 ps->localdomain;
    }

    dprint(1, (debugfile,"User domain name being used \"%s\"\n",
               ps->userdomain == NULL ? "" : ps->userdomain));
    dprint(1, (debugfile,"Local Domain name being used \"%s\"\n",
               ps->localdomain));
    dprint(1, (debugfile,"Host name being used \"%s\"\n",
               ps->hostname));
    dprint(1, (debugfile,"Mail Domain name being used \"%s\"\n",
               ps->maildomain));

    if(ps->maildomain == NULL || strlen(ps->maildomain) == 0) {
        fprintf(stderr, "No host name or domain name set\n");
        return(-1);
    } else {
        return(0);
    }
}



/*----------------------------------------------------------------------
         Read and parse a pinerc file
  
   Args:  Filename   -- name of the .pinerc file to open and read
          vars       -- The vars structure to store values in
          which_vars -- Whether the local or global values are being read

   Result: 

 This may be the local file or the global file. The values found are
merged with the values currently in vars.  All values are strings and
are malloced; and existing values will be freed before the assignment.
Those that are <unset> will be left unset; their values will be NULL.
  ----*/
static int
read_pinerc(filename, vars, which_vars)
     char *filename;
     ParsePinerc which_vars;
     struct variable *vars;
{
    char               *file, *value, *line;
    register char      *p, *p1;
    struct variable    *v;
    struct pinerc_line *pl;
    int                 line_count;

    dprint(1, (debugfile, "reading_pinerc \"%s\"\n", filename));

    file = read_file(filename);
    if(file == NULL) {
        dprint(1, (debugfile, "Open failed: %s\n", error_description(errno)));
        return(-1);
    }

    dprint(1, (debugfile, "Read %d characters\n", strlen(file)));

    /*--- Count up lines and allocate structures */
    for(line_count = 0, p = file; *p != '\0'; p++)
      if(*p == '\n')
        line_count++;
    pinerc_lines = (struct pinerc_line *)
               fs_get((1 + line_count) * sizeof(struct pinerc_line));
    pl = pinerc_lines;

    for(p = file; *p != '\0';) {
        /*----- Grab the line ----*/
        line = p;
        while(*p && *p != '\n')
          p++;
        if(*p == '\n') {
            *p++ = '\0';
        }

        /*----- Comment Line -----*/
        if(*line == '#') {
            if(which_vars == ParseLocal){
                pl->is_var = 0;
                pl->line.stuff = cpystr(line);
                pl++;
            }
            continue;
        }

        /*----- look up the variable ----*/
        for(v = vars; v->name != NULL; v++) {
            if(strncmp(v->name, line, strlen(v->name)) == 0)
              break;
        }

        /*----- Didn't match any variable -----*/
        if(v->name == NULL || !v->is_used){
            if(which_vars == ParseLocal){           
                pl->is_var = 0;
                pl->line.stuff = cpystr(line);
                pl++;
            }
            continue;
        }

        /*---- Matched, but the rest of the line is garbage ----*/
        p1 = line + strlen(v->name);
        while(*p1 && (*p1 == '\t' || *p1 == ' '))
          p1++;
        if(*p1 != '=') {
            if(which_vars == ParseLocal){           
                pl->is_var = 0;
                pl->line.stuff = cpystr(line);
                pl++;
            }
        }
        p1++;

        /*----- Matched a variable, get it's value ----*/
        while(*p1 == ' ' || *p1 == '\t')
          p1++; /* skipping leading space */
        value = p1;

        /*---- variable is unset ----*/
        if(*value == '\0') {
            if(v->is_user && which_vars == ParseLocal) {
                pl->is_var   = 1;
                pl->line.var = v;
                pl++;
            }
            continue;
        }
          

        /*-- value is non empty store it handling quotes and trailing space--*/
        if(*value == '"') {
            value++;
            for(p1 = value; *p1 && *p1 != '"'; p1++);
            if(*p1 == '"')
              *p1 = '\0';
            else
              removing_trailing_white_space(value);
        } else {
            removing_trailing_white_space(value);
        }


        if(which_vars == ParseLocal){
            if(v->is_user) {
                if(v->user_val != NULL)
                  fs_give((void **) &(v->user_val));
                v->user_val  = cpystr(value);
                pl->is_var   = 1;
                pl->line.var = v;
                pl++;
            }
        } else {
            if(v->is_global) {
                if(v->global_val != NULL)
                  fs_give((void **) &(v->global_val));
                v->global_val = cpystr(value);
            }
        }

        dprint(1,(debugfile, "pinerc : %12.12s : %20.20s : \"%s\"\n",
                filename, v->name, which_vars == ParseLocal ? v->user_val :
                                                             v->global_val));
    }
    pl->line.stuff = NULL;
    pl->is_var = 0;
    fs_give((void **)&file);
    return(0);
}



/*----------------------------------------------------------------------
    Write out the .pinerc state information

   Args: ps -- The pine structure to take state to be written from

  This writes to a temporary file first, and then renames that to 
 be the new .pinerc file to protect against disk error. This has the 
 problem of possibly messing up file protections, ownership and links.
  ----*/

write_pinerc(ps)
     struct pine *ps;
{
     char                buf[MAXPATH+1], buf2[MAXPATH+1];
     FILE               *f;
     struct pinerc_line *pl;
     struct variable    *var;

#ifdef	DOS
     build_path(buf,  ps->home_dir, "pinerc.tmp");

     f = fopen(buf, "wt");
#else
     build_path(buf,  ps->home_dir, ".pinerc.temp");

     f = fopen(buf, "w");
#endif
     if(f == NULL) 
       goto io_err;

     for(var = ps->vars; var->name != NULL; var++) 
         var->been_written = 0;


     if(pinerc_lines != NULL) {
         for(pl = pinerc_lines; pl->is_var!=0 || pl->line.stuff!=NULL; pl++){
             if(pl->is_var){
                 if(pl->line.var->user_val == NULL) {
                     if(fprintf(f, "%s=\n", pl->line.var->name) == EOF)
                       goto io_err;
                 } else if(strlen(pl->line.var->user_val) == 0) {
                     if(fprintf(f, "%s=\"\"\n", pl->line.var->name) == EOF)
                       goto io_err;
                 } else {
                     if(fprintf(f, "%s=%s\n", pl->line.var->name,
                                              pl->line.var->user_val) == EOF)
                       goto io_err;
                 }
                 pl->line.var->been_written = 1;
             } else {
                 if(fprintf(f, "%s\n", pl->line.stuff) == EOF)
                   goto io_err;
             }
         }
     }

     
     for(var = ps->vars; var->name != NULL; var++) {
         if(!var->is_user || var->been_written || !var->is_used)
           continue;

         dprint(5,(debugfile,"write_pinerc: %s = %s\n", var->name,
                   var->user_val ? var->user_val : "<not set>"));

         if(var->description != NULL)
           if(fprintf(f, "# %s\n", var->description) == EOF)
             goto io_err;

         if(var->user_val == NULL) {
             if(fprintf(f, "%s=\n\n", var->name) == EOF)
               goto io_err;
         } else if(strlen(var->user_val) == 0) {
             if(fprintf(f, "%s=\"\"\n\n", var->name) == EOF)
               goto io_err;
         } else {
             if(fprintf(f, "%s=%s\n\n", var->name, var->user_val) == EOF)
               goto io_err;
         }
     }

     if(fclose(f) == EOF)
       goto io_err;

#ifdef	DOS
     build_path(buf , ps->home_dir, "pinerc.tmp");
     if(getenv("PINERC"))
       strcpy(buf2, getenv("PINERC"));
     else
       strcpy(buf2, SYSTEM_PINERC);
#else
     build_path(buf , ps->home_dir, ".pinerc.temp");
     build_path(buf2, ps->home_dir, ".pinerc");
#endif

     unlink(buf2);

     if(rename_file(buf, buf2) < 0)
       goto io_err;

     return(0);


   io_err:
     q_status_message1(1, 3, 5,
                 "\007Error saving configuration in file \".pinerc\": %s",
                          error_description(errno));
     dprint(1, (debugfile, "Error writing %s : %s\n", buf,
                                      error_description(errno)));
     return(-1);
}


/*------------------------------------------------------------
    Dump out the typical global pine.conf on the standard output.
  Useful for creating  it the first time on a system.
  ----*/
void
dump_global_conf()
{
     FILE            *f;
     struct variable *var;

     read_pinerc(SYSTEM_PINERC, variables, ParseGlobal);

     f = stdout;
     if(f == NULL) 
       goto io_err;

     fprintf(f, "#      /usr/local/lib/pine.conf -- system wide pine configuration\n#\n");
     fprintf(f, "#  Values here affect all pine users unless they've overidden the values\n");
     fprintf(f, "#  in their .pinerc files.  A blank copy of this file may be obtained\n");
     fprintf(f, "#  by running \"pine -conf\". It will be printed on the standard output.\n#\n");
     fprintf(f,"#  For a variable to be unset it's value must be blank. \n");
     fprintf(f,"#  To set a varible to the empty string it's value should be \"\".\n");
     fprintf(f,"#  Switch variables are either set to \"yes\" or \"no\".\n#\n");
     fprintf(f,"#  (These comments are automatically inserted.)\n\n");

     for(var = variables; var->name != NULL; var++) {
         dprint(5,(debugfile,"write_pinerc: %s = %s\n", var->name,
                   var->user_val ? var->user_val : "<not set>"));
         if(!var->is_global)
           continue;

         if(var->description != NULL)
           fprintf(f, "# %s\n", var->description);

         if(var->global_val == NULL) {
             if(fprintf(f, "%s=\n\n", var->name) == EOF)
               goto io_err;
         } else if(strlen(var->global_val) == 0) {
             if(fprintf(f, "%s=\"\"\n\n", var->name) == EOF)
               goto io_err;
         } else {
             if(fprintf(f, "%s=%s\n\n", var->name, var->global_val) == EOF)
               goto io_err;
         }
     }
     exit(0);


   io_err:
     fprintf(stderr, "Error writing config to stdout: %s\n",
             error_description(errno));
     exit(-1);
}



/*----------------------------------------------------------------------
      Set a user variable and save the .pinerc
   
  Args:  var -- The index of the variable to set from pine.h (V_....)
         value -- The string to set the value to

 Result: -1 is returned on failure and 0 is returned on sucsess

 The vars data structure is update and the pinerc saved.
 ----*/ 
set_variable(var, value)
     char *value;
     int   var;
{
    struct variable *v;

    v = &ps_global->vars[var];

    if(!v->is_user) 
      panic1("Trying to set non-user variable %s", v->name);

    if(v->user_val != NULL)
      fs_give((void **) &v->user_val);

    if(v->current_val != NULL)
      fs_give((void **) &v->current_val);

    v->user_val    = value == NULL ? NULL : cpystr(value);
    v->current_val = value == NULL ? NULL : cpystr(value);

    return(write_pinerc(ps_global));
}
      
    
    

           

/*----------------------------------------------------------------------
    Make sure the pine folders directory exists, with proper folders

   Args: dir -- name of directory to initialize as mail directory

  Result: returns 0 if it exists or it is created and all is well
                  1 if it is missing and can't be created.
  ----*/
init_mail_dir(dir)
     char *dir;
{
     switch(is_writable_dir(dir)) {
      case 0:
        /* --- all is well --- */
        return(0);

        
      case 1:
        printf("The \"mail\" subdirectory already exists, but it is not writable by pine\n");
    	printf("so pine cannot run. Please correct the permissions and restart pine.\n");
    	return(-1);

      case 2:
    	printf("Pine requires a directory called \"mail\" and usualy creates it.\n");
    	printf("You already have a regular file called \"mail\" which is in the way.\n");
    	printf("Please move or remove \"mail\" and start pine again.\n");
        return(-1);
    	
  
      case 3:
        printf("Creating subdirectory \"%s\" where pine will store\nits mail folders.\n", dir);
    	fflush(stdout);
    	sleep(4);
        if(create_mail_dir(dir) < 0) {
            printf("\007Error creating subdirectory \"%s\" : %s\n", dir, 
                   error_description(errno));
            return(-1);
        }
    }
        
    if(folder_exists(dir, ps_global->VAR_DEFAULT_FCC)) 
        create_folder(dir, ps_global->VAR_DEFAULT_FCC);

    if(folder_exists(dir, DEFAULT_SAVE))
	  create_folder(dir, DEFAULT_SAVE);

     return(0);
}






/*----------------------------------------------------------------------
   Routines for pruning old Fcc, usually "sent-mail" folders.     
  ----*/
#define DFCC_LEN  (strlen(ps_global->VAR_DEFAULT_FCC))

struct sm_folder {
    char name[MAXFOLDER+1];
    int  month_num;
};


#ifdef ANSI
static void delete_old_sent_mail(struct sm_folder *);
static struct sm_folder *get_sent_mail_list(char *);

#else
static void delete_old_sent_mail();
static struct sm_folder *get_sent_mail_list();
#endif


/*----------------------------------------------------------------------
      Put sent-mail files in date order 

   Args: a, b  -- The names of two files. Expects names to be sent-mail-mmm-yy
                  Other names will sort in order and come before those
                  in above format.
 ----*/
static int   
compare_sm_files(aa, bb)
     void *aa, *bb;
{
    struct sm_folder *a = (struct sm_folder *)aa,
                     *b = (struct sm_folder *)bb;

    if(a->month_num == -1 && b->month_num == -1)
      return(strucmp(a->name, b->name));
    if(a->month_num == -1)      return(-1);
    if(b->month_num == -1)      return(1);

    return(a->month_num - b->month_num);
}



/*----------------------------------------------------------------------
      Create an ordered list of sent-mail folders and their month numbers

   Args: dir -- The directory to find the list of files in

 Result: Pointer to list of files is returned. 

This list includes all files that start with "sent-mail", but not "sent-mail" 
iself.
  ----*/

static struct sm_folder *
get_sent_mail_list(dir)
     char *dir;
{
#define MAX_FCC_FILES  (150)
    register struct sm_folder *sm;
    struct sm_folder          *sml;
    char                      *filename;

    if(DFCC_LEN == 0) {
        sml  = (struct sm_folder *)fs_get(sizeof(struct sm_folder));
        sml->name[0] = '\0';
        return(sml);
    }
        
    sml  = (struct sm_folder *)fs_get(MAX_FCC_FILES *sizeof(struct sm_folder));
    if(simple_opendir(dir) < 0) {
        fs_give((void **)&sml);
        return((struct sm_folder *)NULL);
    }

    for(sm = sml, filename = simple_readdir();
                         filename != NULL; filename = simple_readdir()) {
        if(strncmp(filename, ps_global->VAR_DEFAULT_FCC, DFCC_LEN) == 0
           && strcmp(filename, ps_global->VAR_DEFAULT_FCC)) {
            strcpy(sm->name,filename);
            sm->month_num = month_num(sm->name + DFCC_LEN + 1);
            sm++;
            if(sm >= &sml[MAX_FCC_FILES])
               break; /* Tooo many files, ignore the rest ; shouldn't occur */
        }
    }
    simple_closedir();

    sm->name[0] = '\0';

    qsort(sml,
          sm - sml,
          sizeof(struct sm_folder),
          compare_sm_files);

    return(sml);
}



/*----------------------------------------------------------------------
      Rename the current sent-mail folder to sent-mail for last month

   open up sent mail and get date of very first message
   if date is last month rename and...
       if file from 3 month ago exists ask if it should be deleted and...
           if files from previous months and yes ask about them to.   

  ----------------------------------------------------------------------*/
int
expire_sent_mail()
{
    char         path[MAXPATH+1], path2[MAXPATH+1],  prompt[80], tmp[20];
    int          rc, file_month, file_year, cur_month, month_to_use;
    long         now;
    struct tm   *tm_now;
    struct sm_folder *sent_mail_list, *sm;

    dprint(5, (debugfile, "==== expire_sent_mail called ====\n"));

    now = time(0);
    tm_now = localtime(&now);

    if(ps_global->last_expire_year != -1 &&
      (tm_now->tm_year <  ps_global->last_expire_year ||
       (tm_now->tm_year == ps_global->last_expire_year &&
        tm_now->tm_mon <= ps_global->last_expire_month)))
      return(0); 
    
    cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon;
    dprint(5, (debugfile, "Current month %d\n", cur_month));

    /*----- Get list of currently existing sent-mail folders -----*/
    sent_mail_list =get_sent_mail_list(ps_global->folders_dir);

    for(sm = sent_mail_list; sm != NULL && sm->name[0] != '\0'; sm++)
      dprint(5, (debugfile,"Old sent-mail: %5d  %s\n",sm->month_num,sm->name));


    for(sm = sent_mail_list; sm != NULL && sm->name[0] != '\0'; sm++)
      if(sm->month_num == cur_month -1)
        break;  /* matched a month */
    
    if(sm == NULL || sm->name[0] == '\0') {
        month_to_use = cur_month - 1;
    } else {
        month_to_use = 0;
    }

    dprint(5, (debugfile, "Month_to_use : %d\n", month_to_use));

    if(month_to_use == 0)
      goto delete_old;

    build_path(path,  ps_global->folders_dir, ps_global->VAR_DEFAULT_FCC);
    build_path(path2, ps_global->folders_dir, ps_global->VAR_DEFAULT_FCC);
    sprintf(path2 + strlen(path2),"-%s-%2d",
            lcase(strcpy(tmp,month_abbrev((month_to_use % 12)+1))),
            month_to_use/12);

    sprintf(prompt, "\007Move current \"%s\" to \"%s-%s-%2d\"",
            pretty_fn(ps_global->VAR_DEFAULT_FCC),
            pretty_fn(ps_global->VAR_DEFAULT_FCC),
            lcase(strcpy(tmp,month_abbrev((month_to_use % 12) + 1))),
                      month_to_use/12);
    if(want_to(prompt,'n', h_wt_expire, 1) == 'n') {
        dprint(5, (debugfile, "User declines renaming %s\n",
                   ps_global->VAR_DEFAULT_FCC));
        goto delete_old;
    }

    /*--- User says OK to rename ---*/
    dprint(5, (debugfile, "rename \"%s\" to \"%s\"\n", path, path2));
    rc = rename_file(path, path2);

    if(rc == -1) {
        q_status_message2(1, 2,4, "\007Error renaming \"%s\": %s",
                          pretty_fn(ps_global->VAR_DEFAULT_FCC),
                         error_description(errno));
        dprint(1, (debugfile, "Error renaming %s to %s: %s\n",
                   path, path2, error_description(errno)));
        display_message('x');
        goto delete_old;
    }
    create_folder(ps_global->folders_dir, ps_global->VAR_DEFAULT_FCC);

  delete_old:
    ps_global->last_expire_year = tm_now->tm_year;
    ps_global->last_expire_month = tm_now->tm_mon;
    sprintf(tmp, "%d.%d", ps_global->last_expire_year,
            ps_global->last_expire_month + 1);
    set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp);

    delete_old_sent_mail(sent_mail_list);
    if(sent_mail_list != NULL)
      fs_give((void **)&sent_mail_list);
    return(1);
}



/*----------------------------------------------------------------------
     Offer to delete old sent-mail folders

  Args: sml -- The list of sent-mail folders
 
  ----*/
static void
delete_old_sent_mail(sml)
     struct sm_folder *sml;
{
    char  path[MAXPATH+1], prompt[150];
    int   rc;
    struct sm_folder *sm;

    for(sm = sml; sm != NULL && sm->name[0] != '\0'; sm++) {
        sprintf(prompt, "Deleting old outgoing mail. Remove \"%s\"", sm->name);
        if(want_to(prompt, 'n', h_wt_delete_old, 1) == 'y'){
            build_path(path, ps_global->folders_dir,sm->name);
            dprint(5, (debugfile, "User wants to delete %s\n", path));
            rc = unlink(path);
            if(rc < 0) {
                q_status_message2(1, 2, 4, "\007Error deleting \"%s\": %s",
                             pretty_fn(sm->name), error_description(errno));
                    dprint(1, (debugfile, "Error unlinking %s: %s\n",
                               path, error_description(errno)));
                    display_message('x');
                    sleep(3);
            }
        } else {
               /* break; /* skip out of the whole thing when he says no */
               /* Decided to keep asking anyway */
        }
    }
}


    
    
#ifdef DEBUG
/*----------------------------------------------------------------------
     Initialize debugging - open the debug log file

  Args: none

 Result: opens the debug logfile for dprints

   Opens the file "~/.pine.debug1. Also maintains .pine.debug[2-4]
by renaming them each time so the last 4 sessions are saved.
  ----*/

int debug = 0 ;
FILE *debugfile = NULL;

void
init_debug()
{
#ifdef	DOS
    char filename[MAXPATH+1];

    strcpy(filename, DEBUGFILE);
    unlink(filename);

    if(debug){
	int i;
#else
    char nbuf[5];
    char newfname[MAXPATH+1], filename[MAXPATH+1];

    /*---- Get rid of very old pine debug files ----*/
    build_path(filename, ps_global->home_dir, "pine-debug-0.9");
    unlink(filename);
    build_path(filename, ps_global->home_dir, ".pine.debug.last");
    unlink(filename);
    
    if (debug) {		/* setup for dprint() statements! */
      int i;

      build_path(filename, ps_global->home_dir, DEBUGFILE);
      (void)unlink(filename); /* Just do it even though it may fail */

      for(i = NUMDEBUGFILES - 1; i > 0; i--) {
        build_path(filename, ps_global->home_dir, DEBUGFILE);
        strcpy(newfname, filename);
        sprintf(nbuf, "%d", i);
        strcat(filename, nbuf);
        sprintf(nbuf, "%d", i+1);
        strcat(newfname, nbuf);
        (void) rename_file(filename, newfname);
      }


      build_path(filename, ps_global->home_dir, DEBUGFILE);
      strcat(filename, "1");
#endif	/* DOS */

      debugfile = fopen(filename, "w");
/* Can't remember why this is here. LL-91.12.13   chown(filename, ps_global->uid, ps_global->gid); /* file owned by user */

      if(debugfile != NULL){
          long now = time(0);
          fprintf(debugfile, 
 "Debug output of the Pine program (at debug level %d).  Version %s\n%s\n",
    	   debug, version_buff, ctime(&now));
      }
    }
}
#endif

