#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: imap.c,v 4.49 1994/09/09 17:34:06 mikes Exp $";
#endif
/*----------------------------------------------------------------------

            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 Builiding, 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"

   USENET News reading additions in part by L Lundblade / NorthWestNet, 1993
   lgl@nwnet.net

   Copyright 1989-1994  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 and Pico are trademarks of the University of Washington.
   No commercial use of these trademarks may be made without prior
   written permission of the University of Washington.

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

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

/*======================================================================
    imap.c
    The call back routines for the c-client/imap
       - handles error messages and other notification
       - handles prelimirary notification of new mail and expunged mail
       - prompting for imap server login and password 

 ====*/

#include "headers.h"


/*
 * struct used to keep track of password/host/user triples.
 * The problem is we only want to try user names and passwords if
 * we've already tried talking to this host before.
 * 
 */
typedef struct mmlogin_s {
    char	   *user,
		   *host,
		   *passwd;
    struct mmlogin_s *next;
} MMLOGIN_S;


#ifdef	ANSI
int   imap_get_passwd(MMLOGIN_S *, char *, char *, char *);
void  imap_set_passwd(MMLOGIN_S **, char *, char *, char *);
#ifdef	DOS
char  xlate_in(int);
char  xlate_out(char);
char *passfile_name(char *, char *);
int   read_passfile(char *, MMLOGIN_S **);
void  write_passfile(char *, MMLOGIN_S *);
int   get_passfile_passwd(char *, char *, char *, char *);
void  set_passfile_passwd(char *, char *, char *, char *);
#endif

#else
int  imap_get_passwd();
void imap_set_passwd();

#endif


/*
 * Exported globals setup by searching functions to tell mm_searched
 * where to put message numbers that matched the search criteria,
 * and to allow mm_searched to return number of matches.
 */
MAILSTREAM *mm_search_stream;
MSGNO_S    *mm_search_map	= NULL,
	   *mm_exclude_map	= NULL;
int         mm_search_count	= 0L;


/*
 * Exported globals used to report folder FIND/LIST responses.
 */
void       *find_folder_list   = NULL;
MAILSTREAM *find_folder_stream = NULL;
long	    find_folder_count  = 0L;
int	    find_folder_inbox    = 0;
#ifdef NEWBB
void	   *newbb_folder_list;
#endif


/*
 * Local global to hook cached list of host/user/passwd triples to.
 */
static	MMLOGIN_S	*mm_login_list = NULL;



/*----------------------------------------------------------------------
      Write imap debugging information into log file

   Args: strings -- the string for the debug file

 Result: message written to the debug log file
  ----*/
void
mm_dlog(string)
    char *string;
{
    dprint(0, (debugfile, "IMAP DEBUG: %s\n", string));
}



/*----------------------------------------------------------------------
      Queue imap log message for display in the message line

   Args: string -- The message 
         errflg -- flag set to 1 if pertains to an error

 Result: Message queued for display

 The c-client/imap reports most of it's status and errors here
  ---*/
void
mm_log(string, errflg)
    char *string;
    long  errflg;
{
    char        message[300];
    char       *occurance;
    int         was_capitalized;
    long        now;
    struct tm  *tm_now;

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

    dprint((errflg == ERROR ? 1 : 2), (debugfile,
                                     "IMAP %d:%d %d/%d mm_log %s: %s\n",
                                     tm_now->tm_hour, tm_now->tm_min,
                                     tm_now->tm_mon+1, tm_now->tm_mday,
                                     errflg == ERROR ? "ERROR" :
                                     errflg == WARN  ? "warn"  :
                                     errflg == PARSE ? "parse" : "babble",
                                     string));

    /*
     * don't display if creating new folder
     * *or* if error on a stream known to be dead
     */
    if(ps_global->try_to_create
       || (ps_global->noticed_dead_stream && !strncmp(string, "[CLOSED]", 8)))
      return;

    /*---- replace all "mailbox" with "folder" ------*/
    strncpy(message, string, sizeof(message));
    message[sizeof(message) - 1] = '\0';
    occurance = srchstr(message, "mailbox");
    while(occurance) {
	if(!*(occurance+7) || isspace(*(occurance+8))){
	    was_capitalized = isupper(*occurance);
	    rplstr(occurance, 7, (errflg == PARSE ? "address" : "folder"));
	    if(was_capitalized)
	      *occurance = (errflg == PARSE ? 'A' : 'F');
	}
	else
	  occurance += 7;

        occurance = srchstr(occurance, "mailbox");
    }

    if(errflg == PARSE || ps_global->noshow_error) 
      strcpy(ps_global->c_client_error, message);

    if(ps_global->noshow_error ||
		       (ps_global->noshow_warn && errflg == WARN) ||
                       (errflg != ERROR && errflg != WARN))
      return; /* Only care about errors; don't print when asked not to */

    /*---- Display the message ------*/
    q_status_message1(1, errflg == ERROR ? 3 : 2, 5, "\007%s", message);
    strcpy(ps_global->last_error, message);
}



/*----------------------------------------------------------------------
         recieve notification from IMAP

  Args: stream  --  Mail stream message is relavant to 
        string  --  The message text
        errflag --  Set if it is a serious error

 Result: message displayed in status line

 The facility is for general notices, such as connection to server;
 server shutting down etc... It is used infrequently.
  ----------------------------------------------------------------------*/
void
mm_notify(stream, string, errflag)
    MAILSTREAM *stream;
    char       *string;
    long        errflag;
{
    int l;

    /* be sure to log the message... */
    sprintf(ps_global->last_error, "%s : %s",
	    (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
	    string);

    dprint(1, (debugfile, "IMAP mm_notify %s : %s\n",
               (!errflag) ? "NIL" : 
		 (errflag == ERROR) ? "error" :
		   (errflag == WARN) ? "warning" :
		     (errflag == BYE) ? "bye" : "unknown",
	       ps_global->last_error));

    /*
     * Then either set special bits in the pine struct or
     * display the message if it's tagged as an "ALERT" or
     * its errflag > NIL (i.e., WARN, or ERROR)
     */
    if(!strncmp(string, "[TRYCREATE]", 11))
      ps_global->try_to_create = 1;
    else if(!strncmp(string, "[READ-ONLY]", l = 11)
	    || !strncmp(string, "[ALERT]", l = 7))
      q_status_message2(0, 3, 3, "\007%s : %s",
			(stream && stream->mailbox)
			  ? stream->mailbox : "-no folder-",
			string + l);
    else if(errflag && errflag != BYE)
      q_status_message2(1, (errflag == ERROR) ? 2 : 1, 6, "%s%s",
			(errflag == ERROR) ? "\007" : "",
			ps_global->last_error);
}



/*----------------------------------------------------------------------
       receive notification of new mail from imap daemon

   Args: stream -- The stream the message count report is for.
         number -- The number of messages now in folder.
 
  Result: Sets value in pine state indicating new mailbox size

     Called when the number of messages in the mailbox goes up.  This
 may also be called as a result of an expunge. It increments the
 new_mail_count based on a the difference between the current idea of
 the maximum number of messages and what mm_exists claims. The new mail
 notification is done in newmail.c

 Only worry about the cases when the number grows, as mm_expunged
 handles shrinkage...

 ----*/
void
mm_exists(stream, number)
    MAILSTREAM *stream;
    long number;
{
    long new_this_call;

    dprint(3, (debugfile, "=== mm_exists(%ld,%s) called ===\n", number,
     !stream ? "(no stream)" : !stream->mailbox ? "(null)" : stream->mailbox));

    if(stream == ps_global->mail_stream){
	if(mn_get_total(ps_global->msgmap) != number){
	    ps_global->mail_box_changed = 1;
	    ps_global->mangled_header   = 1;
	}

        if(mn_get_total(ps_global->msgmap) < number){
	    new_this_call = number - mn_get_total(ps_global->msgmap);
	    ps_global->new_mail_count += new_this_call;
	    if(mn_get_sort(ps_global->msgmap) != SortArrival 
	       || mn_get_revsort(ps_global->msgmap))
	      clear_index_cache();

	    mn_add_raw(ps_global->msgmap, new_this_call);
	}
    } else if(stream == ps_global->inbox_stream) {
	if(mn_get_total(ps_global->inbox_msgmap) != number)
	  ps_global->inbox_changed = 1;

        if(mn_get_total(ps_global->inbox_msgmap) < number){
	    new_this_call = number - mn_get_total(ps_global->inbox_msgmap);
	    ps_global->inbox_new_mail_count += new_this_call;
	    mn_add_raw(ps_global->inbox_msgmap, new_this_call);
	}
    } else {
        /*--- ignore mm_exist for other. These are quick opens --*/
    }
}



/*----------------------------------------------------------------------
    Receive notification from IMAP that a message has been expunged

   Args: stream -- The stream/folder the message is expunged from
         number -- The message number that was expunged

mm_expunged is always called on an expunge.  Simply remove all 
reference to the expunged message, shifting internal mappings as
necessary.
  ----*/
void
mm_expunged(stream, number)
    MAILSTREAM *stream;
    long        number;
{
    MSGNO_S *msgs;

    dprint(3, (debugfile, "mm_expunged called %s %ld\n",
     !stream  ? "(no stream)" : !stream->mailbox ? "(null)" : stream->mailbox, 
	       number));

    /*
     * If we ever deal with more than two streams, this'll break
     */
    if(stream == ps_global->mail_stream){
	long i = mn_raw2m(ps_global->msgmap, number);
	if(i){
	    while(i <= mn_get_total(ps_global->msgmap))	/* flush invalid */
	      clear_index_cache_ent(i++);		/* cache entries! */

	    mn_flush_raw(ps_global->msgmap, number);
	    ps_global->mail_box_changed = 1;
	    ps_global->mangled_header   = 1;
	    ps_global->expunge_count++;
	}
    }
    else if(stream == ps_global->inbox_stream){
	long i = mn_raw2m(ps_global->msgmap, number);
	if(i){
	    mn_flush_raw(ps_global->inbox_msgmap, number);
	    ps_global->inbox_changed = 1;
	    ps_global->inbox_expunge_count++;
	}
    }
}



/*---------------------------------------------------------------------- 
        receive notification that search found something           

 Input:  mail stream and message number of located item

 Result: nothing, not used by pine
  ----*/
void
mm_searched(stream, number)
    MAILSTREAM *stream;
    long        number;
{
    if(stream == mm_search_stream){
	if(mm_search_map){
	    long i = mn_raw2m(ps_global->msgmap, number);
	    if(i && !mn_is_cur(mm_search_map, i)
	       && (!mm_exclude_map || mn_is_cur(mm_exclude_map, i))){
		mm_search_count++;
		if(mn_get_cur(mm_search_map) < 1L){
		    mn_set_cur(mm_search_map, i);
		}
		else{
		    mn_add_cur(mm_search_map, i);
		}
	    }
	}
	else
	  mm_search_count++;
    }
}



/*----------------------------------------------------------------------
      Get login and password from user for IMAP login
  
  Args:  host   -- The host name the user is trying to log in to 
         user   -- Buffer to return the user name in 
         passwd -- Buffer to return the passwd in
         trial  -- The trial number or number of attempts to login

 Result: username and password passed back to imap
  ----*/
void
mm_login(host, user, passwd, trial)
    char *host;
    char *user;
    char *passwd;
    long  trial;
{
    static char junk[] = {'?', '\0'};
    char      prompt[80], *p;
    HelpType  help ;
    int       rc, q_line;

    q_line = -3;				/* 3 from bottom */

    flush_status_messages();

    if(ps_global->anonymous) {
        /*------ Anonymous login mode --------*/
        if(trial >= 1) {
            user[0]   = '\0';
            passwd[0] = '\0';
        } else {
            strcpy(user, "anonymous");
            sprintf(passwd, "%s@%s", ps_global->VAR_USER_ID,
		    ps_global->hostname);
        }
        return;
    }

    /*
     * try last working password associated with this host.
     * NOTE: once we support some sort of user spec (ala {host/user=foo})
     * we can do a little better cache matching
     */
    if(trial == 0L && imap_get_passwd(mm_login_list, passwd, user, host))
      return;

#ifdef	DOS
    /* check to see if there's a password left over from last session */
    if(trial == 0L && get_passfile_passwd(ps_global->pinerc,passwd,user,host)){
	imap_set_passwd(&mm_login_list, passwd, user, host);
	return;
    }
#endif

    ps_global->mangled_footer = 1;
    help = NO_HELP;
    sprintf(prompt, "HOST: %s  ENTER LOGIN NAME: ", host);
    strcpy(user, ps_global->VAR_USER_ID);		/* set default */
    while(1) {
        rc = optionally_enter(user, q_line, 0, MAILTMPLEN - 1, 1, 0,
                              prompt, NULL, help, 0);
        if(rc == 3) {
            help = help == NO_HELP ? h_oe_login : NO_HELP;
            continue;
        }
        if(rc != 4)
          break;
    }

    if(rc == 1 || !user[0]) {
        user[0]   = '\0';
        passwd[0] = '\0';
        return;
    }

    help = NO_HELP;
    sprintf(prompt, "HOST: %s  USER: %s  ENTER PASSWORD: ", host, user);
    *passwd = '\0';
    while(1) {
        rc = optionally_enter(passwd, q_line, 0, MAILTMPLEN - 1, 0,
			      1, prompt, NULL, help, 0);
        if(rc == 3) {
            help = help == NO_HELP ? h_oe_passwd : NO_HELP;
            continue;
        }
        if(rc != 4)
          break;
    }

    if(rc == 1 || !passwd[0]) {
        strcpy(user,junk);
        strcpy(passwd, junk);
        return;
    }

    /* remember the password for next time */
    imap_set_passwd(&mm_login_list, passwd, user, host);
#ifdef	DOS
    /* if requested, remember it on disk for next session */
    set_passfile_passwd(ps_global->pinerc, passwd, user, host);
#endif
}



/*----------------------------------------------------------------------
       Receive notification of an error writing to disk
      
  Args: stream  -- The stream the error occured on
        errcode -- The system error code (errno)
        serious -- Flag indicating error is serious (mail may be lost)

Result: If error is non serious, the stream is marked as having an error
        and deletes are disallowed until error clears
        If error is serious this goes modal, allowing the user to retry
        of get a shell escape to fix the condition. When the condition is
        serious it means that mail existing in the mailbox will be lost
        if Pine exists without writing so we try to induce the user to 
        fix the error, go get some one that can fix the error or whatever
        and don't provide an easy way out.
  ----*/
long
mm_diskerror (stream, errcode, serious)
    MAILSTREAM *stream;
    long        errcode;
    long        serious;
{
    int line, ch;
    char q[200];

    dprint(0, (debugfile,
       "\n***** DISK ERROR on stream %s. Error code %ld. Error is %sserious\n",
               stream ? stream->mailbox : "<no mail folder>", errcode,
               serious ? "" : "not "));
    dprint(0, (debugfile, "***** message: \"%s\"\n\n", ps_global->last_error));

    if(!serious) {
/*        q_status_message2(1, 3, 6,"\007Error saving mail folder \"%s\" : %s",
                    pretty_fn(stream ? stream->mailbox : ""),
                          error_description((int)errcode)); */
        if(stream == ps_global->mail_stream) {
            ps_global->io_error_on_stream = 1;
        }
        return (1) ;
    }

    line = ps_global->ttyo->screen_rows - 3;
    MoveCursor(line, 0);
    CleartoEOLN();
    StartInverse();
    Writechar('\007', 0);
    sprintf(q,  "Serious error saving mail folder \"%s\":",
                    pretty_fn(stream ? stream->mailbox : ""));
    PutLine0(line, 0, q);
    ch = strlen(q); /* Use ch temporarily */
    MoveCursor(line, ch);
    while(ch++ < ps_global->ttyo->screen_cols - 1)
      Writechar(' ', 0);
/*    sprintf(q, "%-*.*s", ps_global->ttyo->screen_cols - 1,
            ps_global->ttyo->screen_cols - 1,
            ps_global->last_error);
    d_q_status_message();
    PutLine0(line+1, 0, q); */
    if(F_ON(F_CAN_SUSPEND,ps_global)) {
        sprintf(q,
		"Press return to retry saving or press ^Z to suspend Pine: ");
    } else {
        sprintf(q,
	  "Press return to retry saving or press ! to get to system prompt: ");
    }
    PutLine0(line+2, 0, q);
    Writechar('\007', 0);
    ch = strlen(q); /* Use ch temporarily */
    MoveCursor(line+2, ch);
    while(ch++ < ps_global->ttyo->screen_cols - 1)
      Writechar(' ', 0);
    MoveCursor(line+2, strlen(q));

    ch = read_char(0);
    while(!(ch == F_ON(F_CAN_SUSPEND,ps_global) ? ctrl('Z') : '!'
	    || ch == '\r')) {
        Writechar('\007', 0);
        ch = read_char(0);
    }
    switch(ch) {
      case '\r':
        Write_to_screen("Retry");
        EndInverse();
        MoveCursor(line -2, 0);
        fflush(stdout);
        return (0L);
       
      case '!':
        EndInverse();
        end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
        end_tty_driver(ps_global);
        printf("\n\nPlease attempt to correct the error preventing the saving of the mail folder.\n");
        printf("For example if the disk is out of space try removing uneeded files.\n");
        printf("You might also contact your system administrator.\n");
        printf("Give the command \"exit\" to get back to Pine and try saving the folder again.\n");

#ifdef	_WINDOWS
#else
        system("csh");
#endif

        init_tty_driver(ps_global);
        init_keyboard(F_ON(F_USE_FK,ps_global));
        MoveCursor(line - 2, 0);
        return (0L); /* go retry it again */
       
      case ctrl('Z'):
        EndInverse();
        do_suspend(ps_global);
        break;
    }
    return(0);
}



/*----------------------------------------------------------------------
      Receive list of folders from c-client/imap.
 
 Puts an entry into the specified list of mail folders associated with
 the global context (aka collection). 
 ----*/
   
void
context_mailbox(name)
    char *name;
{
    dprint(4, (debugfile, "====== context_mailbox: (%s)\n", name));

    /*
     * "Inbox" is filtered out here, since pine only supports one true
     * inbox...
     */
    if((!ps_global->show_dot_names && *name == '.')
       || (!find_folder_inbox && !strucmp(name,"inbox")))
       return;

    find_folder_count++;
    if(find_folder_list){
	FOLDER_S *new_f     = new_folder(name);
	new_f->prefix[0]    = '\0';
	new_f->msg_count    = 0;
	new_f->unread_count = 0;
	folder_insert(-1, new_f, find_folder_list);
    }
}



/*----------------------------------------------------------------------
     Receive list of bulliten boards from c-client/imap

 Puts an entry into the specified list of bulletin boards associated with
 the global context (aka collection). 
 ----*/
void
context_bboard(name)
    char *name;
{
#ifdef NEWBB
    /*----

       Hack to handle mm_bboard calls that result from the 
       nntp_find_new_bboards call to find the newly created news groups
     ----*/
    if(find_folder_list == newbb_folder_list) {
        mark_folder_as_new(name);
        return;
    }
#endif

    find_folder_count++;
    if(find_folder_list){
	FOLDER_S *new_f = new_folder(name);
	folder_insert(folder_total(find_folder_list), new_f, find_folder_list);
    }

    dprint(4, (debugfile, "====== context_bboard: (%s)\n", name));
}


void
mm_fatal(message)
    char *message;
{
    panic(message);
}


void
mm_flags(stream,number)
    MAILSTREAM *stream;
    long number;
{
    long i;

    /*
     * The idea here is to clean up any data pine might have cached
     * that has anything to do with the indicated message number.
     * At the momment, this amounts only to cached index lines, but
     * watch out for future changes...
     */
    if(stream != ps_global->mail_stream)
      return;			/* only worry about displayed msgs */

    /* in case number is current, fix titlebar */
    ps_global->mangled_header = 1;

    /* then clear index entry */
    if(i = mn_raw2m(ps_global->msgmap, number))
      clear_index_cache_ent(i);
}



/*----------------------------------------------------------------------
   Exported method to retrieve logged in user name associated with stream

   Args: mbox -- mailbox to find associated login name with.

 Result: 
  ----*/
char *
imap_user(mbox)
    char *mbox;
{
    MMLOGIN_S *l;
    NETMBX     netmbox;

    if((l = mm_login_list) && mbox
       && mail_valid_net_parse(mbox, &netmbox) && !netmbox.anoflag){
	do{
	    if(!strucmp(netmbox.host, l->host))
	      break;
	}
	while(l = l->next);
    }

    return(l ? l->user : NULL);
}


int
imap_get_passwd(l, passwd, user, host)
    MMLOGIN_S *l;
    char *passwd, *user, *host;
{
    while(l){
	if(!strucmp(host, l->host)){
	    strcpy(user, l->user);
	    strcpy(passwd, l->passwd);
	    break;
	}
	else
	  l = l->next;
    }

    return(l != NULL);
}



void
imap_set_passwd(l, passwd, user, host)
    MMLOGIN_S **l;
    char *passwd, *user, *host;
{
    while(*l){
	if(!strucmp(host, (*l)->host)){
	    fs_give((void **)&(*l)->user);
	    fs_give((void **)&(*l)->passwd);
	    break;
	}
	else
	  l = &(*l)->next;
    }

    if(!*l){
	*l = (MMLOGIN_S *)fs_get(sizeof(MMLOGIN_S));
	memset(*l, 0, sizeof(MMLOGIN_S));
    }

    (*l)->user   = cpystr(user);
    (*l)->passwd = cpystr(passwd);
    if(!(*l)->host)
      (*l)->host = cpystr(host);
}



void
imap_flush_passwd_cache()
{
    MMLOGIN_S *l;
    while(l = mm_login_list){
	mm_login_list = mm_login_list->next;
	if(l->user)
	  fs_give((void **)&l->user);

	if(l->host)
	  fs_give((void **)&l->host);

	if(l->passwd)
	  fs_give((void **)&l->passwd);

	fs_give((void **)&l);
    }
}


/* 
 * DOS-specific functions to support caching username/passwd/host
 * triples on disk for use from one session to the next...
 */
#ifdef	DOS

#define	FIRSTCH		0x20
#define	LASTCH		0x7e
#define	TABSZ		(LASTCH - FIRSTCH + 1)

static	int		xlate_key;
static	MMLOGIN_S	*passfile_cache = NULL;



/*
 * xlate_in() - xlate_in the given character
 */
char
xlate_in(c)
    int	c;
{
    register int  eti;

    eti = xlate_key;
    if((c >= FIRSTCH) && (c <= LASTCH)){
        eti += (c - FIRSTCH);
	eti -= (eti >= 2*TABSZ) ? 2*TABSZ : (eti >= TABSZ) ? TABSZ : 0;
        return((xlate_key = eti) + FIRSTCH);
    }
    else
      return(c);
}



/*
 * xlate_out() - xlate_out the given character
 */
char
xlate_out(c)
    char c;
{
    register int  dti;
    register int  xch;

    if((c >= FIRSTCH) && (c <= LASTCH)){
        xch  = c - (dti = xlate_key);
	xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0;
        dti  = (xch - FIRSTCH) + dti;
	dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0;
        xlate_key = dti;
        return(xch);
    }
    else
      return(c);
}



char *
passfile_name(pinerc, path)
    char *pinerc, *path;
{
    char *p = NULL;
    int   i;
    FILE *fp;
#define	PASSFILE	"pine.pwd"

    if(!path || !pinerc || pinerc[0] == '\0')
      return(NULL);

    if((p = strrchr(pinerc, '\\')) == NULL)
      p = strchr(pinerc, ':');

    if(p){
	strncpy(path, pinerc, i = (p - pinerc) + 1);
	path[i] = '\0';
    }

    strcat(path, PASSFILE);
    return(path);
}



int
read_passfile(pinerc, l)
    char       *pinerc;
    MMLOGIN_S **l;
{
    char  tmp[MAILTMPLEN], *ui[3];
    int   i, j, n;
    FILE *fp;

    /* if there's no password to read, bag it!! */
    if(!passfile_name(pinerc, tmp) || !(fp = fopen(tmp, "r")))
      return(0);

    for(n = 0; fgets(tmp, MAILTMPLEN, fp); n++){
	/*** do any necessary DEcryption here ***/
	xlate_key = n;
	for(i = 0; tmp[i]; i++)
	  tmp[i] = xlate_out(tmp[i]);

	if(i && tmp[i-1] == '\n')
	  tmp[i-1] = '\0';			/* blast '\n' */

	ui[0] = ui[1] = ui[2] = NULL;
	for(i = 0, j = 0; tmp[i] && j < 3; j++){
	    for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
	      ;					/* find end of data */

	    if(tmp[i])
	      tmp[i++] = '\0';			/* tie off data */
	}

	if(ui[0] && ui[1] && ui[2])		/* valid field? */
	  imap_set_passwd(l, ui[0], ui[1], ui[2]);
    }

    fclose(fp);
    return(1);
}



void
write_passfile(pinerc, l)
    char      *pinerc;
    MMLOGIN_S *l;
{
    char  tmp[MAILTMPLEN];
    int   i, n;
    FILE *fp;

    /* if there's no password to read, bag it!! */
    if(!passfile_name(pinerc, tmp) || !(fp = fopen(tmp, "w")))
      return;

    for(n = 0; l; l = l->next, n++){
	/*** do any necessary ENcryption here ***/
	sprintf(tmp, "%s\t%s\t%s\n", l->passwd, l->user, l->host);
	xlate_key = n;
	for(i = 0; tmp[i]; i++)
	  tmp[i] = xlate_in(tmp[i]);

	fputs(tmp, fp);
    }

    fclose(fp);
}



/*
 * get_passfile_passwd - return the password contained in the special passord
 *            cache.  The file is assumed to be in the same directory
 *            as the pinerc with the name defined above.
 */
int
get_passfile_passwd(pinerc, passwd, user, host)
    char *pinerc, *passwd, *user, *host;
{
    return((passfile_cache || read_passfile(pinerc, &passfile_cache))
	     ? imap_get_passwd(passfile_cache, passwd, user, host) : 0);
}



/*
 * set_passfile_passwd - set the password file entry associated with
 *            cache.  The file is assumed to be in the same directory
 *            as the pinerc with the name defined above.
 */
void
set_passfile_passwd(pinerc, passwd, user, host)
    char *pinerc, *passwd, *user, *host;
{
    if((passfile_cache || read_passfile(pinerc, &passfile_cache))
       && want_to("Preserve password on DISK for next login", 'y', 'x',
		  NO_HELP, 0, 0) == 'y'){
	imap_set_passwd(&passfile_cache, passwd, user, host);
	write_passfile(pinerc, passfile_cache);
    }
}
#endif	/* DOS */
