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

            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"

   Date:
   Last Edited:
      
   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   *
    ***********************************************************************
 

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


/*======================================================================
      other.c

      This implements the "other" screen of miscellaneous commands such
  as keyboard lock, and disk usage

  ====*/

#include "headers.h"

#ifndef	DOS
char *crypt(), *byte_string();
#endif

#ifdef ANSI
static void draw_other_body(int, int, int, int, int);
static void draw_klocked_body(char *, char *);
#else  /* ANSI */
static void draw_other_body();
static void draw_klocked_body();
#endif /* ANSI */

static void redraw_kl_body();
static void redraw_klocked_body();
static void redraw_other_body();
static void select_printer();
static void redraw_printer_select();

static struct key_menu other_keymenu0 =
  {0,{
     {"?","Help",0},      {"N", "News",0},         {"K","Lock Kbd",0},
     {"M","Main Menu",0}, {"P","Password",0},      {"L","Printer",0},
     {"D","Disk",0},      {NULL,NULL,0},           {NULL,NULL,0},
     {NULL,NULL,0},       {NULL,NULL,0},           {NULL,NULL,0}}};

static struct key_menu other_keymenu1 =
  {0,{
     {"?","Help",0},      {"N", "News",0},         {"K","Lock Kbd",0},
     {"M","Main Menu",0}, {"P","Password",0},      {"L","Printer",0},
     {NULL,NULL,0},       {NULL,NULL,0},           {NULL,NULL,0},
     {NULL,NULL,0},       {NULL,NULL,0},           {NULL,NULL,0}}};


static struct other_state {
    int files, bytes, space, quota, over;
} other_body_state;

/*----------------------------------------------------------------------
       Main loop for other command screen.

    Args: The Pine state structure

  ----------------------------------------------------------------------*/
void
other_screen(pine_state) 
    struct pine *pine_state;
{
    long bytes, quota;
    int  files, space, over;
    int  redraw, ch, orig_ch;

    ps_global = pine_state;

    dprint(1, (debugfile, "\n\n    ---- OTHER SCREEN ----\n"));
    redraw     = 1;
    files      = 0;
    space      = 0;
    over       = 0;

    ClearScreen();


    bytes = -1;
    quota = -1;
    ch = 'x'; /* For display_message first time through */

    do {
 
        /*
         * Handle New Mail Arrival...
         */
        if(new_mail(NULL, 0, ch==NO_OP_IDLE ? 0 : ch==NO_OP_COMMAND ?1 :2) >=0)
          ps_global->mangled_header = 1;
        if(streams_died())
          ps_global->mangled_header = 1;

        if(ps_global->mangled_header == 1) {
            set_titlebar("OTHER",1, FolderName, 0, 0, 0);
            ps_global->mangled_header = 0;
        }
	
	if(redraw) {
            ClearScreen();

            set_titlebar("OTHER",1, FolderName, 0, 0, 0);

            draw_other_body(files, bytes, space, quota, over);
            ps_global->redrawer = redraw_other_body;

	    format_keymenu(bytes < 0 ? &other_keymenu0 : &other_keymenu1,
                           ps_global->ttyo->screen_cols);
            output_keymenu(bytes < 0 ? &other_keymenu0 : &other_keymenu1,
                           -2, 0);
	    redraw = 0;
	}

        display_message(ch);
        MoveCursor(min(0, ps_global->ttyo->screen_rows -3), 0);
        fflush(stdout);

	ch = read_command();

        orig_ch = ch;
    
	if(ch < 0x100 && isupper(ch))
	  ch = tolower(ch);
	ch = validatekeys(ch);

	switch (ch) {

            /*----- HELP -----*/
	  case PF1:
	  case '?':
	  case ctrl('G'):
            helper(h_other, "HELP FOR OTHER", 0);
            if(ps_global->next_screen != SCREEN_FUN_NULL)
              return;
            redraw++;
	    break;

            /*----- LOCK -----*/
	  case PF3:
	  case 'k' :
#ifdef	DOS
	    q_status_message(1, 3,5,
			     "Keyboard lock unavailable in DOS version of Pine.");
	    break;		/* don't fall through */
#else  /* DOS */
#ifdef  HAVE_SHADOW_PASSWD
            q_status_message(1, 2, 4,
               "\007Keyboard lock unavailable in this version of Pine");
            break;
#else /* HAVE_SHADOW_PASSWD */
	    if(ps_global->restricted){
		q_status_message(1, 3,5,
                                 "Keyboard lock unavailable in restricted demo version of Pine.");
	        break;		/* don't fall through */
	    }

            redraw++;
            if(lock_keyboard() == -1)
              break; /* Lock aborted */
                /* Fall through OK */
#endif /* HAVE_SHADOW_PASSWD */
#endif /* DOS */


            /*----- END -----*/
	  case PF4:
	  case 'm':
            ps_global->next_screen = main_menu_screen;
            ps_global->prev_screen = other_screen;
            return;
		

            /*----- PASSWORD -----*/
	  case PF5:
	  case 'p':
#ifdef	DOS
	    q_status_message(1, 3,5, "Password change unavailable in DOS version of Pine.");
	    display_message('x');
	    sleep(3);
#else
	    if(ps_global->restricted){
		q_status_message(1, 3,5, "Password change unavailable in restricted demo version of Pine.");
                display_message('x');
                sleep(3);
	    } else {
		change_passwd();
		redraw = 1;
	    }
#endif	/* DOS */
            break;


            /*----- DISK SPACE ------*/
	  case PF7:
	  case 'd':
#ifdef	DOS
/* should easily be able to show remaining disk space available on current drive.
 * have to do something a bit more fancy to show space of several drives.
 * what about network drives?
 */
	    q_status_message(1, 3,5, "Disk usage unavailable in DOS version of Pine.");
	    display_message('x');
	    sleep(3);
#else
	    if(bytes < 0) {
                MoveCursor(15, 0);
                CleartoEOLN();
	        PutLine0(15, 3, "Adding up disk space..");
                fflush(stdout);
                space = disk_space(ps_global->folders_dir) * 1000;
                dprint(4, (debugfile, "space : %d\n", space));
                Write_to_screen("..");
                fflush(stdout);
#if defined(USE_QUOTAS)
                quota = disk_quota(ps_global->folders_dir, &over);
                Write_to_screen("..");
                fflush(stdout);
#else
                quota = 0;
                over  = 0;
#endif
                dprint(4, (debugfile, "over : %d  quota: %d\n", over, quota));
                add_up_disk_usage(ps_global->folders_dir, &files, &bytes);
                Write_to_screen("..");
                fflush(stdout);
	        redraw++;
	    } else {
	        q_status_message(0, 2, 2, "Disk space already on screen");
	    }
#endif	/* DOS */
	    break;


            /*------- CHOOSE PRINTER -------*/
          case PF6:
          case 'l':
            select_printer(); 
            redraw++;
            break;

	  case PF2:
	  case 'n':
            dprint(1, (debugfile, "\n\n    ---- NEWS SCREEN ----\n"));
            helper(h_news, "NEWS ABOUT PINE", 0);
            if(ps_global->next_screen != SCREEN_FUN_NULL)
              return;
            redraw++;
            break;


            /*-------- PRINT ALL HELP -----*/
          case 'a': /* Hidden function at the moment */
            print_all_help();
            break;

#ifdef MEMLOG
            /*----- Dump out the memory stats ----*/
          case 'o':
            dump_memlog(0);
            q_status_message(0, 3,4, "Memory stats dumped");
            break;
#endif /* MEMLOG */
                   

            /*--------- SUSPEND PINE ----------*/
          case ctrl('Z'):
            if(!have_job_control())
              goto bleep;
            if(!ps_global->can_suspend) {
                q_status_message(1, 1,3,
                        "\007Pine suspension not enabled - see help text");
                break;
            } else {
                do_suspend(ps_global);
            }
            /*-- Fall through to redraw --*/
      
      
            /*----- REDRAW -----*/
          case KEY_RESIZE:
          case ctrl('L'):
            redraw++;
            break;

            /*----- no op: check for new mail -----*/
          case NO_OP_IDLE:
	  case NO_OP_COMMAND:
	    break;

            /*----- ERROR -----*/
	  default:
          bleep:
	    q_status_message1(0, 0, 1, "\007Unknown command: \"%s\"",
                             (void *)pretty_command(orig_ch));
	    fflush(stdout);
	    break;

         }/* End of switch */

    } while(1);

}



static void
draw_other_body(files, bytes, space, quota, over)
     int files, bytes, space, quota, over;
{
    PutLine1(2, 3,
             "%s   HELP       - Information on your options at this point",
	     ps_global->use_fkeys ? "F1" : "?");
    PutLine1(4, 3,
             "%s   NEWS       - News about Pine changes",
             ps_global->use_fkeys ? "F2" : "N");

    PutLine1(6, 3,
             "%s   LOCK       - Lock this keyboard against others using it",
    	     ps_global->use_fkeys ? "F3" : "K");

    PutLine1(8, 3, "%s   MAIN MENU  - Go back to the main menu",
    	     ps_global->use_fkeys ? "F4" : "M");
    
    PutLine1(10, 3,
             "%s   PASSWORD   - Change the password you use to login",
             ps_global->use_fkeys ? "F5" : "P");

    PutLine1(12,3,
             "%s   PRINTER    - Choose printer or print command",
             ps_global->use_fkeys ? "F6" : "L" );
    PutLine0(13, 20, "Current printer:");
    StartInverse();
    PutLine2(13, 36, "%.*s", (char *)ps_global->ttyo->screen_cols - 37,
             ps_global->VAR_PRINTER == NULL ? "--- none ---" :
             ps_global->VAR_PRINTER);
    EndInverse();


    if(bytes < 0) {
    	PutLine1(15, 3,
           "%s   DISK       - Show space used by mail folders and free space",
    	    ps_global->use_fkeys ? "F7" : "D");
    } else {
    	MoveCursor(15, 0);
    	CleartoEOLN();
        StartInverse();
    	PutLine0(16, 30, "Disk Space Usage");
        EndInverse();
    	PutLine1(17, 3,
                     "Number of folders:                                  %5d",
                     (void *)files);

        PutLine2(18, 3,
                     "Space used by mail folders:                 %13s characters (%s)",
                     comatose(bytes), byte_string(bytes));

        if(over && quota >= 0) {
            PutLine2(19, 3,
                    "NO FREE SPACE! Amount OVER quota:           %13s characters (%s)",
                    comatose(quota), byte_string(quota));
        } else {
            PutLine3(19, 3,
                    "Free space available for mail %s:%13s characters (%s)" ,
                     quota <= 0 || space < quota ? "on disk drive" :
                                                   "in disk quota",
                     comatose(quota <= 0  || space < quota ? space : quota),
                     byte_string(quota<=0 || space < quota ? space : quota));
        }

    	PutLine1(20,3,"Mail directory: %41s", ps_global->folders_dir);
    }
    other_body_state.files = files;
    other_body_state.bytes = bytes;
    other_body_state.space = space;
    other_body_state.quota = quota;
    other_body_state.over  = over;
}



static void
redraw_other_body()
{
    draw_other_body(other_body_state.files, other_body_state.bytes,
                    other_body_state.space, other_body_state.quota,
                    other_body_state.over);
}

    







/*----------------------------------------------------------------------
          Execute the lock keyboard command

    Args: None

  Result: keyboard is locked until user gives password
  ---*/

lock_keyboard()
{
#ifndef	DOS
    struct pine *ps = ps_global;
    char passwd[80], salt[5], *cp, **help;

    ClearScreen();

    set_titlebar("KEYBOARD LOCK",1, FolderName, 0, 0, 0);
    redraw_kl_body();

    ps_global->redrawer = redraw_kl_body;

    blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
                  
    if(want_to("Lock keyboard", 'n', (char **)NULL, 0) == 'n') {
	q_status_message(0, 0,2, "\007Lock keyboard aborted");
        return(-1);
    }

    MoveCursor(3, 3);
    CleartoEOS();

    draw_klocked_body(ps->VAR_USER_ID, ps->VAR_PERSONAL_NAME);

    ps_global->redrawer = redraw_klocked_body;

    salt[0] = ps_global->passwd[0];
    salt[1] = ps_global->passwd[1];
    salt[2] = '\0';
    help = NULL;
    while(1){
        int rc;
        rc =  optionally_enter(passwd,  -3, 0,
                30, 0, 1, "Login password to unlock keyboard : ", NULL,
                               help, 1);
        if(rc == 3) {
           help = help == NULL ? h_oe_keylock : NULL;
           continue;
        }
        if(rc != 4)
          break;
    }
    cp = crypt(passwd, salt);
    while(strcmp(cp, ps_global->passwd)){
        dprint(9, (debugfile, "System passwd: \"%s\"  Typed passwd: \"%s\"  Crypted passwd: \"%s\"\n",
                   ps_global->passwd, passwd, cp);)
        q_status_message1(1, 2,4,
                  "\007Wrong password for user \"%s\" to unlock keyboard",
                         ps_global->VAR_USER_ID);
        display_message('x');
        sleep(4);
        
        while(1){
            int rc;
            rc =  optionally_enter(passwd, -3, 0,
                    30, 0, 1, "Login password to unlock keyboard : ", NULL,
                                   help, 1);
            if(rc == 3) {
               help = help == NULL ? h_oe_keylock : NULL;
               continue;
            }
            if(rc != 4)
              break;
        }
        cp = crypt(passwd, salt);
    }

    /* Blast password from memory */
    for(cp = passwd; cp < &passwd[10]; *cp++ = ' ');

    q_status_message(0, 1,3,"keyboard unlocked");
    return(0);
#endif	/* !DOS */
}

static void
redraw_kl_body()
{
#ifndef	DOS
    PutLine0(3,3 ,
       "You may lock this keyboard so that no one else can access your mail");
    PutLine0(5, 3 ,
       "while you are away. To unlock the keyboard enter your login password");
#endif	/* !DOS */
}


static char *klockin, *klockame;

static void
draw_klocked_body(login, username)
    char *login, *username;
{
#ifndef	DOS
    klockin = login;
    klockame = username;
    redraw_klocked_body();
#endif
}

static void
redraw_klocked_body()
{
#ifndef	DOS
    PutLine2(6, 3, "This keyboard is locked by %s <%s>.",klockame, klockin);
    PutLine1(8, 3, "Enter login password for user \"%s\" to unlock.", klockin);
#endif
}
     


/*----------------------------------------------------------------------
    The printer selection screen

   Draws the screen and prompts for the printer number and the custom
   command if so selected.

 ----*/

static void
select_printer() 
{
#ifndef	DOS
    char           *prompt, pnum_string[80], *j, **help;
    int             rc, pnum, printer_count, qline;

    printer_count = 3;

    ClearScreen();

    set_titlebar("SELECT PRINTER",1, FolderName, 0, 0, 0);
    redraw_printer_select();

    ps_global->redrawer = redraw_printer_select;

    blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);

    display_message('x');

    qline          = -3;
    pnum_string[0] = '\0';
    prompt         = "Enter printer number from above (1-3): ";
    help           = (char **)NULL;
    while (1) {
        rc = optionally_enter(pnum_string, qline, 0,
                              sizeof(pnum_string) - 1, 1, 0, prompt,
                              NULL, help, 0);
        if(rc == 3) {
            help = help == NULL ? h_oe_choosep : NULL;
            continue;
        } 

        if(rc == 0 && *pnum_string != '\0') {
            for(j = pnum_string; isdigit(*j) || *j == '-'; j++);
	    if(*j != '\0') {
	        q_status_message(0, 2,2,
                           "\007Invalid number entered. Use only digits 0-9");
            } else {
                pnum = atoi(pnum_string);
                if(pnum < 1) {
	            q_status_message1(0, 2,2,
                               "\007Printer number %s must be greater than 0",
                                      int2string(pnum));
                } else if(pnum > printer_count) {
                    q_status_message1(0, 2, 2,
                                      "\007Printer number must be %s or less",
                                      int2string(printer_count));
                } else {
                    break;
                }
            }
            pnum_string[0] = '\0';
            display_message(NO_OP_COMMAND);
            sleep (3);
            continue;
	}

        if(rc != 4) {
            q_status_message(0, 0,3, "\007Printer selection aborted");
            return;
        }
    }

    if(pnum) {
        if(pnum == 1) {
            rc = set_variable(V_PRINTER, ANSI_PRINTER);
        } else if(pnum == 2) {
            rc = set_variable(V_PRINTER,ps_global->VAR_STANDARD_PRINTER);
        } else if(pnum == 3) {
            char custom_command[100];
            strcpy(custom_command,
                   ps_global->VAR_PERSONAL_PRINT_COMMAND == NULL ? "" :
                                      ps_global->VAR_PERSONAL_PRINT_COMMAND);
            help = (char **) NULL;
            do {
                rc = optionally_enter(custom_command, -3, 0,
                                      sizeof(custom_command) - 1, 1, 0,
                                      "Command for custom printer: ", NULL,
                                      help, 0);
                if(rc == 3) {
                    help = help == NULL ? h_oe_customp : NULL;
                }
            } while(rc == 3 || rc == 4);
            if(ps_global->restricted) {
                q_status_message(1, 2, 4,
                            "\007Can select custom printer in Pine demo");
                return;
            }
            if(rc == 1) {
                q_status_message(0, 0,2, "\007Printer selection aborted");
                return;
            }
            set_variable(V_PRINTER, custom_command);
            rc = set_variable(V_PERSONAL_PRINT_COMMAND, custom_command);
        }
        if(rc == 0)
          q_status_message1(0, 1,3, "Printer set to \"%s\"; configuration saved",
                            ps_global->VAR_PRINTER);
    }
#endif	/* !DOS */
}


/*----------------------------------------------------------------------
    Paint the printer selection menu body on the screen

   No arguments or return value
 ----*/
static void
redraw_printer_select()
{
#ifndef	DOS
    int   l, matched_printer = 0;

    l = 2;
    PutLine0(l++, 3, "1. Printer attached to IBM PC or compatible, MacIntosh");
    PutLine0(l++, 6,
     "This may not work with all attached printers, and will depend on the");
    PutLine0(l++, 6, 
     "terminal emulation/communications software in use. It is known to work");
    PutLine0(l++, 6,
     "with Versaterm Pro on Macs, and WRQ Reflections on PC's and with the ");
    PutLine0(l++, 6,
     "latest UW version of NCSA telnet for either, and probably Kermit too.");
    PutLine0(l, 10, "Command:");
    if(strcmp(ps_global->VAR_PRINTER, ANSI_PRINTER) == 0) {
        matched_printer = 1;
        StartInverse();
    }
    PutLine0(l++, 19, ANSI_PRINTER);
    if(strcmp(ps_global->VAR_PRINTER, ANSI_PRINTER) == 0)
      EndInverse();

    l++;
    PutLine0(l++, 3, "2. Standard UNIX print command");
    PutLine0(l++,6,
        "Using this option may require setting your \"PRINTER\" environment");
    PutLine0(l++, 6,    "variable using the standard UNIX utilities.");
    PutLine0(l, 10 , "Command: ");
    if(ps_global->VAR_STANDARD_PRINTER != NULL &&
       strcmp(ps_global->VAR_PRINTER, ps_global->VAR_STANDARD_PRINTER) == 0) {
        matched_printer = 1;
        StartInverse();
    }
    PutLine0(l++, 19, ps_global->VAR_STANDARD_PRINTER != NULL ?
             ps_global->VAR_STANDARD_PRINTER : "");
    if(ps_global->VAR_STANDARD_PRINTER != NULL &&
       strcmp(ps_global->VAR_PRINTER, ps_global->VAR_STANDARD_PRINTER) == 0) 
      EndInverse();


    l++;
    PutLine0(l++, 3, "3. Personally selected print command");
    PutLine0(l++, 6,
      "The text to be printed will be piped into the command given here. Some");
    PutLine0(l++, 6,
      "examples are: \"prt\", \"lpr\", or \"enscript\". The command may be given");
    PutLine0(l++, 6,
      "with options, for example \"enscript -2 -r\" or \"lpr -Plpacc170\". The");
    PutLine0(l++, 6,
      "commands and options on your system may be different these examples.");
    PutLine0(l, 10,  "Command: ");
    if(!matched_printer)  
      StartInverse();
    PutLine0(l, 19, ps_global->VAR_PERSONAL_PRINT_COMMAND == NULL ? "" :
                   ps_global->VAR_PERSONAL_PRINT_COMMAND);
    if(!matched_printer)
      EndInverse();
#endif	/* !DOS */
}

        
    
