/***************************************************************************
 *                                                                         *
 *   GALFORAH.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1997, 1998  Galacticomm, Inc.   All Rights Reserved     *
 *                                                                         *
 *                                                                         *
 *   Forums Active HTML                                                    *
 *                                                  - N.C. Osterc  5/27/97 *
 *   Pure HTML modifications                                               *
 *                                                  - P Henning    6/13/98 *
 *                                                                         *
 ***************************************************************************/

#pragma option -w-par
#define _RWSTDLL
#include <windows.h>
#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"
#include "galacth.h"
#include "tvb.h"
#include "dnf.h"
#include "dnfmgr.h"
#include "ahutil.h"
#include "ahusrxrf.h"
#include "webd.h"
#include "gme.h"
#include "ftg.h"
#include "galmsg.h"
#include "iostream.h"
#include "fioapi.h"
#include "msgatt.h"
#include "httpup.h"
#include "cyctimer.h"
#include "gmetvbah.h"
#include "galtext.h"
#include "msgah.h"
#include "galmsgah.h"
#include "efhooks.h"
#include <stdcomp.h>
#pragma option -w-aus
#include <deque>
#pragma option -w-aus
#include <cstring.h>
using namespace std;

#define FILREV "$Revision: 57 $"

#if defined(EXTRA_LOGGING)
#define LOGFILE "galme.log"        //HACK: change when want FOR-only logging
static VOID logmsg(CHAR const * fmt,...);
static VOID vlogmsg(CHAR const * fmt,va_list ap);
#define LOG0(s)               logmsg(s)
#define LOG1(s,v1)            logmsg(s,v1)
#define LOG2(s,v1,v2)         logmsg(s,v1,v2)
#define LOG3(s,v1,v2,v3)      logmsg(s,v1,v2,v3)
#define LOG4(s,v1,v2,v3,v4)   logmsg(s,v1,v2,v3,v4)
#else
#define LOG0(s)               ((VOID)0)
#define LOG1(s,v1)            ((VOID)0)
#define LOG2(s,v1,v2)         ((VOID)0)
#define LOG3(s,v1,v2,v3)      ((VOID)0)
#define LOG4(s,v1,v2,v3,v4)   ((VOID)0)
#endif /* EXTRA_LOGGING */

#define PPFIX "galacth/galforah"
#define GETINPUT(X) (ses->param(X,strInpBuf,STRINPSIZ)) // macro for getting params
#define paramOK(X) (ses->paramIndex(X,m_pIndex)) // macro for checking parameter existence
#define userOK() ((usr=ses->getUser()) != NULL) // macro ensures user exists

#define STRINPSIZ             16385 // max input size of gotten Web parameter
#define TXTBUFLEN             16834 // size of text-storage area
#define CCSIZ                 1024  // size of storage buffer for CC list
#define URLSIZ                1024  // max URL size
#define NAMESIZ               32    // max size of parameter name in /delete
#define WRITECHUNKSIZ         256   // max bytes to write to file per cycle
#define SENDCYCSIZ            128   // max bytes to evaluate in format4send() per cycle
#define MAXQUOSIZ             79    // maximum quotation line size
#define MAXLINSIZ             76    // max regular line size
#define GENBUFSIZ             50    // general purpose buffer size

#define PROCEED_AH_REQUEST    0     // Proceed with Request processing
#define PROCEED_AH_POST       1     // Proceed with Post processing
#define PROCEED_AH_RESPONSE   2     // Proceed with Response processing
#define PROCEED_AH_DELETE     3     // Proceed with Delete processing
#define PROCEED_AH_QUEUEUP    4     // Proceed queueing up message headers
#define PROCEED_AH_READMSG    5     // Proceed m_bReading a message
#define PROCEED_AH_PREVMSG    6     // Proceed rading previous message
#define PROCEED_AH_NEXTMSG    7     // Proceed reading next message
#define PROCEED_AH_EXTHEAD    8     // Proceed doing extended header procing
#define PROCEED_AH_STRIPRTF   9     // Proceed stripping RTF (if required)
#define PROCEED_AH_FORMAT     10    // Proceed with message formatting
#define PROCEED_AH_LINKS      11    // Proceed with adding links
#define PROCEED_AH_REPLY      12    // Proceed with Reply processing
#define PROCEED_AH_NEARGELT   13    // Proceed reading message near in GELT order
#define PROCEED_AH_LISTALL    14    // Proceed Listing All message headers
#define PROCEED_AH_MOREHI     15    // Proceed checking if there are more hi msgs
#define PROCEED_AH_MORELO     16    // Proceed checking if there are more lo msgs
#define PROCEED_AH_VALIDATE   17    // Proceed validating message
#define PROCEED_AH_FORWARD    18    // Proceed forwarding message
#define PROCEED_AH_COPYMSG    19    // Proceed Copying message
#define PROCEED_AH_MARKREAD   20    // Proceed marking msg as read
#define PROCEED_AH_DOWNLOAD   21    // Proceed downloading file attachment
#define PROCEED_AH_QUOTE      22    // Proceed quoting message
#define PROCEED_AH_PRCSUBMIT  23    // Proceed processing /send submit
#define PROCEED_AH_MOVEFILE   24    // Proceed moving file
#define PROCEED_AH_ATTACH     25    // Proceed attachment processing
#define PROCEED_AH_SENDFORMAT 26    // Proceed formatting message when sent
#define PROCEED_AH_VALIDADR   27    // Proceed Validating Address
#define PROCEED_AH_VALIDPRI   28    // Proceed Validating Priority
#define PROCEED_AH_VALIDBLEN  29    // Proceed Validating Body Length
#define PROCEED_AH_VALIDRR    30    // Proceed Validating Return Receipt
#define PROCEED_AH_VALIDATT   31    // Proceed Validating Attachment
#define PROCEED_AH_VALIDPFN   32    // Proceed Profanity Checking
#define PROCEED_AH_VALIDPFBDY 33    // Proceed more Profanity Checking
#define PROCEED_AH_VALIDCCADR 34    // Proceed validating CC addresses
#define PROCEED_AH_VALIDCCATT 35    // Proceed Validating CC attachment
#define PROCEED_AH_READEXEMPT 36    // Proceed Reading message for exempting
#define PROCEED_AH_EXEMPT     37    // Proceed Exempting messages
#define PROCEED_AH_READAPPROVE 38   // Proceed Reading message for approving
#define PROCEED_AH_APPROVE    39    // Proceed Approving message


#define LIST_NEWMSGS          1     // New Messages flag
#define LIST_DIREC_LT         2     // Direction Less-than flag
#define LIST_DIREC_GT         4     // Direction Greater-than flag
#define LIST_DIREC_LE         8     // Direction less-than-equal flag
#define LIST_DIREC_GE         16    // Direction greater-than-equal flag
#define LIST_ORD_ASCEND       32    // Order ascending flag
#define LIST_ORD_DESCEND      64    // Order descending flag
#define LIST_ALL              128   // List all messages flag

#define READ_REFLOW           1     // Reflow message text flag
#define READ_NOTEXT           2     // Don't display text flag
#define READ_NEAR             4     // Read message near flag
#define READ_RETRECREQ        8     // Return receipt flag

#define WRITE_NEW             1     // Write new message flag
#define WRITE_REPLY           2     // Write a reply message flag
#define WRITE_RESEND          4     // Write a resend message flag
#define WRITE_QUOTE           8     // Enable Quoting flag
#define WRITE_TOALL           16    // Write "to all" flag
#define WRITE_PRI             32    // Generate priority msg flag
#define WRITE_RR              64    // Generate return-receipt flag
#define WRITE_KEEPFMT         128   // Keep msg format flag
#define WRITE_INCLIST         256   // Include CC's flag
#define WRITE_NOTEXT          512   // Don't show text

#define SUBMIT_SEND           1     // Flag - actually send message
#define SUBMIT_ATTACH         2     // Flag - send form to submit upload
#define SUBMIT_UPLOAD         4     // Flag - process uploaded file
#define SUBMIT_DETACH         8     // Flag - detach message attachment
#define SUBMIT_PREVIEW        16    // Flag - preview message
#define SUBMIT_REDISPLAY      32    // Flag - redisplay write form
#define SUBMIT_CANCEL         64    // Flag - Cancel message being composed
#define SUBMIT_NOTEXT         128   // Flag - Don't show text

#define ERR_PARAMISSING      -100   // Error code - Parameter is missing
#define ERR_PROFANATT        -101   // Error code - Attachment is profane
#define ERR_PROFANTPC        -102   // Error code - Message topic is profane
#define ERR_PROFANBDY        -103   // Error code - Body text is profane
#define ERR_INVALIDFN        -104   // Error code - Invalid file name
#define ERR_BODY2LONG        -105   // Error code - Body of msg too long
#define ERR_2MANYCC          -106   // Error code - too many CC addresses

static CHAR strInpBuf[STRINPSIZ];   // buffer used to read parameters

typedef struct tagThreadStore {     // thread storage
     struct message hdr;            // thread header
     USHORT num;                    // number of messages
} THREADSTORE, *pTHREADSTORE;

#define MAXSUBTXTLEN     15         // maximium length of a submit type
struct sendtypes {                  // submit type structure
     CHAR text[MAXSUBTXTLEN];       // text of type
     INT  flagval;                  // the flag associated with this submit
};

typedef deque<const CHAR*> FORUMQUE, *pFORUMQUE;
typedef deque<struct message*> MSGQUE, *pMSGQUE;
typedef deque<pTHREADSTORE> THRQUE, *pTHRQUE;

#define SHOW_LT     0x0001          // show messages less-then
#define SHOW_GT     0x0002          // show messages greater-then
#define SHOW_EQ     0x0004          // show mesasges equal to

#define MAPDONE     1               // done building map
#define MAPMORE     2               // more map building to go, call again
#define MAPERROR    3               // error generating map
#define MAPAGAIN    4               // call the map again

#define TT_PLAIN    1               // user wants plain text
#define TT_NATIVE   2               // user wants native text
#define TT_HTML     3               // user wants html formatted text

#define TT_STATE_RAW          0     // text is in its raw format
#define TT_STATE_PLAIN        1     // text has been had any formatting stripped
#define TT_STATE_REFLOWED     2     // text is reflowed
#define TT_STATE_URLLINK      3     // text is linked, and ready for output

#define INT_HTML    1               //
#define INT_JAVA    2

#define QSOPADD     1
#define QSOPDEL     2


#define GRPSTKSZ    20             // group stack size
#define MAXGRPSTR   (GRPSTKSZ*FORNSZ) // max group path size


#define PREFNOSET   -1
#define PREFSETON    1
#define PREFSETOFF   0


#define FORFRAMECOOKIE "forumframe"

static bool isquoted(CHAR const * s);
CHAR * excerpt(CHAR *dst, CHAR *src, UINT maxlen);


CHAR *                             //   returns pointer to destination
catGroup(                          // concatenate a group onto a group path
CHAR *dst,                         //   buffer to concatenate onto
const CHAR *src,                   //   group name to add
size_t dstSiz);                    //   size of destination buffer

static inline bool                 //   new value of flag variable
SetFlags(                          // set flags
ULONG* ulFlags,                    //   pointer to flag variable to modify
ULONG ulSetFlags,                  //   flag value to toggle
bool bSet);                        //   set the flag?

static inline bool                 //   new value of flag variable
SetFlags(                          // set flags
LONG* ulFlags,                     //   pointer to flag variable to modify
LONG ulSetFlags,                   //   flag value to toggle
bool bSet);                        //   set the flag?

static inline bool                 //   new value of flag variable
SetFlags(                          // set flags
SHORT* ulFlags,                    //   pointer to flag variable to modify
SHORT ulSetFlags,                  //   flag value to toggle
bool bSet);                        //   set the flag?

GMEUSERHANDLE                      //   gmeuser handle
GetGmeUserHandle(                  // get handle to gme user
CHAR const * uid);                 //   user to get


enum {CYC,ARCHIVETYPE,TMPID};
enum {FORUMLIST,FDEF_DESCRIPTION,MESSAGELIST,BODYTEXT,THREADLIST,HEADERS};
enum {CHECKMORE_DONE=-1,CHECKMORE_LOWINIT,CHECKMORE_LOWREAD,CHECKMORE_HIGHINIT,CHECKMORE_HIGHREAD};

dnfStep dnfForumListSteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,FORUMLIST,"FORUMLIST"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};
dnfMap forForumListMap(PPFIX "/list/forums/index.htm","ForumList"
                      ,dnfForumListSteps);

dnfStep dnfForumView[]={
     dnfStep(DNFWATCH,FDEF_DESCRIPTION,"FDEF_DESCRIPTION"),
     dnfStep(DNFMAPEND)
};
dnfMap forForumViewMap(PPFIX "/view/forum/index.htm","ForumView",dnfForumView);

dnfStep dnfForumViewError[]={
     dnfStep(DNFMAPEND)
};
dnfMap forForumViewErrorMap(PPFIX "/view/forum/error.htm","ForumViewError"
                           ,dnfForumViewError);

dnfStep dnfListMessagesError[]={
     dnfStep(DNFMAPEND)
};
dnfMap forListMessagesErrorMap(PPFIX "/list/messages/error.htm","MessageListError"
                           ,dnfListMessagesError);

dnfStep dnfMessageViewError[]={
     dnfStep(DNFMAPEND)
};
dnfMap forMessageViewErrorMap(PPFIX "/view/message/error.htm","MessageViewError"
                           ,dnfMessageViewError);

dnfStep dnfListThreadsError[]={
     dnfStep(DNFMAPEND)
};

dnfMap forListThreadsErrorMap(PPFIX "/list/threads/error.htm","ThreadListError"
                           ,dnfListThreadsError);

dnfStep dnfJavaIndex[]={
     dnfStep(DNFMAPEND)
};
dnfMap forJavaIndexMap(PPFIX "/index.htm","JavaIndex",dnfJavaIndex);

dnfStep dnfFrameIndex[]={
     dnfStep(DNFMAPEND)
};
dnfMap forFrameIndexMap(PPFIX "/indexf.htm","FramesIndex",dnfFrameIndex);

dnfStep dnfNoFrameIndex[]={
     dnfStep(DNFMAPEND)
};
dnfMap forNoFrameIndexMap(PPFIX "/indexn.htm","NoFramesIndex",dnfNoFrameIndex);

dnfStep dnfListThreads[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,THREADLIST,"THREADLIST"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};
dnfMap forListThreadsMap(PPFIX "/list/threads/index.htm","ThreadsList",dnfListThreads);

dnfStep dnfMessageView[]={
     dnfStep(DNFWATCH,BODYTEXT,"BODYTEXT"),
     dnfStep(DNFMAPEND)
};
dnfMap forMessageViewMap(PPFIX "/view/message/index.htm","MessageView",dnfMessageView);

dnfStep dnfListMessageSteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,MESSAGELIST,"MESSAGELIST"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};
dnfMap forListMessagesMap(PPFIX "/list/messages/index.htm","MessageList"
                           ,dnfListMessageSteps);

dnfStep dnfConfigPrefSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap forConfigPrefMap(PPFIX "/prefs/index.htm","ConfigPrefs"
                           ,dnfConfigPrefSteps);

dnfStep dnfUpdateQSSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap forUpdateQSMap(PPFIX "/quickscan/updatesettings/index.htm","UpdateQS"
                           ,dnfUpdateQSSteps);


dnfStep dnfFileSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap forFileMap(PPFIX "/file/index.htm","Generic File"
                           ,dnfFileSteps);

dnfStep dnfQSGetSettingsSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap forQSGetSettingsMap(PPFIX "/quickscan/getsettings/index.htm","QSGetSettings"
                           ,dnfQSGetSettingsSteps);

dnfStep dnfQSChangeForumSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap forQSChangeForumMap(PPFIX "/quickscan/changeforum/index.htm","QSChangeForum"
                           ,dnfQSChangeForumSteps);

dnfStep dnfQSChangeForumErrorSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap forQSChangeForumErrorMap(PPFIX "/quickscan/ChangeForumError/index.htm","QSChangeForumError"
                           ,dnfQSChangeForumSteps);

dnfStep dnfWriteSteps[]={
     dnfStep(DNFWATCH,BODYTEXT,"BODYTEXT"),
     dnfStep(DNFMAPEND)
};
dnfMap writeMap(PPFIX "/write/index.htm","Write a Message", dnfWriteSteps);

dnfStep dnfWriteErrorSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap writeErrMap(PPFIX "/write/error.htm","Write Error",dnfWriteErrorSteps);

enum {GRPTREE};
dnfStep grpListStep[]={            // group list DNF map
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,GRPTREE,"GROUPLIST"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};
dnfMap grpListMap(PPFIX "/list/groups/index.htm",
                  "List of Groups",grpListStep);


dnfStep dnfCycleSteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,CYC,"CYCLE"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};
dnfMap forCycleMap(PPFIX "/forcyc.htm", "Cycle", dnfCycleSteps);

dnfStep mainSteps[] =
{
     dnfStep(DNFWATCH,ARCHIVETYPE,"ZIP"),
     dnfStep(DNFMAPEND)
};
dnfMap mainMap( PPFIX "/forums.htm", "Main", mainSteps);

dnfStep ulSteps[] =
{
     dnfStep(DNFWATCH,TMPID,"TMPID"),
     dnfStep(DNFMAPEND)
};
dnfMap ulMap( PPFIX "/ul.htm", "ul", ulSteps);

dnfStep popSteps[] =
{
     dnfStep(DNFWATCH,TMPID,"TMPID"),
     dnfStep(DNFMAPEND)
};
dnfMap popMap( PPFIX "/pop.htm", "pop", popSteps);

dnfStep uploadSteps[] =
{
     dnfStep(DNFMAPEND)
};
dnfMap uploadMap( PPFIX "/upload.htm", "upload", uploadSteps);

dnfStep inProgressSteps[] =
{
     dnfStep(DNFMAPEND)
};
dnfMap inProgressMap( PPFIX "/inprog.htm", "in progress", inProgressSteps);

dnfStep dnfGenMapSteps1[]={
     dnfStep(DNFMAPEND)
};

dnfStep dnfRedispErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap redispErrMap(PPFIX "/write/error.htm","Redisp Error",dnfRedispErrMapSteps);

dnfStep dnfPreviewMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap previewErrMap(PPFIX "/view/message/error.htm","View Error",dnfPreviewMapSteps);

dnfStep dnfCancelMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap cancelMap(PPFIX "/write/cancel.htm","Cancel Success",dnfCancelMapSteps);

dnfStep dnfCancelErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap cancelErrMap(PPFIX "/write/error.htm","Cancel Error",dnfCancelErrMapSteps);

dnfStep dnfAttachErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap attachErrMap(PPFIX "/write/error.htm","Upload Error",dnfAttachErrMapSteps);

dnfStep dnfDetachErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap detachErrMap(PPFIX "/write/error.htm","Detach Error",dnfDetachErrMapSteps);

dnfStep dnfSendMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap sendMap(PPFIX "/write/sentok.htm","Sent ok",dnfSendMapSteps);

dnfStep dnfSendErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap sendErrMap(PPFIX "/write/error.htm","Sent error",dnfSendErrMapSteps);

dnfStep dnfApproveErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap approveErrMap(PPFIX "/approveit/error.htm","Approve Error",dnfApproveErrMapSteps);

dnfStep dnfApproveMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap approveSuccMap(PPFIX "/approveit/index.htm","Approve Success",dnfApproveMapSteps);

dnfStep dnfQSForumsErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap qsforumsErrMap(PPFIX "/quickscan/qsforums/error.htm","QS Forums Error",dnfQSForumsErrMapSteps);

dnfStep dnfAttErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap attErrMap(PPFIX "/att/error.htm","Attachment Download Error",dnfAttErrMapSteps);

dnfStep dnfDeleteErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap deleteErrMap(PPFIX "/delete/error.htm","Deleted Error",dnfDeleteErrMapSteps);

dnfStep dnfDeleteMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap deleteSuccMap(PPFIX "/delete/index.htm","Delete Success",dnfDeleteMapSteps);

dnfStep dnfFwdErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap fwdErrMap(PPFIX "/forward/error.htm","Forward Success",dnfFwdErrMapSteps);

dnfStep dnfFwdMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap fwdSuccMap(PPFIX "/forward/index.htm","Forward Error",dnfFwdMapSteps);

dnfStep dnfExemptErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap exemptErrMap(PPFIX "/exemptit/error.htm","Exemption Error",dnfExemptErrMapSteps);

dnfStep dnfExemptMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap exemptSuccMap(PPFIX "/exemptit/index.htm","Exemption Success",dnfExemptMapSteps);

dnfStep dnfQSForumsMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap qsforumsSuccMap(PPFIX "/quickscan/qsforums/index.htm","QS Forums Success",dnfQSForumsMapSteps);

dnfStep dnfUplErrMapSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap uplErrMap(PPFIX "/write/error.htm","Upload error",dnfUplErrMapSteps);

dnfStep dnfUplSteps[]={
     dnfStep(DNFWATCH,BODYTEXT,"BODYTEXT"),
     dnfStep(DNFMAPEND)
};
dnfMap uplMap(PPFIX "/write/index.htm","Upload ok",dnfUplSteps);

dnfStep dnfDetachSteps[]={
     dnfStep(DNFWATCH,BODYTEXT,"BODYTEXT"),
     dnfStep(DNFMAPEND)
};
dnfMap detachMap(PPFIX "/write/index.htm","Detach Success",dnfDetachSteps);

dnfStep dnfAttachSteps[]={
     dnfStep(DNFWATCH,BODYTEXT,"BODYTEXT"),
     dnfStep(DNFMAPEND)
};
dnfMap attachMap(PPFIX "/write/upload.htm","Upload Success",dnfAttachSteps);

dnfStep dnfPreViewSteps[]={
     dnfStep(DNFWATCH,BODYTEXT,"BODYTEXT"),
     dnfStep(DNFMAPEND)
};
dnfMap previewMap(PPFIX "/view/message/index.htm","View Success",dnfPreViewSteps);

dnfStep dnfRedispSteps[]={
     dnfStep(DNFWATCH,BODYTEXT,"BODYTEXT"),
     dnfStep(DNFMAPEND)
};
dnfMap redispMap(PPFIX "/write/index.htm","Redisp Success",dnfRedispSteps);

dnfStep dnfViewExtSteps[]={
     dnfStep(DNFWATCH,HEADERS,"HEADERS"),
     dnfStep(DNFMAPEND)
};
dnfMap viewExtMap(PPFIX "/view/message/ext/index.htm","View Header Info", dnfViewExtSteps);


// Forum-specific Text variables
gmeTvb flistCount("FLST_COUNT");
gmeTvb flistOrder("FLST_ORDER");
gmeTvb flistLow("FLST_LOW");
gmeTvb flistMoreLow("FLST_MORE_LOW");
gmeTvb flistHigh("FLST_HIGH");
gmeTvb flistMoreHigh("FLST_MORE_HIGH");
gmeTvb flistGroup("FLST_BYGROUP");
gmeTvb mlistLastForid("MLST_LASTFORID");

gmeTvb mlistThreadName("MLST_THRNAME");
gmeTvb mlistThreadID("MLST_THRID");

gmeTvb tlistNumMsg("TLST_NUMMSG");

gmeTvb msgByQS("MSG_BYQUICKS");
gmeTvb msgByThread("MSG_BYTHREAD");

gmeTvb fUseFrames("FPREF_USEFRAMES");

gmeTvb qsOnlyAtt("QS_ONLYATT");
gmeTvb qsOnlyNew("QS_ONLYNEW");
gmeTvb qsOnlyToMe("QS_ONLYTOME");
gmeTvb qsOnlyFromMe("QS_ONLYFRME");
gmeTvb qsKeywords("QS_KEYWORDS");
gmeTvb qsMsgid("QS_MSGID");

class treePictures;

CHAR rootGroupName[FORNSZ];        // name used to describe root group
INT timeSlice;                     // time slice for request processing
treePictures *pTreePix;            // global instance for general use

// group-related text variables

gmeTvb txvGrpPath("GRP_PATH");
gmeTvb txvGrpPathURL("GRP_PATH_U");
gmeTvb txvGrpParent("GRP_PARENT");
gmeTvb txvGrpParentURL("GRP_PARENT_U");
gmeTvb txvGrpName("GRP_NAME");
gmeTvb txvGrpNameURL("GRP_NAME_U");
gmeTvb txvGrpTopic("GRP_TOPIC");
gmeTvb grpNumForums("GRP_NUMFORUMS");


/* Miscellaneous global classes */

/***************************************************************************
 *                                                                         *
 *   Tree Picture Manager Terminology                                      *
 *                                                                         *
 *   The following terminology is used to describe the different tree      *
 *   components:                                                           *
 *                                                                         *
 *   Trunk:    A vertical line, it continues down from a higher group      *
 *             without branching off into any child groups.                *
 *                                                                         *
 *   Branch:   A sideways T, it branches off to a child group that has     *
 *             siblings (groups with the same parent).                     *
 *                                                                         *
 *   Leaf:     An L, it branches off to a child group that has no more     *
 *             siblings.                                                   *
 *                                                                         *
 *   For example:                                                          *
 *                                                                         *
 *                  X (root group)                                         *
 *                  |                                                      *
 *        branch -> +-X Group1                                             *
 *                  | |                                                    *
 *         trunk -> | +-X SubGroup1-1                                      *
 *                  |                                                      *
 *          leaf -> +-X Group2                                             *
 *                                                                         *
 *   The following terminology is used to describe the different types     *
 *   of branches and leaves:                                               *
 *                                                                         *
 *   Bare:     The group has no children.                                  *
 *                                                                         *
 *   Laden:    The group has children, but is not in the expanded group    *
 *             path, thus a plus is displayed on the branch or leaf.       *
 *                                                                         *
 *   Expanded: The group has children and is in the expanded group path,   *
 *             thus a minus is displayed on the branch or leaf.            *
 *                                                                         *
 ***************************************************************************/

class treePictures {               // tree picture management class
public:
     treePictures();
     ~treePictures();

     VOID
     empty(                        // empty group picture
     ostream & ost);               //   output stream to write to

     VOID
     withfor(                      // group with forums picture
     ostream & ost);               //   output stream to write to

     VOID
     blank(                        // tree blank space picture
     ostream & ost);               //   output stream to write to

     VOID
     trunk(                        // tree trunk picture
     ostream & ost);               //   output stream to write to

     VOID
     branchBare(                   // tree branch bare picture
     ostream & ost);               //   output stream to write to

     VOID
     branchLaden(                  // tree branch laden picture
     ostream & ost);               //   output stream to write to

     VOID
     branchExp(                    // tree branch expanded picture
     ostream & ost);               //   output stream to write to

     VOID
     leafBare(                     // tree leaf bare picture
     ostream & ost);               //   output stream to write to

     VOID
     leafLaden(                    // tree leaf laden picture
     ostream & ost);               //   output stream to write to

     VOID
     leafExp(                      // tree leaf expanded picture
     ostream & ost);               //   output stream to write to

private:                           // private member functions

     VOID
     outputPic(                    // output HTML code for a picture
     ostream & ost,                //   output stream to write to
     INT msgNum,                   //   option number to use for format
     CHAR const * picFile,         //   file name of picture file
     CHAR const * altStr);         //   alternate string

private:                           // private data members
     CHAR * m_empty;               //   empty group picture
     CHAR * m_withfor;             //   group with forums picture
     CHAR * m_blank;               //   tree trunk picture
     CHAR * m_trunk;               //   tree trunk picture
     CHAR * m_branchBare;          //   tree branch bare picture
     CHAR * m_branchLaden;         //   tree branch laden picture
     CHAR * m_branchExp;           //   tree branch expanded picture
     CHAR * m_leafBare;            //   tree leaf bare picture
     CHAR * m_leafLaden;           //   tree leaf laden picture
     CHAR * m_leafExp;             //   tree leaf expanded picture
     CHAR * m_altEmpty;            //   empty group alternate string
     CHAR * m_altFor;              //   group with forums alternate string
     CHAR * m_altBlank;            //   tree trunk alternate string
     CHAR * m_altTrunk;            //   tree trunk alternate string
     CHAR * m_altBranchBare;       //   tree branch bare alternate string
     CHAR * m_altBranchLaden;      //   tree branch laden alternate string
     CHAR * m_altBranchExp;        //   tree branch expanded alternate string
     CHAR * m_altLeafBare;         //   tree leaf bare alternate string
     CHAR * m_altLeafLaden;        //   tree leaf laden alternate string
     CHAR * m_altLeafExp;          //   tree leaf expanded alternate string
};



class forAgent : public acthAgent {              // for agent
public:

     VOID
     getAHAxsKey();                // retrieve the ActiveHTML forum access key

     forAgent() : acthAgent("Worldgroup Forums","forums")
                , forahkey(NULL)
     {
          getSettings();
          txvGrpPathURL.setCgiEncode(true);
          txvGrpParentURL.setCgiEncode(true);
          txvGrpNameURL.setCgiEncode(true);

          if (usejava || usehtml) {
               registerAgent(acthVersion);
               ::deldir(AHUPDIR);
          }
     }

     ~forAgent()
     {
          if (forahkey != NULL) {
               free(forahkey);
          }
          delete ::pTreePix;
     }

     acthSynthesis *               // my session info (NULL=can't)
     newSynthesis(                 // instantiate my Synthesis class
     acthSession *ses);            // for passing to acthSynthesis's ctor

     VOID getSettings();

public:
     CHAR* forahkey;
     bool usehtml;
     bool usejava;
     INT   definterface;
     bool defframes;
     INT dftlstc;
     INT dftflstc;
     INT maxlstc;
     INT maxflstc;
     INT iMaxCC;
};

forAgent theforAgent;              // the one and only instance of forAgent

class forSynthesis : public acthSynthesis {      // forah response synthesis
public:
     forSynthesis(acthSession *_ses) :
          acthSynthesis(_ses),
          m_doApprove( false ),
          m_doCopy( false ),
          m_doDl( false ),
          dnf( NULL ),
          m_doneFlg( false ),
          m_doExempt( false ),
          fid( 0 ),
          flags( 0 ),
          m_doFwd( false ),
          m_gotText( false ),
          m_attaching( false ),
          m_httpUp( NULL ),
          m_slExempt ( false ),
          m_doReply( false ),
          m_savoutsz( 0 ),
          m_savMsgId( 0 ),
          thrid( 0 ),
          usr( NULL ),
          m_gmehandle(NULL),
          xrfptr( NULL )

     {
          topic[0]='\0';
          holdid[0]='\0';
          attname[0]='\0';
          m_tmpid = ::GetTickCount();
          cclist[0]='\0';
          m_mhdrptr=&mhdr;
          numptr=&numthrs;
          memset(&mhdr,0,sizeof(struct message));
          otsp=(struct otscan*)(&ots);
          txtbuf=new CHAR[TXTLEN];
          txtbuf2=new CHAR[TXTLEN];
     }

     virtual ~forSynthesis()
     {
          if (dnf != NULL)
          {
               delete dnf;
          }

          if (xrfptr != NULL)
          {
               delete xrfptr;
          }

          if (m_doDl) {
               gdldone(usr->userid(),tagbuf);
          }
          if (txtbuf != NULL)
          {
               delete [] txtbuf;
          }
          if (txtbuf2 != NULL)
          {
               delete [] txtbuf2;
          }
          if (m_httpUp != NULL) {
               delete m_httpUp;
          }
          if (m_gmehandle != NULL) {
               //LOG2("FOR.~forSynthesis closing: this=%08lX h=%lu",this,m_gmehandle);
               gmeUserClose(m_gmehandle);
          }
     }

protected:

     CHAR mywrkarea[GMEWRKSZ];
     CHAR* txtbuf;
     CHAR* txtbuf2;
     CHAR cclist[1024];
     CHAR tagbuf[TSLENG];
     CHAR holdid[MAXADR];
     CHAR attname[GCMAXFNM];
     struct message mhdr;
     struct message *m_mhdrptr;
     struct message mhdrtmp;
     CHAR ots[MAXQSR];
     struct otscan* otsp;
     USHORT numthrs;
     USHORT *numptr;
     struct qscfg* qscp;

protected:

     dnfHandler *dnf;
     acthUserID *usr;
     acthUserXrf *xrfptr;
     LONG flags;
     LONG thrid;
     LONG fid;
     CHAR topic[TPCSIZ];
     ULONG m_savMsgId;
     bool m_doneFlg;
     bool m_doReply;
     bool m_doCopy;
     bool m_doFwd;
     bool m_gotText;
     bool m_doDl;
     bool m_doExempt;
     bool m_doApprove;
     ULONG m_tmpid;
     INT m_savoutsz;
     httpUpload *m_httpUp;
     bool m_attaching;
     bool m_slExempt;
     GMEUSERHANDLE m_gmehandle;

     virtual ACTHCODE              // enumerated response (see galacth.h)
     proceed();                    // process request, synthesize response

     VOID
     abort();                      // handle session abort

     ACTHCODE
     proceedGet();                 // proceed synthesizing response to GET

     ACTHCODE
     proceedPost();                // proceed synthesizing response to POST

     VOID
     boutallfinf(                  // Bout all in-memory forum info
     const struct fordef *finfptr);

     ACTHCODE
     dnf_ReadMsgs(                 // Read Messages in a particular forum
     INT forum);

     ACTHCODE
     dnf_ReadaMsg(                  // Read a part. message
     INT forum,                    //   forum id
     ULONG msgid,                  //   message id
     bool txohdr);

     ACTHCODE
     dnf_ReadThreads(              // Read Threads in a particular forum
     INT forum);

     ACTHCODE
     dnf_ListMsgsinThread(         // List Message part. to a thread
     INT forum,
     LONG thrid);

     ACTHCODE
     dnf_scan();                   // Scan for messages

     ACTHCODE
     dnf_sendmsg();                // Send a message

     ACTHCODE
     dnf_replymsg();               // Reply to a message

     ACTHCODE
     dnf_copymsg();                // Copy a Message

     ACTHCODE
     dnf_forwardmsg();             // Forward a message

     ACTHCODE
     dnf_modifymsg();              // Modify a message

     ACTHCODE
     dnf_delmsg();                 // Delete a message

     ACTHCODE
     dnf_attach();                 // process an attachment upload

     ACTHCODE
     dnf_inProgress();             // attachment-already-in-progress

     ACTHCODE
     dnf_approve();                // Approve a message

     ACTHCODE
     dnf_exempt();                 // Exempt a message

     bool
     getMFParams();                // Handle same parametrs

     bool
     getParams();                  // Handles other params

     VOID
     Gme_ErrorHandler(             // Handle errors
     INT rc);

     bool
     getForumsInQS(       // Are there really forums in the QS?
     struct qscfg *qptr,
     const struct fordef *fptr);

     VOID
     getTopicParam();               // get message topic

     VOID
     getCCParam();              // handle message cc list

     ACTHCODE
     dnf_downloadAtt(              // download file attachment
     INT forum,                    //   forum id of message
     ULONG msgid);                 //   message id

     VOID
     getTmpId();                   // get message temp-id

     const CHAR *
     getTopic();

     const CHAR *
     getHistory();

     const CHAR *
     getAttName();

     const CHAR *
     getDate();

     const CHAR *
     getTime();
};

class errSynthesis : public acthSynthesis {      // forah response synthesis
public:
     errSynthesis(acthSession *_ses) : acthSynthesis(_ses)
     {
     }

     ACTHCODE
     proceed()
     {
          return(ACTHNOTFND);
     }
};

class forHTMLSynthesis : public forSynthesis {
public:
     forHTMLSynthesis(acthSession* _ses, INT _n=0) : forSynthesis(_ses)
                                         , m_init(false)
                                         , m_bOnlyQS(false)
                                         , m_iDirection(SHOW_GT|SHOW_EQ)
                                         , m_iCount()
                                         , m_bIsDescOrder(false)
                                         , m_bAll(false)
                                         , m_timer(::timeSlice)
                                         , m_needcyc(false)
                                         , m_err(0), m_op(0)
                                         , m_bThread(false)
                                         , m_n(_n)
                                         , m_onsuccessFile(NULL)
                                         , m_onerrorFile(NULL)
                                         , m_spEQ(false)



     {
          *m_pForumStart='\0';
          *m_pForumStartParam='\0';
          memset(mywrkarea,0,GMEWRKSZ);
          inigmerq(mywrkarea);
          memset(&m_uaccptr,0,sizeof(struct usracc));
     }


     virtual ~forHTMLSynthesis()
     {
          if (m_onsuccessFile != NULL) {
               delete [] m_onsuccessFile;
          }
          if (m_onerrorFile != NULL) {
               delete [] m_onerrorFile;
          }
          if (gmerqopn(mywrkarea)) {
               clsgmerq(mywrkarea);
          }
     }

     virtual ACTHCODE proceed();
     virtual VOID readParams();
     virtual VOID createDNF();
     virtual ACTHCODE errorResponse(INT errcode, INT operation,dnfMap& mapz);
     virtual VOID setErrorVars(INT errcode, INT operation);

     VOID getQSPtr();
     bool startProc();
     bool contProc(ACTHCODE rc);
     VOID clearAllForTvb();
     VOID readDefaults();
     VOID setBaseTvb();
     ACTHCODE NeedOnSuccessDNF();
     ACTHCODE NeedOnErrorDNF(const CHAR* path);
     VOID setGroupVars(USHORT grpid, const CHAR* gname);
     VOID clearGroupVars();
     VOID setusaptr();
     INT getSeq();

protected:
     bool m_bOnlyQS;
     bool m_init;
     INT m_iDirection;
     INT m_iCount;
     bool m_bIsDescOrder;
     bool m_bAll;
     bool m_needcyc;
     cycleTimer m_timer;
     INT m_err;
     INT m_op;
     bool m_bThread;
     INT m_n;
     LONG webpref;
     CHAR* m_onsuccessFile;
     CHAR* m_onerrorFile;
     CHAR m_pForumStart[FORNSZ];
     CHAR m_pForumStartParam[FORNSZ];
     bool m_spEQ;
     struct usracc m_uaccptr;
};

class forInitialSynthesis : public forHTMLSynthesis {
public:
     forInitialSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,0)
     {
     }

     ACTHCODE proceed();
     VOID getDefaultType();
     VOID paramCheck();
     VOID createDNF();

public:
     bool usehtml;
     bool useframes;
};

class forQSGetSettingsSynthesis : public forHTMLSynthesis {
public:
     forQSGetSettingsSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,2)
     {
     }

     ACTHCODE proceed();
     VOID createDNF();
     VOID setQSVars();
     VOID clearQSVars();
};

class forQSChangeForumSynthesis : public forHTMLSynthesis {
public:
     forQSChangeForumSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,2)
                                                  , qsop(QSOPADD)
     {
     }

     ACTHCODE proceed();
     VOID createDNF();
     bool updateQSForum();
     VOID readQSParam();

public:
     INT qsop;

};


class forFileSynthesis : public forHTMLSynthesis {
public:
     forFileSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,1)
     {
     }

     ACTHCODE proceed();
     VOID createDNF();
};

class forPrefSynthesis : public forHTMLSynthesis {
public:
     forPrefSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,1)
                                         , m_pType(PREFNOSET)
                                         , m_pReflow(PREFNOSET)
                                         , m_pQuote(PREFNOSET)
                                         , m_pForumTo(PREFNOSET)
                                         , m_pFrames(PREFNOSET)
                                         , m_curforid(EMLID)


     {
     }

     ACTHCODE proceed();
     VOID buildPrefs();
     SHORT prefSet(const CHAR* name);
     VOID savePrefs();

     VOID createDNF();
public:
     SHORT m_pType;
     SHORT m_pReflow;
     SHORT m_pQuote;
     SHORT m_pForumTo;
     SHORT m_pFrames;
     USHORT m_curforid;
};

class forUpdateQSSynthesis : public forHTMLSynthesis {
public:
     forUpdateQSSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,2)
                                         , m_keywords(NULL)
                                         , m_pOnlyAtt(PREFNOSET)
                                         , m_pOnlyNew(PREFNOSET)
                                         , m_pOnlyFrom(PREFNOSET)
                                         , m_pOnlyTo(PREFNOSET)


     {
     }

     ~forUpdateQSSynthesis()
     {
          if (m_keywords != NULL) {
               delete [] m_keywords;
          }
     }
     ACTHCODE proceed();
     VOID buildQS();
     SHORT QSSet(const CHAR* name);
     VOID saveQS();

     VOID createDNF();
public:
     SHORT m_pOnlyAtt;
     SHORT m_pOnlyFrom;
     SHORT m_pOnlyTo;
     SHORT m_pOnlyNew;
     CHAR* m_keywords;
};


class forViewForumSynthesis : public forHTMLSynthesis {
public:
     forViewForumSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,2)
                                              , m_bShowDesc(false)

     {
          *m_desc='\0';
     }


     ACTHCODE proceed();
     VOID readParams();
     VOID createDNF();
     VOID setupVars();

public:
     CHAR m_desc[MAXFDESC];
     bool m_bShowDesc;
};

class forListForumsSynthesis : public forHTMLSynthesis {
public:
     forListForumsSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,2)
                                               , m_pGroupName(NULL)
                                               , m_swapdir(false)
                                               , m_firstforum(true)
                                               , m_fcnt(0)
                                               , m_qsidx(0)
                                               , m_startbuild(false)
                                               , m_gid(ROOTGRP)
                                               , m_usegroup(false)
                                               , m_bIsMore(MAPMORE)


     {
          *m_pForumLow='\0';
          *m_pForumHigh='\0';
          m_iCount=theforAgent.dftflstc;
     }

     ~forListForumsSynthesis()
     {
          if (m_pGroupName != NULL) {
               delete [] m_pGroupName;
          }
          emptyqueue();
     }

     ACTHCODE proceed();
     VOID readParams();
     SHORT buildMap();
     VOID addToMap(const CHAR* name);
     VOID createDNF();
     VOID setForumListContextVars();
     bool setupVars();
     VOID emptyqueue();
     SHORT buildQSForumMap();


protected:
     CHAR* m_pGroupName;

     CHAR m_pForumLow[FORNSZ];
     CHAR m_pForumHigh[FORNSZ];
     bool m_swapdir;
     bool m_firstforum;
     bool m_startbuild;
     INT m_fcnt;
     INT m_qsidx;
     FORUMQUE m_fque;
     USHORT m_gid;
     bool m_usegroup;
     SHORT m_bIsMore;
};

class forListMessagesSynthesis : public forHTMLSynthesis {
public:
     forListMessagesSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,2)
                                               , m_swapdir(false)
                                               , m_firstmsg(true)
                                               , m_mcnt(0)
                                               , m_msgid(FIRSTM)
                                               , m_msgidHigh(FIRSTM)
                                               , m_msgidLow(FIRSTM)
                                               , m_bIsMoreHigh(false)
                                               , m_bIsMoreLow(false)
                                               , m_startbuild(false)
                                               , m_morechk(CHECKMORE_LOWINIT)
                                               , m_pStart(NULL)
                                               , m_bIsMore(MAPMORE)


     {
          *m_pThreadTopic='\0';
          m_iCount=theforAgent.dftlstc;
     }

     ~forListMessagesSynthesis()
     {
          emptyqueue();
          if (m_pStart != NULL) {
              delete [] m_pStart;
          }
     }

     ACTHCODE proceed();
     VOID readParams();
     SHORT buildMap();
     VOID addToMap(struct message *mptr);
     VOID createDNF();
     VOID setMessageListContextVars();
     bool setupVars();
     VOID emptyqueue();
     VOID checkMore();
     VOID findHiLow();


protected:
     bool m_swapdir;
     bool m_firstmsg;
     INT m_mcnt;
     MSGQUE m_mque;
     CHAR* m_pStart;
     LONG m_msgid, m_msgidLow, m_msgidHigh;
     bool m_startbuild;
     SHORT m_morechk;
     bool m_bIsMoreHigh;
     bool m_bIsMoreLow;
     CHAR m_pThreadTopic[TPCSIZ];
     SHORT m_bIsMore;
};

class forListThreadsSynthesis : public forHTMLSynthesis {
public:
     forListThreadsSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,2)
                                                , m_startbuild(false)
                                                , m_nummsg(0)
                                                , m_thrcnt(0)

     {
     }

     ~forListThreadsSynthesis()
     {
          emptyqueue();
     }

     ACTHCODE proceed();
     SHORT buildMap();
     VOID addToMap(pTHREADSTORE mptr);
     VOID createDNF();
     VOID setThreadListContextVars();
     bool setupVars();
     VOID emptyqueue();

protected:
     THRQUE tque;
     INT m_nummsg;
     bool m_startbuild;
     INT m_thrcnt;
};

class forViewMessageSynthesis : public forHTMLSynthesis {
public:
     forViewMessageSynthesis(acthSession* _ses) : forHTMLSynthesis(_ses,2)
                                                , m_msgid(FIRSTM)
                                                , m_ttype(TT_HTML)
                                                , m_reflow(true)
                                                , m_bShowText(true)
                                                , m_bReading(false)
                                                , m_morechk(CHECKMORE_LOWINIT)
                                                , m_bIsMoreHigh(false)
                                                , m_bIsMoreLow(false)
                                                , m_ttstate(TT_STATE_RAW)
                                                , m_ttypestr("plain")
                                                , m_msgbuf("")
                                                , m_msgbuf2("")
                                                , m_sAppInfo("")
                                                , m_bExtOnly(false)


     {
          m_iDirection=0;
     }


     ACTHCODE proceed();
     VOID readParams();
     VOID createDNF();
     VOID setupVars();
     INT readMessage();
     VOID checkMore();
     bool processMessage();
     VOID processNative();
     bool processPlain();
     bool processHTML();
     VOID onlyExtHead(bool bHead);


public:
     LONG m_msgid;
     SHORT m_ttype;
     bool m_reflow;
     bool m_bShowText;
     bool m_bReading;
     SHORT m_morechk;
     bool m_bIsMoreHigh, m_bIsMoreLow;
     string m_ttypestr;
     string m_msgbuf;
     string m_msgbuf2;
     INT m_ttstate;
     string m_sAppInfo;
     bool m_bExtOnly;
};

class forumCommon {
public:
     forumCommon(bool _html) :
          m_needToCycle(false),
          m_merr_code(GMEOK),
          m_merr_message(""),
          m_bPureHTML(_html)
     {
          m_timer.setSlice(::timeSlice);
     }

     bool
     startProc();

     bool
     contProc(
     ACTHCODE rc);

public:

     INT m_merr_code;              // store MERR_CODE tvb value
     string m_merr_message;        // store MERR_MESSAGE tvb value
     cycleTimer m_timer;           // instance of cycletimer class
     bool m_needToCycle;
     bool m_bPureHTML;
};

static VOID
handleError(
forumCommon *ecPtr,
INT operation,
const CHAR *additionalErrorMsg);

class rootForumsSynth : public forHTMLSynthesis {
public:
     rootForumsSynth(acthSession *_ses,INT _c):
          forHTMLSynthesis(_ses,_c),
          m_usingFrames(false),
          m_firstTime(true),
          m_dnfPtr(NULL),
          m_qscptr(NULL),
          m_webFlags(0)
     {
          m_ecPtr=new forumCommon(true);
     }

     virtual
     ~rootForumsSynth()
     {
          if (m_dnfPtr != NULL) {
               delete m_dnfPtr;
          }
          if (m_ecPtr != NULL) {
               delete m_ecPtr;
          }
     }

     virtual
     ACTHCODE
     proceed();                    // virtual proceed

     ACTHCODE
     respDynafile();               // generic dynafile

     VOID
     initHTMLGMEUser();            // setup user-context stuff etc.

     VOID
     destructHTMLGMEUser();        // reset user-context stuff etc.

     VOID
     initPrefVariables();          // initialize preference text variables

public:
     bool m_usingFrames;          // are we using frames?
     bool m_firstTime;            // first time in proceed?
     dnfHandler *m_dnfPtr;         // Pointer to DNF object
     forumCommon *m_ecPtr;         // pointer to common stuff
     struct qscfg *m_qscptr;       // pointer to qs config
     LONG m_webFlags;              // web preferences
};


class pureHTMLSynth : public rootForumsSynth {
public:
     pureHTMLSynth(acthSession *_ses, INT _c) :
          rootForumsSynth(_ses,_c),
          m_ahState(PROCEED_AH_REQUEST),
          m_moreLow(false),
          m_moreHi(false),
          m_showExtHeadInfo(false),
          m_wrkHeader(NULL),
          m_commentBufPtr(NULL),
          m_orgForId(EMLID),
          m_orgMsgId(0),
          m_writeFlags(0),
          m_sendFlags(0),
          m_forum(0),
          m_forumName(""),
          m_toAdr(""),
          m_topic(""),
          m_cclist(""),
          m_paramText(""),
          m_orgText(""),
          m_attachName(""),
          m_attFile(""),
          m_curPos(0),
          m_ofstr(NULL),
          m_gmefname(""),
          m_tickId(::GetTickCount()),
          m_readFlags(0),
          m_succTempPtr(NULL),
          m_errTempPtr(NULL),
          m_iSendPos(0),
          m_lastSpaceLoc(0),
          m_lastCRLF(0),
          m_lineSize(0),
          m_ccPtr(NULL),
          m_ccCount(0),
          m_byteCount(0),
          m_pfnLoc(0),
          m_msgHdrPtr(NULL)
     {
          m_wrkHeader=new struct message;
          m_commentBufPtr=new CHAR[TXTLEN];
     }

     virtual
     ~pureHTMLSynth()              // destructor
     {
          if (m_wrkHeader != NULL) {
               delete m_wrkHeader;
          }
          if (m_commentBufPtr != NULL) {
               delete [] m_commentBufPtr;
          }
          if (m_ofstr != NULL) {
               delete m_ofstr;
          }
     }

     virtual
     ACTHCODE
     proceed();                    // AH request/response entry point method

     VOID
     getTemplates();               // get templates

     VOID
     initSuccVariables();          // initializes success text variables

     VOID
     initErrVariables();           // initializes error text variables

     INT
     quickDelete();                // deletes message w/out cycling

     ACTHCODE
     handleTemplates(              // handle template response
     dnfMap *succMap,              // success map
     dnfMap *errMap,               // error map
     const CHAR * baseDir,         // base directory
     const CHAR *synthDir);        // synthesis directory

     VOID
     initVars();                   // init vars based on success/error status

     VOID
     updatePref(                   // update a part. preference
     LONG pref,
     bool on);

     bool                         // successfully got all params
     getCompositionParams();       // get common write/send parameters

     VOID
     initCompositionVbs();         // set up write/send variables

     ACTHCODE
     dynaWriteResp();              // dynafile "/write" type response

     ACTHCODE
     dynaReadResp();               // dynafile "/read" type response

     VOID
     sendTemplates();              // handle send custom template stuff

     ULONG
     validateMsgId(                // assure message id isn't out of range
     ULONG msgid);                 // the msgid

     bool
     format4Send();                // format text on a send

     VOID
     removeSubString(              // in-place remove sub string
     CHAR* string,                 // string in question
     INT idx,                      // index to star removing
     INT len);                     // length of string to remove

     VOID
     insert(                       // in-place insert sub string
     CHAR* orgString,              // original string
     const CHAR* subString,        // string to insert
     INT idx,                      // index to insert at
     INT bufSiz);                  // total size of buffer

protected:

     ULONG m_mhdr_msgid;           // variable contains MHDR_MSGID value
     SHORT m_ahState;              // handling request or response?
     CHAR m_errorTemplate[URLSIZ]; // error template from "onError" param
     CHAR m_successTemplate[URLSIZ];// success template from "onSuccess" param
     bool m_moreLow;              // more message below
     bool m_moreHi;               // more messages above
     bool m_showExtHeadInfo;      // display extended header info
     INT (*m_readMethod)(VOID *,struct message*,CHAR *);
     INT m_readType;               // type of read to do
     string m_sMsgTxt;             // store message body text
     INT m_pIndex;                 // dummy value for paramIndex req
     struct message *m_wrkHeader;  // message struct pointer
     CHAR *m_commentBufPtr;        // pointer to buffer for comment storage
     ULONG m_writeFlags;           // store write bit flags
     ULONG m_sendFlags;            // stoer send bit flags
     USHORT m_orgForId;            // orig. forum req'ed on reply/resend
     ULONG m_orgMsgId;             // orig. msgid req'ed on reply/resend
     USHORT m_forum;               // where new msg should be placed
     string m_forumName;           // name of forum msg should be placed
     string m_toAdr;               // to address
     string m_topic;               // topic
     string m_cclist;              // carbon copy list
     string m_paramText;           // text in param
     string m_orgText;             // actual orig. text
     string m_attachName;          // name of attachment
     string m_attFile;             // name of file attached
     istream *m_ulistr;            // pointer to pointer to an istream
     ofstream *m_ofstr;            // output file stream instance
     LONG m_curPos;                // current position in file
     string m_gmefname;            // GME's filename
     ULONG m_tickId;               // id to write uploaded file to
     LONG m_readFlags;             // flags used during read processing
     CHAR *m_succTempPtr;          // pointer to succes template buffer
     CHAR *m_errTempPtr;           // pointer to error template buffer
     CHAR m_writeBuf[WRITECHUNKSIZ]; // where to store stream data for writing
     INT m_iSendPos;               // position in text to send
     INT m_lastSpaceLoc;           // position of last space
     INT m_lastCRLF;               // position of last CR/LF
     INT m_lineSize;               // size of line we're working with
     const CHAR *m_ccPtr;          // pointer to CC addresses
     INT m_ccCount;                // count of CC addresses
     LONG m_byteCount;             // how many bytes have been written
     INT m_pfnLoc;                 // location inside body of message for profanity checking
     struct message *m_msgHdrPtr;  // message Header pointer
};

class writeSynthesis : public pureHTMLSynth {
public:
     writeSynthesis(acthSession *_ses) :
          pureHTMLSynth(_ses,1)
     {
     }


     ACTHCODE
     proceed();
};

/* Group management syntheses */

#define GL_HASSIB   1              // group has siblings
#define GL_HASCHL   2              // group has children
#define GL_HASFOR   4              // group has forums

class synGrpList : public forHTMLSynthesis { // group list handler
public:
     synGrpList(
     acthSession *ses);

     ~synGrpList();

     ACTHCODE                      //   returns enumerated status value
     proceed();                    // agent's request handler

private:                           // private member functions

     bool
     nextGroup();                  // find the next group

     bool
     pushGroup();                  // push group context onto stack

     VOID
     popGroup();                   // pop group context from stack

     VOID
     formTree();                   // form HTML code for tree

     VOID
     setGrpPaths();                // set current path and parent

     VOID
     setGrpTextVars();             // set all group-related text vars

     VOID
     clrGrpTextVars();             // clear all group-related text vars

     CHAR *                        //   returns pointer to destination
     groupPath(                    // generate current group path
     INT sp,                       //   group stack pointer
     CHAR *dst,                    //   buffer to form in
     size_t dstSiz);               //   size of destination buffer

     bool
     isExpanded();                 // is the current group expanded

     VOID
     freeOut();                    // free output stream

     VOID
     createDNF();


private:                           // private data members
     ostrstream *m_psout;          //   string in which to form tree HTML
     INT m_sp;                     //   group stack pointer
     INT m_treeDone;               //   number of tree HTML bytes output
     enum {DNFSTART,OUTDNF,OUTTREE} m_state;     // processing state
     bool m_firstGroup;            //   is this the very first group output?
     struct {                      //   group stack structure
          USHORT flags;            //     boolean properties
          USHORT id;               //     group ID
          CHAR name[FORNSZ];       //     group name
     } m_grpstk[GRPSTKSZ+1];       //   group stack
     CHAR m_grpExp[MAXGRPSTR];     //   group path to expand
     CHAR m_grpPath[MAXGRPSTR];    //   current group path
     CHAR m_grpParent[MAXGRPSTR];  //   current group's parent's path
     CHAR m_grpTopic[TPCSIZ];      //   current group topic
};

class sendSynthesis : public pureHTMLSynth {
public:
     sendSynthesis(acthSession *_ses) :
          pureHTMLSynth(_ses,1)
     {
     }


     ACTHCODE
     proceed();                    // overriden proceed

     VOID
     submitError();                // submit hard-coded error

     SHORT
     sendHeaderSetup();            // set up message header stuff

     VOID
     deleteAttachment();           // delete attachment on message "DETACH"

     VOID
     addCCs();                     // add carbon copy addresses to msg text

     bool
     writeFileStream();            // do the bulk of writing to file

     bool
     getFileStream();              // get the file stream

     VOID
     preventTimeout();             // prevent a browser timeout on big upload

     SHORT
     getState();                   // determine next PROCEED state
};

class forwardSynthesis : public pureHTMLSynth {
public:
     forwardSynthesis(acthSession *_ses) :
          pureHTMLSynth(_ses,1),
          m_bDeleteMsg(false)
     {
     }


     ACTHCODE
     proceed();

private:

     bool m_bDeleteMsg;           // delete message on forward?
};



class downloadSynthesis : public pureHTMLSynth {
public:
     downloadSynthesis(acthSession *_ses) :
          pureHTMLSynth(_ses,2)
     {
     }


     ACTHCODE
     proceed();

     VOID
     abort();
};

class deleteSynthesis : public pureHTMLSynth {
public:
     deleteSynthesis(acthSession *_ses) :
          pureHTMLSynth(_ses,1),
          m_delCount(0)
     {
     }

     ACTHCODE
     proceed();

private:

     INT m_delCount;               // amount of delete parameters
};

class exemptSynthesis : public pureHTMLSynth {
public:
     exemptSynthesis(acthSession *_ses) :
          pureHTMLSynth(_ses,1),
          m_exCount(0)
     {
     }

     ACTHCODE
     proceed();

private:

     INT m_exCount;               // amount of exempt parameters
};

class approveSynthesis : public pureHTMLSynth {
public:
     approveSynthesis(acthSession *_ses) :
          pureHTMLSynth(_ses,1),
          m_apCount(0)
     {
     }

     ACTHCODE
     proceed();

private:

     INT m_apCount;               // amount of approve parameters
};

class qsforumsSynthesis : public pureHTMLSynth {
public:
     qsforumsSynthesis(acthSession *_ses) :
          pureHTMLSynth(_ses,1),
          m_qsCount(0)
     {
     }

     ACTHCODE
     proceed();

private:

     INT m_qsCount;               // amount of forumparameters
};


// macro ensures user has access
#define userAcc() (ses->getUser()->hasKey(theforAgent.forahkey))

MARKSOURCE(galforah);

//
//   forumCommon Member Functions
//

bool
forumCommon::startProc()           // start processing?
{
     return(m_timer.start());
}

bool
forumCommon::contProc(             // continue to process?
ACTHCODE rc)                       // are we all done?
{
     return(rc == ACTHMORE && !m_needToCycle && m_timer.haveTime());
}

//
//  rootForumsSynth Member Functions

ACTHCODE
rootForumsSynth::proceed()
{
     return(ACTHDONE);
}

VOID
rootForumsSynth::initHTMLGMEUser()    // set up essentials for session
{
     setmbk(gmeAHmbk);
     usaptr=GetAccPtr(usr->userid());
     if (m_gmehandle == NULL) {
          m_gmehandle=::GetGmeUserHandle(usr->userid());
     }
     //LOG2("FOR.initHTMLGMEUser GetUH: this=%08lX h=%lu",this,m_gmehandle);
     setUserNum4AH(ses->getUser()->userid());
}

VOID
rootForumsSynth::destructHTMLGMEUser()// reset essentials
{
     rstmbk();
     //LOG2("FOR.destructHTMLGMEUser closing: this=%08lX h=%lu",this,m_gmehandle);
     if (m_gmehandle != NULL) {
          gmeUserClose(m_gmehandle);
          m_gmehandle=NULL;
     }
     if (ses->getUser() != NULL) {
          resetUserNum4AH(ses->getUser()->userid());
     }
}

VOID
rootForumsSynth::initPrefVariables() // init preference variables
{
     m_qscptr=gmeUserGetQS(m_gmehandle);

     ASSERT(m_qscptr != NULL);

     m_webFlags=getWebPrefs(usr->userid());

     if (m_webFlags&FPREF_TYPE_HTML) {
          forType.set("html");
          forTypeHTML.set("CHECKED");
          forTypeJava.clr();
     }
     else if (m_webFlags&FPREF_TYPE_JAVA) {
          forType.set("java");
          forTypeJava.set("CHECKED");
          forTypeHTML.clr();
     }
     const CHAR* value=getCookie(FORFRAMECOOKIE,ses);
     if (value != NULL) {
          setForTargetTvb(sameas(value,"1") ? true : false);
     }
     else {
          setForTargetTvb(getWebPrefs(usr->userid())&FPREF_USEFRAMES);
     }
     forReflowPref.set((m_webFlags&FPREF_REFLOW) ? "CHECKED" : "");
     forQuotePref.set((m_webFlags&FPREF_QUOTE) ? "CHECKED" : "");
     ePrefForumTo.set((m_qscptr->flags&FORUM2) ? "CHECKED" : "");
}

ACTHCODE
rootForumsSynth::respDynafile()      // response dynafile
{
     ACTHCODE retval=ACTHMORE;

     if (m_dnfPtr == NULL) {
          return(ACTHDONE);
     }

     if (m_ecPtr->startProc()) {
          do {
               switch(m_dnfPtr->process()) {
               case DNFEND:
                    retval=ACTHDONE;
                    break;
               }
          } while (m_ecPtr->contProc(retval));
     }
     return(retval);
}

//
//  pureHTMLSynth Member Functions
//

ACTHCODE
pureHTMLSynth::proceed()           // base forums class check access and user
{
     ACTHCODE retval=ACTHMORE;

     if (!userOK()) {
          return(ACTHNOANON);
     }
     else if (!userAcc()) {
          retval=ACTHFORBID;
     }
     else {
          if (ses->urlargc() == 0
           && (strchr(ses->urlsfx(),'.')) == '\0') {
               if (ses->forceDir()) {
                    return(ACTHDONE);
               }
          }
          CHAR *ckPtr=getCookie(FORFRAMECOOKIE,ses);
          if (ckPtr != NULL) {
               setForTargetTvb(sameas(ckPtr,"1") ? true : false);
          }
          else {
               setForTargetTvb(getWebPrefs(usr->userid())&FPREF_USEFRAMES);
          }
          clearAllDefs();
          initHTMLGMEUser();
          initPrefVariables();
          getTemplates();
          retval=forHTMLSynthesis::proceed();
     }
     return(retval);
}

VOID
pureHTMLSynth::removeSubString(    // in-place remove sub string
CHAR* string,                      // string in question
INT idx,                           // index to star removing
INT len)                           // length of string to remove
{
     ASSERT(len != 0);
     ASSERT(idx+len < strlen(string)+1);

     CHAR *ptr=string+idx+len;
     CHAR *prevptr=string+idx;

     while (*ptr != '\0') {
          *prevptr++=*ptr++;
     }
     *prevptr='\0';
}

VOID
pureHTMLSynth::insert(             // in-place insert sub string
CHAR* orgString,                   // original string
const CHAR* subString,             // string to insert
INT idx,                           // index to insert at
INT bufSiz)                        // total size of buffer
{
     ASSERT(orgString != NULL && subString != NULL);
     if (strlen(orgString)+strlen(subString) >= bufSiz) {
          return;
     }

     CHAR *wrkBuf= new CHAR[bufSiz];
     setmem(wrkBuf,bufSiz,0);
     if (idx > 0) {
          stzcpy(wrkBuf,orgString,bufSiz);
          wrkBuf[idx]='\0';
     }
     stzcat(wrkBuf,subString,bufSiz);
     if (idx > 0) {
          stzcat(wrkBuf,orgString+(idx-1),bufSiz);
     }
     else {
          stzcat(wrkBuf,orgString,bufSiz);
     }
     stzcpy(orgString,wrkBuf,bufSiz);

     delete [] wrkBuf;
}

bool
pureHTMLSynth::format4Send()
{
     CHAR * OutBuf=new CHAR[TXTLEN];
     CHAR * pOut=OutBuf;
     CHAR const * pIn=m_paramText.c_str();
     bool more=true;

     // copy the input into the output buffer
     do {

          // find end/length of current line
          INT LineLen;
          CHAR const * pEOL=::strchr(pIn,'\r');
          if (pEOL == NULL) {
               LineLen=::strlen(pIn);
          }
          else {
               LineLen=(INT)(pEOL-pIn);
               if (*++pEOL == '\n') {
                    ++pEOL;
               }
          }

          // remove trailing whitespace
          while (LineLen > 0 && isspace(pIn[LineLen-1])) {
               --LineLen;
          }

          // process current line
          CHAR const * pNext=pEOL;      // point to start of next line
          if (::isquoted(pIn) || LineLen <= MAXLINSIZ) {
               more=(pEOL != NULL);
          }
          else {                   // word-wrap non-quoted long lines

               // find last space on line
               pEOL=pIn+MAXLINSIZ;
               while (!isspace(*pEOL) && pEOL > pIn) {
                    --pEOL;
               }

               // do word wrapping
               if (isspace(*pEOL)) {
                    LineLen=(INT)(pEOL-pIn);
                    pNext=pEOL+1;

                    // remove trailing whitespace
                    while (LineLen > 0 && isspace(pIn[LineLen-1])) {
                         --LineLen;
                    }
               }
               else {                   // no spaces, just chop it
                    LineLen=MAXLINSIZ;
                    pNext=pIn+MAXLINSIZ;
               }
          }

          // account for output buffer room
          INT OutUsed=(INT)(pOut-OutBuf);
          INT OutAvailable=TXTLEN-OutUsed;
          INT OutRequired=LineLen+2;         // +2 for '\r' & '\0'
          if (OutRequired > OutAvailable) {
               more=false;
               LineLen=OutAvailable-2;
          }

          // add current line to output buffer
          *pOut++='\r';
          if (LineLen != 0) {
               ::memcpy(pOut,pIn,LineLen);
               pOut+=LineLen;
          }

          // point to next line
          ASSERT(!more || pNext != NULL);
          pIn=pNext;
     } while (more);

     // terminate output
     *pOut='\0';

     // remove any trailing garbage (usually blank lines)
     --pOut;
     while (pOut >= OutBuf && *pOut <= ' ') {
          *pOut--='\0';
     }

     m_paramText=OutBuf;
     delete [] OutBuf;
     return(true);
}

ACTHCODE
pureHTMLSynth::dynaWriteResp()     // dynafile "/write" response
{
     ACTHCODE retval=ACTHMORE;

     if (m_ecPtr->startProc()) {
          do {
               switch(m_dnfPtr->process()) {
               case BODYTEXT:
                    if (m_sendFlags != 0 && !(m_sendFlags&SUBMIT_SEND) &&
                     !(m_sendFlags&SUBMIT_CANCEL)
                     && m_writeFlags&WRITE_NOTEXT) {
                         bout << " ";
                         break;
                    }
                    if (m_paramText.length() != 0) {
                         if (m_sendFlags&SUBMIT_ATTACH) {
                              bout << cgiEncode(m_paramText.c_str());
                         }
                         else {
                              bout << cgiDecode(m_paramText.c_str());
                         }
                    }
                    else if (m_writeFlags&WRITE_RESEND
                        || m_webFlags&FPREF_QUOTE || m_writeFlags&WRITE_QUOTE
                        || m_writeFlags&WRITE_KEEPFMT) {
                         bout << m_orgText.c_str();
                    }
                    else {
                         bout << " ";
                    }
                    break;
               case DNFEND:
                    retval=ACTHDONE;
                    break;
               }
          } while (m_ecPtr->contProc(retval));
     }
     return(retval);
}


ACTHCODE
pureHTMLSynth::dynaReadResp()      // read/view response dynafile
{
     ACTHCODE retval=ACTHMORE;

     setMoreLowTvb(m_moreLow);
     setMoreHiTvb(m_moreHi);

     if (m_ecPtr->startProc()) {
          do {
               switch(m_dnfPtr->process()) {
               case BODYTEXT:
                    if (!(m_readFlags&READ_NOTEXT)) {
                         bout << m_sMsgTxt.c_str();
                    }
                    break;
               case DNFEND:
                    retval=ACTHDONE;
                    break;
               }
          } while (m_ecPtr->contProc(retval));
     }
     return(retval);
}

bool
pureHTMLSynth::getCompositionParams()  // get common write parameters
{
     GETINPUT("type");
     if (strInpBuf[0] != '\0') {
          if (sameas(strInpBuf,"new")) {
               m_writeFlags|=WRITE_NEW;
          }
          else if (sameas(strInpBuf,"reply")) {
               m_writeFlags|=WRITE_REPLY;
          }
          else if (sameas(strInpBuf,"resend")) {
               m_writeFlags|=WRITE_RESEND;
          }
          else {
               m_writeFlags|=WRITE_NEW;
          }
     }
     else {
          m_writeFlags|=WRITE_NEW;
     }
     GETINPUT("orgforum");
     if (strInpBuf[0] != '\0' && alldgs(strInpBuf)) {
          m_orgForId=atoi(strInpBuf);
     }
     else if (m_writeFlags&(WRITE_REPLY|WRITE_RESEND)) {
          m_ecPtr->m_merr_code=ERR_PARAMISSING;
          clrprf();
          prfmsg(ERPMISS,"orgforum");
          m_ecPtr->m_merr_message=stpans(prfbuf);
          clrprf();
          return(false);
     }
     GETINPUT("orgmsgid");
     if (strInpBuf[0] != '\0' && alldgs(strInpBuf)) {
          m_orgMsgId=atol(strInpBuf);
     }
     else if (m_writeFlags&(WRITE_REPLY|WRITE_RESEND)) {
          m_ecPtr->m_merr_code=ERR_PARAMISSING;
          clrprf();
          prfmsg(ERPMISS,"orgmsgid");
          m_ecPtr->m_merr_message=stpans(prfbuf);
          clrprf();
          return(false);
     }
     GETINPUT("override");
     if (strInpBuf[0] != '\0') {
          if (samein("quote",strInpBuf)) {
               if (paramOK("quote") && m_writeFlags&WRITE_REPLY) {
                    m_writeFlags|=WRITE_QUOTE;
               }
               else if (paramOK("quote set") && m_writeFlags&WRITE_REPLY) {
                    m_writeFlags|=WRITE_QUOTE;
                    updatePref(FPREF_QUOTE,true);
               }
          }
          if (samein("inclist",strInpBuf)) {
               if (paramOK("inclist")) {
                    m_writeFlags|=WRITE_INCLIST;
               }
               else if (paramOK("inclist set")) {
                    m_writeFlags|=WRITE_INCLIST;
                    updatePref(MPREF_INCLIST,true);
               }
          }
     }
     if (paramOK("toall") && m_writeFlags&WRITE_REPLY) {
          m_writeFlags|=WRITE_TOALL;
     }
     GETINPUT("forumid");
     if (strInpBuf[0] != '\0' && alldgs(strInpBuf)) {
          m_forum=atoi(strInpBuf);
     }
     GETINPUT("forumname");
     if (strInpBuf[0] != '\0') {
          m_forumName=strInpBuf;
     }
     if (m_forum == 0 && *(m_forumName.c_str()) != '\0') {
          m_forum=getfid(m_forumName.c_str());
     }
     GETINPUT("to");
     if (strInpBuf[0] != '\0') {
          m_toAdr=strInpBuf;
     }
     GETINPUT("topic");
     if (strInpBuf[0] != '\0') {
          m_topic=strInpBuf;
     }
     SetFlags(&m_writeFlags,WRITE_PRI,paramOK("pri"));
     SetFlags(&m_writeFlags,WRITE_RR,paramOK("rr"));
     GETINPUT("cclist");
     if (strInpBuf[0] != '\0') {
          m_cclist=strInpBuf;
          if (strInpBuf[strlen(strInpBuf)-1] != ';') {
               m_cclist+=";";
          }
     }
     GETINPUT("text");
     if (strInpBuf[0] != '\0') {
          m_paramText=strInpBuf;
     }
     SetFlags(&m_writeFlags,WRITE_KEEPFMT,paramOK("keepformat"));
     GETINPUT("tmpid");
     if (strInpBuf[0] != '\0' && alldgs(strInpBuf)) {
          m_tmpid=atol(strInpBuf);
          GETINPUT("attname");
          if (strInpBuf[0] != '\0') {
               m_attachName=mkdosn(strInpBuf);
          }
     }
     SetFlags(&m_writeFlags,WRITE_NOTEXT,paramOK("notext"));
     return(true);
}

VOID
pureHTMLSynth::updatePref(         // update a preference value
LONG pref,
bool on)
{
     LONG curPrefs=getWebPrefs(ses->getUser()->userid());

     if (on) {
          curPrefs|=pref;
     }
     else {
          curPrefs&=~pref;
     }
     setWebPrefs(ses->getUser()->userid(),curPrefs);
}

VOID
pureHTMLSynth::getTemplates()       // reads in template URLs/files
{
     ses->param("onsuccess",m_successTemplate,URLSIZ);
     ses->param("onerror",m_errorTemplate,URLSIZ);
     m_succTempPtr=m_successTemplate;
     m_errTempPtr=m_errorTemplate;
}

VOID
pureHTMLSynth::initSuccVariables() // initialize variables on success
{
     if (m_dnfPtr != NULL) {
          dnfSetTemplateTvb(m_dnfPtr);
     }
     if (m_mhdrptr != NULL) {
          setUpMessageHeaderDefs(m_mhdrptr);
     }
}

VOID
pureHTMLSynth::initVars()          // init appropriate variables
{
     if (m_ecPtr->m_merr_code >= GMEOK && m_ecPtr->m_merr_code <= GMEAFWD) {
          initSuccVariables();
     }
     else {
          initErrVariables();
     }
}

VOID
pureHTMLSynth::initErrVariables()  // init error variables
{
     mMsgId.set("%ld",m_mhdr_msgid);
     if (m_dnfPtr != NULL) {
          setDnfTemplateTvb(m_dnfPtr);
     }
     setErrorCode(m_ecPtr->m_merr_code);
     setErrorMessage(m_ecPtr->m_merr_message.c_str());
}

INT
pureHTMLSynth::quickDelete()       // non-cycled delete.. assumes work area/msgid valid
{
     INT rc=GMENDEL;
     if (m_forum != 0)  {
          inictx(mywrkarea,usr->userid(),FSQFOR,m_forum,m_savMsgId,0);
          rc=delmsg(mywrkarea);
     }
     return(rc);
}

ULONG
pureHTMLSynth::validateMsgId(      // validate msg id
ULONG msgid)
{
     if (msgid > (himsgid()+1)) {
          return(himsgid()+1);
     }
     return(msgid);
}

VOID
pureHTMLSynth::initCompositionVbs()// initialize variables on message compose
{
     if (m_dnfPtr != NULL) {
          dnfSetTemplateTvb(m_dnfPtr);
     }
     if (m_forum != 0) {
          mForId.set("%d",m_forum);
     }
     else if (m_forumName.length() != 0) {
          mForId.set("%d",getfid(m_forumName.c_str()));
     }
     else {
          mForId.set("%d",EMLID);
     }
     const CHAR *fnam=NULL;
     if (m_forum != 0) {
          if ((fnam=getfnm(m_forum)) != NULL) {
               setInQScanVar(fnam,ses->getUser()->userid());
               mForName.set(fnam);
               prfmsg(DISPFOR,fnam);
               mForDisp.set(stpans(prfbuf));
               clrprf();
          }
     }
     else {
          forUsrAccess.set("%d",OPAXES);  // since it is an email, give high access
          mForDisp.set(stpans(getmsg(DISPEML)));
     }
     if (m_toAdr != "") {
          mTo.set(m_toAdr.c_str());
     }
     else {
          mTo.set(ALLINF);
     }
     mTopic.set(m_topic.c_str());
     if (m_writeFlags&WRITE_PRI) {
          mPriority.set(stpans(getmsg(PRIDSP)));
     }
     if (m_writeFlags&WRITE_RR) {
          mReceipt.set(stpans(getmsg(RRRDSP)));
     }
     if (m_writeFlags&WRITE_PRI && m_forum == 0) {
          mPriImage.set(stpans(getmsg(IMGPRIY)));
     }
     else {
          mPriImage.set(stpans(getmsg(IMGPRIN)));
     }
     if (m_writeFlags&WRITE_RR && m_forum == 0) {
          mRRRImage.set(stpans(getmsg(IMGRRRY)));
     }
     else {
          mRRRImage.set(stpans(getmsg(IMGRRRN)));
     }

     if (m_writeFlags&(WRITE_NEW|WRITE_RESEND)) {
          mWriteType.set("new");
     }
     else if (m_writeFlags&WRITE_REPLY) {
          mWriteType.set("reply");
     }
     mWriteOrgForId.set("%d",m_orgForId);
     mWriteOrgMsgId.set("%ld",m_orgMsgId);
     if (m_writeFlags&WRITE_PRI) {
          mWritePriChk.set("CHECKED");
     }
     if (m_writeFlags&WRITE_RR) {
          mWriteRRRChk.set("CHECKED");
     }
     mWriteCCList.set(m_cclist.c_str());
     mWriteTmpid.set(spr("%lu",m_tmpid));  // insert initial tmpid value here
     if (m_writeFlags&WRITE_INCLIST || m_webFlags&MPREF_INCLIST) {
          mWriteIncList.set("CHECKED");
     }
     mAttName.set(m_attachName.c_str());
     if (m_attachName.length() == 0) {
          mAttPfx.set(stpans(getmsg(ATTFMT)));
          mAttImage.set(stpans(getmsg(IMGATTN)));
          mWriteAttFlag.set("0");
     }
     else {
          mAttImage.set(stpans(getmsg(IMGATTY)));
          mWriteAttFlag.set("1");
     }
}

VOID
pureHTMLSynth::sendTemplates()     // handle send custom template stuff
{
     bool foundSuccess=false;
     bool foundError=false;

     if (m_sendFlags != 0) {
          static struct sendtypes submitArr[]={
               {"send",SUBMIT_SEND},
               {"attach",SUBMIT_ATTACH},
               {"upload",SUBMIT_UPLOAD},
               {"detach",SUBMIT_DETACH},
               {"preview",SUBMIT_PREVIEW},
               {"redisplay",SUBMIT_REDISPLAY},
               {"cancel",SUBMIT_CANCEL},
               {"",0}
          };
          CHAR *truncPtr=NULL;
          INT fndidx=0;

          INT i=0;
          while (submitArr[i].text[0] != '\0') {
               if (m_sendFlags&submitArr[i].flagval) {
                    if (m_successTemplate[0] != '\0'
                     && (fndidx=findstg(submitArr[i].text,m_successTemplate)) != 0) {
                         if (m_successTemplate[fndidx+1] != '\0') {
                              m_succTempPtr=&(m_successTemplate[fndidx+1]);
                              truncPtr=strchr(m_succTempPtr,',');
                              if (truncPtr != NULL) {
                                   *truncPtr='\0';
                              }
                              foundSuccess=true;
                         }
                    }
                    if (m_errorTemplate[0] != '\0'
                     && (fndidx=findstg(submitArr[i].text,m_errorTemplate)) != 0) {
                         if (m_errorTemplate[fndidx+1] != '\0') {
                              m_errTempPtr=&(m_errorTemplate[fndidx+1]);
                              truncPtr=strchr(m_errTempPtr,',');
                              if (truncPtr != NULL) {
                                   *truncPtr='\0';
                              }
                              foundError=true;
                         }
                    }
                    break;
               }
               i++;
          }
          if (!foundSuccess) {
               m_successTemplate[0]='\0';
               m_succTempPtr=m_successTemplate;
          }
          if (!foundError) {
               m_errorTemplate[0]='\0';
               m_errTempPtr=m_errorTemplate;
          }
     }
}

ACTHCODE
pureHTMLSynth::handleTemplates( // handle template response
dnfMap *succMap,                   // success map
dnfMap *errMap,                    // error map
const CHAR * baseDir,              // base directory
const CHAR *synthDir)
{
     ACTHCODE retval=ACTHMORE;
     string theDir;

     sendTemplates();

     if (m_ecPtr->m_merr_code >= GMEOK && m_ecPtr->m_merr_code <= GMEAFWD) {
          if (*m_succTempPtr != '\0') {
               if (isURLTemplate(m_succTempPtr)) {
                    ses->redirect(m_succTempPtr);
                    return(ACTHDONE);
               }
               else if (succMap != NULL) {
                    theDir=baseDir;
                    theDir+=synthDir;
                    theDir+=m_succTempPtr;
                    succMap=dnfGetMap(succMap,theDir.c_str());
                    if (m_dnfPtr != NULL) {
                         delete m_dnfPtr;
                         m_dnfPtr=NULL;
                    }
                    m_dnfPtr=new dnfHandler(*succMap,bout);
               }
          }
          else if (succMap != NULL) {
               m_dnfPtr=dnfCreateHandlerURL(ses,succMap,baseDir);
          }
     }
     else {
          if (*m_errTempPtr != '\0') {
               if (isURLTemplate(m_errTempPtr)) {
                    ses->redirect(m_errTempPtr);
                    return(ACTHDONE);
               }
               else if (errMap != NULL) {
                    theDir=baseDir;
                    theDir+=synthDir;
                    theDir+=m_errTempPtr;
                    errMap=dnfGetMap(errMap,theDir.c_str());
                    if (m_dnfPtr != NULL) {
                         delete m_dnfPtr;
                         m_dnfPtr=NULL;
                    }
                    m_dnfPtr=new dnfHandler(*errMap,bout);
               }
          }
          else if (errMap != NULL) {
               if (m_dnfPtr != NULL) {
                    delete m_dnfPtr;
                    m_dnfPtr=NULL;
               }
               m_dnfPtr=new dnfHandler(*errMap,bout);
          }
     }
     if (m_dnfPtr == NULL) {
          return(ACTHNOTFND);
     }
     return(retval);
}

//
//  writeSynthesis Member Functions
//

ACTHCODE
writeSynthesis::proceed()
{
     ACTHCODE retval=pureHTMLSynth::proceed();
     m_init=true;
     quotfunc qfunc;

     if (retval == ACTHMORE ) {
          switch(m_ahState) {
          case PROCEED_AH_REQUEST:
               if (!getCompositionParams()) {
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               if (m_orgForId != EMLID && !fidxst(m_orgForId))  {
                    m_ecPtr->m_merr_code=GMENFND;
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               if (m_writeFlags&(WRITE_REPLY|WRITE_RESEND)) {
                    SHORT seq=ESQTOU;
                    if (m_orgForId != EMLID) {
                         seq=FSQFOR;
                    }
                    inictx(mywrkarea,usr->userid(),seq,m_orgForId,m_orgMsgId,0);
                    m_ahState=PROCEED_AH_REPLY;
               }
               else {
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               break;
          case PROCEED_AH_REPLY:
               switch ((m_ecPtr->m_merr_code=readmsgf(mywrkarea,m_mhdrptr,txtbuf))) {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    m_orgText=txtbuf;
                    m_toAdr=m_mhdrptr->from;
                    m_topic=m_mhdrptr->topic;
                    if (m_writeFlags&WRITE_TOALL) {
                         m_cclist=getCCAddresses(txtbuf,strlen(txtbuf)+1,
                          usr->userid());
                    }
                    if (((m_writeFlags&WRITE_RESEND)
                     || (m_webFlags&FPREF_QUOTE || m_writeFlags&WRITE_QUOTE))
                     && !(m_writeFlags&WRITE_KEEPFMT)) {
                         m_ahState=PROCEED_AH_STRIPRTF;
                    }
                    else {
                         m_ahState=PROCEED_AH_RESPONSE;
                    }
                    break;
               default:
                    handleError(m_ecPtr,OP_GENERIC,NULL);
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               break;
          case PROCEED_AH_STRIPRTF:
               memset(txtbuf,0,TXTLEN);
               stlcpy(txtbuf,m_orgText.c_str(),TXTLEN);
               gmePlainText(txtbuf);
               m_orgText=txtbuf;
               m_ahState=PROCEED_AH_QUOTE;
               break;
          case PROCEED_AH_QUOTE:
               m_ahState=PROCEED_AH_RESPONSE;
               if (!(m_writeFlags&WRITE_RESEND)) {
                    stlcpy(txtbuf2,m_orgText.c_str(),TXTLEN);
                    qfunc=setquoter(NULL);
                    stlcpy(m_mhdrptr->to,m_mhdrptr->from,MAXADR);
                    (*qfunc)(m_mhdrptr,txtbuf2,TXTLEN);
                    setquoter(*qfunc);
                    m_orgText=txtbuf2;
               }
               break;
          case PROCEED_AH_RESPONSE:
               if (m_dnfPtr == NULL) {
                    retval=handleTemplates(&writeMap,&writeErrMap,PPFIX,"/write/");
               }
               else {
                    dnfSetTemplateTvb(m_dnfPtr);
                    if (m_ecPtr->m_merr_code >= GMEOK
                      && m_ecPtr->m_merr_code <= GMEAFWD) {
                          initCompositionVbs();
                          mWriteTmpid.set("%d",m_tickId);  // insert initial tmpid value here
                          retval=dynaWriteResp();
                    }
                    else {
                          initErrVariables();
                          retval=respDynafile();
                    }
               }
               break;
          }
     }
     destructHTMLGMEUser();
     usaptr=uacoff(usrnum);
     return(retval);
}

//
//  sendSynthesis Member Functions
//

ACTHCODE
sendSynthesis::proceed()
{
     ACTHCODE retval=pureHTMLSynth::proceed();
     CHAR ccadr[MAXADR];
     string fname;
     INT bdylen;
     INT numSubTypes=0;

     if (!m_init) {
          readParams();
          m_init=true;
     }

     if (retval == ACTHMORE) {
          switch(m_ahState) {
          case PROCEED_AH_REQUEST:
               if (SetFlags(&m_sendFlags,SUBMIT_SEND,paramOK("send"))) {
                    ++numSubTypes;
               }
               if (SetFlags(&m_sendFlags,SUBMIT_ATTACH,paramOK("attach"))) {
                    ++numSubTypes;
               }
               if (SetFlags(&m_sendFlags,SUBMIT_UPLOAD,paramOK("upload"))) {
                    ++numSubTypes;
               }
               if (SetFlags(&m_sendFlags,SUBMIT_DETACH,paramOK("detach"))) {
                    ++numSubTypes;
               }
               if (SetFlags(&m_sendFlags,SUBMIT_PREVIEW,paramOK("preview"))) {
                    ++numSubTypes;
               }
               if (SetFlags(&m_sendFlags,SUBMIT_REDISPLAY,paramOK("redisplay"))) {
                    ++numSubTypes;
               }
               if (SetFlags(&m_sendFlags,SUBMIT_CANCEL,paramOK("cancel"))) {
                    ++numSubTypes;
               }
               if (numSubTypes != 1) {
                    submitError();
                    retval=ACTHDONE;
                    break;
               }
               if (!getCompositionParams()) {
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               if (m_sendFlags&SUBMIT_SEND) {
                    memset(m_mhdrptr,0,sizeof(struct message));
                    if (m_writeFlags&WRITE_REPLY) {
                         SHORT seq=ESQTOU;
                         if (m_orgForId != EMLID) {
                              seq=FSQFOR;
                         }
                         inictx(mywrkarea,usr->userid(),seq,m_orgForId,m_orgMsgId,0);
                         m_ahState=PROCEED_AH_READMSG;
                    }
                    else {
                         m_ahState=sendHeaderSetup();
                    }
               }
               else if (m_sendFlags&(SUBMIT_ATTACH|SUBMIT_REDISPLAY)) {
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               else if (m_sendFlags&(SUBMIT_UPLOAD|SUBMIT_DETACH|SUBMIT_CANCEL)) {
                    m_ahState=PROCEED_AH_PRCSUBMIT;
               }
               else if (m_sendFlags&SUBMIT_PREVIEW) {
                    memset(m_mhdrptr,0,sizeof(struct message));
                    sendHeaderSetup();
                    m_mhdrptr->crdate=today();
                    m_mhdrptr->crtime=now();
                    m_ahState=PROCEED_AH_VALIDADR;
               }
               break;
          case PROCEED_AH_VALIDADR:
               if ((m_ecPtr->m_merr_code=valadr(mywrkarea,m_mhdrptr->from,m_mhdrptr->to,m_mhdrptr->forum)) != VALYES) {
                    handleError(m_ecPtr,OP_VALIDATION,stpans(getmsg(VALADR)));
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               m_ahState=PROCEED_AH_VALIDATT;
               break;
          case PROCEED_AH_VALIDATT:
               if (strlen(m_mhdrptr->attname) > 0) {
                    if ((m_ecPtr->m_merr_code=valatt(mywrkarea,m_mhdrptr->from,m_mhdrptr->to,m_mhdrptr->forum)) != VALYES) {
                         handleError(m_ecPtr,OP_VALIDATION,stpans(getmsg(VALATT)));
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }
                    if (!isvalfn(m_mhdrptr->attname)) {
                        m_ecPtr->m_merr_code=ERR_INVALIDFN;
                        m_ecPtr->m_merr_message=stpans(stpans(getmsg(ERINVFN)));
                        m_ahState=PROCEED_AH_RESPONSE;
                        break;
                    }
               }
               m_ahState=PROCEED_AH_VALIDPRI;
               // intentional drop through
          case PROCEED_AH_VALIDPRI:
               if (m_writeFlags&WRITE_PRI
                && (m_ecPtr->m_merr_code=valpri(mywrkarea,m_mhdrptr->from,m_mhdrptr->to,m_mhdrptr->forum)) != VALYES) {
                    handleError(m_ecPtr,OP_VALIDATION,stpans(getmsg(VALPRI)));
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               m_ahState=PROCEED_AH_VALIDRR;
               break;
          case PROCEED_AH_VALIDRR:
               if (m_writeFlags&WRITE_RR
                && (m_ecPtr->m_merr_code=valrrr(mywrkarea,m_mhdrptr->from,m_mhdrptr->to,m_mhdrptr->forum)) != VALYES) {
                    handleError(m_ecPtr,OP_VALIDATION,stpans(getmsg(VALRR)));
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               m_ahState=PROCEED_AH_VALIDBLEN;
               break;
          case PROCEED_AH_VALIDBLEN:
               if ((bdylen=m_paramText.length()) > TXTLEN) {
                    clrprf();
                    prfmsg(ERBDYTL,TXTLEN,ses->paramRoom("text"));
                    m_ecPtr->m_merr_code=ERR_BODY2LONG;
                    m_ecPtr->m_merr_message=stpans(prfbuf);
                    clrprf();
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               m_ahState=PROCEED_AH_VALIDPFN;
               break;
          case PROCEED_AH_VALIDPFN:
               if (m_attachName.length() > 0) {
                    if (pfnerr(usr->userid(),(CHAR*)m_attachName.c_str(),m_mhdrptr->forum)) {
                         m_ecPtr->m_merr_code=ERR_PROFANATT;
                         m_ecPtr->m_merr_message=stpans(getmsg(ERPRFAT));
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }
               }
               if (strlen(m_mhdrptr->topic) != 0) {
                    if (pfnerr(usr->userid(),m_mhdrptr->topic,m_mhdrptr->forum)) {
                         m_ecPtr->m_merr_code=ERR_PROFANTPC;
                         m_ecPtr->m_merr_message=stpans(getmsg(ERPRFTP));
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }
               }
               m_ahState=PROCEED_AH_VALIDPFBDY;
               break;
          case PROCEED_AH_VALIDPFBDY:
               if (m_paramText.length() > 0) {
                    if(pfnerr(usr->userid(),(CHAR*)m_paramText.c_str(),m_mhdrptr->forum)) {
                         m_ecPtr->m_merr_code=ERR_PROFANBDY;
                         m_ecPtr->m_merr_message=stpans(getmsg(ERPRFBT));
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }
               }
               if (m_cclist.length() != 0) {
                    m_ccPtr=m_cclist.c_str();
                    m_ahState=PROCEED_AH_VALIDCCADR;
               }
               else {
                    m_ahState=getState();
               }
               break;
          case PROCEED_AH_VALIDCCADR:
               if ((m_ccPtr=parscc(ccadr,m_ccPtr)) != NULL) {
                    ++m_ccCount;
                    if (m_ccCount > theforAgent.iMaxCC) {
                         m_ecPtr->m_merr_code=ERR_2MANYCC;
                         clrprf();
                         prfmsg(ERCC2MNY,spr("%d",theforAgent.iMaxCC));
                         m_ecPtr->m_merr_message=stpans(prfbuf);
                         clrprf();
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }
                    if ((m_ecPtr->m_merr_code=valadr(mywrkarea,m_mhdrptr->from,ccadr,m_mhdrptr->forum)) != VALYES) {
                         handleError(m_ecPtr,OP_VALIDATION,stpans(getmsg(VALCCADR)));
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }
               }
               else {
                    if (strlen(m_mhdrptr->attname) > 0) {
                         m_ccPtr=m_cclist.c_str();
                         m_ahState=PROCEED_AH_VALIDCCATT;
                    }
                    else {
                         m_ahState=getState();
                    }
               }
               break;
          case PROCEED_AH_VALIDCCATT:
               if ((m_ccPtr=parscc(ccadr,m_ccPtr)) != NULL) {
                    if ((m_ecPtr->m_merr_code=valatt(mywrkarea,m_mhdrptr->from,ccadr,m_mhdrptr->forum)) != VALYES) {
                         handleError(m_ecPtr,OP_VALIDATION,stpans(getmsg(VALCCATT)));
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }
               }
               else {
                    m_ahState=getState();
               }
               break;
          case PROCEED_AH_SENDFORMAT:
               if (format4Send()) {
                    if (m_sendFlags&SUBMIT_PREVIEW) {
                         m_ahState=PROCEED_AH_FORMAT;
                    }
                    else {
                         m_ahState=PROCEED_AH_PRCSUBMIT;
                    }
               }
               break;
          case PROCEED_AH_ATTACH:
               if (writeFileStream()) {
                    m_ahState=PROCEED_AH_MOVEFILE;
               }
               break;
          case PROCEED_AH_MOVEFILE:
               fname=AHUPDIR;
               fname+="\\";
               fname+=spr("%lu",m_tmpid);
               if (rename(fname.c_str(),m_gmefname.c_str())) {
                    m_ahState=PROCEED_AH_VALIDADR;
               }
               break;
          case PROCEED_AH_READMSG:
               switch (m_ecPtr->m_merr_code=readmsgf(mywrkarea,m_mhdrptr,txtbuf)) {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    m_ahState=sendHeaderSetup();
                    break;
               default:
                    handleError(m_ecPtr,OP_GENERIC,NULL);
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               break;
          case PROCEED_AH_STRIPRTF:
               setmem(txtbuf2,TXTLEN,0);
               stlcpy(txtbuf,m_paramText.c_str(),TXTLEN);
               if (stripfmt(txtbuf,txtbuf2,TXTLEN)) {
                    m_paramText=txtbuf2;
               }
               else {
                    m_paramText=txtbuf;
               }
               m_ahState=PROCEED_AH_SENDFORMAT;
               break;
          case PROCEED_AH_FORMAT:
               m_paramText=formatMessage(m_paramText.c_str(),FALSE,FALSE);
               m_ahState=PROCEED_AH_LINKS;
               break;
          case PROCEED_AH_LINKS:
               m_paramText=formatURLs(m_paramText.c_str(),anchorTag);
               m_ahState=PROCEED_AH_RESPONSE;
               break;

          case PROCEED_AH_PRCSUBMIT:
               if (m_sendFlags&SUBMIT_SEND) {
                    if (m_writeFlags&WRITE_REPLY) {
                         m_ecPtr->m_merr_code
                              =reply(mywrkarea,m_mhdrptr,m_paramText.c_str()
                                    ,m_gmefname.c_str(),m_cclist.c_str());
                         if (m_ecPtr->m_merr_code != GMEAGAIN) {
                              m_ahState=PROCEED_AH_RESPONSE;
                              if (m_ecPtr->m_merr_code < GMEAGAIN) {
                                   handleError(m_ecPtr,OP_GENERIC,NULL);
                              }
                         }
                    }
                    else {
                         m_ecPtr->m_merr_code
                              =gmeSendMsg(mywrkarea,m_mhdrptr
                                         ,m_paramText.c_str(),m_gmefname.c_str()
                                         ,m_cclist.c_str());
                         if (m_ecPtr->m_merr_code != GMEAGAIN) {
                              m_ahState=PROCEED_AH_RESPONSE;
                              if (m_ecPtr->m_merr_code < GMEAGAIN) {
                                   handleError(m_ecPtr,OP_GENERIC,NULL);
                              }
                         }
                    }
               }
               else if (m_sendFlags&SUBMIT_UPLOAD) {
                    if (m_firstTime) {
                         if (gforac(usr->userid(),m_forum) < ULAXES) {
                              m_ecPtr->m_merr_code=VALACC;
                              handleError(m_ecPtr,OP_VALIDATION,NULL);
                              m_ahState=PROCEED_AH_RESPONSE;
                              break;
                         }
                         if (!paramOK("attfile")) {
                              clrprf();
                              prfmsg(ERPMISS,"attfile");
                              m_ecPtr->m_merr_code=ERR_PARAMISSING;
                              m_ecPtr->m_merr_message=stpans(prfbuf);
                              clrprf();
                              m_ahState=PROCEED_AH_RESPONSE;
                              break;
                         }
                         if (m_tmpid == 0) {
                              clrprf();
                              prfmsg(ERPMISS,"tmpid");
                              m_ecPtr->m_merr_code=ERR_PARAMISSING;
                              m_ecPtr->m_merr_message=stpans(prfbuf);
                              clrprf();
                              m_ahState=PROCEED_AH_RESPONSE;
                              break;
                         }
                         if (!getFileStream()) {
                              clrprf();
                              prfmsg(ERPMISS,"attachment file data");
                              m_ecPtr->m_merr_code=ERR_PARAMISSING;
                              m_ecPtr->m_merr_message=stpans(prfbuf);
                              clrprf();
                              m_ahState=PROCEED_AH_RESPONSE;
                              break;
                         }
                         m_firstTime=false;
                    }
                    if (writeFileStream()) {
                         m_ahState=PROCEED_AH_RESPONSE;
                    }
               }
               else if (m_sendFlags&SUBMIT_DETACH) {
                    if (m_tmpid == 0) {
                         clrprf();
                         prfmsg(ERPMISS,"tmpid");
                         m_ecPtr->m_merr_code=ERR_PARAMISSING;
                         m_ecPtr->m_merr_message=stpans(prfbuf);
                         clrprf();
                    }
                    else {
                         deleteAttachment();
                    }
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               else if (m_sendFlags&SUBMIT_CANCEL) {
                    if (m_tmpid != 0) {
                         deleteAttachment();
                    }
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               break;
          case PROCEED_AH_RESPONSE:
               if (m_ecPtr->m_merr_code < GMEOK || m_ecPtr->m_merr_code > GMEAFWD) {
                    initErrVariables();
               }
               else {
                    initCompositionVbs();
                    if (m_forum != 0) {
                         setForumDefByID(m_forum);
                    }
               }
               if (m_dnfPtr == NULL) {
                    if (m_sendFlags&SUBMIT_SEND) {
                         retval=handleTemplates(&sendMap,&sendErrMap,PPFIX,"/write/");
                    }
                    else if (m_sendFlags&SUBMIT_ATTACH) {
                         if (gforac(usr->userid(),m_forum) < ULAXES) {
                              m_ecPtr->m_merr_code=VALACC;
                              handleError(m_ecPtr,OP_VALIDATION,NULL);
                         }
                         retval=handleTemplates(&attachMap,&attachErrMap,PPFIX,"/write/");
                    }
                    else if (m_sendFlags&SUBMIT_UPLOAD) {
                         retval=handleTemplates(&uplMap,&uplErrMap,PPFIX,"/write/");
                    }
                    else if (m_sendFlags&SUBMIT_DETACH) {
                         retval=handleTemplates(&detachMap,&detachErrMap,PPFIX,"/write/");
                    }
                    else if (m_sendFlags&SUBMIT_PREVIEW) {
                         retval=handleTemplates(&previewMap,&previewErrMap,PPFIX,"/write/");
                    }
                    else if (m_sendFlags&SUBMIT_REDISPLAY) {
                         retval=handleTemplates(&redispMap,&redispErrMap,PPFIX,"/write/");
                    }
                    else if (m_sendFlags&SUBMIT_CANCEL) {
                         retval=handleTemplates(&cancelMap,&cancelErrMap,PPFIX,"/write/");
                    }
               }
               else {
                    dnfSetTemplateTvb(m_dnfPtr);
                    if (m_sendFlags&(SUBMIT_UPLOAD|SUBMIT_DETACH|SUBMIT_REDISPLAY|SUBMIT_ATTACH)) {
                         if (m_ecPtr->m_merr_message.length() == 0) {
                              initCompositionVbs();
                              if (m_forum != 0) {
                                    setForumDefByID(m_forum);
                              }
                              retval=dynaWriteResp();
                              break;
                         }
                    }
                    else if (m_sendFlags&SUBMIT_PREVIEW) {
                         initSuccVariables();
                         m_sMsgTxt=m_paramText;
                         retval=dynaReadResp();
                         break;
                    }
                    retval=respDynafile();
               }
               break;
          }
          destructHTMLGMEUser();
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

SHORT
sendSynthesis::getState()          // determine state based on formatting and flags
{
     if (m_sendFlags&SUBMIT_SEND && !isfmtted(m_paramText.c_str())) {
          return(PROCEED_AH_SENDFORMAT);
     }
     else if (m_sendFlags&SUBMIT_PREVIEW) {
          return(PROCEED_AH_STRIPRTF);
     }
     return(PROCEED_AH_PRCSUBMIT);
}

VOID
sendSynthesis::deleteAttachment()  // nuke attachment
{
     string fname=AHUPDIR;
     fname+="\\";
     fname+=spr("%lu",m_tmpid);
     unlink(fname.c_str());
     m_attachName="";
}

VOID
sendSynthesis::submitError()       // submit type error
{
     shocst("Send Submit Type Error",
            "AH Email Error: Multiple (or no) submit types in the \"send\" URL.");
     bout << "There are multiple (or no) submit types in the \"send\" URL." <<
             "  Please notify the System Administrator.";
}

VOID
sendSynthesis::addCCs()            // add CCs to body of message
{
     if (m_cclist.length() > 0) {
          ccadfunc ccfunc;
          ccfunc=setccadder(NULL);
          stlcpy(txtbuf2,m_paramText.c_str(),TXTLEN);
          (*ccfunc)(txtbuf2,(CHAR*)m_cclist.c_str(),m_mhdrptr->to);
          setccadder(*ccfunc);
          m_paramText=txtbuf2;
     }
}

SHORT
sendSynthesis::sendHeaderSetup()   // set up header info
{
     if (m_writeFlags&WRITE_REPLY && !(m_sendFlags&SUBMIT_PREVIEW)) {
          stlcpy(m_mhdrptr->to,m_toAdr.c_str(),MAXADR);
     }
     else {
          stlcpy(m_mhdrptr->to,m_toAdr.c_str(),MAXADR);
     }
     stlcpy(m_mhdrptr->from,usr->userid(),MAXADR);
     stlcpy(m_mhdrptr->topic,m_topic.c_str(),TPCSIZ);
     m_mhdrptr->forum=m_forum;
     m_mhdrptr->flags=0;
     if (m_mhdrptr->forum == EMLID) {
          SetFlags(&m_mhdrptr->flags,RECREQ,((m_writeFlags&WRITE_RR) != 0L));
          SetFlags(&m_mhdrptr->flags,PRIMSG,((m_writeFlags&WRITE_PRI) != 0L));
     }
     if (m_writeFlags&WRITE_INCLIST
      || getWebPrefs(usr->userid())&MPREF_INCLIST) {
          addCCs();
     }
     if (m_attachName.length() > 0) {
          stlcpy(m_mhdrptr->attname,m_attachName.c_str(),GCMAXFNM);
          m_mhdrptr->flags|=FILATT;
          m_mhdrptr->flags&=~FILIND;
          m_gmefname=ulname(m_mhdrptr);
          return(PROCEED_AH_MOVEFILE);
     }
     else if (paramOK("attfile")) {
          m_tmpid=::GetTickCount();
          if (!getFileStream()) {
               m_tmpid=0;
               return(PROCEED_AH_VALIDADR);
          }
          stlcpy(m_mhdrptr->attname,m_attachName.c_str(),GCMAXFNM);
          m_mhdrptr->flags|=FILATT;
          m_mhdrptr->flags&=~FILIND;
          m_gmefname=ulname(m_mhdrptr);
          return(PROCEED_AH_ATTACH);
     }
     return (PROCEED_AH_VALIDADR);
}

bool
sendSynthesis::writeFileStream()   // do the bulk of writing to file
{
     ASSERT(m_ulistr != NULL);

     if (m_ulistr != NULL) {
          memset(m_writeBuf,0,sizeof(m_writeBuf));
          m_ulistr->read(m_writeBuf,sizeof(m_writeBuf));
          m_ofstr->write(m_writeBuf,m_ulistr->gcount());
          preventTimeout();
          if (m_ulistr->eof() || m_ulistr->fail()) {
               ses->paramStreamClose(m_ulistr);
          }
          else {
               return(false);
          }
     }
     return(true);
}

VOID
sendSynthesis::preventTimeout()    // prevent a timeout on big upload
{
     bout << "<!--\n";
     bout << "\\-->";
}

bool
sendSynthesis::getFileStream()     // get the file stream
{
     string hdr;
     size_t valsiz=STRINPSIZ;

     ses->paramHeader("attfile","Content-Disposition",
          strInpBuf,STRINPSIZ);
     hdr=strInpBuf;
     acthGetHeaderParam(hdr.c_str(),"filename",&valsiz,strInpBuf);
     if (strInpBuf[0] == '\0') {
          return(false);
     }
     if (!isdir(AHUPDIR)) {
          mkdir(AHUPDIR);
     }
     string fname=AHUPDIR;
     fname+="\\";
     fname+=spr("%lu",m_tmpid);
     CHAR fbuf[GCMAXFNM];
     m_attachName=fileparts(GCPART_FNAM,strInpBuf,fbuf,GCMAXFNM);
     m_attachName=mkdosn(m_attachName.c_str());
     if (m_ofstr != NULL) {
          delete m_ofstr;
     }
     m_ofstr=new ofstream(fname.c_str(),ios::binary);
     ses->paramStreamOpen("attfile",&m_ulistr);
     if (m_ulistr != NULL) {
          memset(m_writeBuf,0,sizeof(m_writeBuf));
          m_ulistr->read(m_writeBuf,sizeof(m_writeBuf));
          INT len=m_ulistr->gcount();
          if (len == 0) {
               ses->paramStreamClose(m_ulistr);
               m_attachName="";
               return(false);
          }
          else {
               m_ofstr->write(m_writeBuf,len);
          }
     }
     return(true);
}

//
//  downloadSynthesis Member Functions
//

ACTHCODE
downloadSynthesis::proceed()
{
     ACTHCODE retval=pureHTMLSynth::proceed();

     if (retval == ACTHMORE ) {
          if (ses->urlargc() >= 3 && alldgs(ses->urlargv(1))
           && alldgs(ses->urlargv(2))) {
               switch (m_ahState) {
               case PROCEED_AH_REQUEST:
                    m_mhdr_msgid=validateMsgId(atol(ses->urlargv(2)));
                    m_forum=atoi(ses->urlargv(1));
                    if (m_forum == EMLID || !fidxst(m_forum)) {
                         m_ecPtr->m_merr_code=GMENFND;
                         handleError(m_ecPtr,OP_GENERIC,NULL);
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }
                    inictx(mywrkarea,usr->userid(),FSQFOR,m_forum
                          ,m_mhdr_msgid,0);
                    m_ahState=PROCEED_AH_DOWNLOAD;
                    break;
               case PROCEED_AH_DOWNLOAD:
                    switch ((m_ecPtr->m_merr_code=readmsgf(mywrkarea,m_mhdrptr,txtbuf))) {
                    case GMEAGAIN:
                         break;
                    case GMEOK:
                         m_ecPtr->m_merr_code=tagatt(mywrkarea
                                                    ,m_mhdrptr,tagbuf);
                         if (m_ecPtr->m_merr_code == GMEOK) {
                              if (gdlstart(usr->userid(),tagbuf)) {

                                   // set Content-Length and -Type headers
                                   string pth=dlname(m_mhdrptr);
                                   struct ffblk fb;
                                   fndfile(&fb,pth.c_str(),0);
                                   ostrstream ost;
                                   ost << "Content-Length: " << fb.ff_fsize << ends;
                                   ses->headerField(ost.str());
                                   ost.rdbuf()->freeze(0);
                                   CHAR ext[GCMAXEXT+1];
                                   fileparts(GCPART_EXTN,pth.c_str(),ext,GCMAXEXT+1);
                                   ses->contypeFext(ext);

                                   // send the file
                                   ses->sndfile(pth.c_str());
                                   m_doDl=true;
                                   retval=ACTHDONE;
                                   break;
                              }
                              else {
                                   m_ecPtr->m_merr_code=GMECRD;
                              }
                         } // intentional fall through to handleError()
                    default:
                          handleError(m_ecPtr,OP_GENERIC,NULL);
                          m_ahState=PROCEED_AH_RESPONSE;
                          break;
                    }
                    break;
               case PROCEED_AH_RESPONSE:
                    initVars();
                    if (m_dnfPtr == NULL) {
                         retval=handleTemplates(NULL,&attErrMap,PPFIX,"/att/");
                    }
                    else {
                         retval=respDynafile();
                    }
                    break;
               }
          }
          else {
               retval=ACTHNOTFND;
          }
          destructHTMLGMEUser();
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

VOID
downloadSynthesis::abort()
{
     if (m_doDl) {
          gdlabt(usr->userid(),tagbuf);
          m_doDl=false;
     }
}


//
//  deleteSynthesis Member Functions
//

ACTHCODE
deleteSynthesis::proceed()
{
     ACTHCODE retval=pureHTMLSynth::proceed();

     if (retval == ACTHMORE ) {
          switch (m_ahState) {
          case PROCEED_AH_REQUEST:
               if (m_delCount < ses->nparam()) {
                    CHAR namBuf[NAMESIZ];
                    ses->param(m_delCount,namBuf,NAMESIZ,strInpBuf,STRINPSIZ);
                    if (sameas(namBuf,"id") && alldgs(strInpBuf)) {
                         m_savMsgId=validateMsgId(atol(strInpBuf));
                         ASSERT(gmerqopn(mywrkarea));
                         getCompositionParams();
                         m_ahState=PROCEED_AH_DELETE;
                         m_mhdr_msgid=m_savMsgId;
                    }
                    ++m_delCount;
               }
               else {
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               break;
          case PROCEED_AH_DELETE:
               if ((m_ecPtr->m_merr_code=quickDelete()) != GMEAGAIN) {
                    if (m_ecPtr->m_merr_code != GMEOK) {
                         handleError(m_ecPtr,OP_GENERIC,NULL);
                         m_ahState=PROCEED_AH_RESPONSE;
                    }
                    else {
                         m_ahState=PROCEED_AH_REQUEST;
                    }
               }
               break;
          case PROCEED_AH_RESPONSE:
               if (m_dnfPtr == NULL) {
                    retval=handleTemplates(&deleteSuccMap,&deleteErrMap,PPFIX,"/delete/");
               }
               else {
                    initVars();
                    retval=respDynafile();
               }
               break;
          }
          destructHTMLGMEUser();
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

ACTHCODE
exemptSynthesis::proceed()
{
     ACTHCODE retval=pureHTMLSynth::proceed();

     if (retval == ACTHMORE ) {
          switch (m_ahState) {
          case PROCEED_AH_REQUEST:
               if (m_exCount < ses->nparam()) {
                    CHAR namBuf[NAMESIZ];
                    ses->param(m_exCount,namBuf,NAMESIZ,strInpBuf,STRINPSIZ);
                    if (sameas(namBuf,"id") && alldgs(strInpBuf)) {
                         m_savMsgId=validateMsgId(atol(strInpBuf));
                         ASSERT(gmerqopn(mywrkarea));
                         getCompositionParams();
                         if (m_forum == EMLID) {
                              m_ecPtr->m_merr_code=GMENFND;
                              handleError(m_ecPtr,OP_GENERIC,NULL);
                              m_ahState=PROCEED_AH_RESPONSE;
                              break;
                         }
                         inictx(mywrkarea,usr->userid(),FSQFOR,m_forum,m_savMsgId,0);
                         m_ahState=PROCEED_AH_READEXEMPT;
                         m_mhdr_msgid=m_savMsgId;
                    }
                    ++m_exCount;
                    if (m_ecPtr->m_merr_code != GMEOK) {
                         handleError(m_ecPtr,OP_GENERIC,NULL);
                         m_ahState=PROCEED_AH_RESPONSE;
                    }
               }
               else {
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               break;
          case PROCEED_AH_READEXEMPT:
               switch ((m_ecPtr->m_merr_code=readmsgf(mywrkarea,m_mhdrptr,txtbuf2))) {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    m_ahState=PROCEED_AH_EXEMPT;
                    break;
               default:
                    handleError(m_ecPtr,OP_GENERIC,NULL);
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               break;
          case PROCEED_AH_EXEMPT:
               switch ((m_ecPtr->m_merr_code=exmtmsg(mywrkarea
                                                   ,m_mhdrptr,txtbuf2
                                                   ,(m_mhdrptr->flags&EXEMPT) == 0))) {
               case GMEAGAIN:
                  break;
               case GMEOK:
                  m_ahState=PROCEED_AH_RESPONSE;
                  break;
               default:
                  handleError(m_ecPtr,OP_GENERIC,NULL);
                  m_ahState=PROCEED_AH_RESPONSE;
               }
          case PROCEED_AH_RESPONSE:
               if (m_dnfPtr == NULL) {
                    retval=handleTemplates(&exemptSuccMap,&exemptErrMap,PPFIX,"/exemptit/");
               }
               else {
                    initVars();
                    retval=respDynafile();
               }
               break;
          }
          destructHTMLGMEUser();
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

ACTHCODE
approveSynthesis::proceed()
{
     ACTHCODE retval=pureHTMLSynth::proceed();

     if (retval == ACTHMORE ) {
          switch (m_ahState) {
          case PROCEED_AH_REQUEST:
               if (m_apCount < ses->nparam()) {
                    CHAR namBuf[NAMESIZ];
                    ses->param(m_apCount,namBuf,NAMESIZ,strInpBuf,STRINPSIZ);
                    if (sameas(namBuf,"id") && alldgs(strInpBuf)) {
                         m_savMsgId=validateMsgId(atol(strInpBuf));
                         ASSERT(gmerqopn(mywrkarea));
                         getCompositionParams();
                         if (m_forum == EMLID) {
                              m_ecPtr->m_merr_code=GMENFND;
                              handleError(m_ecPtr,OP_GENERIC,NULL);
                              m_ahState=PROCEED_AH_RESPONSE;
                              break;
                         }
                         inictx(mywrkarea,usr->userid(),FSQFOR,m_forum,m_savMsgId,0);
                         m_ahState=PROCEED_AH_READAPPROVE;
                         m_mhdr_msgid=m_savMsgId;
                    }
                    ++m_apCount;
                    if (m_ecPtr->m_merr_code != GMEOK) {
                         handleError(m_ecPtr,OP_GENERIC,NULL);
                         m_ahState=PROCEED_AH_RESPONSE;
                    }
               }
               else {
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               break;
          case PROCEED_AH_READAPPROVE:
               switch ((m_ecPtr->m_merr_code=readmsgf(mywrkarea,m_mhdrptr,txtbuf2))) {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    m_ahState=PROCEED_AH_APPROVE;
                    break;
               default:
                    handleError(m_ecPtr,OP_GENERIC,NULL);
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               break;
          case PROCEED_AH_APPROVE:
               switch ((m_ecPtr->m_merr_code=aprvmsg(mywrkarea
                                                   ,m_mhdrptr,txtbuf2
                                                   ,(m_mhdrptr->flags&FILAPV) == 0))) {
               case GMEAGAIN:
                  break;
               case GMEOK:
                  m_ahState=PROCEED_AH_RESPONSE;
                  break;
               default:
                  handleError(m_ecPtr,OP_GENERIC,NULL);
                  m_ahState=PROCEED_AH_RESPONSE;
               }
          case PROCEED_AH_RESPONSE:
               if (m_dnfPtr == NULL) {
                    retval=handleTemplates(&approveSuccMap,&approveErrMap,PPFIX,"/approveit/");
               }
               else {
                    initVars();
                    retval=respDynafile();
               }
               break;
          }
          destructHTMLGMEUser();
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

ACTHCODE
qsforumsSynthesis::proceed()
{
     ACTHCODE retval=pureHTMLSynth::proceed();

     if (retval == ACTHMORE ) {
          switch (m_ahState) {
          case PROCEED_AH_REQUEST:
               if (!m_init) {
                    if (m_qsCount == 0) {
                         getQSPtr();
                         *m_pForumStart='\0';
                         ++m_qsCount;
                    }
                    else {
                         const struct fordef* fdef=nxtdefp(m_pForumStart);
                         if (fdef == NULL) {
                              m_init=true;
                              m_qsCount=0;
                         }
                         else {
                              delqs(qscp,fdef->forum);
                              stlcpy(m_pForumStart,fdef->name,FORNSZ);
                         }
                    }
                    break;
               }
               if (m_qsCount < ses->nparam()) {
                    CHAR namBuf[NAMESIZ];
                    ses->param(m_qsCount,namBuf,NAMESIZ,strInpBuf,STRINPSIZ);
                    if (sameas(namBuf,"forumid") && alldgs(strInpBuf)) {
                         m_forum=atoi(strInpBuf);
                         if (!sfinqs(qscp,m_forum,true)) {
                              m_ecPtr->m_merr_code = GME2MFR;
                              handleError(m_ecPtr,OP_GENERIC,NULL);
                              m_ahState=PROCEED_AH_RESPONSE;
                         }
                    }
                    ++m_qsCount;
               }
               else {
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               break;
          case PROCEED_AH_RESPONSE:
               if (m_dnfPtr == NULL) {
                    retval=handleTemplates(&qsforumsSuccMap,&qsforumsErrMap,PPFIX,"/quickscan/qsforums/");
               }
               else {
                    initVars();
                    retval=respDynafile();
               }
               break;
          }
          destructHTMLGMEUser();
     }
     usaptr=uacoff(usrnum);
     return(retval);
}


//
//  forwardSynthesis Member Functions
//

ACTHCODE
forwardSynthesis::proceed()
{
     INT adrCode,attCode,priCode;  // validation codes for address, etc.
     ACTHCODE retval=pureHTMLSynth::proceed();

     if (retval == ACTHMORE) {
          switch (m_ahState) {
          case PROCEED_AH_REQUEST:
               getCompositionParams();
               GETINPUT("id");
               if (strInpBuf[0] == '\0' || !alldgs(strInpBuf)) {
                   retval=ACTHFORBID;
                   break;
               }
               m_savMsgId=validateMsgId(atol(strInpBuf));
               if (m_forum == 0) {
                    retval=ACTHFORBID;
                    break;
               }
               if (gforac(usr->userid(),m_forum) < OPAXES) {
                    retval=ACTHFORBID;
                    break;
               }
               if (paramOK("orgdelete")) {
                   m_bDeleteMsg=true;
               }
               ses->param("comment",m_commentBufPtr,TXTLEN);
               ses->param("to",holdid,MAXADR);
               inictx(mywrkarea,usr->userid(),FSQFOR,m_forum,m_savMsgId,0);
               m_ahState=PROCEED_AH_READMSG;
               break;
          case PROCEED_AH_READMSG:
               switch ((m_ecPtr->m_merr_code=readmsgf(mywrkarea,m_mhdrptr,txtbuf))) {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    m_ahState=PROCEED_AH_VALIDATE;
                    break;
               default:
                    handleError(m_ecPtr,OP_GENERIC,NULL);
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }
               break;
          case PROCEED_AH_VALIDATE:

               // handle copy vs. forward
               if (m_bDeleteMsg) {   // forwarding, use vfwd*()
                    adrCode=vfwdadr(mywrkarea,usr->userid(),holdid,EMLID);
                    attCode=vfwdatt(mywrkarea,usr->userid(),holdid,EMLID);
                    priCode=vfwdpri(mywrkarea,usr->userid(),holdid,EMLID);
                    m_ahState=PROCEED_AH_FORWARD;
               }
               else {              // copying, use val*()
                    adrCode=valadr(mywrkarea,usr->userid(),holdid,EMLID);
                    attCode=valatt(mywrkarea,usr->userid(),holdid,EMLID);
                    priCode=valpri(mywrkarea,usr->userid(),holdid,EMLID);
                    m_ahState=PROCEED_AH_COPYMSG;
               }

               // validate destination address
               if (adrCode != VALYES) {
                    m_ecPtr->m_merr_code=adrCode;
                    ::handleError(m_ecPtr,OP_VALIDATION,getmsg(VALADR));
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }

               // validate attachment
               if ((m_mhdrptr->flags&FILATT) && attCode != VALYES) {
                    m_ecPtr->m_merr_code=attCode;
                    ::handleError(m_ecPtr,OP_VALIDATION,getmsg(VALATT));
                    m_ahState=PROCEED_AH_RESPONSE;
                    break;
               }

               // deal with priority
               if ((m_mhdrptr->flags&PRIMSG) && priCode != VALYES) {
                    m_mhdrptr->flags&=~PRIMSG;
               }

               // add comments if any
               if (m_commentBufPtr[0] != '\0') {

                    // check for profanity
                    USHORT tmpfid=isforum(holdid) ? getAdrForID(holdid) : EMLID;
                    if (pfnerr(usr->userid(),m_commentBufPtr,tmpfid)) {
                         m_ecPtr->m_merr_code=ERR_PROFANBDY;
                         m_ecPtr->m_merr_message=stpans(getmsg(ERPRFBT));
                         m_ahState=PROCEED_AH_RESPONSE;
                         break;
                    }

                    // prepend to text
                    AddComment(txtbuf,m_commentBufPtr,txtbuf2,TXTLEN
                              ,ses->getUser()->userid());
                    m_sMsgTxt=txtbuf2;
               }
               else {
                    m_sMsgTxt=txtbuf;
               }

               memmove(m_wrkHeader,m_mhdrptr,sizeof(struct message));
               m_mhdrptr->forum=EMLID;
               stlcpy(m_mhdrptr->to,holdid,MAXADR);
               break;
          case PROCEED_AH_FORWARD:
               m_ecPtr->m_merr_code
                    =fwdmsg(mywrkarea,m_mhdrptr,m_sMsgTxt.c_str());
               if (m_ecPtr->m_merr_code != GMEAGAIN) {
                    m_ahState=PROCEED_AH_RESPONSE;
                    if (m_ecPtr->m_merr_code < GMEAGAIN) {
                         handleError(m_ecPtr,OP_GENERIC,NULL);
                    }
               }
               break;
          case PROCEED_AH_COPYMSG:
               m_ecPtr->m_merr_code
                    =copymsg(mywrkarea,m_mhdrptr,m_sMsgTxt.c_str());
               if (m_ecPtr->m_merr_code > GMEAGAIN) {
                    m_ahState=PROCEED_AH_MARKREAD;
               }
               else if (m_ecPtr->m_merr_code < GMEAGAIN) {
                    handleError(m_ecPtr,OP_GENERIC,NULL);
                    m_ahState=PROCEED_AH_RESPONSE;
               }
               break;
          case PROCEED_AH_MARKREAD:
               m_ecPtr->m_merr_code=markreadf(mywrkarea,m_wrkHeader,txtbuf);
               if (m_ecPtr->m_merr_code != GMEAGAIN) {
                    m_ahState=PROCEED_AH_RESPONSE;
                    if (m_ecPtr->m_merr_code < GMEAGAIN) {
                         handleError(m_ecPtr,OP_GENERIC,NULL);
                    }
               }
               break;
          case PROCEED_AH_RESPONSE:
               if (m_dnfPtr == NULL) {
                    retval=handleTemplates(&fwdSuccMap,&fwdErrMap,PPFIX,"/forward/");
               }
               else {
                    mWriteOrgMsgId.set("%ld",m_savMsgId);
                    initVars();
                    retval=respDynafile();
               }
               break;
          }
          destructHTMLGMEUser();
     }
     usaptr=uacoff(usrnum);
     return(retval);
}



//
//  forHTMLSynthesis Member Functions
//

VOID
forHTMLSynthesis::createDNF()
{
}

VOID
forHTMLSynthesis::getQSPtr()
{
     qscp=gmeUserGetQS(m_gmehandle);
}

INT
forHTMLSynthesis::getSeq()
{
     if (m_bOnlyQS) {
          return(FSQSCN);
     }
     return(m_bThread ? FSQTHR : FSQFOR);
}

VOID
forHTMLSynthesis::setBaseTvb()
{
     if (webpref&FPREF_TYPE_HTML) {
          forTypeJava.clr();
          forTypeHTML.set("CHECKED");
          forType.set("html");
     }
     else {
          forTypeHTML.clr();
          forTypeJava.set("CHECKED");
          forType.set("java");
     }
     forReflowPref.set((webpref&FPREF_REFLOW) ? "CHECKED" : "");
     forQuotePref.set((webpref&FPREF_QUOTE) ? "CHECKED" : "");
     fUseFrames.set((webpref&FPREF_USEFRAMES) ? "CHECKED" : "");
     const CHAR* value=getCookie(FORFRAMECOOKIE,ses);
     if (value != NULL) {
          setForTargetTvb(sameas(value,"1") ? true : false);
     }
     else {
          setForTargetTvb(getWebPrefs(usr->userid())&FPREF_USEFRAMES);
     }
     dnfSetTemplateTvb(dnf);
     getQSPtr();
     ePrefForumTo.set((qscp->flags&FORUM2) ? "CHECKED" : "");
     setCurForumDefByID(qscp->curfor);
     setCurForInQScanVar(getfnm(qscp->curfor),usr->userid());
}

ACTHCODE
forHTMLSynthesis::errorResponse(
INT errcode,
INT operation,
dnfMap& map)
{
     m_err=errcode;
     m_op=operation;
     static CHAR buf[GCMAXPTH];

     const CHAR* fnm=map.getFile();
     if (NeedOnErrorDNF(fileparts(GCPART_PATH,fnm,buf,GCMAXPTH)) != ACTHDONE) {
          setErrorVars(errcode,operation);
          dnf=dnfCreateHandler(&bout,&map,m_onerrorFile);
          return(ACTHMORE);
     }
     return(ACTHDONE);
}

VOID
forHTMLSynthesis::setErrorVars(
INT errcode,
INT operation)
{
     const CHAR* emsg=gmeErrorHandler(errcode,operation,NULL);
     setErrorCode(errcode);
     setErrorMessage(emsg);
}


ACTHCODE
forHTMLSynthesis::proceed()  // proceed with HTML synthesis
{
     ACTHCODE retval=ACTHMORE;

     //LOG0("forHTMLSynthesis::proceed BEG");
     clearAllForTvb();
     //LOG0("forHTMLSynthesis::proceed after TvbClear");
     if (ses->urlargc() == m_n && ses->forceDir()) {
          //LOG0("forHTMLSynthesis::proceed END ACTHDONE");
          return(ACTHDONE);
     }
     if (m_err != 0) {
         //LOG0("forHTMLSynthesis::proceed calling setErrorVars");
         setErrorVars(m_err,m_op);
     }
     //LOG0("forHTMLSynthesis::proceed calling getUser");
     if ((usr=ses->getUser()) == NULL) {
          //LOG0("forHTMLSynthesis::proceed END ACTHNOANON");
          return(ACTHNOANON);
     }
     else if (!usr->hasKey(theforAgent.forahkey)) {
          //LOG0("forHTMLSynthesis::proceed END ACTHDONE 2");
          return(ACTHDONE);
     }
     //LOG0("forHTMLSynthesis::proceed before m_init");
     if (!m_init) {
          //LOG0("forHTMLSynthesis::proceed before reading defaults");
          readDefaults();
          if (m_gmehandle == NULL) {
               //LOG0("forHTMLSynthesis::proceed getting gme handle");
               m_gmehandle=::GetGmeUserHandle(usr->userid());
          }
          //LOG2("FOR.forHTMLSyn.proceed GetUH: this=%08lX h=%lu",this,m_gmehandle);
     }
     //LOG0("forHTMLSynthesis::proceed setting base Tvbs");
     setBaseTvb();
     //LOG0("forHTMLSynthesis::proceed setting usaptr");
     setusaptr();
     //LOG0("forHTMLSynthesis::proceed END retval");
     return(retval);
}

VOID
forHTMLSynthesis::setusaptr()
{
     if (onsysn(usr->userid(),1)) {
          for (int i=0; i < nterms; ++i) {
               if (usroff(i)->usrcls >= SUPLON
                && sameas(usr->userid(),uacoff(i)->userid)) {
                    usaptr=uacoff(i);
                    return;
               }
          }
     }
     if (*(m_uaccptr.userid) == '\0') {
          dfaSetBlk(accbb);
          dfaAcqEQ(&m_uaccptr,usr->userid(),0);
          dfaRstBlk();
     }
     usaptr=&m_uaccptr;
}

VOID
forHTMLSynthesis::readParams()
{
     CHAR tmpbuf[GENBUFSIZ];

     if (ses->param("quickscan",tmpbuf,GENBUFSIZ)) {
          if (atoi(tmpbuf) == 1) {
               m_bOnlyQS=true;
          }
     }
     if (ses->param("direction",tmpbuf,GENBUFSIZ)) {
          if (sameas(tmpbuf,"lt")) {
               m_iDirection=SHOW_LT;
          }
          else if (sameas(tmpbuf,"le")) {
               m_iDirection=SHOW_LT|SHOW_EQ;
               m_spEQ=true;
          }
          else if (sameas(tmpbuf,"gt")) {
               m_iDirection=SHOW_GT;
          }
          else if (sameas(tmpbuf,"ge")) {
               m_iDirection=SHOW_GT|SHOW_EQ;
               m_spEQ=true;
          }
          else if (sameas(tmpbuf,"near")) {
               m_iDirection=SHOW_EQ;
               m_spEQ=true;
          }
     }
     if (ses->param("count",tmpbuf,GENBUFSIZ)) {
          INT newcnt=atoi(tmpbuf);
          if (newcnt > 0) {
               m_iCount=newcnt;
          }
     }
     if (ses->param("order",tmpbuf,GENBUFSIZ)) {
          if (sameas(tmpbuf,"descending")) {
               m_bIsDescOrder=true;
          }
     }
     m_bAll=ses->param("all");
     if (ses->param("forumid",tmpbuf,GENBUFSIZ)) {
          fid=atoi(tmpbuf);
     }
     else if (ses->param("forumname",tmpbuf,GENBUFSIZ)) {
          fid=getfid(tmpbuf);
     }
     if (ses->param("thread",tmpbuf,GENBUFSIZ)) {
          thrid=atol(tmpbuf);
          if (thrid != 0) {
               m_bThread=true;
          }
     }
}

bool
forHTMLSynthesis::startProc()
{
     return(m_timer.start());
}

bool
forHTMLSynthesis::contProc(
ACTHCODE rc)
{
     return(rc == ACTHMORE && m_timer.haveTime() && !m_needcyc);
}

VOID
forHTMLSynthesis::clearAllForTvb()
{
     clearAllDefs();
     flistCount.clr();
     flistOrder.clr();
     flistLow.clr();
     flistMoreLow.set("0");
     flistHigh.clr();
     flistMoreHigh.set("0");
     clearGroupVars();
     tlistNumMsg.clr();
     mlistThreadName.clr();
     mlistThreadID.clr();
     mlistLastForid.clr();
     msgByQS.set("0");
     msgByThread.set("0");
}


VOID
forHTMLSynthesis::setGroupVars(USHORT grpid,const CHAR* gname)
{
     const struct forgrp* fgrp;
     clearGroupVars();
     if ((fgrp=gmeGetGrpP(grpid)) != NULL) {
          ::txvGrpPath.set(gname);
          ::txvGrpPathURL.set(gname);
          ::txvGrpName.set(fgrp->name);
          ::txvGrpNameURL.set(fgrp->name);
          ::txvGrpTopic.set(fgrp->topic);
          ::grpNumForums.set("%d",fgrp->nforums);
     }
}


VOID
forHTMLSynthesis::clearGroupVars()
{
     txvGrpPath.clr();
     txvGrpPathURL.clr();
     txvGrpParent.clr();
     txvGrpParentURL.clr();
     txvGrpName.clr();
     txvGrpNameURL.clr();
     txvGrpTopic.clr();
     grpNumForums.clr();
}



VOID
forHTMLSynthesis::readDefaults()
{
     webpref=getWebPrefs(usr->userid());
     if (!(webpref&FPREF_TYPE_HTML) && !(webpref&FPREF_TYPE_JAVA)) {
          // first time user is using AH Forums
          if (theforAgent.usejava && theforAgent.definterface == INT_JAVA) {
               webpref|=FPREF_TYPE_JAVA;
          }
          else {
               webpref|=FPREF_TYPE_HTML;
          }
          SetFlags(&webpref,FPREF_USEFRAMES,theforAgent.defframes);
          webpref|=(FPREF_REFLOW|FPREF_QUOTE);
          setWebPrefs(usr->userid(),webpref);
     }
}

ACTHCODE
forHTMLSynthesis::NeedOnSuccessDNF()
{
     if (ses->param("onsuccess")) {

          if (m_onsuccessFile != NULL) {
               delete [] m_onsuccessFile;
          }
          INT sz=ses->paramRoom("onsuccess")+1;
          m_onsuccessFile=new CHAR[sz];
          ses->param("onsuccess",m_onsuccessFile,sz);
          if (sameto("http://",m_onsuccessFile) || *m_onsuccessFile == '/') {
               ses->redirect(m_onsuccessFile);
               return(ACTHDONE);
          }
          if (!isvalfn(m_onsuccessFile)) {
               if (sz < strlen("index.htm")+1) {
                    delete [] m_onsuccessFile;
                    m_onsuccessFile=NULL;
               }
          }
          return(ACTHMORE);
     }
     else if (m_onsuccessFile != NULL) {
          delete [] m_onsuccessFile;
          m_onsuccessFile=NULL;
     }
     return(ACTHMORE);
}

ACTHCODE
forHTMLSynthesis::NeedOnErrorDNF(
const CHAR* path)
{
     INT sz;

     if (ses->param("onerror")) {
          if (m_onerrorFile != NULL) {
               delete [] m_onerrorFile;
          }
          m_onerrorFile=new CHAR[(sz=ses->paramRoom("onerror")+1)];
          ses->param("onerror",m_onerrorFile,sz);
          if (sameto("http://",m_onerrorFile) || *m_onerrorFile == '/') {
               ses->redirect(m_onerrorFile);
               return(ACTHDONE);
          }
          if (!isvalfn(m_onerrorFile)) {
               if (sz < strlen(path)+strlen("error.htm")+1) {
                    delete [] m_onerrorFile;
                    m_onerrorFile=new CHAR[(sz=strlen("error.htm")+strlen(path)+1)];
                    stlcpy(m_onerrorFile,path,sz);
                    stlcat(m_onerrorFile,"error.htm",sz);
                    return(ACTHMORE);
               }
          }
          return(ACTHMORE);
     }
     if (m_onerrorFile != NULL) {
          delete [] m_onerrorFile;
     }
     m_onerrorFile=new CHAR[(sz=strlen("error.htm")+strlen(path)+1)];
     stlcpy(m_onerrorFile,path,sz);
     stlcat(m_onerrorFile,"error.htm",sz);
     return(ACTHMORE);
}

//
//  forInitialSynthesis Member Function
//

VOID
forInitialSynthesis::getDefaultType()
{
     usehtml=((webpref&FPREF_TYPE_HTML) != 0);
     useframes=((webpref&FPREF_USEFRAMES) != 0);
}

VOID
forInitialSynthesis::paramCheck()
{
     CHAR* typ;
     bool set=false;
     LONG savpref=webpref;
     INT sz;

     getDefaultType();
     if (ses->param("useframes")) {
          sz=ses->paramRoom("useframes")+1;
          typ=new CHAR[sz];
          ses->param("useframes",typ,sz);
          if (sameas("1",typ)) {
               webpref|=FPREF_USEFRAMES;
          }
          else if (sameas("1 set",typ)) {
               webpref|=FPREF_USEFRAMES;
               savpref|=FPREF_USEFRAMES;
          }
          else if (sameas("0 set",typ)) {
               webpref&=~FPREF_USEFRAMES;
               savpref&=~FPREF_USEFRAMES;
          }
          else if (sameas("0",typ)) {
               webpref&=~FPREF_USEFRAMES;
          }
          else if (sameas("~",typ)) {
               SetFlags(&webpref,FPREF_USEFRAMES,!(webpref&FPREF_USEFRAMES));
          }
          else if (sameas("~ set",typ)) {
               SetFlags(&webpref,FPREF_USEFRAMES,!(webpref&FPREF_USEFRAMES));
               SetFlags(&savpref,FPREF_USEFRAMES,!(webpref&FPREF_USEFRAMES));
          }
          delete [] typ;
     }
     if (ses->param("type")) {
          sz=ses->paramRoom("type")+1;
          typ=new CHAR[sz];
          ses->param("type",typ,sz);
          if (sameas("html",typ)) {
               webpref|=FPREF_TYPE_HTML;
               webpref&=~FPREF_TYPE_JAVA;
          }
          else if (sameas("html set",typ)) {
               savpref|=FPREF_TYPE_HTML;
               savpref&=~FPREF_TYPE_JAVA;
               webpref|=FPREF_TYPE_HTML;
               webpref&=~FPREF_TYPE_JAVA;
          }
          else if (theforAgent.usejava && sameas("java set",typ)) {
               savpref&=~FPREF_TYPE_HTML;
               savpref|=FPREF_TYPE_JAVA;
               webpref&=~FPREF_TYPE_HTML;
               webpref|=FPREF_TYPE_JAVA;
          }
          else if (theforAgent.usejava && sameas("java",typ)) {
               webpref&=~FPREF_TYPE_HTML;
               webpref|=FPREF_TYPE_JAVA;
          }
          delete [] typ;
     }
     setWebPrefs(usr->userid(),savpref);
     getDefaultType();
}

ACTHCODE
forInitialSynthesis::proceed()
{
     ACTHCODE retval=ACTHMORE;

     if ((retval=forHTMLSynthesis::proceed()) != ACTHMORE) {
          return(retval);
     }
     if (!m_init) {
          paramCheck();
          m_init=true;
     }
     if (dnf == NULL) {
          createDNF();
     }
     else {
          setForTargetTvb(useframes);
          dnfSetTemplateTvb(dnf);
          switch(dnf->process()) {
          case DNFEND:
               retval=ACTHDONE;
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

VOID
forInitialSynthesis::createDNF()
{
     dnfMap* pMap;
     const CHAR* pCookieVal;
     if (usehtml || !(theforAgent.usejava)) {
          pMap=(useframes ? &forFrameIndexMap : &forNoFrameIndexMap);
          pCookieVal=(useframes ? "1" : "0");
     }
     else {
          pMap=&forJavaIndexMap;
          pCookieVal="0";
     }
     dnf=dnfCreateHandlerURL(ses,pMap,PPFIX);
     setCookie(FORFRAMECOOKIE,pCookieVal,NULL,NULL,NULL,false,ses);
}

//
//  forListForumsSynthesis Member Functions
//

VOID
forListForumsSynthesis::readParams()
{
     forHTMLSynthesis::readParams();
     m_iCount=min(theforAgent.maxflstc,m_iCount);

     if (ses->param("grouppath")) {
          INT grpsiz=ses->paramRoom("grouppath");
          m_pGroupName=new CHAR[grpsiz];
          ses->param("grouppath",m_pGroupName,grpsiz);
          const CHAR* ptr=m_pGroupName;
          if (*ptr == '/') {
               ptr++;         // gmeGetGrpID doesn't want a '/' on front
          }
          m_gid=gmeGetGrpID(ptr);
          m_usegroup=true;
     }
     ses->param("start",m_pForumStart,FORNSZ);
     stlcpy(m_pForumStartParam,m_pForumStart,FORNSZ);
     m_bAll=(m_bOnlyQS ? true : m_bAll);
}

SHORT
forListForumsSynthesis::buildQSForumMap()
{
     if (!m_startbuild) {
          m_startbuild=true;
          m_swapdir=false;
          m_bIsDescOrder=false;
          getQSPtr();
          memset(otsp,0,MAXQSR);
          qsc2ots(qscp,otsp);
          if (otsp->nforums == 0) {
               // no forums in scan
               return(MAPDONE);
          }
     }
     if (m_qsidx == otsp->nforums) {
          return(MAPDONE);
     }
     const fordef* pFdef=getdefp(otsp->forlst[m_qsidx++]);
     if (pFdef != NULL && gforac(usr->userid(),pFdef->forum) >= RDAXES) {
          if (!m_bAll) {
               addToMap(pFdef->name);
          }
          stlcpy(m_pForumStart,pFdef->name,FORNSZ);
          m_fcnt++;
          return(MAPMORE);
     }
     return(MAPAGAIN);
}

SHORT
forListForumsSynthesis::buildMap()
{
     struct fordef const * pFdef;
     if (m_fcnt == m_iCount && !m_bAll) {
          return(MAPDONE);
     }
     if (m_bOnlyQS) {
          return(buildQSForumMap());
     }
     if (m_firstforum) {
          if (m_bAll) {
               *m_pForumStart='\0';
          }
          m_firstforum=false;
          if (m_iDirection&SHOW_EQ) {
               USHORT forum=getfid(m_pForumStart);
               if (forum != EMLID && fidxst(forum)) {
                    pFdef=(m_usegroup ? gmeGetGrpFDefP(m_gid,m_pForumStart)
                                        : getdefp(forum));

                    if (pFdef == NULL) {
                         return(MAPAGAIN);
                    }
                    stlcpy(m_pForumStart,pFdef->name,FORNSZ);
                    if (gforac(usr->userid(),pFdef->forum) >= RDAXES) {
                         if (!m_bAll) {
                              addToMap(pFdef->name);
                         }
                         m_fcnt++;
                         return(MAPMORE);
                    }
               }
          }
          return(MAPAGAIN);
     }
     else {
          if (m_swapdir) {
               pFdef=(m_usegroup ? gmePrevGrpFDefP(m_gid,m_pForumStart)
                                   : prvdefp(m_pForumStart));
          }
          else {
               pFdef=(m_usegroup ? gmeNextGrpFDefP(m_gid,m_pForumStart)
                                   : nxtdefp(m_pForumStart));
          }
          if (pFdef == NULL) {
               return(MAPDONE);
          }
          stlcpy(m_pForumStart,pFdef->name,FORNSZ);
          if (gforac(usr->userid(),pFdef->forum) >= RDAXES) {
               if (!m_bAll) {
                    addToMap(m_pForumStart);
               }
               m_fcnt++;
               return(MAPMORE);
          }
          return(MAPAGAIN);
     }
}

VOID
forListForumsSynthesis::emptyqueue()
{
     while (!m_fque.empty()) {
          VOID* ptr=(VOID*)(m_fque.front());   // cast is necessary
          free(ptr);
          m_fque.pop_front();
     }
}


VOID
forListForumsSynthesis::addToMap(const CHAR* name)
{
     if (m_swapdir) {
          m_fque.push_front(strdup(name));
     }
     else {
          m_fque.push_back(strdup(name));
     }
}

VOID
forListForumsSynthesis::createDNF()
{
     dnf=dnfCreateHandlerURL(ses,&forForumListMap,PPFIX);
}

VOID
forListForumsSynthesis::setForumListContextVars()
{
     const struct fordef* pFdef;

     setForumDefByName(m_pForumStart);

     setInQScanVar(m_pForumStart,usr->userid());
     flistCount.set("%d",m_iCount);
     flistOrder.set(m_bIsDescOrder ? "descending" : "ascending");

     flistLow.set(m_pForumLow);
     flistHigh.set(m_pForumHigh);
     clearGroupVars();
     flistGroup.set("0");

     if (!m_bOnlyQS) {
          if (m_usegroup) {
               pFdef=m_bIsDescOrder ? gmeNextGrpFDefP(m_gid,flistLow.get())
                    : gmePrevGrpFDefP(m_gid,flistLow.get());
          }
          else {
               pFdef=m_bIsDescOrder ? nxtdefp(flistLow.get())
                    : prvdefp(flistLow.get());
          }
          flistMoreLow.set(pFdef == NULL ? "0" : "1");
          if (m_usegroup) {
               pFdef=m_bIsDescOrder ? gmePrevGrpFDefP(m_gid,flistHigh.get())
                    : gmeNextGrpFDefP(m_gid,flistHigh.get());
               setGroupVars(m_gid,m_pGroupName);
               flistGroup.set("1");
          }
          else {
               pFdef=m_bIsDescOrder ? prvdefp(flistHigh.get())
                    : nxtdefp(flistHigh.get());
          }
          flistMoreHigh.set(pFdef == NULL ? "0" : "1");
     }
}

bool
forListForumsSynthesis::setupVars()
{
     const CHAR* name;

     if (m_bIsDescOrder) {
          name=m_fque.empty() ? NULL : m_fque.back();
     }
     else {
          name=m_fque.empty() ? NULL : m_fque.front();
     }
     if (name == NULL) {
          return(false);
     }
     stlcpy(m_pForumStart,name,FORNSZ);
     free((VOID*)name);       // cast is necessary
     if (m_bIsDescOrder) {
          m_fque.pop_back();
     }
     else {
          m_fque.pop_front();
     }
     return(true);
}

ACTHCODE
forListForumsSynthesis::proceed()  // proceed with HTML synthesis
{
     ACTHCODE retval=ACTHMORE;

     if (startProc()) {
          retval=forHTMLSynthesis::proceed();
          if (retval == ACTHMORE) {
               if (!m_init) {
                    readParams();
                    m_swapdir=(m_bAll ? false : (m_iDirection&SHOW_LT) != 0);
                    m_init=true;
               }
               do {
                    if (m_bAll) {
                         if (m_err == 0 && m_bIsMore != MAPDONE) {
                              if ((m_bIsMore=buildMap()) == MAPDONE) {
                                   *m_pForumStart='\0';
                              }
                              else if (m_bIsMore == MAPAGAIN) {
                                   continue;  // drop down to contProc()
                              }
                         }
                         if (dnf == NULL) {
                              createDNF();
                         }
                         if (m_bIsMore != MAPAGAIN || m_err != 0) {
                              if (m_err == 0) {
                                   m_bIsMore=MAPDONE;
                                   setForumListContextVars();
                                   dnfSetTemplateTvb(dnf);
                              }
                              switch (dnf->process()) {
                              case DNFROWBEGIN:
                                   if (m_bIsMore == MAPDONE
                                    && *m_pForumStart == '\0') {
                                        dnf->tableDone();
                                   }
                                   break;
                              case FORUMLIST:
                                   break;
                              case DNFROWEND:
                                   m_bIsMore=MAPMORE;
                                   break;
                              case DNFEND:
                                   retval=ACTHDONE;
                                   break;
                              default:
                                   break;
                              }
                         }
                    }
                    else if (dnf == NULL) {
                         if (buildMap() == MAPDONE) {
                              if (m_fcnt > 0) {
                                  stlcpy(m_pForumLow
                                   ,m_bIsDescOrder ? m_fque.back() : m_fque.front()
                                   ,FORNSZ);
                                  stlcpy(m_pForumHigh
                                   ,m_bIsDescOrder ? m_fque.front() : m_fque.back()
                                   ,FORNSZ);
                              }
                              else {
                                  stlcpy(m_pForumLow,m_pForumStartParam,FORNSZ);
                                  stlcpy(m_pForumHigh,m_pForumStartParam,FORNSZ);
                              }
                               createDNF();
                         }
                    }
                    else {
                         if (m_err == 0) {
                              setForumListContextVars();
                         }
                         dnfSetTemplateTvb(dnf);
                         switch (dnf->process()) {
                         case DNFROWBEGIN:
                              if (!setupVars()) {
                                   dnf->tableDone();
                              }
                              break;
                         case FORUMLIST:
                              break;
                         case DNFROWEND:
                              break;
                         case DNFEND:
                              retval=ACTHDONE;
                         }
                    }
               } while (contProc(retval));
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

//
//  forListMessagesSynthesis Member Functions
//

VOID
forListMessagesSynthesis::readParams()
{
     forHTMLSynthesis::readParams();
     m_iCount=min(theforAgent.maxlstc,m_iCount);
     if (ses->param("start")) {
          INT sz=ses->paramRoom("start")+1;
          INT tmsgid;
          m_pStart=new CHAR[sz];
          ses->param("start",m_pStart,sz);
          if ((tmsgid=atol(m_pStart)) > 0) {
               m_msgid=tmsgid;
          }
     }
     else if (fidxst(fid)) {
          m_msgid=firstnew(usr->userid(),fid);
          if (!m_spEQ) {
               m_iDirection&=~SHOW_EQ;
          }
     }
     if (!m_bIsDescOrder) {
          m_msgidHigh=m_msgid;
          m_msgidLow=m_msgid+1;
     }
     else {
          m_msgidLow=m_msgid;
          m_msgidHigh=m_msgid-1;
     }
}

SHORT
forListMessagesSynthesis::buildMap()
{

     struct message* storeptr;
     INT rc;
     INT scnt=m_mcnt;

     if (m_bOnlyQS && !m_startbuild) {
          m_morechk=CHECKMORE_HIGHINIT;
          m_bIsDescOrder=false;
          getQSPtr();
          memset(otsp,0,MAXQSR);
          qsc2ots(qscp,otsp);
          if (otsp->nforums == 0) {
               // no forums in scan
               return(MAPDONE);
          }
          setscan(mywrkarea,otsp);
          if (fid == EMLID)  {
               fid=fstscnf(usr->userid(),otsp);
          }
          if (fid == EMLID) {
               return(MAPDONE);
          }
          m_msgid=max(fstscnm(usr->userid(),otsp,fid),m_msgid);
          inormrd(mywrkarea,usr->userid(),fid,m_msgid);
          seqctx(mywrkarea,FSQSCN);
          m_startbuild=true;
     }
     if (m_mcnt == m_iCount && !m_bAll) {
          return(MAPDONE);
     }
     if (m_firstmsg) {
          if (!m_startbuild) {
               LONG msid;
               if (m_swapdir) {
                    msid=m_bAll ? LASTM : (m_msgid+1);
               }
               else {
                    msid=m_bAll ? FIRSTM : (m_msgid-1);
               }
               if (msid < 0) {
                    msid=0;
               }
               inictx(mywrkarea,usr->userid(),getSeq(),fid,msid,thrid);
               m_startbuild=true;
          }
          if (m_swapdir && !m_bOnlyQS) {
               rc=prevmsg(mywrkarea,m_mhdrptr,txtbuf);
          }
          else {
               rc=nextmsg(mywrkarea,m_mhdrptr,txtbuf);
          }
          switch (rc) {
          case GMEAGAIN:
               break;
          case GMEOK:
               if (m_iDirection&SHOW_EQ
                || (m_mhdrptr->msgid > m_msgid && !m_swapdir)
                || (m_mhdrptr->msgid < m_msgid && m_swapdir)
                || m_bAll) {
                    if (!m_bAll) {
                         storeptr=new message;
                         memmove(storeptr,m_mhdrptr,sizeof(message));
                         addToMap(storeptr);
                    }
                    m_mcnt++;
               }
               m_msgid=m_mhdrptr->msgid;
               m_firstmsg=false;
               break;
          case GMENFND:
               return(MAPDONE);
          default:
               return(rc);
          }
     }
     else {
          if (m_swapdir && !m_bOnlyQS) {
               rc=prevmsg(mywrkarea,m_mhdrptr,txtbuf);
          }
          else {
               rc=nextmsg(mywrkarea,m_mhdrptr,txtbuf);
          }
          switch (rc) {
          case GMEAGAIN:
               break;
          case GMEOK:
               m_msgid=m_mhdrptr->msgid;
               if (!m_bAll) {
                    storeptr=new message;
                    memmove(storeptr,m_mhdrptr,sizeof(message));
                    addToMap(storeptr);
               }
               m_mcnt++;
               break;
          case GMENFND:
               return(MAPDONE);
          default:
               return(rc);
          }
     }
     return((scnt == m_mcnt) ? MAPAGAIN : MAPMORE);
}

VOID
forListMessagesSynthesis::createDNF()
{
     dnf=dnfCreateHandlerURL(ses,&forListMessagesMap,PPFIX);
}

VOID
forListMessagesSynthesis::emptyqueue()
{
     while (!m_mque.empty()) {
          struct message* ptr=m_mque.front();
          delete ptr;
          m_mque.pop_front();
     }
}

VOID
forListMessagesSynthesis::setMessageListContextVars()
{
     LONG tfid=fid;

     if (mhdr.msgid != 0) {
          tfid=mhdr.forum;
     }
     if (m_msgidHigh == LASTM) {
          m_msgidHigh=m_msgid-1;
     }
     if (m_msgidLow == LASTM) {
          m_msgidLow=m_msgid+1;
     }
     setMsgIdHiTvb(m_msgidHigh);
     setMoreHiTvb((GBOOL)m_bIsMoreHigh);
     if (!m_mque.empty()) {
          struct message* tmpmsg=(m_swapdir ? m_mque.front()
                                            : m_mque.back());
          mlistLastForid.set("%hd",tmpmsg->forum);
     }

     if (m_bOnlyQS) {
          setMsgIdLowTvb(0);
          setMoreLowTvb(false);
          msgByQS.set("1");
     }
     else {
          setMsgIdLowTvb(m_msgidLow);
          setMoreLowTvb((GBOOL)m_bIsMoreLow);
          if (m_bThread && mhdr.msgid != 0) {
               msgByThread.set("1");
               mlistThreadName.set(m_pThreadTopic);
               mlistThreadID.set("%ld",thrid);
          }
          else {
               mlistThreadName.clr();
               msgByThread.set("0");
               mlistThreadID.clr();
          }
          msgByQS.set("0");
          setBaseTvb();
     }
     setCountTvb(m_iCount);
     setOrderTvb(m_bIsDescOrder ? "descending" : "ascending");
     if (m_mcnt == 0) {
          setUpNoMessages();
          if (m_bOnlyQS) {
               return;
          }
     }
     setForumDefByID(tfid);
     setInQScanVar(getfnm(tfid),usr->userid());
     setAuthorVar(usr->userid(),mhdr.from);
     if (mhdr.msgid != 0) {
          setUpMessageHeaderDefs(&mhdr);
     }
}

VOID
forListMessagesSynthesis::checkMore()
{
     INT rc;
     USHORT tfid=fid;

     switch (m_morechk) {
     case CHECKMORE_LOWINIT:
          // figure out low - first in
          if (!m_mque.empty()) {
               struct message* tmpmsg=(m_swapdir ? m_mque.back()
                                                 : m_mque.front());
               tfid=tmpmsg->forum;
          }
          inictx(mywrkarea,usr->userid(),getSeq(),tfid,m_msgidLow,thrid);
          m_morechk=CHECKMORE_LOWREAD;
          // fall through intentional
     case CHECKMORE_LOWREAD:
          rc=m_bIsDescOrder ? nextmsg(mywrkarea,m_mhdrptr,txtbuf)
                            : prevmsg(mywrkarea,m_mhdrptr,txtbuf);
          if (rc != GMEAGAIN) {
               m_bIsMoreLow=(rc == GMEOK);
               m_morechk=CHECKMORE_HIGHINIT;
          }
          break;
     case CHECKMORE_HIGHINIT:
          // figure out high - first in
          if (!m_mque.empty()) {
               struct message* tmpmsg=(m_swapdir ? m_mque.front()
                                                 : m_mque.back());
               tfid=tmpmsg->forum;
          }
          inictx(mywrkarea,usr->userid(),getSeq(),tfid,m_msgidHigh,thrid);
          m_morechk=CHECKMORE_HIGHREAD;
          // fall through intentional
     case CHECKMORE_HIGHREAD:
          rc=m_bIsDescOrder ? prevmsg(mywrkarea,m_mhdrptr,txtbuf)
                            : nextmsg(mywrkarea,m_mhdrptr,txtbuf);

          if (rc != GMEAGAIN) {
               m_bIsMoreHigh=(rc == GMEOK);
               m_morechk=CHECKMORE_DONE;
          }
          break;
     }
}


bool
forListMessagesSynthesis::setupVars()
{
     if (m_mque.empty()) {
          return(FALSE);
     }
     struct message* tmpmsg=(m_bIsDescOrder ? m_mque.back() : m_mque.front());
     memmove(&mhdr,tmpmsg,sizeof(struct message));
     delete tmpmsg;
     if (m_bIsDescOrder) {
          m_mque.pop_back();
     }
     else {
          m_mque.pop_front();
     }
     return(true);
}

VOID
forListMessagesSynthesis::addToMap(struct message* mptr)
{
     if (m_swapdir) {
          m_mque.push_front(mptr);
     }
     else {
          m_mque.push_back(mptr);
     }
}

ACTHCODE
forListMessagesSynthesis::proceed()  // proceed with HTML synthesis
{
     ACTHCODE retval=ACTHMORE;

     if (startProc()) {
          retval=forHTMLSynthesis::proceed();
          if (retval == ACTHMORE) {
               if (!m_init) {
                    readParams();
                    m_init=true;
                    m_swapdir=(m_bAll ? false : (m_iDirection&SHOW_LT != 0));
                    if ((fid == EMLID || !fidxst(fid)) && !m_bOnlyQS) {
                         usaptr=uacoff(usrnum);
                         return(errorResponse(m_err=GMENFND,m_op=OP_GENERIC
                                   ,forListMessagesErrorMap));
                    }
                    else if (!m_bOnlyQS && gforac(usr->userid(),fid) < RDAXES) {
                         usaptr=uacoff(usrnum);
                         return(errorResponse(m_err=VALACC,m_op=OP_GENERIC
                                   ,forListMessagesErrorMap));
                    }
                    if (!m_bOnlyQS) {
                         getQSPtr();
                         qscp->curfor=fid;
                         setBaseTvb();
                    }
               }
               do {
                    if (m_bAll) {
                         if (m_err == 0 && m_bIsMore != MAPDONE) {
                              mhdr.msgid=0;
                              if ((m_bIsMore=buildMap()) == MAPAGAIN) {
                                  continue;
                              }
                              if (m_bIsMore == MAPERROR) {
                                   usaptr=uacoff(usrnum);
                                   return(errorResponse(m_err=m_bIsMore,m_op=OP_GENERIC
                                             ,forListMessagesErrorMap));
                              }
                         }
                         if (dnf == NULL) {
                              createDNF();
                         }
                         else {
                              if (m_err != 0) {
                                   setErrorVars(m_err,m_op);
                              }
                              else {
                                   m_bIsMore=MAPDONE;
                                   setMessageListContextVars();
                                   dnfSetTemplateTvb(dnf);
                              }
                              switch (dnf->process()) {
                              case DNFROWBEGIN:
                                   if (m_bIsMore == MAPDONE
                                    && mhdr.msgid == 0) {
                                        dnf->tableDone();
                                   }
                                   break;
                              case MESSAGELIST:
                                   break;
                              case DNFROWEND:
                                   m_bIsMore=MAPMORE;
                                   break;
                              case DNFEND:
                                   retval=ACTHDONE;
                                   break;
                              default:
                                   break;
                              }
                         }
                    }
                    else if (dnf == NULL) {
                         INT rc=buildMap();
                         if (rc != MAPMORE && rc != MAPAGAIN) {
                              if (m_bOnlyQS) {
                                   mhdr.msgid=0;
                              }
                              if (m_bThread && mhdr.msgid != 0) {
                                   stlcpy(m_pThreadTopic,mhdr.topic,TPCSIZ);
                              }
                              switch (rc) {
                              case MAPDONE:
                                   if (m_mcnt > 0) {
                                        findHiLow();
                                   }
                                   else if (!m_bOnlyQS) {
                                        m_msgidLow=(m_swapdir ? m_msgid : LASTM);
                                        m_msgidHigh=(m_swapdir ? LASTM : m_msgid);
                                   }
                                   createDNF();
                                   break;
                              default:
                                   usaptr=uacoff(usrnum);
                                   return(errorResponse(m_err=rc,m_op=OP_GENERIC
                                             ,forListMessagesErrorMap));
                              }
                         }
                    }
                    else {
                         if (m_err == 0) {
                              if (m_morechk != CHECKMORE_DONE && fid != EMLID) {
                                   checkMore();
                                   continue;
                              }
                              setMessageListContextVars();
                         }
                         else {
                              setErrorVars(m_err,m_op);
                         }
                         dnfSetTemplateTvb(dnf);
                         switch (dnf->process()) {
                         case DNFROWBEGIN:
                              if (!setupVars()) {
                                   dnf->tableDone();
                              }
                              break;
                         case MESSAGELIST:
                              break;
                         case DNFROWEND:
                              break;
                         case DNFEND:
                              retval=ACTHDONE;
                              break;
                         }
                    }
               } while (contProc(retval));
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

VOID
forListMessagesSynthesis::findHiLow()
{
     struct message* mfptr=m_mque.front();
     struct message* mbptr=m_mque.back();
     m_msgidLow=m_bIsDescOrder ? mbptr->msgid : mfptr->msgid;
     m_msgidHigh=m_bIsDescOrder ? mfptr->msgid : mbptr->msgid;
}

//
//  forListThreadsSynthesis Member Functions
//

VOID
forListThreadsSynthesis::emptyqueue()
{
     while (!tque.empty()) {
          delete tque.front();
          tque.pop_front();
     }
}

SHORT
forListThreadsSynthesis::buildMap()
{
     INT rc;
     USHORT tmpnum;
     pTHREADSTORE pmsg;
     if (!m_startbuild) {
          inormrd(mywrkarea,usr->userid(),fid,FIRSTM);
          thrctx(mywrkarea,thrid);
          m_startbuild=true;
     }
     rc=thrinfo(mywrkarea,1,&tmpnum,&mhdr,txtbuf);
     switch (rc) {
     case GMEAGAIN:
          break;
     case GMEOK:
          if (mhdr.forum != fid) {
               return(MAPDONE);
          }
          pmsg=new THREADSTORE;
          memmove(&(pmsg->hdr),&mhdr,sizeof(struct message));
          pmsg->num=tmpnum;
          addToMap(pmsg);
          thrctx(mywrkarea,mhdr.thrid);
          m_thrcnt++;
          break;
     default:
          return(MAPDONE);
     }
     return(MAPMORE);
}

VOID
forListThreadsSynthesis::createDNF()
{
     dnf=dnfCreateHandlerURL(ses,&forListThreadsMap,PPFIX);
}

bool
forListThreadsSynthesis::setupVars()
{
     if (tque.empty()) {
          return(false);
     }
     pTHREADSTORE tmpmsg=tque.front();
     memmove(&mhdr,&(tmpmsg->hdr),sizeof(struct message));
     m_nummsg=tmpmsg->num;
     delete tmpmsg;
     tque.pop_front();
     return(true);
}

VOID
forListThreadsSynthesis::setThreadListContextVars()
{
     setForumDefByID(fid);
     setInQScanVar(getfnm(fid),usr->userid());
     if (mhdr.msgid != 0) {
          setUpMessageHeaderDefs(&mhdr);
     }
     tlistNumMsg.set("%hd",m_nummsg);
     if (m_thrcnt == 0) {
          setUpNoMessages();
     }
}

VOID
forListThreadsSynthesis::addToMap(pTHREADSTORE mptr)
{
     tque.push_front(mptr);
}

ACTHCODE
forListThreadsSynthesis::proceed()  // proceed with HTML synthesis
{
     ACTHCODE retval=ACTHMORE;

     if (startProc()) {
          retval=forHTMLSynthesis::proceed();
          if (retval == ACTHMORE) {
               if (!m_init) {
                    m_init=true;
                    readParams();
                    if (fid == EMLID || !fidxst(fid)) {
                         usaptr=uacoff(usrnum);
                         return(errorResponse(m_err=GMENFND,m_op=OP_GENERIC
                                   ,forListThreadsErrorMap));
                    }
                    if (gforac(usr->userid(),fid) < RDAXES) {
                         usaptr=uacoff(usrnum);
                         return(errorResponse(m_err=VALACC,m_op=OP_VALIDATION
                                   ,forListThreadsErrorMap));
                    }
               }
               do {
                    if (dnf == NULL) {
                         INT rc=buildMap();
                         if (rc != MAPMORE) {
                              switch (rc) {
                              case MAPDONE:
                                   createDNF();
                                   break;
                              default:
                                   usaptr=uacoff(usrnum);
                                   return(errorResponse(m_err=rc,m_op=OP_GENERIC
                                             ,forListMessagesErrorMap));
                              }
                         }
                    }
                    else {
                         if (m_err == 0) {
                              setThreadListContextVars();
                         }
                         else {
                              setErrorVars(m_err,m_op);
                         }
                         dnfSetTemplateTvb(dnf);
                         switch (dnf->process()) {
                         case DNFROWBEGIN:
                              if (!setupVars()) {
                                   dnf->tableDone();
                              }
                              break;
                         case THREADLIST:
                              break;
                         case DNFROWEND:
                              break;
                         case DNFEND:
                              retval=ACTHDONE;
                              break;
                         }
                    }
               } while (contProc(retval));
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

//
//  forViewFroumSynthesis Member Functions
//


VOID
forViewForumSynthesis::readParams()
{
     CHAR tmpbuf[FORNSZ];

     if (ses->param("forumid",tmpbuf,FORNSZ)) {
          fid=atoi(tmpbuf);
     }
     else if (ses->param("forumname",tmpbuf,FORNSZ)) {
          fid=getfid(tmpbuf);
     }
     if (ses->param("showdescription",tmpbuf,FORNSZ)) {
          if (atoi(tmpbuf) != 0) {
               m_bShowDesc=true;
          }
     }
}


VOID
forViewForumSynthesis::createDNF()
{
     dnf=dnfCreateHandlerURL(ses,&forForumViewMap,PPFIX);
}

VOID
forViewForumSynthesis::setupVars()
{
     setForumDefByID(fid);
     setInQScanVar(getfnm(fid),usr->userid());
}

ACTHCODE
forViewForumSynthesis::proceed()
{
     ACTHCODE retval=ACTHMORE;
     struct fordsk fdsk;

     if (startProc()) {
          retval=forHTMLSynthesis::proceed();
          if (retval == ACTHMORE) {
               if (!m_init) {
                    readParams();
                    m_init=true;
                    if (fid == EMLID || !fidxst(fid)) {
                         usaptr=uacoff(usrnum);
                         return(errorResponse(m_err=GMENFND,m_op=OP_GENERIC
                                   ,forForumViewErrorMap));
                    }
                    if (gforac(usr->userid(),fid) < RDAXES) {
                         usaptr=uacoff(usrnum);
                         return(errorResponse(m_err=VALACC,m_op=OP_GENERIC
                                   ,forForumViewErrorMap));
                    }
               }
               do {
                    if (dnf == NULL) {
                         createDNF();
                    }
                    else {
                         if (m_err == 0) {
                              setupVars();
                         }
                         else {
                              setErrorVars(m_err,m_op);
                         }
                         dnfSetTemplateTvb(dnf);
                         switch (dnf->process()) {
                         case FDEF_DESCRIPTION:
                              if (m_bShowDesc) {
                                   getallf(fid,&fdsk,m_desc,NULL);
                                   bout << formatURLs(formatMessage(m_desc,true,false),anchorTag);
                              }
                              break;
                         case DNFEND:
                              retval=ACTHDONE;
                              break;
                         }
                    }
               } while (contProc(retval));
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

//
//  forViewMessageSynthesis Member Functions
//

VOID
forViewMessageSynthesis::onlyExtHead(bool bHead)
{
     //LOG0("ViewMsg.onlyExtHead BEG");
     if (m_bExtOnly != bHead) {
          m_bExtOnly=bHead;
          if (m_bExtOnly) {
               m_n++;
          }
          else {
               m_n--;
          }
     }
     //LOG0("ViewMsg.onlyExtHead END");
}

VOID
forViewMessageSynthesis::readParams()
{
     CHAR* tmp;
     CHAR tmpbuf[GENBUFSIZ];

     //LOG0("ViewMsg.readParams BEG");
     forHTMLSynthesis::readParams();
     INT sz=ses->paramRoom("id")+1;
     tmp=new CHAR[sz];
     LONG tmsgid;
     ses->param("id",tmp,sz);
     if ((tmsgid=atol(tmp)) > 0) {
          m_msgid=tmsgid;
     }
     delete [] tmp;

     m_reflow=((webpref&FPREF_REFLOW) != 0L);
     if (ses->param("reflow",tmpbuf,GENBUFSIZ)) {
          if (*tmpbuf == '0' && atoi(tmpbuf) == 0) {
               m_reflow=false;
          }
          else if (atoi(tmpbuf) == 1) {
               m_reflow=true;
          }
          else if (*tmpbuf == '~') {
               m_reflow=(!m_reflow);
          }
          if (samend(tmpbuf,"set")) {
               SetFlags(&webpref,FPREF_REFLOW,m_reflow);
               setWebPrefs(usr->userid(),webpref);
          }
     }
     if (ses->param("texttype",tmpbuf,GENBUFSIZ)) {
          if (sameas(tmpbuf,"plain")) {
               m_ttype=TT_PLAIN;
          }
          else if (sameas(tmpbuf,"native")) {
               m_ttype=TT_NATIVE;
          }
     }
     if (ses->param("notext")) {
          m_bShowText=false;
     }
     //LOG0("ViewMsg.readParams END");
}

INT
forViewMessageSynthesis::readMessage()
{
     //LOG0("ViewMsg.readMessage BEG");
     if (!m_bReading) {
          if (m_bOnlyQS) {
               getQSPtr();
               memset(otsp,0,MAXQSR);
               qsc2ots(qscp,otsp);
               setscan(mywrkarea,otsp);
               m_morechk=CHECKMORE_HIGHINIT;
          }
          inictx(mywrkarea,usr->userid()
           ,(m_iDirection == 0 || m_iDirection == SHOW_EQ) ? FSQFOR : getSeq()
           ,fid,m_msgid,thrid);
          m_bReading=true;
          m_ttstate=TT_STATE_RAW;
     }
     if (m_iDirection == 0 || m_iDirection == SHOW_EQ) {
          //LOG0("ViewMsg.readMessage END readmsgf");
          return(readmsgf(mywrkarea,m_mhdrptr,txtbuf));
     }
     if (m_iDirection&SHOW_EQ) {
          if (m_iDirection&SHOW_LT) {
               m_mhdrptr->msgid++;
          }
          else {
               m_mhdrptr->msgid=m_mhdrptr->msgid > 0 ? (m_mhdrptr->msgid)-1 : 0;
          }
     }
     if (m_iDirection&SHOW_LT && !m_bOnlyQS) {
          //LOG0("ViewMsg.readMessage END prevmsgf");
          return(prevmsgf(mywrkarea,m_mhdrptr,txtbuf));
     }
     // m_iDirection&SHOW_GT
     //LOG0("ViewMsg.readMessage END nextmsgf");
     return(nextmsgf(mywrkarea,m_mhdrptr,txtbuf));
}

VOID
forViewMessageSynthesis::createDNF()
{
     //LOG0("ViewMsg.createDNF BEG");
     dnfMap* pMap=(m_bExtOnly ? &viewExtMap : &forMessageViewMap);
     dnf=dnfCreateHandlerURL(ses,pMap,PPFIX);
     //LOG0("ViewMsg.createDNF END");
}

VOID
forViewMessageSynthesis::setupVars()
{
     //LOG0("ViewMsg.setupVars BEG");
     setForumDefByID(mhdr.forum);
     setAuthorVar(usr->userid(),mhdr.from);
     setInQScanVar(getfnm(mhdr.forum),usr->userid());

     setMoreLowTvb((GBOOL)m_bIsMoreLow);
     setMoreHiTvb((GBOOL)m_bIsMoreHigh);
     if (m_bThread && mhdr.msgid != 0) {
          msgByThread.set("1");
          mlistThreadName.set(mhdr.topic);
          mlistThreadID.set("%ld",thrid);
     }
     else {
          mlistThreadName.clr();
          msgByThread.set("0");
          mlistThreadID.clr();
          if (m_bOnlyQS) {
               msgByQS.set("1");
          }
     }
     forReflowPref.set(m_reflow ? "CHECKED" : "");
     if (mhdr.msgid != 0) {
          setUpMessageHeaderDefs(&mhdr);
     }
     //LOG0("ViewMsg.setupVars END");
}

VOID
forViewMessageSynthesis::checkMore()
{
     INT rc;
     //LOG0("ViewMsg.checkMore BEG");
     switch (m_morechk) {
     case CHECKMORE_LOWINIT:
          // figure out low - first in
          inictx(mywrkarea,usr->userid(),getSeq(),mhdr.forum,mhdr.msgid,thrid);
          m_morechk=CHECKMORE_LOWREAD;
          // fall through intentional
     case CHECKMORE_LOWREAD:
          rc=prevmsg(mywrkarea,&mhdrtmp,txtbuf2);
          if (rc != GMEAGAIN) {
               m_bIsMoreLow=(rc == GMEOK);
               m_morechk=CHECKMORE_HIGHINIT;
          }
          break;
     case CHECKMORE_HIGHINIT:
          // figure out high - first in
          inictx(mywrkarea,usr->userid(),getSeq(),mhdr.forum,mhdr.msgid,thrid);
          m_morechk=CHECKMORE_HIGHREAD;
          // fall through intentional
     case CHECKMORE_HIGHREAD:
          rc=nextmsg(mywrkarea,&mhdrtmp,txtbuf2);
          if (rc != GMEAGAIN) {
               m_bIsMoreHigh=(rc == GMEOK);
               m_morechk=CHECKMORE_DONE;
          }
          break;
     }
     //LOG0("ViewMsg.checkMore END");
}

ACTHCODE
forViewMessageSynthesis::proceed()
{
     ACTHCODE retval=ACTHMORE;

     LOG0("ViewMsg.proceed BEG");
#if defined(EXTRA_LOGGING)
     if (firsttime()) {
          LOG1("ViewMsg.proceed: first time, URL='%s'",ses->url());
     }
#endif // defined(EXTRA_LOGGING)
     if (startProc()) {
          retval=forHTMLSynthesis::proceed();
          if (retval == ACTHMORE) {
               if (!m_init) {
                    LOG0("ViewMsg.proceed: readParams");
                    readParams();
                    LOG0("ViewMsg.proceed: setBaseTvb");
                    setBaseTvb();
                    LOG0("ViewMsg.proceed: setBaseTvb DONE");
                    m_init=true;
               }
               do {
                    if (dnf == NULL) {
                         INT rc;
                         if (fid == EMLID || !fidxst(fid)) {
                              usaptr=uacoff(usrnum);
                              LOG0("ViewMsg.proceed: errorResp(GMENFND)");
                              rc=errorResponse(m_err=GMENFND,m_op=OP_GENERIC
                                   ,forMessageViewErrorMap);
                              LOG0("ViewMsg.proceed END error GMENFND");
                              return((ACTHCODE)rc);
                         }
                         LOG0("ViewMsg.proceed: readMessage");
                         rc=readMessage();
                         LOG1("ViewMsg.proceed: readMessage DONE (%d)",rc);
                         if (rc == GMEOK) {
                              if (m_bExtOnly) {
                                   m_msgbuf=gmeGetAppInfo();
                                   if (m_msgbuf.length() == 0) {
                                        setmbk(gmeAHmbk);
                                        m_msgbuf=stpans(getmsg(NOXHDR));
                                        rstmbk();
                                   }
                              }
                              else {
                                   m_msgbuf=txtbuf;
                              }
                              createDNF();
                         }
                         else if (rc != GMEAGAIN) {
                              usaptr=uacoff(usrnum);
                              LOG0("ViewMsg.proceed: errorResp(rc)");
                              rc=errorResponse(m_err=rc,m_op=OP_GENERIC
                                              ,forMessageViewErrorMap);
                              LOG0("ViewMsg.proceed END error rc");
                              return((ACTHCODE)rc);
                         }
                    }
                    else {
                         if (m_err == 0) {
                              if (m_morechk != CHECKMORE_DONE) {
                                   LOG0("ViewMsg.proceed: checkMore");
                                   checkMore();
                                   LOG0("ViewMsg.proceed: checkMore DONE");
                                   continue;
                              }
                              setupVars();
                         }
                         else {
                              setErrorVars(m_err,m_op);
                         }
                         dnfSetTemplateTvb(dnf);
                         LOG0("ViewMsg.proceed: processMessage");
                         if (processMessage()) {
                              LOG0("ViewMsg.proceed: processMessage DONE true");
                              switch (dnf->process()) {
                              case BODYTEXT:
                              case HEADERS:
                                   if (m_bShowText || m_bExtOnly) {
                                        bout << m_msgbuf.c_str();
                                        while (markreadf(mywrkarea,m_mhdrptr
                                                        ,txtbuf) == GMEAGAIN) {
                                        }
                                   }
                                   break;
                              case DNFEND:
                                   retval=ACTHDONE;
                                   break;
                              }
                         }
#if defined(EXTRA_LOGGING)
                         else {
                              LOG0("ViewMsg.proceed: processMessage DONE false");
                         }
#endif // defined(EXTRA_LOGGING)
                    }
               } while (contProc(retval));
          }
     }
     usaptr=uacoff(usrnum);
     LOG0("ViewMsg.proceed END");
     return(retval);
}

bool
forViewMessageSynthesis::processMessage()
{
     //LOG0("ViewMsg.processMessage BEG");
     switch (m_ttype) {
     case TT_NATIVE:
          processNative();
          break;
     case TT_PLAIN:
          //LOG0("ViewMsg.processMessage END plain");
          return(processPlain());
     case TT_HTML:
          //LOG0("ViewMsg.processMessage END html");
          return(processHTML());
     }
     //LOG0("ViewMsg.processMessage END");
     return(true);
}

VOID
forViewMessageSynthesis::processNative()
{
     //LOG0("ViewMsg.processNative BEG");
     if (isfmtted(m_msgbuf.c_str()))  {
          m_ttypestr=fmtname(m_msgbuf.c_str());
     }
     //LOG0("ViewMsg.processNative END");
}

bool
forViewMessageSynthesis::processPlain()
{
     //LOG0("ViewMsg.processPlain BEG");

     switch (m_ttstate) {
     case TT_STATE_RAW:
          if (isfmtted(m_msgbuf.c_str())) {
               m_ttypestr=fmtname(m_msgbuf.c_str());
               cvt2asc(m_msgbuf.c_str(),txtbuf,TXTLEN);
               m_msgbuf2=txtbuf;
          }
          else {
               m_msgbuf2=m_msgbuf;
          }
          m_ttstate=TT_STATE_PLAIN;
          //LOG0("ViewMsg.processPlain END false");
          return(false);
     case TT_STATE_PLAIN:
          m_msgbuf=formatMessage(m_msgbuf2.c_str(),m_reflow,true);
          m_ttstate=TT_STATE_REFLOWED;
     }
     //LOG0("ViewMsg.processPlain END true");
     return(true);
}

bool
forViewMessageSynthesis::processHTML()
{
     //LOG0("ViewMsg.processHTML BEG");
     switch (m_ttstate) {
     case TT_STATE_RAW:
          if (isfmtted(m_msgbuf.c_str())) {
               m_ttypestr=fmtname(m_msgbuf.c_str());
               cvt2asc(m_msgbuf.c_str(),txtbuf,TXTLEN);
               m_msgbuf2=txtbuf;
          }
          else {
               m_msgbuf2=m_msgbuf;
          }
          m_ttstate=TT_STATE_PLAIN;
          //LOG0("ViewMsg.processHTML END false");
          return(false);
     case TT_STATE_PLAIN:
          m_msgbuf=formatMessage(m_msgbuf2.c_str(),m_reflow,false);
          m_ttstate=TT_STATE_REFLOWED;
          //LOG0("ViewMsg.processHTML END false 2");
          return(false);
     case TT_STATE_REFLOWED:
          m_msgbuf2=m_msgbuf;
          m_msgbuf=formatURLs(m_msgbuf2.c_str(),anchorTag);
          m_ttstate=TT_STATE_URLLINK;
          break;
     }
     //LOG0("ViewMsg.processHTML END true");
     return(true);
}

//
//  forFileSynthesis Member Functions
//

ACTHCODE
forFileSynthesis::proceed()
{
     ACTHCODE retval=ACTHMORE;

     if ((retval=forHTMLSynthesis::proceed()) != ACTHMORE) {
          return(retval);
     }
     if (dnf == NULL) {
          createDNF();
     }
     else {
          dnfSetTemplateTvb(dnf);
          if (dnf->process() == DNFEND) {
               retval=ACTHDONE;
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

VOID
forFileSynthesis::createDNF()
{
     dnf=dnfCreateHandlerURL(ses,&forFileMap,PPFIX);
     if (dnf != NULL) {
          CHAR fileExtension[GCMAXEXT+1];
          const CHAR* fileName=dnf->getMap().getFile();
          fileparts(GCPART_EXTN,fileName,fileExtension,GCMAXEXT+1);
          ses->contypeFext(fileExtension);
     }
}

//
//  forUpdateQSSynthesis Member Functions
//

ACTHCODE
forUpdateQSSynthesis::proceed()
{
     ACTHCODE retval;

     if ((retval=forHTMLSynthesis::proceed()) != ACTHMORE) {
          return(retval);
     }
     if (startProc()) {
          if (!m_init) {
               buildQS();
               saveQS();
               m_init=true;
          }
          if (dnf == NULL) {
               if ((retval=NeedOnSuccessDNF()) != ACTHDONE)  {
                    createDNF();
               }
          }
          else {
               setBaseTvb();
               dnfSetTemplateTvb(dnf);
               do {
                    if (dnf->process() == DNFEND) {
                         retval=ACTHDONE;
                    }
               } while (contProc(retval));
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

VOID
forUpdateQSSynthesis::createDNF()
{
     if (m_onsuccessFile != NULL) {
          dnf=dnfCreateHandler(&bout,&forUpdateQSMap,m_onsuccessFile);
     }
     else {
          dnf=dnfCreateHandlerURL(ses,&forUpdateQSMap,PPFIX);
     }

}

VOID
forUpdateQSSynthesis::buildQS()
{
     INT sz;
     if (ses->param("keywords")) {
          if (m_keywords != NULL)  {
               delete [] m_keywords;
          }
          m_keywords=new CHAR[(sz=ses->paramRoom("keywords")+1)];
          ses->param("keywords",m_keywords,sz);
     }
     if (ses->param("msgid")) {
          CHAR* tmp=new CHAR[(sz=ses->paramRoom("msgid")+1)];
          ses->param("msgid",tmp,sz);
          if (alldgs(tmp)) {
               m_savMsgId=atoi(tmp);
          }
          else {
               m_savMsgId=LASTM;
          }
          delete [] tmp;
     }
     m_pOnlyNew=QSSet("onlynew");
     m_pOnlyAtt=QSSet("onlyattach");
     m_pOnlyTo=QSSet("onlyto");
     m_pOnlyFrom=QSSet("onlyfrom");
}

VOID
forUpdateQSSynthesis::saveQS()
{
     if (m_keywords != NULL) {
           stlcpy(qscp->kwds,m_keywords,MAXSKWD);
     }
     if (m_savMsgId != LASTM) {
           qscp->stmsg=m_savMsgId;
     }
     if (m_pOnlyNew != PREFNOSET) {
          SetFlags(&qscp->flags,NEWMSG,(m_pOnlyNew == PREFSETON));
     }
     if (m_pOnlyAtt != PREFNOSET) {
          SetFlags(&qscp->flags,WATONL,(m_pOnlyAtt == PREFSETON));
     }
     if (m_pOnlyTo != PREFNOSET) {
          SetFlags(&qscp->flags,TOMEONL,(m_pOnlyTo == PREFSETON));
     }
     if (m_pOnlyFrom != PREFNOSET) {
          SetFlags(&qscp->flags,FRMEONL,(m_pOnlyFrom == PREFSETON));
     }
}

SHORT
forUpdateQSSynthesis::QSSet(
const CHAR* name)
{
     SHORT retval=PREFNOSET;
     bool ovr=false;

     if (ses->param("override")) {
          INT sz;
          CHAR* tmpstg=new CHAR[(sz=ses->paramRoom("override")+1)];
          ses->param("override",tmpstg,sz);
          if (findstg((CHAR*)name,tmpstg) > 0)  {
               ovr=true;
          }
          delete [] tmpstg;
     }
     if (ses->param(name)) {
          retval=PREFSETON;
     }
     else if (ovr) {
          retval=PREFSETOFF;
     }
     return(retval);
}

//
//  forPrefSynthesis Member Functions
//

ACTHCODE
forPrefSynthesis::proceed()
{
     ACTHCODE retval=ACTHMORE;

     if ((retval=forHTMLSynthesis::proceed()) != ACTHMORE) {
          return(retval);
     }
     if (startProc()) {
          if (!m_init) {
               buildPrefs();
               savePrefs();
               m_init=true;
          }
          if (dnf == NULL) {
               if ((retval=NeedOnSuccessDNF()) != ACTHDONE)  {
                    createDNF();
               }
          }
          else {
               setBaseTvb();
               dnfSetTemplateTvb(dnf);
               do {
                    if (dnf->process() == DNFEND) {
                         retval=ACTHDONE;
                    }
               } while (contProc(retval));
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}



VOID
forPrefSynthesis::createDNF()
{
     if (m_onsuccessFile != NULL) {
          dnf=dnfCreateHandler(&bout,&forConfigPrefMap,m_onsuccessFile);
     }
     else {
          dnf=dnfCreateHandlerURL(ses,&forConfigPrefMap,PPFIX);
     }

}

VOID
forPrefSynthesis::buildPrefs()
{
     if (ses->param("type")) {
          INT sz;
          CHAR* tmpstg=new CHAR[(sz=ses->paramRoom("type")+1)];
          ses->param("type",tmpstg,sz);
          if (sameas(tmpstg,"html")) {
               m_pType=INT_HTML;
          }
          else if (sameas(tmpstg,"java")) {
               m_pType=INT_JAVA;
          }
          delete [] tmpstg;
     }
     m_pFrames=prefSet("useframes");
     m_pQuote=prefSet("quote");
     m_pForumTo=prefSet("forumto");
     m_pReflow=prefSet("reflow");
     if (ses->param("curforumid")) {
          INT sz;
          INT tfid;
          CHAR* tmpstg=new CHAR[(sz=ses->paramRoom("curforumid")+1)];
          ses->param("curforumid",tmpstg,sz);
          tfid=atol(tmpstg);
          if (fidxst(tfid)) {
               m_curforid=tfid;
          }
     }
     if (ses->param("curforumname") && m_curforid == EMLID) {
          INT sz;
          CHAR* tmpstg=new CHAR[(sz=ses->paramRoom("curforumname")+1)];
          ses->param("curforumname",tmpstg,sz);
          if (fnmxst(tmpstg)) {
               m_curforid=getfid(tmpstg);
          }
     }
}

VOID
forPrefSynthesis::savePrefs()
{
     if (m_pType != PREFNOSET) {
           webpref&=~FPREF_TYPE_JAVA;
           webpref&=~FPREF_TYPE_HTML;
           if (m_pType == INT_HTML) {
               webpref|=FPREF_TYPE_HTML;
           }
           else {
               webpref|=FPREF_TYPE_JAVA;
           }
     }
     if (m_pFrames != PREFNOSET) {
          SetFlags(&webpref,FPREF_USEFRAMES,(m_pFrames == PREFSETON));
     }
     if (m_pQuote != PREFNOSET) {
          SetFlags(&webpref,FPREF_QUOTE,(m_pQuote == PREFSETON));
     }
     if (m_pReflow != PREFNOSET) {
          SetFlags(&webpref,FPREF_REFLOW,(m_pReflow == PREFSETON));
     }
     qscp=gmeUserGetQS(m_gmehandle);
     ASSERT (qscp != NULL);
     if (m_pForumTo != PREFNOSET) {
          SetFlags(&qscp->flags,FORUM2,(m_pForumTo == PREFSETON));
     }
     if (m_curforid != EMLID) {
          qscp->curfor=m_curforid;
     }
     setWebPrefs(usr->userid(),webpref);
}

SHORT
forPrefSynthesis::prefSet(
const CHAR* name)
{
     SHORT retval=PREFNOSET;
     bool ovr=false;

     if (ses->param("override")) {
          INT sz;
          CHAR* tmpstg=new CHAR[(sz=ses->paramRoom("override")+1)];
          ses->param("override",tmpstg,sz);
          if (findstg((CHAR*)name,tmpstg) > 0)  {
               ovr=true;
          }
          delete [] tmpstg;
     }
     if (ses->param(name)) {
          retval=PREFSETON;
     }
     else if (ovr) {
          retval=PREFSETOFF;
     }
     return(retval);
}

//
//  forQSGetSettingsSynthesis Member Functions
//


ACTHCODE
forQSGetSettingsSynthesis::proceed()
{
     ACTHCODE retval=ACTHMORE;

     if ((retval=forHTMLSynthesis::proceed()) != ACTHMORE) {
          return(retval);
     }
     if (dnf == NULL) {
          createDNF();
     }
     else {
          if (startProc()) {
               getQSPtr();
               setQSVars();
               dnfSetTemplateTvb(dnf);
               do {
                    if (dnf->process() == DNFEND) {
                         retval=ACTHDONE;
                    }
               } while (contProc(retval));
          }
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

VOID
forQSGetSettingsSynthesis::createDNF()
{
     dnf=dnfCreateHandlerURL(ses,&forQSGetSettingsMap,PPFIX);
}

VOID
forQSGetSettingsSynthesis::setQSVars()
{
     clearQSVars();
     if (qscp->flags&WATONL) {
          qsOnlyAtt.set("CHECKED");
     }
     if (qscp->flags&NEWMSG) {
          qsOnlyNew.set("CHECKED");
     }
     if (qscp->flags&TOMEONL) {
          qsOnlyToMe.set("CHECKED");
     }
     if (qscp->flags&FRMEONL) {
          qsOnlyFromMe.set("CHECKED");
     }
     qsKeywords.set(qscp->kwds);
     qsMsgid.set("%ld",qscp->stmsg);
}

VOID
forQSGetSettingsSynthesis::clearQSVars()
{
     qsOnlyAtt.clr();
     qsOnlyNew.clr();
     qsOnlyToMe.clr();
     qsOnlyFromMe.clr();
     qsKeywords.clr();
     qsMsgid.clr();
}

//
//  forQSChangeForumSynthesis Member Functions
//

ACTHCODE
forQSChangeForumSynthesis::proceed()
{
     ACTHCODE retval=ACTHMORE;

     if ((retval=forHTMLSynthesis::proceed()) != ACTHMORE) {
          return(retval);
     }
     if (!m_init) {
          readQSParam();
          getQSPtr();
          updateQSForum();
     }
     if (startProc()) {
          do {
               if (dnf == NULL) {
                    if (updateQSForum()) {
                         if (m_err == 0) {
                              if ((retval=NeedOnSuccessDNF()) != ACTHDONE)  {
                                   createDNF();
                              }
                         }
                         else {
                              usaptr=uacoff(usrnum);
                              return(errorResponse(m_err,m_op
                                   ,forQSChangeForumErrorMap));
                         }
                    }
               }
               else {
                    if (m_err != 0) {
                         setErrorVars(m_err,m_op);
                    }
                    dnfSetTemplateTvb(dnf);
                    if (dnf->process() == DNFEND) {
                         retval=ACTHDONE;
                    }
               }
          } while (contProc(retval));
     }
     usaptr=uacoff(usrnum);
     return(retval);
}

VOID
forQSChangeForumSynthesis::readQSParam()
{
     CHAR tmp[GENBUFSIZ];

     forHTMLSynthesis::readParams();
     if (ses->param("operation",tmp,GENBUFSIZ)) {
          if (sameas(tmp,"0")) {
               qsop=QSOPDEL;
          }
     }
     if (m_bAll) {
          *m_pForumStart='\0';
     }
}

bool
forQSChangeForumSynthesis::updateQSForum()
{
     bool retval=false;
     if (!m_bAll) {
          if (qsop == QSOPADD) {
               if (!sfinqs(qscp,fid,true)) {
                    m_err=GME2MFR;
                    m_op=OP_GENERIC;
               }
          }
          else {
               delqs(qscp,fid);
          }
          retval=true;
     }
     else {
          const struct fordef* fdef=nxtdefp(m_pForumStart);
          if (fdef == NULL) {
               retval=true;
          }
          else if (qsop == QSOPADD) {
               if (!sfinqs(qscp,fdef->forum,true)) {
                    m_err=GME2MFR;
                    m_op=OP_GENERIC;
                    retval=true;
               }
               stlcpy(m_pForumStart,fdef->name,FORNSZ);
          }
          else {
               delqs(qscp,fdef->forum);
               stlcpy(m_pForumStart,fdef->name,FORNSZ);
               retval=false;
          }
     }
     return(retval);
}

VOID
forQSChangeForumSynthesis::createDNF()
{
     dnf=dnfCreateHandlerURL(ses,&forQSChangeForumMap,PPFIX);
}

/* Group list functions */

synGrpList::synGrpList(
acthSession *ses) :
     forHTMLSynthesis(ses,2),
     m_psout(NULL),
     m_sp(0),
     m_state(DNFSTART),
     m_firstGroup(true)
{
     ::memset(m_grpstk,0,sizeof(m_grpstk));
     *m_grpPath='\0';
     *m_grpParent='\0';
}

ACTHCODE
synGrpList::proceed()
{
     ACTHCODE rc=ACTHMORE;

     if ((rc=forHTMLSynthesis::proceed()) != ACTHMORE) {
          return(rc);
     }
     switch (m_state) {
     case DNFSTART:

          // initialize expanded group path
          if (ses->param("grouppath",m_grpExp,MAXGRPSTR-1)) {

               // make sure first char is '/'
               if (m_grpExp[0] != FORIDC) {
                    // the next line is why above is MAXGRPSTR-1
                    ::strmove(m_grpExp+1,m_grpExp);
                    m_grpExp[0]=FORIDC;
               }

               // make sure last char is NOT '/'
               ASSERT(::strlen(m_grpExp) > 0);
               CHAR * cp=m_grpExp+::strlen(m_grpExp)-1;
               if (*cp == FORIDC) {
                    *cp='\0';
               }
          }
          else {    // path not specified -> root group
               ::strcpy(m_grpExp,"/");
          }

          // initialize stack with root group
          ::stlcpy(m_grpstk[0].name,::rootGroupName,FORNSZ);
          if (::gmeNextGrpFDefP(ROOTGRP,"") != NULL) {
               m_grpstk[0].flags|=GL_HASFOR;
          }

          // prepare for output
          createDNF();
          setGrpPaths();
          formTree();
          clrGrpTextVars();
          m_state=OUTDNF;
          m_init=true;
          break;
     case OUTDNF:
          setGrpTextVars();
          switch (dnf->process()) {
          case DNFROWBEGIN:
               if (m_firstGroup) { // first row is root group
                    m_firstGroup=false;
               }
               else if (nextGroup()) {
                    setGrpPaths();
                    struct forgrp const * grpptr
                         =::gmeGetGrpP(m_grpstk[m_sp].id);
                    ::stlcpy(m_grpTopic,grpptr->topic,TPCSIZ);
                    formTree();
               }
               else {
                    dnf->tableDone();
               }
               break;
          case GRPTREE:
               m_treeDone=0;
               m_state=OUTTREE;
               break;
          case DNFEND:
               rc=ACTHDONE;
               break;
          }
          clrGrpTextVars();
          break;
     case OUTTREE:
          bout.flush();
          m_treeDone+=ses->sndrsp(m_psout->str()+m_treeDone,
                                  m_psout->pcount()-m_treeDone);
          if (m_psout->pcount() <= m_treeDone) {
               freeOut();
               m_state=OUTDNF;
          }
          break;
     }
     usaptr=uacoff(usrnum);
     return(rc);
}

VOID
synGrpList::createDNF()
{
     dnf=dnfCreateHandlerURL(ses,&grpListMap,PPFIX);
}


bool
synGrpList::nextGroup()            // find the next group
{
     // if this branch is expanded, trace down a level
     if (isExpanded() && !pushGroup()) {
          return(false);
     }

     // find next group to output
     while (true) {
          ASSERT(m_sp > 0);

          // get next sibling
          USHORT parid=m_grpstk[m_sp-1].id;
          struct forgrp const * grpptr=::gmeNextGrpP(parid,m_grpstk[m_sp].name);

          // if no more siblings, go back up one level
          if (grpptr == NULL) {
               if (parid == ROOTGRP) {
                    return(false);
               }
               popGroup();
          }
          // else if user has key we've found a group,
          // so update the current stack info and exit
          else if (usr->hasKey(grpptr->key)) {
               m_grpstk[m_sp].id=grpptr->grpid;
               ::stlcpy(m_grpstk[m_sp].name,grpptr->name,FORNSZ);
               m_grpstk[m_sp].flags=0;
               if (::gmeNextGrpP(parid,m_grpstk[m_sp].name) != NULL) {
                    m_grpstk[m_sp].flags|=GL_HASSIB;
               }
               if (::gmeNextGrpP(m_grpstk[m_sp].id,"") != NULL) {
                    m_grpstk[m_sp].flags|=GL_HASCHL;
               }
               if (::gmeNextGrpFDefP(m_grpstk[m_sp].id,"") != NULL) {
                    m_grpstk[m_sp].flags|=GL_HASFOR;
               }
               return(true);
          }
          else {
               // else if user doesn't have the key, keep searching
               ::stlcpy(m_grpstk[m_sp].name,grpptr->name,FORNSZ);
          }
     }
}

bool
synGrpList::pushGroup()            // push group context onto stack
{
     if (m_sp >= GRPSTKSZ) {
          CHAR * tmpBuf=new CHAR[FORNSZ*m_sp];
          groupPath(m_sp,tmpBuf,FORNSZ*m_sp);
          tmpBuf[min(AUDDETSIZ,FORNSZ*m_sp)-1]='\0';
          ::shocst("FORUM GROUPS NESTED TOO DEEPLY",tmpBuf);
          delete[] tmpBuf;
          return(false);
     }
     ++m_sp;
     return(true);
}

VOID
synGrpList::popGroup()             // pop group context from stack
{
     ASSERT(m_sp > 0);
     ::memset(&m_grpstk[m_sp],0,sizeof(m_grpstk[0]));
     --m_sp;
}

VOID
synGrpList::setGrpPaths()          // set current path and parent
{
     if (m_sp > 0) {
          groupPath(m_sp-1,m_grpParent,MAXGRPSTR);
     }
     else {
          *m_grpParent='\0';
     }
     groupPath(m_sp,m_grpPath,MAXGRPSTR);
}

VOID
synGrpList::setGrpTextVars()       // set all group-related text vars
{
     const struct forgrp* fgrp;
     ::txvGrpPath.set(m_grpPath);
     ::txvGrpPathURL.set(m_grpPath);
     ::txvGrpParent.set(m_grpParent);
     ::txvGrpParentURL.set(m_grpParent);
     ::txvGrpName.set(m_grpstk[m_sp].name);
     ::txvGrpNameURL.set(m_grpstk[m_sp].name);
     ::txvGrpTopic.set(m_grpTopic);
     if ((fgrp=gmeGetGrpP(gmeGetGrpID(m_grpPath+1))) != NULL) {
          ::grpNumForums.set("%d",fgrp->nforums);
     }
}

VOID
synGrpList::clrGrpTextVars()       // clear all group-related text vars
{
     ::txvGrpName.clr();
     ::txvGrpNameURL.clr();
     ::txvGrpPath.clr();
     ::txvGrpPathURL.clr();
     ::txvGrpTopic.clr();
     ::grpNumForums.clr();
}

CHAR *                             //   returns pointer to destination
synGrpList::groupPath(             // generate current group path
INT sp,                            //   group stack pointer
CHAR * dst,                        //   buffer to form in
size_t dstSiz)                     //   size of destination buffer
{
     ::stlcpy(dst,"/",dstSiz);
     for (INT i=1 ; i <= sp ; ++i) {
          ::catGroup(dst,m_grpstk[i].name,dstSiz);
     }
     return(dst);
}

VOID
synGrpList::formTree()             // form HTML code for tree
{
     ASSERT(m_psout == NULL);
     setGrpTextVars();             // make sure we're up-to-date
     m_psout=new ostrstream;
     if (m_sp != 0) {

          // output parent tree components
          for (INT i=1 ; i < m_sp ; ++i) {
               m_grpstk[i].flags&GL_HASSIB ? pTreePix->trunk(*m_psout)
                                           : pTreePix->blank(*m_psout);
          }

          // current group
          if (m_grpstk[m_sp].flags&GL_HASCHL) {
               if (isExpanded()) {
                    m_grpstk[m_sp].flags&GL_HASSIB ? pTreePix->branchExp(*m_psout)
                                                   : pTreePix->leafExp(*m_psout);
               }
               else {
                    m_grpstk[m_sp].flags&GL_HASSIB ? pTreePix->branchLaden(*m_psout)
                                                   : pTreePix->leafLaden(*m_psout);
               }
          }
          else {
               m_grpstk[m_sp].flags&GL_HASSIB ? pTreePix->branchBare(*m_psout)
                                              : pTreePix->leafBare(*m_psout);
          }
     }

     // output folder
     m_grpstk[m_sp].flags&GL_HASFOR ? pTreePix->withfor(*m_psout)
                                    : pTreePix->empty(*m_psout);
}

bool
synGrpList::isExpanded()           // is the current group expanded
{
     // the root group is always expanded
     if (m_sp == 0) {
          return(true);
     }

     // "/a/b" is on the way to "/a/b/c", but not to "/a/bc"
     // thus sameto("/a/b","/a/b/c") and "/a/b/c"[strlen("/a/b")] == '/'
     size_t l=::strlen(m_grpPath);
     return(::sameto(m_grpPath,m_grpExp)
         && (m_grpExp[l] == FORIDC || m_grpExp[l] == '\0'));
}

VOID
synGrpList::freeOut()              // free output stream
{
     ASSERT(m_psout != NULL);
     m_psout->rdbuf()->freeze(0);
     delete m_psout;
     m_psout=NULL;
}

synGrpList::~synGrpList()
{
     if (m_psout != NULL) {
          freeOut();
     }
}

/* Tree picture management functions */

treePictures::treePictures()
{
     ::setmbk(gmeAHmbk);
     m_empty=::stgopt(GRPMT);
     m_withfor=::stgopt(GRPFOR);
     m_blank=::stgopt(GRPBLANK);
     m_trunk=::stgopt(GRPTRUNK);
     m_branchBare=::stgopt(GRPBRNCH);
     m_branchLaden=::stgopt(GRPBRNCL);
     m_branchExp=::stgopt(GRPBRNCX);
     m_leafBare=::stgopt(GRPLEAF);
     m_leafLaden=::stgopt(GRPLEAFL);
     m_leafExp=::stgopt(GRPLEAFX);
     m_altEmpty=::stgopt(GRPMTALT);
     m_altFor=::stgopt(GRPFOALT);
     m_altBlank=::stgopt(GRPBKALT);
     m_altTrunk=::stgopt(GRPTRALT);
     m_altBranchBare=::stgopt(GRPBRALT);
     m_altBranchLaden=::stgopt(GRPBLALT);
     m_altBranchExp=::stgopt(GRPBXALT);
     m_altLeafBare=::stgopt(GRPLEALT);
     m_altLeafLaden=::stgopt(GRPLLALT);
     m_altLeafExp=::stgopt(GRPLXALT);
     ::rstmbk();
}

VOID
treePictures::empty(               // empty group picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,FOLDER,m_empty,m_altEmpty);
}

VOID
treePictures::withfor(             // group with forums picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,FOLDER,m_withfor,m_altFor);
}

VOID
treePictures::blank(               // tree blank space picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,TREEBAR,m_blank,m_altBlank);
}

VOID
treePictures::trunk(               // tree trunk picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,TREEBAR,m_trunk,m_altTrunk);
}

VOID
treePictures::branchBare(          // tree branch bare picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,TREEBAR,m_branchBare,m_altBranchBare);
}

VOID
treePictures::branchLaden(         // tree branch laden picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,TREELAD,m_branchLaden,m_altBranchLaden);
}

VOID
treePictures::branchExp(           // tree branch expanded picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,TREEEXP,m_branchExp,m_altBranchExp);
}

VOID
treePictures::leafBare(            // tree leaf bare picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,TREEBAR,m_leafBare,m_altLeafBare);
}

VOID
treePictures::leafLaden(           // tree leaf laden picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,TREELAD,m_leafLaden,m_altLeafLaden);
}

VOID
treePictures::leafExp(             // tree leaf expanded picture
ostream & ost)                     //   output stream to write to
{
     outputPic(ost,TREEEXP,m_leafExp,m_altLeafExp);
}

VOID
treePictures::outputPic(           // output HTML code for a picture
ostream & ost,                     //   output stream to write to
INT msgNum,                        //   option number to use for format
CHAR const * picFile,              //   file name of picture file
CHAR const * altStr)               //   alternate string
{
     // output into prfbuf
     ::setmbk(gmeAHmbk);
     ::prfmsg(msgNum,picFile,altStr);
     ::rstmbk();

     // create input stream from prfbuf
     istrstream ist(::stpans(::prfbuf));

     // copy to output, translating text vars in the process
     tvbTranslation tt(ist,ost);
     tt.proceed();
     ::clrprf();
}

treePictures::~treePictures()
{
     ::free(m_empty);
     ::free(m_withfor);
     ::free(m_blank);
     ::free(m_trunk);
     ::free(m_branchBare);
     ::free(m_branchLaden);
     ::free(m_branchExp);
     ::free(m_leafBare);
     ::free(m_leafLaden);
     ::free(m_leafExp);
     ::free(m_altEmpty);
     ::free(m_altFor);
     ::free(m_altBlank);
     ::free(m_altTrunk);
     ::free(m_altBranchBare);
     ::free(m_altBranchLaden);
     ::free(m_altBranchExp);
     ::free(m_altLeafBare);
     ::free(m_altLeafLaden);
     ::free(m_altLeafExp);
}



VOID
forAgent::getSettings()            // retrieve the ActiveHTML forum access key
{
     setmbk(gmeAHmbk);

     ::init__galme();
     usehtml=ynopt(FUSEHTML);
     usejava=ynopt(FUSEJAVA);
     definterface=tokopt(FPRFTYP,"HTML","JAVA",NULL);
     if (definterface == 0) {
          catastro("ACTIVE HTML FORUMS","Invalid value in msgopt FPRFTYP");
     }
     defframes=ynopt(FPRFFRM);
     forahkey=stgopt(HFORKEY);
     dftlstc=numopt(DFTLSTC,1,32767);
     dftflstc=numopt(DFTFLSTC,1,32767);
     maxlstc=numopt(MAXLSTC,1,32767);
     maxflstc=numopt(MAXFLSTC,1,32767);
     stlcpy(rootGroupName,rawmsg(ROOTNAME),FORNSZ);
     timeSlice=(USHORT)numopt(TIMSLC,1,1000);
     pTreePix=new treePictures;
     setmbk(efmbAH);
     iMaxCC=numopt(CSMAXCC,0,32767);
}

acthSynthesis *                    // new session-specific structure
forAgent::newSynthesis(            // instantiate Synthesis class
acthSession *ses)                  // passed to acthSynthesis's constructor
{
     INT narg=ses->urlargc();

     LOG1("newSynthesis: %s",ses->url());
     if (theforAgent.usehtml) {
          if (narg > 1 && sameas(ses->urlargv(0),"list")) {
               if (sameas(ses->urlargv(1),"forums")) {
                    return(new forListForumsSynthesis(ses));
               }
               else if (sameas(ses->urlargv(1),"messages")) {
                    return(new forListMessagesSynthesis(ses));
               }
               else if (sameas(ses->urlargv(1),"threads")) {
                    return(new forListThreadsSynthesis(ses));
               }
               else if (sameas(ses->urlargv(1),"groups")) {
                    return(new synGrpList(ses));
               }
          }
          else if (narg > 1 && sameas(ses->urlargv(0),"view")) {
               if (sameas(ses->urlargv(1),"forum")) {
                    return(new forViewForumSynthesis(ses));
               }
               else if (sameas(ses->urlargv(1),"message")) {
                    forViewMessageSynthesis* ptr=new forViewMessageSynthesis(ses);
                    if (narg > 2 && sameas(ses->urlargv(2),"ext")) {
                         ptr->onlyExtHead(true);
                    }
                    return(ptr);
               }
          }
          else if (narg > 0 && sameas(ses->urlargv(0),"prefs")) {
               return(new forPrefSynthesis(ses));
          }
          else if (narg > 1 && sameas(ses->urlargv(0),"file")) {
               return(new forFileSynthesis(ses));
          }
          else if (narg > 1 && sameas(ses->urlargv(0),"quickscan")) {
               if (sameas(ses->urlargv(1),"getsettings")) {
                    return(new forQSGetSettingsSynthesis(ses));
               }
               else if (sameas(ses->urlargv(1),"changeforum")) {
                    return(new forQSChangeForumSynthesis(ses));
               }
               else if (sameas(ses->urlargv(1),"updatesettings")) {
                    return(new forUpdateQSSynthesis(ses));
               }
               else if (sameas(ses->urlargv(1),"qsforums")) {
                    return(new qsforumsSynthesis(ses));
               }
          }
          else if (narg > 0 && sameas(ses->urlargv(0),"write")) {
               return(new writeSynthesis(ses));
          }
          else if (narg > 0 && sameas(ses->urlargv(0),"send")) {
               return(new sendSynthesis(ses));
          }
          else if (narg > 0 && sameas(ses->urlargv(0),"forward")) {
               return(new forwardSynthesis(ses));
          }
          else if (narg > 0 && sameas(ses->urlargv(0),"delete")) {
               return(new deleteSynthesis(ses));
          }
          else if (narg > 0 && sameas(ses->urlargv(0),"exemptit")) {
               return(new exemptSynthesis(ses));
          }
          else if (narg > 0 && sameas(ses->urlargv(0),"approveit")) {
               return(new approveSynthesis(ses));
          }
          else if (narg > 0 && sameas(ses->urlargv(0),"att")) {
               return(new downloadSynthesis(ses));
          }
          else if (theforAgent.usejava && (narg == 1
           && (sameto("forums.",ses->urlargv(0)) || sameto("ul.",ses->urlargv(0))
             || sameto("pop.",ses->urlargv(0))))) {
               return(new forSynthesis(ses));
          }
          else if (narg == 0 || (narg == 1 && strchr(ses->urlargv(0),'.') != 0)) {
               return(new forInitialSynthesis(ses));
          }
     }
     if (theforAgent.usejava) {
          return(new forSynthesis(ses));
     }
     return(new errSynthesis(ses));
}

ACTHCODE
forSynthesis::proceed()            // proceed with HTML synthesis
{
     ACTHCODE retval;

     if ((usr=ses->getUser()) == NULL)
     {
          return(ACTHNOANON);
     }
     if (usr->hasKey(theforAgent.forahkey))
     {
         bool loaded=false;
         usaptr=GetAccPtr(usr->userid());
         if (m_gmehandle == NULL) {
              m_gmehandle=::GetGmeUserHandle(usr->userid());
         }
         //LOG2("FOR.forSyn.proceed GetUH: this=%08lX h=%lu",this,m_gmehandle);
         setUserNum4AH(usr->userid());
         if (sameas(ses->method(),"GET"))
         {
              retval=proceedGet();
         }
         else if (sameas(ses->method(),"POST"))
         {
              retval=proceedPost();
         }
         else
         {
              ses->setStatus("501 Not implemented");
                retval=ACTHDONE;
         }
          //LOG2("FOR.forSyn.proceed closing: this=%08lX h=%lu",this,m_gmehandle);
         gmeUserClose(m_gmehandle);
         m_gmehandle=NULL;
         resetUserNum4AH(usr->userid());
     }
     else
     {
          retval=ACTHDONE;
     }
     if ( retval == ACTHDONE && !m_slExempt )
     {
          streambuf *stbuf = bout.rdbuf();

          if ( stbuf->out_waiting() <= 0 )
          {
               bout << "/";
          }
     }
     return(retval);
}

VOID
forSynthesis::abort()              // handle session abort
{
     if (m_doDl) {
          gdlabt(usr->userid(),tagbuf);
          m_doDl=false;
     }
}

ACTHCODE
forSynthesis::proceedGet()         // proceed synthesizing response to GET
{
     ACTHCODE retval=ACTHMORE;
     INT narg;
     const struct fordef *finfptr;
     struct qscfg *qscptr=gmeUserGetQS(m_gmehandle);

     if ((narg=ses->urlargc()) == 1 && strchr(ses->urlargv(0),'.') != NULL)
     {
          if (ses->getUser() == NULL)
          {
               retval=ACTHNOANON;
          }
          else if (sameto( "forums.", ses->urlargv( 0 ) ))
          {
               if (dnf == NULL)
               {
                    dnf = new dnfHandler(mainMap,bout);
               }

               switch( dnf->process() )
               {
                    case ARCHIVETYPE:
                         {
                              CHAR      agentStr[ 1024 ];
                              bool     useJar = false;

                              if (ses->header( "User-Agent", agentStr, sizeof( agentStr ) ))
                              {
                                   BrowserType type = getClientType( agentStr );
                                   int  ver = getClientVer( agentStr );

                                   if ((type == BROWSER_NETSCAPE) && (ver >= 4))
                                   {
                                        useJar = true;
                                   }
                              }

                              if (useJar)
                              {
                                   bout << "jar";
                              }
                              else
                              {
                                   bout << "zip";
                              }
                         }
                         break;

                    case DNFEND:
                         retval = ACTHDONE;
                         break;
               }
          }
          else if (sameto( "ul", ses->urlargv( 0 ) ))
          {
               if (dnf == NULL)
               {
                    dnf = new dnfHandler(ulMap,bout);
               }

               switch( dnf->process() )
               {
                    case TMPID:
                         getTmpId();
                         bout << m_tmpid;
                         break;

                    case DNFEND:
                         retval = ACTHDONE;
                         break;
               }
          }
          else if (sameto( "pop", ses->urlargv( 0 ) ))
          {
               if (dnf == NULL)
               {
                    dnf = new dnfHandler(popMap,bout);
               }

               switch( dnf->process() )
               {
                    case TMPID:
                         getTmpId();
                         bout << m_tmpid;
                         break;

                    case DNFEND:
                         retval = ACTHDONE;
                         break;
               }
          }
          else
          {
               m_slExempt = true;
               ostrstream ost;
               ost << PPFIX << "/" << ses->urlargv(0) << ends;
               ses->sndfile(ost.str());
               ost.rdbuf()->freeze(0);
               retval=ACTHDONE;
          }
     }
     else if (narg == 1) {
          if (sameas(ses->urlargv(0),"ulabort"))
          {
               CHAR tmpid[20];

               ses->param("tmpid",tmpid,sizeof(tmpid));
               messageAborted( atol(tmpid) );
               retval = ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"uinf"))
          {
               stlcpy(usaptr->userid,usr->userid(),UIDSIZ);

               bout << usr->userid() << "\t";
               if (!fidxst(qscptr->curfor)) {
                    qscptr->curfor=dftfor;
               }
               finfptr=getdefp(qscptr->curfor);
               ASSERT(finfptr != NULL);
               bout << qscptr->curfor << "\t";
               bout << finfptr->name << endl;

               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"xrf"))
          {
               CHAR tempid[UIDSIZ];
               CHAR *uidp;

               ses->param("partid",tempid,UIDSIZ);
               xrfptr = new acthUserXrf(tempid,10);
               while ((uidp=xrfptr->getmatch()) != NULL)
               {
                    bout << uidp << endl;
               }
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"firstnew"))
          {
               CHAR fid[5];

               ses->param("forum",fid,5);

               bout << firstnew(usr->userid(),(USHORT)atoi(fid)) << endl;
               retval = ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"access"))
          {
               CHAR fid[5];
               ses->param("forum",fid,5);
               INT ac;

               if (getfnm(atoi(fid)) != NULL)
               {
                    if ((ac=gforac(usr->userid(),atoi(fid))) != NOTSET)
                    {
                         bout << ac << endl;
                    }
                    else
                    {
                         if ((finfptr=getdefp(atoi(fid))) != NULL)
                         {
                              if (usr->hasKey(finfptr->forlok))
                              {
                                   bout << finfptr->dfprv << endl;
                              }
                              else
                              {
                                   bout << finfptr->dfnpv << endl;
                              }
                         }
                         else
                         {
                              bout << "0" << endl;
                         }
                    }
               }
               else
               {
                    bout << "Request contained invalid parameters" << endl;
               }
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"allforums")) {
               stlcpy(usaptr->userid,usr->userid(),UIDSIZ);

               if (dnf == NULL) {
                    dnf = new dnfHandler(forCycleMap,bout);
               }
               else {
                    switch(dnf->process()) {
                    case DNFROWBEGIN:
                         if (dnf->rowNumber() >= numforums()) {
                              dnf->tableDone();
                              break;
                         }
                         finfptr=fiddefp(dnf->rowNumber());
                         ASSERT(finfptr != NULL);
                         if ((gforac(usr->userid(),finfptr->forum)) > NOAXES)
                         {
                              bout << finfptr->forum << "\t" <<
                                      finfptr->name << "\t" <<
                                      finfptr->nmsgs << "\t" <<
                                      finfptr->nfiles << "\t" <<
                                      finfptr->nthrs << "\t" <<
                                      finfptr->forop << "\t";

                              if (strlen(finfptr->topic) == 0)
                              {
                                   bout << "(no description)";
                              }
                              else
                              {
                                   bout << finfptr->topic;
                              }
                              bout << "\t" << firstnew(usr->userid(),finfptr->forum) << endl;
                         }
                         break;
                    case DNFEND:
                         retval=ACTHDONE;
                         break;
                    }
               }
          }
          else if (sameas(ses->urlargv(0),"allgroups")) {
               const struct forgrp *ginfptr;

               if ((ginfptr=gmeGetGrpP(ROOTGRP)) != NULL)
               {
                    if (usr->hasKey(ginfptr->key))
                    {
                         bout << ginfptr->grpid << "\t" << ginfptr->name << endl;
                    }
                    while ((ginfptr=gmeNextGrpP(ginfptr->parid,
                            ginfptr->name)) != NULL)
                    {
                         if (usr->hasKey(ginfptr->key))
                         {
                              bout << ginfptr->grpid << endl;
                              bout << ginfptr->name << endl;
                         }
                    }
               }
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"qscaninfo")) {
               if (qscptr != NULL)
               {
                    strlen(qscptr->kwds) == 0 ? bout << "(none)" :
                    bout << qscptr->kwds;
                    bout << "\t" << qscptr->curfor << "\t" <<
                            qscptr->nforums << "\t" << qscptr->flags << "\t" <<
                            qscptr->stmsg << endl;
               }
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"qscanforums")) {

               if (dnf == NULL) {
                    dnf = new dnfHandler(forCycleMap,bout);
               }
               else {
                    bool found;
                    if (qscptr != NULL) {
                         switch(dnf->process()) {
                         case DNFROWBEGIN:
                              if (dnf->rowNumber() >= numforums()) {
                                   dnf->tableDone();
                                   break;
                              }
                              finfptr=fiddefp(dnf->rowNumber());
                              ASSERT(finfptr != NULL);
                              found = getForumsInQS(qscptr,finfptr);
                              if (found)
                              {
                                   if ((gforac(usr->userid(),finfptr->forum)
                                       > NOAXES))
                                   {
                                            bout << finfptr->forum << "\t" <<
                                                    finfptr->name << "\t" <<
                                                    "1" << endl;
                                   }
                              }
                              else
                              {
                                   if ((gforac(usr->userid(),finfptr->forum))
                                       > NOAXES)
                                   {
                                        bout << finfptr->forum << "\t" <<
                                                finfptr->name << "\t" <<
                                                "0" << endl;
                                   }
                              }
                              break;
                         case DNFEND:
                              retval=ACTHDONE;
                              break;
                         }
                    }
                    else {
                         retval=ACTHDONE;
                    }
               }
          }
          else if (sameas(ses->urlargv(0),"qscansave")) {

               CHAR kwds[MAXSKWD];
               CHAR curfor[5];
               CHAR numfor[5];
               CHAR flagnu[2];
               CHAR flagmwat[2];
               CHAR flagm2u[2];
               CHAR flagmfu[2];
               CHAR startm[10];

               ses->param("kwds",kwds,MAXSKWD);
               ses->param("curfor",curfor,5);
               ses->param("numfor",numfor,5);
               ses->param("flagnu",flagnu,2);
               ses->param("flagm2u",flagm2u,2);
               ses->param("flagmwat",flagmwat,2);
               ses->param("flagmfu",flagmfu,2);
               ses->param("startm",startm,10);

               if (curfor[0] != '\0' && alldgs(curfor) &&
                   numfor[0] != '\0' && alldgs(numfor) &&
                   startm[0] != '\0' && alldgs(startm) &&
                   kwds[0] != '\0')
               {
                    if (kwds[0] == '0')
                    {
                         kwds[0]='\0';
                    }
                    if (qscptr != NULL)
                    {
                         stlcpy(qscptr->kwds,kwds,MAXSKWD);
                         qscptr->curfor=atoi(curfor);
                         qscptr->nforums=atoi(numfor);
                         if (flagnu[0] == '1')
                         {
                              qscptr->flags|=NEWMSG;
                         }
                         else
                         {
                              qscptr->flags&=~NEWMSG;
                         }
                         if (flagmwat[0] == '1')
                         {
                              qscptr->flags|=WATONL;
                         }
                         else
                         {
                              qscptr->flags&=~WATONL;
                         }
                         if (flagm2u[0] == '1')
                         {
                              qscptr->flags|=TOMEONL;
                         }
                         else
                         {
                              qscptr->flags&=~TOMEONL;
                         }
                         if (flagmfu[0] == '1')
                         {
                              qscptr->flags|=FRMEONL;
                         }
                         else
                         {
                              qscptr->flags&=~FRMEONL;
                         }
                         qscptr->stmsg=atol(startm);
                    }
                    else
                    {
                         bout << "Request contained invalid parameters" << endl;
                    }
               }
               else
               {
                    bout << "Request contained invalid parameters" << endl;
               }

               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"doscan"))
          {
               if (dnf == NULL)
               {
                    dnf = new dnfHandler(forCycleMap,bout);
               }
               else
               {
                    retval=dnf_scan();
               }
          }
          else if (sameas(ses->urlargv(0),"himsgid"))
          {
               bout << himsgid() << endl;
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"copymsg"))
          {

               if (dnf == NULL)
               {
                    if (!getParams())
                    {
                         bout << "Input Error" << endl;
                         retval=ACTHDONE;
                    }
               }
               else
               {
                    retval= dnf_copymsg();
               }
          }
          else if (sameas(ses->urlargv(0),"forwardmsg")) {

               if (dnf == NULL)
               {
                    if (!getParams())
                    {
                         bout << "Input Error" << endl;
                         retval=ACTHDONE;
                    }
               }
               else
               {
                    retval= dnf_forwardmsg();
               }
          }
          else if (sameas(ses->urlargv(0),"delmsg")) {

               if (dnf == NULL)
               {
                    if (!getMFParams())
                    {
                         bout << "Request contained invalid parameters" << endl;
                         retval=ACTHDONE;
                    }
               }
               else
               {
                    retval = dnf_delmsg();
               }
          }
          else if (sameas(ses->urlargv(0),"approve")) {

               if (dnf == NULL)
               {
                    if (!getMFParams())
                    {
                         bout << "Request contained invalid parameters" << endl;
                         retval=ACTHDONE;
                    }
               }
               else
               {
                    retval = dnf_approve();
               }
          }
          else if (sameas(ses->urlargv(0),"exempt"))
          {

               if (dnf == NULL)
               {
                    if (!getMFParams())
                    {
                         bout << "Request contained invalid parameters" << endl;
                         retval=ACTHDONE;
                    }
               }
               else
               {
                    retval = dnf_exempt();
               }
          }
          else
          {
               retval=ACTHNOTFND;
          }
     }
     else if (narg == 2)
     {

          if (sameas(ses->urlargv(0),"forumsingroup"))
          {
               const struct forgrp *ginfptr;
               if ((ginfptr=gmeGetGrpP(atoi(ses->urlargv(1)))) != NULL)
               {
                    for (INT i=0 ; i < ginfptr->nforums ; i++)
                    {
                         if ((finfptr=fiddefp(ginfptr->forarr[i])) != NULL)
                         {
                              bout << finfptr->forum << endl;
                              bout << finfptr->name << endl;
                         }
                    }
               }
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"foruminfo"))
          {
               finfptr=fiddefp(atoi(ses->urlargv(1))-1);
               if ( finfptr != NULL )
               {
                    boutallfinf(finfptr);
               }
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"messagelist"))
          {
               if (alldgs(ses->urlargv(1)))
               {
                    if (dnf == NULL)
                    {

                         if (qscptr != NULL)
                         {
                              qscptr->curfor=atoi(ses->urlargv(1));
                         }
                         dnf = new dnfHandler(forCycleMap,bout);
                    }
                    else
                    {
                         retval=dnf_ReadMsgs(atoi(ses->urlargv(1)));
                    }
               }
               else
               {
                    retval=ACTHDONE;
               }
          }
          else if (sameas(ses->urlargv(0),"threadlist"))
          {
               if (alldgs(ses->urlargv(1)))
               {
                    if (dnf == NULL)
                    {

                         if (qscptr != NULL)
                         {
                              qscptr->curfor=atoi(ses->urlargv(1));
                         }
                         dnf = new dnfHandler(forCycleMap,bout);
                    }
                    else
                    {
                         retval=dnf_ReadThreads(atoi(ses->urlargv(1)));
                    }
               }
               else
               {
                    retval=ACTHDONE;
               }
          }
          else
          {
               retval=ACTHDONE;
          }
     }
     else if (narg == 3)
     {
          if (sameas(ses->urlargv(0),"msgsinthisthread"))
          {
               if (dnf == NULL)
               {
                    dnf = new dnfHandler(forCycleMap,bout);
               }
               else
               {
                    retval=dnf_ListMsgsinThread(atoi(ses->urlargv(1)),
                                                atoi(ses->urlargv(2)));
               }
          }
          else
          {
               retval=ACTHDONE;
          }
     }
     else if (narg == 4)
     {
          if (sameas(ses->urlargv(0),"dl"))
          {

               const CHAR* fidptr = ses->urlargv(1);
               const CHAR* midptr = ses->urlargv(2);

               if ( dnf == NULL )
               {
                    m_slExempt = true;

                    if ( alldgs(midptr) && alldgs(fidptr))
                    {
                         dnf = new dnfHandler ( forCycleMap , bout );
                    }
                    else
                    {
                         retval = ACTHDONE;
                    }
               }
               else
               {
                    retval = dnf_downloadAtt(atoi(fidptr),atol(midptr));
               }
          }
          else if (sameas(ses->urlargv(0),"ReadMsg"))
          {
               if (alldgs(ses->urlargv(1)) &&
                   alldgs(ses->urlargv(2)))
               {
                    if (dnf == NULL)
                    {
                         dnf = new dnfHandler(forCycleMap,bout);
                    }
                    else
                    {
                         retval=dnf_ReadaMsg(atoi(ses->urlargv(1)),
                                             atoi(ses->urlargv(2)),
                                             atoi(ses->urlargv(3)));
                    }
               }
               else
               {
                    retval=ACTHDONE;
               }
          }
          else
          {
               retval=ACTHDONE;
          }
     }
     else if (narg == 0)
     {
          m_slExempt = true;
          ses->redirect( "/forums/index.htm" );
          retval = ACTHDONE;
     }
     else      // avoid infinite loops
     {
          retval = ACTHDONE;
     }
     return(retval);
}

ACTHCODE
forSynthesis::proceedPost()        // proceed synthesizing response to POST
{
     ACTHCODE retval=ACTHMORE;
     struct qscfg *qscptr=gmeUserGetQS(m_gmehandle);
     CHAR forum[5];
     CHAR to[MAXADR];
     CHAR rr[5];
     CHAR pri[5];
     CHAR mid[10];

     if (ses->urlargc() >= 1)
     {
          if (sameas(ses->urlargv(0),"sendmsg"))
          {
               if (dnf == NULL)
               {
                   getTmpId();
                   ses->param("forum",forum,5);
                   ses->param("to",to,MAXADR);
                   getTopicParam();
                   getCCParam();
                   ses->param("text",txtbuf2,TXTLEN);
                   if (strlen(txtbuf2) > TXTLEN)
                   {
                        *(txtbuf2+(TXTLEN-1))='\0';
                   }

                   stpans( txtbuf2 );

                   memset(mywrkarea,0,sizeof(mywrkarea));
                   inigmerq(mywrkarea);
                   stlcpy(m_mhdrptr->from,usr->userid(),MAXADR);
                   if (valadr(mywrkarea,m_mhdrptr->from,to,atoi(forum))
                       == VALYES)
                   {
                        m_mhdrptr->forum=atoi(forum);
                        stlcpy(m_mhdrptr->to,to,MAXADR);
                        if (topic != NULL)
                        {
                             stlcpy(m_mhdrptr->topic,stpans(topic),TPCSIZ);
                        }
                        dnf = new dnfHandler(forCycleMap,bout);
                   }
                   else
                   {
                        bout << "The recipient address is invalid.";
                        retval=ACTHDONE;
                   }
               }
               else
               {
                    retval=dnf_sendmsg();
               }
          }
          else if (sameas(ses->urlargv(0),"replymsg")) {
               if (dnf == NULL)
               {
                    getTmpId();
                    ses->param("forum",forum,5);
                    if (forum[0] == '\0' || !alldgs(forum))
                    {
                         bout << "Request contained invalid parameters" << endl;
                         return(ACTHDONE);
                    }
                    else
                    {
                         fid=atoi(forum);
                    }
                    ses->param("msgid",mid,10);
                    if (mid[0] == '\0' || !alldgs(mid))
                    {
                         bout << "Request contained invalid parameters" << endl;
                         return(ACTHDONE);
                    }
                    else
                    {
                         m_savMsgId=atol(mid);
                    }
                    getTopicParam();
                    if (ses->param("rr",rr,3))
                    {
                        flags|=RECREQ;
                    }
                    if (ses->param("pri",pri,3))
                    {
                        flags|=PRIMSG;
                    }
                    getCCParam();
                    ses->param("text",txtbuf2,TXTLEN);
                    if (strlen(txtbuf2) > TXTLEN)
                    {
                         *(txtbuf2+(TXTLEN-1))='\0';
                    }

                    stpans ( txtbuf2 );

                    dnf = new dnfHandler(forCycleMap,bout);
               }
               else
               {
                    retval=dnf_replymsg();
               }
          }
          else if (sameas(ses->urlargv(0),"modifymsg"))
          {
               if (dnf == NULL)
               {

                    ses->param("forum",forum,5);
                    if (forum[0] == '\0' || !alldgs(forum))
                    {
                         bout << "Request contained invalid parameters" << endl;
                         return(ACTHDONE);
                    }
                    else
                    {
                         fid=atoi(forum);
                    }
                    ses->param("msgid",mid,10);
                    if (mid[0] == '\0' || !alldgs(mid))
                    {
                         bout << "Request contained invalid parameters" << endl;
                         return(ACTHDONE);
                    }
                    else
                    {
                         m_savMsgId=atol(mid);
                    }
                    ses->param("topic",topic,TPCSIZ);
                    if (topic != NULL)
                    {
                         stlcpy(m_mhdrptr->topic,stpans(topic),TPCSIZ);
                    }
                    ses->param("text",txtbuf2,TXTLEN);
                    if (strlen(txtbuf2) > TXTLEN)
                    {
                         *(txtbuf2+(TXTLEN-1))='\0';
                    }

                    stpans ( txtbuf2 );

                    dnf = new dnfHandler(forCycleMap,bout);
               }
               else
               {
                    retval=dnf_modifymsg();
               }
          }
          else if (sameas(ses->urlargv(0),"qscanfsave")) {

               CHAR qfors[4800];

               if (qscptr != NULL)
               {

                    ses->param("qfors",qfors,4800);

                    if (!sameas(qfors,"0")) {

                         INT amt;
                         CHAR *ptr;
                         amt = itemcntd(qfors,",");
                         LONG himsg;

                         for (INT i= 0 ; i < qscptr->nforums ; i++)
                         {
                              if ((himsg=igethi(qscptr,i)) > 0) {
                                   isethi(qscptr,i,-himsg);
                              }
                         }

                         for (INT i=0 ; i < amt ; i++)
                         {
                              ptr=itemidxd(qfors,i,",");

                              if (alldgs(ptr) && getfnm(atoi(ptr)) != NULL)
                              {
                                   sfinqs ( qscptr, atoi(ptr) , true );
                              }

                         }
                    }
               }
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"attach"))
          {
               if (dnf == NULL)
               {
                    if (ses->urlargc() > 1)
                    {
                         m_tmpid = atol( ses->urlargv( 1 ) );
                    }

                    if (attachInProgress( m_tmpid ))
                    {
                         dnf = new dnfHandler( inProgressMap, bout );
                         m_attaching = false;
                    }
                    else
                    {
                         m_httpUp = new httpUpload( ses );

                         char      tmpname[ 20 ];
                         sprintf( tmpname, "%lu", m_tmpid );

                         if (startFile( m_tmpid, tmpname ))
                         {
                              dnf = new dnfHandler( uploadMap, bout );
                              m_httpUp->setFile( getFile( m_tmpid ) );
                              ses->setStatus( "200 OK" );
                              bout << "<html><head><title>File Upload</title></head><body>\n";
                              bout.flush();
                              m_attaching = true;
                         }
                    }

                    if (dnf == NULL)
                    {
                         retval = ACTHNOTFND;
                    }
               }
               else if (m_attaching)
               {
                    retval = dnf_attach();
               }
               else
               {
                    retval = dnf_inProgress();
               }
          }
          else {
               retval=ACTHNOTFND;
          }
     }
     else           //   avoid infinite loops
     {
          return( ACTHDONE );
     }

     return(retval);
}

VOID
forSynthesis::boutallfinf(         // Send all forum information
const struct fordef *finfptr)
{
    bout << finfptr->forum << "\t";
    bout << finfptr->name << "\t";
    bout << finfptr->topic << "\t";
    bout << finfptr->forop << "\t";
    bout << finfptr->dfnum << "\t";
    bout << finfptr->attpath << "\t";
    bout << finfptr->nthrs << "\t";
    bout << finfptr->nmsgs << "\t";
    bout << finfptr->nfiles << "\t";
    bout << finfptr->nw4app << "\t";
    bout << finfptr->forlok << "\t";
    bout << finfptr->dfnpv << "\t";
    bout << finfptr->dfprv << "\t";
    bout << finfptr->mxnpv << "\t";
    bout << finfptr->msglif << "\t";
    bout << finfptr->chgmsg << "\t";
    bout << finfptr->chgrdm << "\t";
    bout << finfptr->chgatt << "\t";
    bout << finfptr->chgadl << "\t";
    bout << finfptr->chgupk << "\t";
    bout << finfptr->chgdpk << "\t";
    bout << finfptr->ccr << "\t";
    bout << finfptr->pfnlvl << "\t";
    bout << finfptr->crdate << "\t";
    bout << finfptr->crtime << "\t";
    bout << finfptr->seqid << "\t";
    bout << finfptr->necho << "\t";
    bout << finfptr->echoes << endl;
}

ACTHCODE
forSynthesis::dnf_ReadMsgs(        // Sequentially read through messages
INT forum)
{
  // ^^^
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context
          memset(mywrkarea,0,sizeof(mywrkarea));
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,forum,m_savMsgId,0);
          break;
     case DNFROWBEGIN:
          switch ((rc=nextmsg(mywrkarea,m_mhdrptr,txtbuf))) {
          case GMEAGAIN:
                break;
          case GMEOK:
                bout << m_mhdrptr->msgid << "\t" << m_mhdrptr->from << "\t" <<
                m_mhdrptr->to << "\t";
                bout << getHistory() << "\t";
                bout << getAttName() << "\t";
                bout << getDate() << "\t";
                bout << getTime() << "\t";
                bout << m_mhdrptr->flags << "\t" << getTopic() << endl;
                m_savMsgId=m_mhdrptr->msgid;
                break;
          case GMENFND:
                m_doneFlg=true;
                break;
          default:
                Gme_ErrorHandler(rc);
                break;
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          else
          {
               inictx(mywrkarea,usr->userid(),FSQFOR,forum,m_savMsgId,0);
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);

          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_downloadAtt(     // download file attachment
INT forum,                         //   forum id of message
ULONG msgid)                       //   message id
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process()) {
     case DNFBEGIN:               // initialize the read context
          memset(mywrkarea,0,sizeof(mywrkarea));
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,forum,msgid,0);
          break;
     case DNFROWBEGIN:
          switch ((rc=readmsg(mywrkarea,m_mhdrptr,txtbuf))) {
          case GMEAGAIN:
               break;
          case GMEOK:             // got the message!
               if ((rc=tagatt(mywrkarea,m_mhdrptr,tagbuf)) == GMEOK) {
                    if (gdlstart(usr->userid(),tagbuf)) {
                         ses->sndfile(dlname(m_mhdrptr));
                         m_doDl=true;
                    }
                    else {
                         Gme_ErrorHandler(GMECRD);
                    }
                    m_doneFlg=true;
               }
               else {
                    Gme_ErrorHandler(rc);
                    m_doneFlg = true;
               }
               break;

          default:

               Gme_ErrorHandler(rc);
               m_doneFlg=true;
               break;

          }
          break;

     case DNFROWEND:

          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;

     case DNFEND:

          retval = ACTHDONE;
          break;
     }

     return(retval);


}

ACTHCODE
forSynthesis::dnf_ReadaMsg(        // Read a part. message
INT forum,                         //   forum id
ULONG msgid,                       //   message id
bool txohdr)
{
     ACTHCODE retval=ACTHMORE;
     INT rc;
     INT tmpsiz;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context

          memset(mywrkarea,0,sizeof(mywrkarea));
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,forum,msgid,0);
          break;
     case DNFROWBEGIN:
          if (!m_gotText)
          {
              switch ((rc=readmsg(mywrkarea,m_mhdrptr,txtbuf)))
              {
              case GMEAGAIN:
                   break;
              case GMEOK:             // got the message!
                   if (txohdr)
                   {
                        m_gotText=true;
                   }
                   else
                   {
                        bout << m_mhdrptr->msgid << "\t" << m_mhdrptr->from << "\t" <<
                        m_mhdrptr->to << "\t";
                        bout << getHistory() << "\t";
                        bout << getAttName() << "\t";
                        bout << getDate() << "\t";
                        bout << getTime() << "\t";
                        bout << m_mhdrptr->flags << "\t" << getTopic() <<
                        endl;
                        m_doneFlg=true;
                   }
                   break;
              default:
                   Gme_ErrorHandler(rc);
                   m_doneFlg=true;
                   break;
              }
          }
          else
          {
               tmpsiz=ses->sndrsp(txtbuf+m_savoutsz);
               m_savoutsz+=tmpsiz;
               if (m_savoutsz >= strlen(txtbuf))
               {
                    m_doneFlg=true;
               }
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          switch (rc=markread(mywrkarea,m_mhdrptr,txtbuf)) {
          case GMEAGAIN:
                break;
          case GMEOK:
          default:
                Gme_ErrorHandler(rc);
                clsgmerq(mywrkarea);

                retval=ACTHDONE;
                break;
          }
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_ReadThreads(     // Read Threads in a forum
INT forum)                         //   forum ID
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context
          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inormrd(mywrkarea,usr->userid(),forum,FIRSTM);
          thrctx(mywrkarea,thrid);
          break;
     case DNFROWBEGIN:
          switch ((rc=thrinfo(mywrkarea,1,numptr,m_mhdrptr,txtbuf)))
          {
          case GMEAGAIN:
               break;
          case GMEOK:
               if (m_mhdrptr->forum == forum) {
                    bout << m_mhdrptr->thrid << "\t" << m_mhdrptr->msgid << "\t"
                         << numthrs << "\t" << getTopic() << endl;
                    thrid=m_mhdrptr->thrid;
                    m_savMsgId=m_mhdrptr->msgid;
               }
               else {
                    retval=ACTHDONE;
               }
               break;
          case GMENFND:
               m_doneFlg=true;
               break;
          default:
               Gme_ErrorHandler(rc);
               break;
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          else
          {
               inormrd(mywrkarea,usr->userid(),forum,m_savMsgId);
               thrctx(mywrkarea,thrid);
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);

          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_ListMsgsinThread( // List messages in a part. Thread
INT forum,
LONG thrid)
{
     ACTHCODE retval=ACTHMORE;
     CHAR crpbuf[30];
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context

          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQTHR,forum,FIRSTM,thrid);
          break;
     case DNFROWBEGIN:
          switch ((rc=nextmsg(mywrkarea,m_mhdrptr,txtbuf)))
          {
          case GMEAGAIN:
               break;
          case GMEOK:

               bout << m_mhdrptr->msgid << "\t" << m_mhdrptr->from << "\t" <<
               m_mhdrptr->to << "\t";
               bout << getHistory() << "\t";
               bout << getAttName() << "\t";
               bout << getDate() << "\t";
               bout << getTime() << "\t";
               bout << m_mhdrptr->flags << "\t" << excerpt(crpbuf,txtbuf,30)
                    << "\t" << getTopic() << endl;
               break;
          case GMENFND:
               m_doneFlg=true;
               break;
          default:
               Gme_ErrorHandler(rc);
               break;
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          else
          {
               seqctx(mywrkarea,FSQTHR);
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_scan()          // Do "one-time" scan
{
     ACTHCODE retval=ACTHMORE;
     struct qscfg *qscptr=gmeUserGetQS(m_gmehandle);
     INT rc;
     const struct fordef *finfptr;

     switch (dnf->process()) {
     case DNFBEGIN:               // initialize the read context
          if (qscptr != NULL)
          {
               if (qscptr->nforums > 0)
               {
                   INT fid;
                   ULONG mid;
                   memset(otsp,0,MAXQSR);
                   qsc2ots(qscptr,otsp);
                   if (otsp->nforums == 0) {
                        return(ACTHDONE);
                   }
                   setmem(mywrkarea,sizeof(mywrkarea),0);
                   inigmerq(mywrkarea);
                   setscan(mywrkarea,otsp);
                   fid=fstscnf(usr->userid(),otsp);
                   if (fid != EMLID) {
                        mid=fstscnm(usr->userid(),otsp,fid);
                        inormrd(mywrkarea,usr->userid(),fid,mid);
                        seqctx(mywrkarea,FSQSCN);
                   }
                   else {
                        retval=ACTHDONE;
                   }
               }
               else
               {
                    retval=ACTHDONE;
               }
          }
          else
          {
               retval=ACTHDONE;
          }
          break;
     case DNFROWBEGIN:
          switch ((rc=nextmsg(mywrkarea,m_mhdrptr,txtbuf)))
          {
          case GMEAGAIN:
               break;
          case GMEOK:
               bout << m_mhdrptr->msgid << "\t" << m_mhdrptr->from << "\t" <<
               m_mhdrptr->to << "\t";
               bout << getHistory() << "\t";
               bout << getAttName() << "\t";
               bout << getDate() << "\t";
               bout << getTime() << "\t";
               bout << m_mhdrptr->forum << "\t";
               if ((finfptr=getdefp(m_mhdrptr->forum)) != NULL) {
                    bout << finfptr->name;
               }
               else {
                    bout << "NOSUCH";   // should never occur
               }
               bout << "\t";
               bout << m_mhdrptr->flags << "\t" << getTopic() << endl;
               break;
          case GMENFND:
               m_doneFlg=true;
               break;
          default:
               Gme_ErrorHandler(rc);
               break;
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          else
          {
               seqctx(mywrkarea,FSQSCN);
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_sendmsg()       // Send a message ("post" - not a reply)
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFROWBEGIN:
          if (readyToSend( m_tmpid ))
          {
               switch( rc = sendMessage( m_tmpid ))
               {
                    case GMEAGAIN:
                         break;
                    case GMEOK:
                         m_doneFlg=true;
                         break;
                    default:
                         Gme_ErrorHandler(rc);
                         m_doneFlg=true;
                         break;
               }
          }
          else
          {
               storeMessageData( m_tmpid, false, mywrkarea, txtbuf2 , cclist, m_mhdrptr );
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_replymsg()        // Reply to a message
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context

          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,fid,m_savMsgId,0);
          break;
     case DNFROWBEGIN:
          if (!m_doReply) {
               switch ((rc=readmsg(mywrkarea,m_mhdrptr,txtbuf)))
               {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    stlcpy(m_mhdrptr->to,m_mhdrptr->from,MAXADR);
                    stlcpy(m_mhdrptr->from,usr->userid(),MAXADR);
                    if (topic != NULL)
                    {
                         stlcpy(m_mhdrptr->topic,topic,TPCSIZ);
                    }
                    if (valadr(mywrkarea,m_mhdrptr->from,m_mhdrptr->to,fid) ==
                        VALYES)
                    {
                         m_doReply=true;
                    }
                    else
                    {
                         bout << "Validation error" << endl;
                         return(ACTHDONE);
                    }
                    break;
               default:
                    Gme_ErrorHandler(rc);
                    m_doneFlg=true;
                    break;
               }
          }
          else
          {
               if (readyToSend( m_tmpid ))
               {
                    switch (rc = sendMessage( m_tmpid ))
                    {
                         case GMEAGAIN:
                              break;
                         case GMEOK:
                              m_doneFlg=true;
                              break;
                         default:
                              Gme_ErrorHandler(rc);
                              m_doneFlg=true;
                              break;
                    }
               }
               else
               {
                    storeMessageData( m_tmpid, true, mywrkarea, txtbuf2 , cclist, m_mhdrptr );
               }
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_copymsg()        // Copy a message
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context

          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,fid,m_savMsgId,0);
          break;
     case DNFROWBEGIN:
          if (!m_doCopy)
          {
               switch ((rc=readmsg(mywrkarea,m_mhdrptr,txtbuf))) {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    if (valadr(mywrkarea,usr->userid(),holdid,fid) == VALYES) {

                         if (holdid[0] == '/' && fnmxst(holdid+1))
                         {
                              m_mhdrptr->forum=getfid(holdid+1);
                         }
                         else
                         {
                              stlcpy(m_mhdrptr->to,holdid,MAXADR);
                              m_mhdrptr->forum=EMLID;
                         }

                         if ( m_mhdrptr->flags&PRIMSG &&
                              (rc=valpri(mywrkarea,m_mhdrptr->from,
                               holdid,m_mhdrptr->forum)) != VALYES)
                         {

                              Gme_ErrorHandler ( rc );
                              return ( ACTHDONE );

                         }

                         if ( m_mhdrptr->flags&(FILATT|FILIND) &&
                               (rc=valatt(mywrkarea,usr->userid(),
                                holdid,m_mhdrptr->forum)) != VALYES)
                         {

                              Gme_ErrorHandler ( rc );
                              return ( ACTHDONE );

                         }

                         m_doCopy=true;
                    }
                    else
                    {
                         bout << "Copy validation error!" << endl;
                         m_doneFlg=true;
                    }
                    break;
               default:
                    Gme_ErrorHandler(rc);
                    m_doneFlg=true;
                    break;
               }
          }
          else
          {
               rc=copymsg(mywrkarea,m_mhdrptr,txtbuf);
               m_doneFlg=(rc != GMEAGAIN);
               if (rc < GMEAGAIN) {
                    Gme_ErrorHandler(rc);
               }
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);

          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_forwardmsg()        // Forward Message
{
     ACTHCODE retval=ACTHMORE;
     INT rc;
     INT id;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context

          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,fid,m_savMsgId,0);
          break;
     case DNFROWBEGIN:
          if (!m_doFwd)
          {
               switch ((rc=readmsg(mywrkarea,m_mhdrptr,txtbuf)))
               {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    if ((id=fnmidx(holdid+1)) == NOIDX)
                    {
                         id=EMLID;
                    }
                    if (vfwdadr(mywrkarea,usr->userid(),holdid,id)
                        == VALYES)
                    {
                         m_mhdrptr->forum=id;
                         if (holdid[0] == '/' && fnmxst(holdid+1))
                         {
                              m_mhdrptr->forum=getfid(holdid+1);
                         }
                         else
                         {
                              stlcpy(m_mhdrptr->to,holdid,MAXADR);
                              m_mhdrptr->forum=EMLID;
                         }

                         if ( m_mhdrptr->flags&PRIMSG &&
                              (rc=vfwdpri(mywrkarea,usr->userid(),
                               holdid,m_mhdrptr->forum)) != VALYES)
                         {

                              Gme_ErrorHandler ( rc );
                              return ( ACTHDONE );

                         }

                         if ( m_mhdrptr->flags&(FILATT|FILIND) &&
                              (rc=vfwdatt(mywrkarea,usr->userid(),
                              holdid,m_mhdrptr->forum)) != VALYES)
                         {

                              Gme_ErrorHandler ( rc );
                              return ( ACTHDONE );

                         }

                         m_doFwd=true;
                    }
                    else
                    {
                         bout << "Forward Validation error!" << endl;
                         m_doneFlg=true;
                    }
                    break;
               default:
                    Gme_ErrorHandler(rc);
                    m_doneFlg=true;
                    break;
               }
          }
          else
          {
               rc=fwdmsg(mywrkarea,m_mhdrptr,txtbuf);
               m_doneFlg=(rc != GMEAGAIN);
               if (rc < GMEAGAIN) {
                    Gme_ErrorHandler(rc);
               }
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);

          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_modifymsg()        // Modify Message
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context

          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,fid,m_savMsgId,0);
          break;
     case DNFROWBEGIN:
          switch((rc=modmsg(mywrkarea,m_mhdrptr,txtbuf2)))
          {
          case GMEAGAIN:
              break;
          case GMEOK:
              m_doneFlg=true;
              break;
          default:
              Gme_ErrorHandler(rc);
              m_doneFlg=true;
              break;
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_delmsg()        // Delete Message
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context
          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,fid,m_savMsgId,0);
          break;
     case DNFROWBEGIN:
          switch ((rc=delmsg(mywrkarea)))
          {
          case GMEAGAIN:
              break;
          case GMEOK:
              m_doneFlg=true;
              break;
          default:
              Gme_ErrorHandler(rc);
              m_doneFlg=true;
              break;
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_exempt()        // Exempt msg. from deletion
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context
          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,fid,m_savMsgId,0);
          break;
     case DNFROWBEGIN:
          if (!m_doExempt)
          {
              switch ((rc=readmsg(mywrkarea,m_mhdrptr,txtbuf2)))
              {
              case GMEAGAIN:
                  break;
              case GMEOK:
                  m_doExempt= true;
                  break;
              default:
                  Gme_ErrorHandler(rc);
                  m_doneFlg=true;
                  break;
              }
          }
          else {
               bool ex;

               if ( m_mhdrptr->flags&EXEMPT )
               {
                    ex = false;
               }
               else {
                    ex = true;
               }

               switch ((rc=exmtmsg(mywrkarea,m_mhdrptr,txtbuf2,ex))) {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    m_doneFlg=true;
                    break;
               default:
                    Gme_ErrorHandler(rc);
                    m_doneFlg=true;
                    break;
               }
          }
         break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
forSynthesis::dnf_approve()        // Approve att.
{
     ACTHCODE retval=ACTHMORE;
     INT rc;

     switch (dnf->process())
     {
     case DNFBEGIN:               // initialize the read context
          setmem(mywrkarea,sizeof(mywrkarea),0);
          inigmerq(mywrkarea);
          inictx(mywrkarea,usr->userid(),FSQFOR,fid,m_savMsgId,0);
          break;
     case DNFROWBEGIN:
          if (!m_doApprove)
          {
              switch ((rc=readmsg(mywrkarea,m_mhdrptr,txtbuf2)))
              {
              case GMEAGAIN:
                  break;
              case GMEOK:
                  m_doApprove = true;
                  break;
              default:
                  Gme_ErrorHandler(rc);
                  m_doneFlg=true;
                  break;
              }
          }
          else {

               bool apv;

               if ( m_mhdrptr->flags&FILAPV )
               {
                    apv = false;
               }
               else
               {
                    apv = true;
               }

               switch ((rc=aprvmsg(mywrkarea,m_mhdrptr,txtbuf2,apv)))
               {
               case GMEAGAIN:
                    break;
               case GMEOK:
                    m_doneFlg=true;
                    break;
               default:
                    Gme_ErrorHandler(rc);
                    m_doneFlg=true;
                    break;
               }
          }
          break;
     case DNFROWEND:
          if (m_doneFlg)
          {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          clsgmerq(mywrkarea);
          retval=ACTHDONE;
     }
     return(retval);
}

bool
forSynthesis::getMFParams()     // Handle parameters
{
      CHAR mid[10];
      CHAR forid[5];

      ses->param("forum",forid,5);
      ses->param("msgid",mid,10);
      if (mid[0] != '\0' && alldgs(mid)
          && getfnm(atoi(forid)) != NULL)
      {
            m_savMsgId=atol(mid);
            fid=atoi(forid);
            dnf= new dnfHandler(forCycleMap,bout);
            return(true);
      }
      return(false);
}

bool
forSynthesis::getParams()     // Handle parameters
{
     CHAR mid[10];
     CHAR forid[5];

     ses->param("forum",forid,5);
     ses->param("msgid",mid,10);
     ses->param("to",holdid,MAXADR);
     if (mid[0] != '\0' && alldgs(mid) && getfnm(atoi(forid)) != NULL)
     {
           m_savMsgId=atol(mid);
           fid=atoi(forid);
           dnf= new dnfHandler(forCycleMap,bout);
           return(true);
     }
     return(false);
}

VOID
forSynthesis::Gme_ErrorHandler(    // Verbose error message
INT rc)
{
     switch (rc)
     {
     case GMEERR:
          bout << "An internal error occured processing your request." << endl;
          break;
     case GMECRD:
          bout << "You don't have sufficient credits for this operation." << endl;
          break;
     case GMEIVA:
          bout << "The file attachment is invalid!" << endl;
          break;
     case GMENOAT:
          bout << "No file attachment found!" << endl;
          break;
     case GMEMEM:
          bout << "Cannot process request due to low memory on the Server." << endl;
          break;
     case GMENCFL:
          bout << "Cannot copy/forward to a distribution list!" << endl;
          break;
     case GMEACC:
          bout << "You don't have access for this operation!" << endl;
          break;
     case GMEUSE:
          bout << "Can't perform operation.  Message in use!" << endl;
          break;
     case GMENDEL:
          bout << "This message cannot be deleted!" << endl;
          break;
     case GMENMOD:
          bout << "This message cannot be modified!" << endl;
          break;
     case GMENFND:
          bout << "This message couldn't be found on the Server!" << endl;
          break;
     }
}

bool
forSynthesis::getForumsInQS( // Really any forums in QS?
struct qscfg *qptr,
const struct fordef *fptr)
{
     struct fmidky *fm;

     fm=(struct fmidky *)qptr->accmsg;
     for (INT j=0 ; j < qptr->nforums ; j++)
     {
          if (fm[j].forum == fptr->forum &&
              fm[j].msgid >= 0)
          {
              return(true);
          }
     }
     return(false);
}

VOID
forSynthesis::getCCParam()      // handle message cc list
{
     ses->param("cclist",cclist,1024);

     if (cclist[0] == '0')
     {
          cclist[0]='\0';
     }
}

VOID
forSynthesis::getTopicParam()       // get message topic
{
     ses->param("topic",topic,TPCSIZ);

     if (topic[0] == '0')
     {
          topic[0]='\0';
     }
     else {
          stpans(topic);
     }
}


VOID
forSynthesis::getTmpId()           // get message temp-id
{
     CHAR      idparam[ 20 ];

     if (ses->param( "tmpid", idparam, sizeof( idparam ) ))
     {
          m_tmpid = atol( idparam );
     }
}

ACTHCODE
forSynthesis::dnf_inProgress()     // attachment-already-in-progress
{
     ACTHCODE result = ACTHMORE;

     switch( dnf->process() )
     {
          case DNFEND:
               result = ACTHDONE;
               break;
     }

     return( result );
}


ACTHCODE
forSynthesis::dnf_attach()                  // Attach a message
{
     ACTHCODE result = m_httpUp->proceed();

     if (result == ACTHDONE)
     {
          finishFile( m_tmpid );
          renameFile( m_tmpid, m_httpUp->getFileName() );

          if (m_httpUp->needIeAddon())
          {
               bout << "Need IE addon.";
          }
          else
          {
               bout << "<strong>" << m_httpUp->getFileName() << "</strong>"
                    << " (" << m_httpUp->getFileSize() << " bytes)<br>"
                    << "Uploaded successfully.\n";
          }

          bout << "<div align=right>"
               << "<FORM NAME=\"closeform\">"
               << "<INPUT TYPE=\"button\" NAME=\"closer\" VALUE=\"Close\" onClick=\"window.close()\">"
               << "</FORM>"
               << "</div>\n";

          bout << "</body></html>\n";
     }

     return( result );
}

const CHAR *
forSynthesis::getTopic()
{
     return ( m_mhdrptr->topic[0] != '\0' ? m_mhdrptr->topic : "(none)" );
}

const CHAR *
forSynthesis::getAttName()
{
     return ( m_mhdrptr->attname[0] != '\0' ? m_mhdrptr->attname : "(none)" );
}

const CHAR *
forSynthesis::getHistory()
{
     return ( m_mhdrptr->history[0] != '\0' ? m_mhdrptr->history : "(none)" );
}

const CHAR *
forSynthesis::getTime()
{
    return( nctime(m_mhdrptr->crtime) );
}

const CHAR *
forSynthesis::getDate()
{
    return ( ::prndat(PRND_MMDYY,m_mhdrptr->crdate,0) );
}

CHAR *                             /*   returns copy of dst                */
excerpt(                           /* generate an "excerpt" of a message   */
CHAR *dst,                         /*   buffer to fill in                  */
CHAR *src,                         /*   source buffer (message text)       */
UINT maxlen)                       /*   maximum size of filled-in dst      */
{                                  /*   NOTE: assumes EOLs are \r          */
     INT i;
     bool lastsp;
     CHAR *cp;

     // find start of non-blank, non-quoted text
     do {
          while (*src <= ' ' && *src != '\0') {
               ++src;
          }
          if (isquoted(src)) {
               if ((cp=strchr(src,'\r')) == NULL) {
                    break;
               }
               src=cp+1;
          }
          else {
               break;
          }
     } while (*src != '\0');

     // copy over requested number of characters
     // (remove EOLs and excess white space)
     for (cp=dst,i=0,lastsp=false ; i < maxlen && *src != '\0' ; ++i,++src) {
          if (*src <= ' ') {
               if (!lastsp) {
                    *cp++=' ';
               }
               lastsp=true;
          }
          else {
               *cp++=*src;
               lastsp=false;
          }
     }
     *cp='\0';
     return(dst);
}

static bool
isquoted(                          /* does line start with quote symbol?   */
CHAR const * s)                    /*   pointer to start of line           */
{
     if (isalnum(*s)) {
          ++s;
          if (isalnum(*s)) {
               ++s;
          }
     }
     return(strchr("<>{}[]|:",*s) != NULL);
}

CHAR *                             //   returns pointer to destination
catGroup(                          // concatenate a group onto a group path
CHAR *dst,                         //   buffer to concatenate onto
const CHAR *src,                   //   group name to add
size_t dstSiz)                     //   size of destination buffer
{
     ASSERT(dst != NULL);
     ASSERT(src != NULL);
     if (!samend(dst,"/")) {
          stlcat(dst,"/",dstSiz);
     }
     return(stlcat(dst,src,dstSiz));
}

static VOID
handleError(
forumCommon *ecPtr,
INT operation,
const CHAR *additionalErrorMsg)
{
     ecPtr->m_merr_message=gmeErrorHandler(ecPtr->m_merr_code,operation,NULL);
     if (additionalErrorMsg != NULL) {
          ecPtr->m_merr_message+=" (";
          ecPtr->m_merr_message+=additionalErrorMsg;
          ecPtr->m_merr_message+=")";
     }
}

static bool
SetFlags(
ULONG* ulFlags,
ULONG ulSetFlags,
bool bSet)
{
     if (bSet) {
          *ulFlags|=ulSetFlags;
     }
     else {
          *ulFlags&=~ulSetFlags;
     }
     return(bSet);
}

static bool
SetFlags(
LONG* lFlags,
LONG lSetFlags,
bool bSet)
{
     if (bSet) {
          *lFlags|=lSetFlags;
     }
     else {
          *lFlags&=~lSetFlags;
     }
     return(bSet);
}


static bool
SetFlags(
SHORT* iFlags,
SHORT iSetFlags,
bool bSet)
{
     if (bSet) {
          *iFlags|=iSetFlags;
     }
     else {
          *iFlags&=~iSetFlags;
     }
     return(bSet);
}

GMEUSERHANDLE                      //   gmeuser handle
GetGmeUserHandle(                  // get handle to gme user
CHAR const * uid)                  //   user to get
{
     GMEUSERHANDLE hUser;
     struct usracc* uac=GetAccPtr(uid);
     switch(gmeUserOpen(uac,&hUser)) {
     case GMERST:
          if (onsysn(uid,1)) {
               updaccu(uac);
          }
     case GMEOK:
          return(hUser);
     default:
          ASSERT(FALSE);
          return(NULL);
     }
}

#if defined(EXTRA_LOGGING)

static VOID
vlogmsg(                           /* output to log file                   */
CHAR const * fmt,
va_list ap)
{
     FILE * fp;

     if ((fp=fopen(LOGFILE,FOPAA)) != NULL) {
          fprintf(fp,"%s %s u=%d c=%lu: "
                 ,ncdate(today()),nctime(now()),usrnum,CycleCount);
          vfprintf(fp,fmt,ap);
          fprintf(fp,"\n");
          fclose(fp);
     }
}

static VOID
logmsg(                            /* output to log file                   */
CHAR const * fmt,
...)
{
     va_list ap;

     va_start(ap,fmt);
     vlogmsg(fmt,ap);
     va_end(ap);
}

#endif /* EXTRA_LOGGING */
