/*****************************************************************************/
/*
                                  folder.c

Provide HTML form selection lists and buttons for VMS Mail files/folders.


COPYRIGHT
---------
Copyright (C) 2005-2025 Mark G.Daniel
This program, comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under the
conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version.


VERSION HISTORY
---------------
10-JAN-2020  MGD  FolderRecent() maintain list of most recent folders
07-JUL-2008  MGD  refine PMDF IMAP folder (.MAILBOX) processing
27-JAN-2007  MGD  specially process PMDF IMAP folder subdirectories
30-DEC-2006  MGD  remove "(none)" folder list options
27-DEC-2006  MGD  FolderButtonListEach() simplify button width calculation
21-OCT-2006  MGD  multiple Mail file [copy][move] destination selectors
                    provide all files (drawers) and associated folders 
20-JUL-2006  MGD  try to make font width allowances for monospace
04-JUL-2006  MGD  FolderButtonListEach() refine (perhaps) button width kludge
09-APR-2006  MGD  FolderOpenSingleFileListEach() support public wildcard
                    folder configuration with MAIL and NEWMAIL prohibition
06-APR-2006  MGD  remove default MAIL and NEWMAIL folders
02-APR-2006  MGD  FolderOpenSelect() modifications for public wildcard access
07-FEB-2006  MGD  folder buttons for left-side of message listing
01-FEB-2005  MGD  initial
*/

/*****************************************************************************/

#ifdef SOYMAIL_VMS_V7
#undef _VMS_V6_SOURCE
#define _VMS_V6_SOURCE
#undef __VMS_VER
#define __VMS_VER 70000000
#undef __CRTL_VER
#define __CRTL_VER 70000000
#endif

#pragma nomember_alignment

/* standard C header files */
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* VMS related header files */
#include <descrip.h>
#include <mailmsgdef.h>
#include <ssdef.h>
#include <stsdef.h>

/* application header file */
#include "soymail.h"
#include "attach.h"
#include "callmail.h"
#include "cgilib.h"
#include "compose.h"
#include "folder.h"
#include "mainmenu.h"
#include "newmail.h"
#include "other.h"
#include "message.h"

#define FI_LI "FOLDER", __LINE__

/********************/
/* external storage */
/********************/

extern BOOL  Debug,
             WatchEnabled;

extern int  VmsVersion;

extern char  CurrentVmsTimeString[],
             SoftwareId[],
             SoftwareVn[];

extern USER_OPTIONS  UserOptions;
extern REQUEST_DATA  RequestData;
extern VMS_MAIL_USER  VmsMailUser;


/*****************************************************************************/
/*
Reset (empty) the mail file and folder lists.
Called upon certain triggers and periodically.
*/

void FolderListReset (REQUEST_DATA *rdptr)

{
   int  cnt, status;
   char  *cptr, *sptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   muptr = &VmsMailUser;

   if (WatchEnabled) WatchThis (FI_LI, "FolderListReset()");

   if (muptr->MailFolderList.ListPtr)
   {
      CgiLibVeeMemFree (muptr->MailFolderList.ListPtr);
      muptr->MailFolderList.ListPtr = NULL;
      muptr->MailFolderList.ListSize =
         muptr->MailFolderList.ListLength =
         muptr->MailFolderList.ListCount = 0;
   }

   if (muptr->DestFolderList.ListPtr)
   {
      CgiLibVeeMemFree (muptr->DestFolderList.ListPtr);
      muptr->DestFolderList.ListPtr = NULL;
      muptr->DestFolderList.ListSize =
         muptr->DestFolderList.ListLength =
         muptr->DestFolderList.ListCount = 0;
   }
}

/*****************************************************************************/
/*
Get the list of mail files and a list of folders in the default and destination
mail file (as applicable).  This is quite messy as only one mail file may be
open at a time so we must dance around the primary mail file and folder list.
*/

void FolderListInit (REQUEST_DATA *rdptr)

{
   int  cnt, len, status;
   char  *cptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   muptr = &VmsMailUser;
   uoptr = &rdptr->UserOptions;

   if (WatchEnabled)
      WatchThis (FI_LI, "FolderListInit() all:!UL pmdf:!UL",
                 uoptr->AllMailFiles, uoptr->PmdfImap);

   if (uoptr->AllMailFiles && !muptr->MailFileList.ListPtr)
      CallMailFileList (muptr);

   if (!muptr->MailFolderList.ListPtr)
   {
      status = CallMailFolderList (muptr, FALSE, uoptr->PmdfImap);
      if (VMSnok (status))
      {
         if (muptr->MailVmsStatus == MAIL$_OPENIN &&
             muptr->MailFileDeletedCount)
            StatusMessage (FI_LI, -1, LangFor("mail_file_deleted"),
                           rdptr->MailFileName);
         else
         if (uoptr->PmdfImap && (status & 0x80000000))
            StatusMessage (FI_LI, 1, "IMAP: %s.",
                           SysGetMsg(status & 0x7fffffff));
         else
            StatusMessage (FI_LI, 1, "MAIL: %s.", SysGetMsg(status));
      }
      time ((unsigned long*)&rdptr->FolderRefreshSeconds);
   }

   if (!muptr->DestFolderList.ListPtr)
   {
      if (!uoptr->AllMailFiles ||
          !strcmp (rdptr->MailFileName, rdptr->DestMailFileName))
      {
         /* clone the mail folder list */
         if (WatchEnabled) WatchThis (FI_LI, "clone");
         muptr->DestFolderList.ListCount = muptr->MailFolderList.ListCount;
         muptr->DestFolderList.ListLength = muptr->MailFolderList.ListLength;
         muptr->DestFolderList.ListSize = muptr->MailFolderList.ListLength + 1;
         muptr->DestFolderList.ListPtr = 
           CgiLibVeeMemCalloc (muptr->DestFolderList.ListSize);
         if (!muptr->DestFolderList.ListPtr) ErrorExit (vaxc$errno, FI_LI);
         if (muptr->MailFolderList.ListPtr)
            memcpy (muptr->DestFolderList.ListPtr,
                    muptr->MailFolderList.ListPtr,
                    muptr->DestFolderList.ListSize);
         time ((unsigned long*)&rdptr->FolderRefreshSeconds);
      }
      else
      {
         /* will handle all the opening and closing of the dest mail file */
         status = CallMailFolderList (muptr, TRUE, uoptr->PmdfImap);
         if (VMSnok (status))
            /* quite possible a specified mail file does not exist (yet) */
            if (status != MAIL$_OPENIN)
               StatusMessage (FI_LI, 1, "MAIL: %s.", SysGetMsg(status));

         time ((unsigned long*)&rdptr->FolderRefreshSeconds);
      }
   }

   /* find the length of the longest mail file name */
   muptr->MailFolderMaxLen = 0;
   cptr = muptr->MailFileList.ListPtr;
   for (cnt = muptr->MailFileList.ListCount; cnt; cnt--)
   {
      if ((len = strlen(cptr)) > muptr->MailFolderMaxLen)
         muptr->MailFolderMaxLen = len;
      while (*cptr) cptr++;
      cptr++;
   }
   muptr->DestFolderMaxLen = muptr->MailFolderMaxLen;

   /* integrate with the length of the longest folder name */
   cptr = muptr->MailFolderList.ListPtr;
   for (cnt = muptr->MailFolderList.ListCount; cnt; cnt--)
   {
      if ((len = strlen(cptr)) > muptr->MailFolderMaxLen)
         muptr->MailFolderMaxLen = len;
      while (*cptr) cptr++;
      cptr++;
   }

   /* and with the length of the longest destination folder name */
   cptr = muptr->DestFolderList.ListPtr;
   for (cnt = muptr->DestFolderList.ListCount; cnt; cnt--)
   {
      if ((len = strlen(cptr)) > muptr->DestFolderMaxLen)
         muptr->DestFolderMaxLen = len;
      while (*cptr) cptr++;
      cptr++;
   }

   if (WatchEnabled)
   {
      fprintf (stdout, "MAIL: %d (%d)\n",
               muptr->MailFolderList.ListCount, muptr->MailFolderMaxLen);
      cptr = muptr->MailFolderList.ListPtr;
      for (cnt = muptr->MailFolderList.ListCount; cnt; cnt--)
      {
         fprintf (stdout, "%2d |%s|\n", strlen(cptr), cptr);
         while (*cptr) cptr++;
         cptr++;
      }
      fprintf (stdout, "DEST: %d (%d)\n",
               muptr->DestFolderList.ListCount, muptr->DestFolderMaxLen);
      cptr = muptr->DestFolderList.ListPtr;
      for (cnt = muptr->DestFolderList.ListCount; cnt; cnt--)
      {
         fprintf (stdout, "%2d |%s|\n", strlen(cptr), cptr);
         while (*cptr) cptr++;
         cptr++;
      }
   }
}

/*****************************************************************************/
/*
Generate a selection list for the available mail folders and [open] button. 
If private access then include the [9 new] button.
*/

void FolderOpenSelect (REQUEST_DATA *rdptr)

{
   int  status;
   char  newBuf [256];
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderOpenSelect()");

   muptr = &VmsMailUser;
   uoptr = &rdptr->UserOptions;

   fprintf (stdout,
"<!-- folder open begin -->\n\
<input type=\"hidden\" name=\"folder_list_change\" \
id=\"folder_list_change\" value=\"\">\n\
<select class=\"css_select_2\" name=\"folder_list_open\"%s \
onchange=\"document.getElementById(\'folder_list_change\').value=true;\
startWorking();document.getElementById(\'soymail_form\').submit();\">\n",
            muptr->MailFolderMaxLen > 20 ? " style=\"width:12em;\"" : "");

   FolderOpenListEach (rdptr);

   sprintf (newBuf, "%d %s", rdptr->NewMessages, LangFor("new"));

   fprintf (stdout,
"</select>&nbsp;&nbsp;\n\
<input class=\"css_button_panel_2\" type=\"submit\" \
name=\"main_btn\" value=\"%s\"%s>&nbsp;\n",
            LangFor("open"),
            CLICK_STARTWORKING);

   if (rdptr->PrivateAccess)
      fprintf (stdout,
"<input class=\"css_button_panel_2\" type=\"submit\" \
name=\"main_btn\" value=\"%s\"%s>&nbsp; \n",
               newBuf,
               NewMailInclude(rdptr) ?
                  " onclick=\"refreshNewMail(-1);return false;\"" :
                  CLICK_STARTWORKING);

   fprintf (stdout,
"<!-- folder open end -->\n\n");
}

/*****************************************************************************/
/*
Provide a selection list for the message folders.
*/

void FolderOpenListEach (REQUEST_DATA *rdptr)

{
   int  cnt;
   char  *cptr, *sptr, *tptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderOpenListEach()");

   muptr = &VmsMailUser;
   uoptr = &rdptr->UserOptions;

   if (uoptr->PmdfImap)
      FolderPmdfImapListEach (rdptr, FALSE);
   else
   if (uoptr->AllMailFiles || muptr->MailFileList.ListCount > 1)
      FolderMultiFileListEach (rdptr, FALSE);
   else
      FolderSingleFileListEach (rdptr, FALSE);
}

/*****************************************************************************/
/*
Provide a selection list for the message action destination folders.
*/

void FolderDestListEach (REQUEST_DATA *rdptr)

{
   int  cnt;
   char  *cptr, *sptr, *tptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderDestListEach()");

   muptr = &VmsMailUser;
   uoptr = &rdptr->UserOptions;

   if (uoptr->PmdfImap)
      FolderPmdfImapListEach (rdptr, TRUE);
   else
   if (uoptr->AllMailFiles || muptr->MailFileList.ListCount > 1)
      FolderMultiFileListEach (rdptr, TRUE);
   else
      FolderSingleFileListEach (rdptr, TRUE);
}

/*****************************************************************************/
/*
Only a single MAIL.MAI file or optioned to only show MAIL.MAI.
List each folder.
*/

void FolderSingleFileListEach
(
REQUEST_DATA *rdptr,
BOOL DestFolder
)
{
   int  fldcnt,
        ListCount;
   char  *cptr, *fldptr, *selptr,
         *FolderNamePtr;
   VMS_MAIL_LIST  *mlptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderSingleFileListEach()");

   muptr = &VmsMailUser;

   if (DestFolder)
   {
       mlptr = &muptr->RecentDestFolderList;
       FolderNamePtr = rdptr->DestFolderName;
   }
   else
   {
       mlptr = &muptr->RecentMailFolderList;
       FolderNamePtr = rdptr->FolderName;
   }

   if (!rdptr->NewMessages && !strcmp (FolderNamePtr, "NEWMAIL"))
      fputs ("<option value=\"NEWMAIL\" selected>NEWMAIL\n", stdout);

   fldptr = muptr->MailFolderList.ListPtr;
   for (fldcnt = muptr->MailFolderList.ListCount; fldcnt; fldcnt--)
   {
      if (rdptr->PublicWildcard &&
          rdptr->PublicNoNewMail &&
          (!strcmp (fldptr, "MAIL") ||
           !strcmp (fldptr, "NEWMAIL")))
      {
         /* next folder in list */
         while (*fldptr) fldptr++;
         fldptr++;
         continue;
      }

      if (!strcmp (fldptr, FolderNamePtr))
         selptr = " selected";
      else
         selptr = "";

      fprintf (stdout, "<option value=\"%s\"%s>%s\n",
               HTML_ESCAPE(fldptr), selptr, HTML_ESCAPE(fldptr));

      if (*selptr) FolderRecent (mlptr, fldptr);

      /* next folder in list */
      while (*fldptr) fldptr++;
      fldptr++;
   }
}

/*****************************************************************************/
/*
Multiple mail .MAI files.
List each folder for the currently selected mail .MAI file.
*/

void FolderMultiFileListEach
(
REQUEST_DATA *rdptr,
BOOL DestFolder
)
{
   int  filcnt, fldcnt;
   char  *filptr, *fldptr, *selptr,
         *FolderNamePtr,
         *ListPtr,
         *MailFilePtr;
   VMS_MAIL_LIST  *mlptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderMultiFileListEach()");

   muptr = &VmsMailUser;

   if (DestFolder)
   {
       mlptr = &muptr->RecentDestFolderList;
       MailFilePtr = rdptr->DestMailFileName;
       FolderNamePtr = rdptr->DestFolderName;
   }
   else
   {
       mlptr = &muptr->RecentMailFolderList;
       MailFilePtr = rdptr->MailFileName;
       FolderNamePtr = rdptr->FolderName;
   }

   filptr = muptr->MailFileList.ListPtr;
   for (filcnt = muptr->MailFileList.ListCount; filcnt; filcnt--)
   {
      if (!strcmp (filptr, MailFilePtr))
      {
         /* current mail file */
         fprintf (stdout, "<option value=\"%s/MAIL\" selected>%s/\n",
                  filptr, filptr);
         fldptr = mlptr->ListPtr;
         for (fldcnt = mlptr->ListCount; fldcnt; fldcnt--)
         {
            if (!strcmp (fldptr, FolderNamePtr))
               selptr = " selected";
            else
               selptr = "";

            /* Opera 9 doesn't notice the style padding hence the &nbsp; */
            fprintf (stdout, "<option value=\"%s/%s\"%s>&nbsp;&nbsp;/%s\n",
                     filptr, HTML_ESCAPE(fldptr), selptr, HTML_ESCAPE(fldptr));

            if (*selptr)
            {
               char  *cptr, *sptr, *zptr;
               char  buf [512];
               zptr = (sptr = buf) + sizeof(buf)-1;
               for (cptr = filptr; *cptr && sptr < zptr; *sptr++ = *cptr++);
               if (sptr < zptr) *sptr++ = '/';
               for (cptr = fldptr; *cptr && sptr < zptr; *sptr++ = *cptr++);
               FolderRecent (mlptr, buf);
            }

            /* next folder name in the list */
            while (*fldptr) fldptr++;
            fldptr++;
         }
      }
      else
      {
         /* show an empty folder for each mail file */
         fprintf (stdout, "<option value=\"%s/MAIL\">%s/\n",
                  filptr, filptr);
      }

      /* next mail file name in the list */
      while (*filptr) filptr++;
      filptr++;
   }
}

/*****************************************************************************/
/*
List each folder in the PMDF IMAP mailbox.
*/

void FolderPmdfImapListEach
(
REQUEST_DATA *rdptr,
BOOL DestFolder
)
{
   int  fldcnt,
        ListCount;
   char  *cptr, *fldptr, *selptr, *sptr, *zptr,
         *FileNamePtr,
         *FolderNamePtr,
         *ListPtr;
   char  FolderName [256];
   VMS_MAIL_LIST  *mlptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderPmdfImapListEach()");

   muptr = &VmsMailUser;

   if (DestFolder)
   {
       mlptr = &muptr->DestFolderList;
       FileNamePtr = rdptr->DestMailFileName;
       FolderNamePtr = rdptr->DestFolderName;
   }
   else
   {
       mlptr = &muptr->MailFolderList;
       FileNamePtr = rdptr->MailFileName;
       FolderNamePtr = rdptr->FolderName;
   }

   /* check for the PMDF IMAP indicative trailing slash */
   for (cptr = FileNamePtr; *cptr && *cptr != '/'; cptr++);
   if (!*cptr) FileNamePtr = NULL;

   fldptr = mlptr->ListPtr;
   for (fldcnt = mlptr->ListCount; fldcnt; fldcnt--)
   {
      if (FileNamePtr)
      {
         /* PMDF IMAP 'mail file' so always compare using that */
         zptr = (sptr = FolderName) + sizeof(FolderName)-1;
         for (cptr = FileNamePtr; *cptr && sptr < zptr; *sptr++ = *cptr++);
         for (cptr = FolderNamePtr; *cptr && sptr < zptr; *sptr++ = *cptr++);
         *sptr = '\0';
         if (!strcmp (fldptr, FolderName))
            selptr = " selected";
         else
            selptr = "";
      }
      else
      {
         if (!strcmp (fldptr, FolderNamePtr))
            selptr = " selected";
         else
            selptr = "";
      }

      for (cptr = fldptr; *cptr && *cptr != '/'; cptr++);

      fprintf (stdout, "<option value=\"%s%s\"%s>%s\n",
                *cptr ? "" : "MAIL/",
                HTML_ESCAPE(fldptr), selptr,
                HTML_ESCAPE(fldptr));

      if (*selptr) FolderRecent (mlptr, fldptr);

      /* next folder in list */
      while (*fldptr) fldptr++;
      fldptr++;
   }
}

/*****************************************************************************/
/*
Differences in the CSS rendering of (only) buttons inside table data (the CSS
lay-up technicalities are well outside any expertise I might have) for Firefox
1.5, Mozilla 1.7, Opera 8.5 and MSIE 6.0 make this guesstimate of the width of
the required buttons absolutely necessary.  I played around with the various
bits of CSS I understood and still got vastly differing behaviours between the
agents and eventually resorted to this nightmare.  Talk about making it up as
you go along!
*/

void FolderButtonListEach (REQUEST_DATA *rdptr)

{
   int  ButtonWidth;
   float  WidthFactor;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderButtonListEach()");

   muptr = &VmsMailUser;
   uoptr = &rdptr->UserOptions;

   ButtonWidth = muptr->MailFolderMaxLen;

   if (ButtonWidth > 30)
      WidthFactor = 0.70;
   else
   if (ButtonWidth > 20)
      WidthFactor = 0.65;
   else
   if (ButtonWidth > 10)
      WidthFactor = 0.80;
   else
      WidthFactor = 1.10;

   /* MSIE6 seems to render fonts slightly larger than the others tested */
   if (rdptr->UserAgent == SOY_USER_AGENT_MSIE) WidthFactor += 0.1;

   /* who uses monospace?! (at least the one person who reported issues :-) */
   if (uoptr->FontIsMono) WidthFactor += 0.15;

   ButtonWidth = (int)((float)ButtonWidth * WidthFactor);

   if (uoptr->PmdfImap)
      FolderButtonPmdfImapListEach (rdptr, ButtonWidth);
   else
   if (uoptr->AllMailFiles || muptr->MailFileList.ListCount > 1)
      FolderButtonMultiFileListEach (rdptr, ButtonWidth);
   else
      FolderButtonSingleFileListEach (rdptr, ButtonWidth);
}

/*****************************************************************************/
/*
Only a single MAIL.MAI file or optioned to only show MAIL.MAI.
Provide a button for each folder.
*/

void FolderButtonSingleFileListEach
(
REQUEST_DATA *rdptr,
int ButtonWidth
)
{
   int  cnt, fldcnt, idx;
   char  *cptr, *fldptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderButtonSingleFileListEach()");

   muptr = &VmsMailUser;
   uoptr = &rdptr->UserOptions;

   cnt = 0;
   fldptr = muptr->MailFolderList.ListPtr;
   for (fldcnt = muptr->MailFolderList.ListCount; fldcnt; fldcnt--)
   {
      if (cnt && !(cnt % uoptr->FolderButtons)) 
         fprintf (stdout, "<p class=\"css_p02\">");
      cnt++;

      fprintf (stdout,
"<input class=\"css_button_panel_1\" \
style=\"width:%dem;text-align:left;padding-left:0.5em;\" \
type=\"submit\" name=\"folder_btn\" value=\"%s\">\n",
               ButtonWidth, HTML_ESCAPE(fldptr));

      /* next folder in list */
      while (*fldptr) fldptr++;
      fldptr++;
   }
}

/*****************************************************************************/
/*
Multiple mail .MAI files.
Provide a button for each mail file and each folder for the currently selected
mail .MAI file.
*/

void FolderButtonMultiFileListEach
(
REQUEST_DATA *rdptr,
int ButtonWidth
)
{
   int  cnt, filcnt, fldcnt, idx;
   char  *cptr, *filptr, *fldptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderButtonMultiFileListEach()");

   muptr = &VmsMailUser;
   uoptr = &rdptr->UserOptions;

   /* folder buttons don't contain the mail file, supply using this */
   fprintf (stdout,
"<input type=\"hidden\" name=\"folder_btn_mailfile\" value=\"%s\">\n",
            rdptr->MailFileName); 

   cnt = 0;

   filptr = muptr->MailFileList.ListPtr;
   for (filcnt = muptr->MailFileList.ListCount; filcnt; filcnt--)
   {
      if (cnt && !(cnt % uoptr->FolderButtons)) 
         fprintf (stdout, "<p class=\"css_p02\">");
      cnt++;

      /* show a button for each mail file */
      fprintf (stdout,
"<input class=\"css_button_panel_1\" \
style=\"width:%dem;text-align:left;padding-left:0.5em;\" \
type=\"submit\" name=\"folder_btn\" value=\"%s/\" \
onclick=\"this.value=\'%s/\';return true;\">\n",
               ButtonWidth, filptr, filptr);

      if (!strcmp (filptr, rdptr->MailFileName))
      {
         /* current mail file */
         fldptr = muptr->MailFolderList.ListPtr;
         for (fldcnt = muptr->MailFolderList.ListCount; fldcnt; fldcnt--)
         {
            if (cnt && !(cnt % uoptr->FolderButtons)) 
               fprintf (stdout, "<p class=\"css_p02\">");
            cnt++;

            fprintf (stdout,
"<input class=\"css_button_panel_1\" \
style=\"width:%dem;text-align:left;padding-left:2em;\" \
type=\"submit\" name=\"folder_btn\" value=\"/%s\">\n",
                     ButtonWidth, HTML_ESCAPE(fldptr));

            /* next folder name in the list */
            while (*fldptr) fldptr++;
            fldptr++;
         }
      }

      /* next mail file name in the list */
      while (*filptr) filptr++;
      filptr++;
   }
}

/*****************************************************************************/
/*
List each folder in the PMDF IMAP mailbox.
*/

void FolderButtonPmdfImapListEach
(
REQUEST_DATA *rdptr,
int ButtonWidth
)
{
   int  cnt, fldcnt,
        SlashCount;
   char  *cptr, *sptr, *zptr, *fldptr;
   char  MailFileName [128];
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderButtonPmdfImapListEach()");

   muptr = &VmsMailUser;
   uoptr = &rdptr->UserOptions;

   cnt = 0;

   fldptr = muptr->MailFolderList.ListPtr;
   for (fldcnt = muptr->MailFolderList.ListCount; fldcnt; fldcnt--)
   {
      if (cnt && !(cnt % uoptr->FolderButtons)) 
         fprintf (stdout, "<p class=\"css_p02\">");
      cnt++;

      zptr = (sptr = MailFileName) + sizeof(MailFileName)-1;
      SlashCount = 0;
      for (cptr = fldptr; *cptr; cptr++) if (*cptr == '/') SlashCount++;
      if (SlashCount)
      {
         /* hint: a PMDF IMAP mail file will contain at least one slash */
         while (*fldptr && SlashCount && sptr < zptr)
         {
            if (*fldptr == '/') SlashCount--;
            *sptr++ = *fldptr++;
         }
      }
      else
         for (cptr = "MAIL"; *cptr && sptr < zptr; *sptr++ = *cptr++);
      *sptr = '\0';

      if (!strcmp (MailFileName, "MAIL"))
         fprintf (stdout,
"<input class=\"css_button_panel_1\" \
style=\"width:%dem;text-align:left;padding-left:2em;\" \
type=\"submit\" name=\"folder_btn\" value=\"%s\">\n",
                  ButtonWidth, HTML_ESCAPE(fldptr));
      else
         fprintf (stdout,
"<input class=\"css_button_panel_1\" \
style=\"width:%dem;text-align:left;padding-left:2em;\" \
type=\"submit\" name=\"folder_btn\" value=\"%s/%s\">\n",
                  ButtonWidth, HTML_ESCAPE(MailFileName),
                  HTML_ESCAPE(fldptr));

      /* next folder in list */
      while (*fldptr) fldptr++;
      fldptr++;
   }
}

/*****************************************************************************/
/*
A faked folder select specifically for the help menu bar page.
*/

void FolderOpenHelp (REQUEST_DATA *rdptr)

{
   int  status;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderOpenHelp()");

   fprintf (stdout,
"<!-- folder open help begin -->\n\
<td nowrap align=\"left\" valign=\"middle\">\n\
<select class=\"css_select_2\" name=\"folder_list_help\">\n\
<option value=\"%s\" selected>&nbsp;%s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n\
</select>&nbsp;&nbsp;\n\
<input class=\"css_button_panel_2\" type=\"submit\" \
name=\"main_btn\" value=\"%s\">&nbsp; \n\
<input class=\"css_button_panel_2\" type=\"submit\" \
name=\"main_btn\" value=\"0 %s\">&nbsp; \n\
</td>\n\
<!-- folder open help end -->\n\n",
            LangFor("help"), LangFor("help"),
            LangFor("open"),
            LangFor("new"));
}

/*****************************************************************************/
/*
Generate an appropriate folder description (i.e. if it's not the default mail
file then include it in the description) and return a pointer to dynamic
storage.
*/

char* FolderDescribe
(
char *MailFileName,
char *FolderName
)
{
   char  *cptr, *sptr, *zptr;
   char  buf [256];

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderDescribe()");

   zptr = (sptr = buf) + sizeof(buf)-1;
   if (MailFileName)
   {
      if (strcmp (MailFileName, "MAIL"))
      {
         /* not the default MAIL.MAI */
         for (cptr = MailFileName; *cptr && sptr < zptr; *sptr++ = *cptr++);
         if (sptr > buf && *(sptr-1) == '/') sptr--;
         if (sptr < zptr) *sptr++ = '/';
      }
   }
   if (FolderName)
      for (cptr = FolderName; *cptr && sptr < zptr; *sptr++ = *cptr++);
   *sptr = '\0';
   cptr = CgiLibVeeMemCalloc (sptr-buf+1);
   strcpy (cptr, buf);

   if (WatchEnabled) WatchThis (FI_LI, "!AZ", cptr);

   return (cptr);
}

/*****************************************************************************/
/*
Maintain the specified recent folder list.
*/

void FolderRecent
(
VMS_MAIL_LIST *mlptr,
char *fname
)
{
   int  cnt, fldcnt, size;
   char  *cptr, *fldptr, *newptr, *sptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "FolderRecent()");

   if (mlptr->ListCount)
   {
      /* list the (current) recent folders */
      fldptr = mlptr->ListPtr;
      cnt = 0;
      for (fldcnt = mlptr->ListCount; fldcnt; fldcnt--)
      {
         /* if this one is NOT the current */
         if (strcmp (fname, fldptr))
         {
            /* plus one are stored but only max are displayed */
            if (++cnt > FOLDER_LIST_RECENT_MAX) break;
            fprintf (stdout, "<option value=\"%s\">&hellip;%s\n",
                     HTML_ESCAPE(fldptr), HTML_ESCAPE(fldptr));
         }
         while (*fldptr) fldptr++;
         fldptr++;
      }
   }

   /* determine size of new list (close enough for jazz) */
   size = strlen(fname) + 1;
   fldptr = mlptr->ListPtr;
   for (fldcnt = mlptr->ListCount; fldcnt; fldcnt--)
   {
      size += strlen(fldptr) + 1;
      while (*fldptr) fldptr++;
      fldptr++;
   }
   newptr = sptr = CgiLibVeeMemCalloc (size);

   /* build the new list */
   for (cptr = fname; *cptr; *sptr++ = *cptr++);
   *sptr++ = '\0';
   cnt = 1;
   fldptr = mlptr->ListPtr;
   for (fldcnt = mlptr->ListCount; fldcnt; fldcnt--)
   {
      /* no duplicates */
      if (!strcmp (fname, fldptr))
         while (*fldptr) fldptr++;
      else
      {
         /* plus one are stored but only max are displayed */
         if (++cnt > FOLDER_LIST_RECENT_MAX + 1) break;
         while (*fldptr) *sptr++ = *fldptr++;
         *sptr++ = '\0';
      }
      fldptr++;
   }

   /* just update the pointer and count, no need to free previous list */
   mlptr->ListPtr = newptr;
   mlptr->ListCount = cnt;
   mlptr->ListLength = sptr - newptr;
}

/*****************************************************************************/

