// --------------------------------------------------------------------------
// Citadel: Msg.H
//
// All about messages.

#include "auxtab.h"

enum ReadMessageStatus
	{
	RMS_OK, 	RMS_NOMESSAGE,	RMS_BADMESSAGE,
	};


enum SaveMessageCode
	{
	SMC_NORMAL, SMC_NETWORK,	SMC_LESAVE, 	SMC_RESIZE,
	};


enum ReadMessageCode
	{
	RMC_NORMAL, RMC_NETWORK,	RMC_DUPLICATECHECK
	};


enum MaySeeMsgType
	{
	MSM_ERROR,			MSM_COPYSCROLLED,	MSM_CENSORED,		MSM_GOOD,
	MSM_NOTLOGGEDIN,	MSM_COPYOWNTWIT,	MSM_RELEASEDORIG,	MSM_TWIT,
	MSM_MODERATED,		MSM_NOTINGROUP, 	MSM_GROUPBAD,		MSM_MAIL,
	};


// --------------------------------------------------------------------------
// These are flags used in the message table, which is an index into MSG.DAT.
// This table is stored in MSG.TAB.

class msgflags
	{
	Bool	MAIL		: 1;
	Bool	RECEIVED	: 1;	// Was it received?
	Bool	REPLY		: 1;	// Reply sent?
	Bool	PROBLEM 	: 1;	// Problem User message?
	Bool	MADEVIS 	: 1;	// Made Visible?
	Bool	LIMITED 	: 1;	// Limited-access message?
	Bool	MODERATED	: 1;	// Moderated message?
	Bool	reserved	: 1;	// Extra bit (was Released)
	Bool	COPY		: 1;	// Is message a copy?
	Bool	NET 		: 1;	// Networked mail en-route?
	Bool	FORWARDED	: 1;	// Forwarded E-Mail?
	Bool	NETWORKED	: 1;	// Networked Message?
	Bool	MASSEMAIL	: 1;	// E-Mail to a group?
	Bool	CENSORED	: 1;	// Censored message?
	Bool	LOCAL		: 1;	// Local message?
	Bool	INUSE		: 1;	// Slot being used by a valid message?

public:
	void SetMail(Bool New)			{	MAIL = !!New;		}
	Bool IsMail(void) const 		{	return (MAIL);		}

	void SetReceived(Bool New)		{	RECEIVED = !!New;	}
	Bool IsReceived(void) const 	{	return (RECEIVED);	}

	void SetReply(Bool New) 		{	REPLY = !!New;		}
	Bool IsReply(void) const		{	return (REPLY); 	}

	void SetProblem(Bool New)		{	PROBLEM = !!New;	}
	Bool IsProblem(void) const		{	return (PROBLEM);	}

	void SetMadevis(Bool New)		{	MADEVIS = !!New;	}
	Bool IsMadevis(void) const		{	return (MADEVIS);	}

	void SetLimited(Bool New)		{	LIMITED = !!New;	}
	Bool IsLimited(void) const		{	return (LIMITED);	}

	void SetModerated(Bool New) 	{	MODERATED = !!New;	}
	Bool IsModerated(void) const	{	return (MODERATED); }

	void SetCopy(Bool New)			{	COPY = !!New;		}
	Bool IsCopy(void) const 		{	return (COPY);		}

	void SetNet(Bool New)			{	NET = !!New;		}
	Bool IsNet(void) const			{	return (NET);		}

	void SetForwarded(Bool New) 	{	FORWARDED = !!New;	}
	Bool IsForwarded(void) const	{	return (FORWARDED); }

	void SetNetworked(Bool New) 	{	NETWORKED = !!New;	}
	Bool IsNetworked(void) const	{	return (NETWORKED); }

	void SetMassemail(Bool New) 	{	MASSEMAIL = !!New;	}
	Bool IsMassemail(void) const	{	return (MASSEMAIL); }

	void SetCensored(Bool New)		{	CENSORED = !!New;	}
	Bool IsCensored(void) const 	{	return (CENSORED);	}

	void SetLocal(Bool New) 		{	LOCAL = !!New;		}
	Bool IsLocal(void) const		{	return (LOCAL); 	}

	void SetInuse(Bool New) 		{	INUSE = !!New;		}
	Bool IsInuse(void) const		{	return (INUSE); 	}
	};


// --------------------------------------------------------------------------
// And this is the actual message table. Note that we only have a structure
// for the Auxmem and Windows version of Citadel: the table is implemented as
// simple pointers to ints in the Regular version. This is because of
// segments. Because we do not want to suffer the overhead of huge pointers,
// we want to make sure that we do not go over 64K on an array. By breaking
// the message table up into ints, we can have 32K messages and stay within a
// segment. Under windows, we just suffer the overhead of using huge
// pointers. This is because we want to be able to have as many messages as
// the Auxmem version: practically only limited by free memory.
//
// This table is not really what it seems: because it is so big, we have let
// readability suffer and re-use some of the fields to store certain elements.
// The reuse is as following:
//
// Copied messages: The offset to the original message is stored in both the
//					mttohash field and the mtauthhash field. We can get away
//					with this because copied messages never have authors or
//					recipients.
//
//					The implementation of this is hidden behind the getCopy()
//					macro for reading the value. You have to deal with
//					writing the data yourself. Cope.
//
// Origin ID:		We store only the low word of the origin ID. This is more
//					than enough for accuracy.

#ifdef AUXMEM
typedef struct
	{
	msgflags mtmsgflags;
	long	mtmsgLoc;
	int 	mtroomno;
	int 	mttohash;
	int 	mtauthhash;
	uint	mtomesg;
	long	PrevRoomMsg;
	long	NextRoomMsg;

	DEBUGCODE(Bool Checked : 1;)
	} messagetable;
#endif


#ifdef WINCIT
// --------------------------------------------------------------------------
// The Windows version uses huge pointers, so we have to pad the structure
// out to 32 bytes. This is only a waste of 10 bytes; people will not even
// notice it, compared to the room table padding.

typedef struct
	{
	msgflags mtmsgflags;	// + 2 =  2
	long	mtmsgLoc;		// + 4 =  6
	int 	mtroomno;		// + 2 =  8
	int 	mttohash;		// + 2 = 10
	int 	mtauthhash; 	// + 2 = 12
	uint	mtomesg;		// + 2 = 14
	long	PrevRoomMsg;	// + 4 = 18
	long	NextRoomMsg;	// + 4 = 22

	Bool	Checked : 1;	// + 2 = 24

	char	pad[8]; 		// + 8 = 32
	} messagetable;
#endif


#ifdef AUXMEM
	extern m_slot *FirstMessageInRoom;	// array of table indexes
	extern m_slot *LastMessageInRoom;	// array of table indexes
#else
	#ifdef WINCIT
		extern messagetable huge *msgTabWin;	// the windows message table
	#else
		extern msgflags *msgTab_mtmsgflags; // every message gets flags
		extern int		*msgTab_mtmsgLocLO; // offset where message starts
		extern int		*msgTab_mtmsgLocHI; // offset where message starts
		extern int		*msgTab_mtroomno;	// room # of message
		extern int		*msgTab_mttohash;	// hash of recipient or group
		extern int		*msgTab_mtauthhash; // hash of author of message
		extern uint 	*msgTab_mtomesg;	// LO net id, forwardee hash,
											//		copy offset
	#endif
#endif


// --------------------------------------------------------------------------
// This next class contains everything that we can know about a message.
// However, it is not stored to MSG.DAT as most information is stored to
// other .DAT files: only those fields that are used are stored to the file.
// For character strings (which is almost all there is), only the string up
// to the terminating nul (ASCII 0) byte is stored. That is, although the
// subject can be up to 81 bytes long including the terminating nul
// character, only the first six bytes of it would be stored if it is "Stuff"
// (the five bytes of "Stuff" plus the terminating nul character). How and
// why this is done is explained in MSGMAKE.CPP, explaining the putMessage
// function.


// --------------------------------------------------------------------------
// Note that we cannot save a 0xFF to MSG.DAT except at the start of a
// message. That is why we don't define any attribute as 128, and instead
// stop at 64.

#define ATTR_RECEIVED	1
#define ATTR_REPLY		2
#define ATTR_MADEVIS	4
#define ATTR_COMPRESSED 8
#define ATTR_CENSORED	16
#define ATTR_BIGROOM	32
#define ATTR_MORE		64
#define ATTR2_LOCAL 	1


// --------------------------------------------------------------------------
// For storing and passing unknown message fields.

#define MAXUNKLEN 255
struct unkLst
	{
	unkLst *next;
	char whatField;
	char theValue[MAXUNKLEN+1];
	};

class Message
	{
	char	Text[MAXTEXT];		// buffer text is edited in
								// make sure this is first, and it's
								// size is MAXTEXT always. Otherwise,
								// ClearHeader fails.

	unkLst	*firstUnk;			// Unknown message field stuff
	strList *Comments;			// Comments.

	long	HeadLoc;			// Start of message
	r_slot	RoomNumber; 		// room # of message
	uchar	Attribute;			// 6 attribute bits + flag for more
	uchar	Attribute2; 		// 1 attribute bit

	label	MoreFlags;			// # field...
	label	EncryptionKey;		// the key for encryption...

	label	Author; 			// name of author
	label	RealName;			// real name of author
	label	Surname;			// surname
	label	Title;				// Title...
	label	Forward;			// forwarding address
	label	OriginNodeName; 	// name of origin system
	label	OriginRegion;		//	"       "   region
	label	OriginCountry;		//	"       "   country
	label	TwitRegion; 		// twit region
	label	TwitCountry;		// twit country
	label	OriginPhoneNumber;	// origin phone, like "(206) 542-7825"
	label	Cit86Country;		// For netting to Citadel-86
	label	DestinationAddress; // destination address

	label	CreationRoom;		// creation room
	char	Signature[91];		// signature field
	char	UserSignature[91];	// user signature field
	char	Subject[81];		// subject
	label	OriginSoftware; 	// origin software
	label	SourceID;			// message ID on system of origin
	char	FromPath[PATHSIZE+1];// where did the message come from?

	label	ToUser; 			// private message to
	label	PublicToUser;		// public message to
	label	ToNodeName; 		// name of destination system
	label	ToRegion;			//	"           "       region
	label	ToCountry;			//	"           "       country
	label	ToPhoneNumber;		// NOT USED
	char	ToPath[PATHSIZE+1]; // forced routing via path
	label	LocalID;			// local number of message
	label	CopyOfMessage;		// message ID of copy this msg is of
	label	Group;				// group name for limited access msg
	label	CreationTime;		// creation time
	label	EZCreationTime; 	// creation time
	label	ReplyToMessage; 	// message ID of message replied to
	label	X;					// twit/moderated message header
	char	FileLink[64];		// file linked message
	char	LinkedApplication[128];// application linked message
	label	SourceRoomName; 	// room name on the source system


	// These are not saved to MSG.DAT, but used internally.
	Bool	referenceFlag;		// TRUE if this is a duplicate
	ulong	referenceId;		// ID of original, if above is TRUE
	uchar	referenceAttr;		// Attribute of original, if ^^ TRUE
	r_slot	referenceRoom;		// Room of original, if ^^ TRUE

	Bool	onceWasCompressed;	// Ever has been?

	void SetCompressed(Bool New)
		{
		assert(this);
		if (New)
			{
			onceWasCompressed = TRUE;
			Attribute |= ATTR_COMPRESSED;
			}
		else
			{
			Attribute &= ~ATTR_COMPRESSED;
			}
		}


	Bool IsCompressed(void) const
		{
		assert(this);
		return (Attribute & ATTR_COMPRESSED);
		}

public:
	Message(const Message &Original);

	Message(void)
		{
		assert(this);
		firstUnk = NULL;		// Because ClearAll() tries to dispose it.
		Comments = NULL;		// same thing.
		ClearAll();
		}

	~Message(void)
		{
		assert(this);
		VerifyHeap();
		disposeUnknownList();
		disposeComments();
		VerifyHeap();
		}

	Message& operator =(const Message &Original);
	void Store(SaveMessageCode HowToSave, FILE *SaveFile);
	ReadMessageStatus ReadHeader(ReadMessageCode HowToRead, FILE *NetFile);
	ReadMessageStatus ReadAll(ReadMessageCode HowToRead, FILE *NetFile);
	ReadMessageStatus ReadCit86Packet(FILE *NetFile);
	void MakeCit86Packet(FILE *NetFile);
	void FixupNetwork(void);

	void ClearAll(void)
		{
		assert(this);
		VerifyHeap();
		disposeUnknownList();
		disposeComments();
		memset(this, 0, sizeof(Message));
		VerifyHeap();
		}

	void ClearHeader(void)
		{
		assert(this);
		VerifyHeap();
		disposeUnknownList();
		disposeComments();
		memset(((char *) this) + MAXTEXT, 0, sizeof(Message) - MAXTEXT);
		VerifyHeap();
		}

	Bool IsReceived(void) const
		{
		assert(this);
		return (Attribute & ATTR_RECEIVED);
		}

	Bool IsRepliedTo(void) const
		{
		assert(this);
		return (Attribute & ATTR_REPLY);
		}

	Bool IsMadeVisible(void) const
		{
		assert(this);
		return (Attribute & ATTR_MADEVIS);
		}

	Bool Compress(void)
		{
		if (cfg.msgCompress && !IsCompressed())
			{
			if (compress(GetTextPointer()))
				{
				SetCompressed(TRUE);
				}
			}

		return (IsCompressed());
		}

	void Decompress(void)
		{
		if (IsCompressed())
			{
			decompress((uchar *) GetTextPointer());
			SetCompressed(FALSE);
			}
		}

	Bool WasEverCompressed(void) const
		{
		return (onceWasCompressed);
		}

	Bool IsCensored(void) const
		{
		assert(this);
		return (Attribute & ATTR_CENSORED);
		}

	Bool IsBigRoom(void) const
		{
		assert(this);
		return (Attribute & ATTR_BIGROOM);
		}

	Bool IsLocal(void) const
		{
		assert(this);
		return (Attribute2 & ATTR2_LOCAL);
		}

	void SetReceived(Bool New)
		{
		assert(this);
		if (New)
			{
			Attribute |= ATTR_RECEIVED;
			}
		else
			{
			Attribute &= ~ATTR_RECEIVED;
			}
		}

	void SetRepliedTo(Bool New)
		{
		assert(this);
		if (New)
			{
			Attribute |= ATTR_REPLY;
			}
		else
			{
			Attribute &= ~ATTR_REPLY;
			}
		}

	void SetMadeVisible(Bool New)
		{
		assert(this);
		if (New)
			{
			Attribute |= ATTR_MADEVIS;
			}
		else
			{
			Attribute &= ~ATTR_MADEVIS;
			}
		}

	void SetCensored(Bool New)
		{
		assert(this);
		if (New)
			{
			Attribute |= ATTR_CENSORED;
			}
		else
			{
			Attribute &= ~ATTR_CENSORED;
			}
		}

	void SetBigRoom(Bool New)
		{
		assert(this);
		if (New)
			{
			Attribute |= ATTR_BIGROOM;
			}
		else
			{
			Attribute &= ~ATTR_BIGROOM;
			}
		}

	void SetLocal(Bool New)
		{
		assert(this);
		if (New)
			{
			Attribute2 |= ATTR2_LOCAL;
			}
		else
			{
			Attribute2 &= ~ATTR2_LOCAL;
			}

		if (Attribute2)
			{
			Attribute |= ATTR_MORE;
			}
		else
			{
			Attribute &= ~ATTR_MORE;
			}
		}

	void SetTextWithFormat(const char *NewText, ...)
		{
		assert(this);
		va_list ap;

		va_start(ap, NewText);
		vsprintf(Text, NewText, ap);
		va_end(ap);
		}

	void AppendText(const char *NewText)
		{
		assert(this);
		strncat(Text, NewText, MAXTEXT - strlen(Text) - 1);
		Text[MAXTEXT - 1] = 0;
		}

	unkLst *GetFirstUnknown(void)
		{
		assert(this);
		return (firstUnk);
		}

	const char *GetComment(long Index) const
		{
		strList *Wow = (strList *) getLLNum(Comments, Index);

		if (Wow)
			{
			return (Wow->string);
			}
		else
			{
			return (NULL);
			}
		}

	long GetCommentCount(void) const
		{
		assert(this);
		return (getLLCount(Comments));
		}

	unkLst *addUnknownList(void)
		{
		assert(this);
		return ((unkLst *) addLL((void **) &firstUnk, sizeof(*firstUnk)));
		}

	Bool AddComment(const char *New)
		{
		assert(this);
		assert(New);
		assert(*New);

		strList *sl = (strList *) addLL((void **) &Comments,
				sizeof(*Comments) + strlen(New));

		if (sl)
			{
			strcpy(sl->string, New);
			return (TRUE);
			}
		else
			{
			return (FALSE);
			}
		}

	void disposeUnknownList(void)
		{
		assert(this);
		disposeLL(&((void *) firstUnk));
		}

	void disposeComments(void)
		{
		assert(this);
		disposeLL(&((void *) Comments));
		}

	// try not to use these next four. they are evil.
	char *GetTextPointer(void)
		{
		assert(this);
		return (Text);
		}

	char GetAttribute(void)
		{
		assert(this);
		return (Attribute);
		}

	char GetAttribute2(void)
		{
		assert(this);
		return (Attribute2);
		}

	void SetAttribute(char New)
		{
		assert(this);
		assert(New != -1);
		Attribute = New;
		}

	// end of evil.

	const char *GetText() const
		{
		assert(this);
		return (Text);
		}

	long GetHeadLoc(void) const
		{
		assert(this);
		return (HeadLoc);
		}

	r_slot GetRoomNumber(void) const
		{
		assert(this);
		return (RoomNumber);
		}

	const char *GetMoreFlags(void) const
		{
		assert(this);
		return (MoreFlags);
		}

	Bool IsMoreFlag(const char Flag) const
		{
		assert(this);
		assert(Flag);
		return (!!strchr(MoreFlags, Flag));
		}

	Bool IsReceiptConfirmationRequested(void) const
		{
		assert(this);
		return (IsMoreFlag(getmsg(1685)[0]));
		}

	Bool IsEncrypted(void) const
		{
		assert(this);
		return (IsMoreFlag(getmsg(1685)[1]));
		}

	const char *GetEncryptionKey(void) const
		{
		assert(this);
		return (EncryptionKey);
		}

	const char *GetAuthor(void) const
		{
		assert(this);
		return (Author);
		}

	const char *GetRealName(void) const
		{
		assert(this);
		return (RealName);
		}

	const char *GetSurname(void) const
		{
		assert(this);
		return (Surname);
		}

	const char *GetTitle(void) const
		{
		assert(this);
		return (Title);
		}

	const char *GetForward(void) const
		{
		assert(this);
		return (Forward);
		}

	const char *GetOriginNodeName(void) const
		{
		assert(this);
		return (OriginNodeName);
		}

	const char *GetOriginRegion(void) const
		{
		assert(this);
		return (OriginRegion);
		}

	const char *GetOriginCountry(void) const
		{
		assert(this);
		return (OriginCountry);
		}

	const char *GetTwitRegion(void) const
		{
		assert(this);
		return (TwitRegion);
		}

	const char *GetTwitCountry(void) const
		{
		assert(this);
		return (TwitCountry);
		}

	const char *GetOriginPhoneNumber(void) const
		{
		assert(this);
		return (OriginPhoneNumber);
		}

	const char *GetCit86Country(void) const
		{
		assert(this);
		return (Cit86Country);
		}

	const char *GetDestinationAddress(void) const
		{
		assert(this);
		return (DestinationAddress);
		}

	const char *GetCreationRoom(void) const
		{
		assert(this);
		return (CreationRoom);
		}

	const char *GetSignature(void) const
		{
		assert(this);
		return (Signature);
		}

	const char *GetUserSignature(void) const
		{
		assert(this);
		return (UserSignature);
		}

	const char *GetSubject(void) const
		{
		assert(this);
		return (Subject);
		}

	const char *GetOriginSoftware(void) const
		{
		assert(this);
		return (OriginSoftware);
		}

	const char *GetSourceID(void) const
		{
		assert(this);
		return (SourceID);
		}

	const char *GetFromPath(void) const
		{
		assert(this);
		return (FromPath);
		}

	const char *GetToUser(void) const
		{
		assert(this);
		return (ToUser);
		}

	const char *GetPublicToUser(void) const
		{
		assert(this);
		return (PublicToUser);
		}

	const char *GetToNodeName(void) const
		{
		assert(this);
		return (ToNodeName);
		}

	const char *GetToRegion(void) const
		{
		assert(this);
		return (ToRegion);
		}

	const char *GetToCountry(void) const
		{
		assert(this);
		return (ToCountry);
		}

	const char *GetToPhoneNumber(void) const
		{
		assert(this);
		return (ToPhoneNumber);
		}

	const char *GetToPath(void) const
		{
		assert(this);
		return (ToPath);
		}

	const char *GetLocalID(void) const
		{
		assert(this);
		return (LocalID);
		}

	const char *GetCopyOfMessage(void) const
		{
		assert(this);
		return (CopyOfMessage);
		}

	const char *GetGroup(void) const
		{
		assert(this);
		return (Group);
		}

	const char *GetCreationTime(void) const
		{
		assert(this);
		return (CreationTime);
		}

	const char *GetEZCreationTime(void) const
		{
		assert(this);
		return (EZCreationTime);
		}

	const char *GetReplyToMessage(void) const
		{
		assert(this);
		return (ReplyToMessage);
		}

	const char *GetX(void) const
		{
		assert(this);
		return (X);
		}

	const char *GetFileLink(void) const
		{
		assert(this);
		return (FileLink);
		}

	const char *GetLinkedApplication(void) const
		{
		assert(this);
		return (LinkedApplication);
		}

	const char *GetSourceRoomName(void) const
		{
		assert(this);
		return (SourceRoomName);
		}

	void SetText(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Text, New, MAXTEXT - 1);
		}

	void SetHeadLoc(long New)
		{
		assert(this);
		HeadLoc = New;
		}

	void SetRoomNumber(r_slot New)
		{
		assert(this);
		RoomNumber = New;

		if (RoomNumber > 254)
			{
			SetBigRoom(TRUE);
			}
		}

	Bool SetMoreFlag(const char Flag, Bool New);

	Bool SetReceiptConfirmationRequested(Bool New)
		{
		assert(this);
		return (SetMoreFlag(getmsg(1685)[0], New));
		}

	Bool SetEncrypted(Bool New)
		{
		assert(this);
		return (SetMoreFlag(getmsg(1685)[1], New));
		}

	void SetEncryptionKey(const char *New)
		{
		assert(this);
		CopyStringToBuffer(EncryptionKey, New, LABELSIZE);
		}

	void SetAuthor(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Author, New, LABELSIZE);
		}

	void SetRealName(const char *New)
		{
		assert(this);
		CopyStringToBuffer(RealName, New, LABELSIZE);
		}

	void SetSurname(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Surname, New, LABELSIZE);
		}

	void SetTitle(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Title, New, LABELSIZE);
		}

	void SetForward(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Forward, New, LABELSIZE);
		}

	void SetOriginNodeName(const char *New)
		{
		assert(this);
		CopyStringToBuffer(OriginNodeName, New, LABELSIZE);
		}

	void SetOriginRegion(const char *New)
		{
		assert(this);
		CopyStringToBuffer(OriginRegion, New, LABELSIZE);
		}

	void SetOriginCountry(const char *New)
		{
		assert(this);
		CopyStringToBuffer(OriginCountry, New, LABELSIZE);
		}

	void SetTwitRegion(const char *New)
		{
		assert(this);
		CopyStringToBuffer(TwitRegion, New, LABELSIZE);
		}

	void SetTwitCountry(const char *New)
		{
		assert(this);
		CopyStringToBuffer(TwitCountry, New, LABELSIZE);
		}

	void SetOriginPhoneNumber(const char *New)
		{
		assert(this);
		CopyStringToBuffer(OriginPhoneNumber, New, LABELSIZE);
		}

	void SetCit86Country(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Cit86Country, New, LABELSIZE);
		}

	void SetDestinationAddress(const char *New)
		{
		assert(this);
		CopyStringToBuffer(DestinationAddress, New, LABELSIZE);
		}

	void SetCreationRoom(const char *New)
		{
		assert(this);
		CopyStringToBuffer(CreationRoom, New, LABELSIZE);
		}

	void SetSignature(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Signature, New, sizeof(Signature) - 1);
		}

	void SetUserSignature(const char *New)
		{
		assert(this);
		CopyStringToBuffer(UserSignature, New, sizeof(UserSignature) - 1);
		}

	void SetSubject(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Subject, New, sizeof(Subject) - 1);
		}

	void SetOriginSoftware(const char *New)
		{
		assert(this);
		CopyStringToBuffer(OriginSoftware, New, LABELSIZE);
		}

	void SetSourceID(const char *New)
		{
		assert(this);
		assert(isNumeric(New));
		CopyStringToBuffer(SourceID, New, LABELSIZE);
		}

	void SetFromPath(const char *New)
		{
		assert(this);
		CopyStringToBuffer(FromPath, New, PATHSIZE);
		}

	void SetToUser(const char *New)
		{
		assert(this);
		CopyStringToBuffer(ToUser, New, LABELSIZE);
		}

	void SetPublicToUser(const char *New)
		{
		assert(this);
		CopyStringToBuffer(PublicToUser, New, LABELSIZE);
		}

	void SetToNodeName(const char *New)
		{
		assert(this);
		CopyStringToBuffer(ToNodeName, New, LABELSIZE);
		}

	void SetToRegion(const char *New)
		{
		assert(this);
		CopyStringToBuffer(ToRegion, New, LABELSIZE);
		}

	void SetToCountry(const char *New)
		{
		assert(this);
		CopyStringToBuffer(ToCountry, New, LABELSIZE);
		}

	void SetToPhoneNumber(const char *New)
		{
		assert(this);
		CopyStringToBuffer(ToPhoneNumber, New, LABELSIZE);
		}

	void SetToPath(const char *New)
		{
		assert(this);
		CopyStringToBuffer(ToPath, New, PATHSIZE);
		}

	void SetLocalID(const char *New)
		{
		assert(this);
		assert(isNumeric(New));

		CopyStringToBuffer(LocalID, New, LABELSIZE);
		}

	void SetCopyOfMessage(const char *New)
		{
		assert(this);
		assert(isNumeric(New));

		CopyStringToBuffer(CopyOfMessage, New, LABELSIZE);
		}

	void SetGroup(const char *New)
		{
		assert(this);
		CopyStringToBuffer(Group, New, LABELSIZE);
		}

	void SetCreationTime(const char *New)
		{
		assert(this);
		assert(isNumeric(New));

		CopyStringToBuffer(CreationTime, New, LABELSIZE);
		}

	void SetEZCreationTime(const char *New)
		{
		assert(this);
		CopyStringToBuffer(EZCreationTime, New, LABELSIZE);
		}

	void SetReplyToMessage(const char *New)
		{
		assert(this);
		assert(isNumeric(New));

		CopyStringToBuffer(ReplyToMessage, New, LABELSIZE);
		}

	void SetX(const char *New)
		{
		assert(this);
		CopyStringToBuffer(X, New, LABELSIZE);
		}

	void SetFileLink(const char *New)
		{
		assert(this);
		CopyStringToBuffer(FileLink, New, sizeof(FileLink) - 1);
		}

	void SetLinkedApplication(const char *New)
		{
		assert(this);
		CopyStringToBuffer(LinkedApplication, New,
				sizeof(LinkedApplication) - 1);
		}

	void SetSourceRoomName(const char *New)
		{
		assert(this);
		CopyStringToBuffer(SourceRoomName, New, LABELSIZE);
		}

	Bool IsViewDuplicate(void)
		{
		assert(this);
		return (referenceFlag);
		}

	void SetViewDuplicate(Bool New)
		{
		assert(this);
		referenceFlag = !!New;
		}

	ulong GetOriginalID(void) const
		{
		assert(this);
		return (referenceId);
		}

	void SetOriginalID(ulong New)
		{
		assert(this);
		referenceId = New;
		}

	uchar GetOriginalAttribute(void) const
		{
		assert(this);
		return (referenceAttr);
		}

	void SetOriginalAttribute(uchar New)
		{
		assert(this);
		referenceAttr = New;
		}

	r_slot GetOriginalRoom(void) const
		{
		assert(this);
		return (referenceRoom);
		}

	void SetOriginalRoom(r_slot New)
		{
		assert(this);
		referenceRoom = New;
		}

	Bool IsNetworkedMail(void) const
		{
		return (*GetToNodeName() || *GetDestinationAddress());
		}

	Bool IsMail(void) const
		{
		return (*GetToUser());
		}

	Bool IsMassEMail(void) const
		{
		return (IsMail() && *GetGroup());
		}

	Bool IsNetworkedMailForHere(void) const
		{
		return (// It is mail
				IsNetworkedMail() &&

					(
					// Message is to this node name
					SameString(GetToNodeName(), cfg.nodeTitle) ||

					// or to this address
					SameString(GetDestinationAddress(), cfg.Address)
					)
			);
		}

	Bool IsAnonymous(void) const
		{
		return (!*GetAuthor() || SameString(GetAuthor(), "****"));
		}
	};


// MSG.CPP
m_slot indexslot(ulong msgno);
MaySeeMsgType MaySeeIndexMsg(m_slot slot);
m_slot sizetable(void);


// MSGADDR.CPP
Bool ResolveMessageAddressingIssues(Message *Msg, ModemConsoleE ReportTo);


// MSGMOD.CPP
Bool censor_message(Message *Msg);
void markroom(void);
void printmsglist(void);
void insertmsglist(void);
void killmsglist(void);
void sortmsglist(char direction);
void clearmsglist(void);
void automark(void);
void insert(ulong id);
Bool markIt(Message *Msg);
Bool censorIt(Message *Msg);
void markmsg(Message *Msg);
Bool pullIt(Message *Msg);
void changeheader(ulong id, int roomno, uchar attr);
void massdelete(void);
void copymsg(void);


// MSGOVL.CPP
MaySeeMsgType MaySeeMsg(Message *Msg);
void indexmessage(Message *Msg);
void VerifyMsgTab(void);


// MSGMAKE.CPP
void SaveStub(Message *Msg, const char *TypeStr, Bool PoopDebug);
Bool makeMessage(Message *Msg, Message *ReplyTo, r_slot PutInRoom = CERROR);
void systemMessage(Message *Msg);
void putMsgChar(char c);
void crunchmsgTab(ulong howmany);
void overwrite(int bytes);
Bool putAndNoteMessage(Message *Msg, Bool censor);
void msgtosysop(Message *Msg);


// MSGSCRPT.CPP
void PrepareMsgScriptInit(Message *Msg);
Bool CheckMsgScript(Message *Msg);
void InitMsgScript(ScriptInfoS *si);


// MSGREAD.CPP
void PrintMessageByID(ulong id);
Message *LoadMessageByID(ulong ID, Bool CheckMaySeeIt, Bool Display);
void ActuallyDisplayMessage(Message *Msg, Bool ShowHeader, Bool ShowBody,
		Bool ShowSignature);
void PrintMessage(Message *Msg);
void showMessages(OldNewPick whichMess, Bool revOrder, long howMany);
int getMsgChar(void);
void roomJournal(void);
void resetMsgfilter(int resetOld);
extern void readbymsgno(void);


// MSGLOAD.CPP
Bool GetFStr(FILE *fl, char *str, int mlen, int fLevel);



#define getCopy(i)		(m_slot)long_JOIN(getToHash((m_slot)i),\
								getAuthHash((m_slot)i))

#ifdef AUXMEM

extern auxTabList	*mtList;			// base of our linked list
extern int			msgBlocksInHeap;	// how many we have
#define MSGTABPERPAGE	(AUXPAGESIZE / sizeof(messagetable))

#define getMsgTab(s)	(assert(s < LONG_MAX),\
								(messagetable *) (LoadAuxmemBlock((s),\
								&mtList,\
								MSGTABPERPAGE, sizeof(messagetable))))

#ifndef NDBEUG

	// Why aren't these inlines? So the asserts get useful line numbers.

	#define getFlags(slot)		(assert(slot < LONG_MAX),\
									(&(getMsgTab(slot)->mtmsgflags)))
	#define getLocation(slot)	(assert(slot < LONG_MAX),\
									(getMsgTab(slot)->mtmsgLoc))
	#define getRoomNum(slot)	(assert(slot < LONG_MAX),\
									(getMsgTab(slot)->mtroomno))
	#define getToHash(slot) 	(assert(slot < LONG_MAX),\
									(getMsgTab(slot)->mttohash))
	#define getAuthHash(slot)	(assert(slot < LONG_MAX),\
									(getMsgTab(slot)->mtauthhash))
	#define getOriginID(slot)	(assert(slot < LONG_MAX),\
									(getMsgTab(slot)->mtomesg))
	#define getPrevRoomMsg(slot) (assert(slot < LONG_MAX),\
									(getMsgTab(slot)->PrevRoomMsg))
	#define getNextRoomMsg(slot) (assert(slot < LONG_MAX),\
									(getMsgTab(slot)->NextRoomMsg))

#else

	inline msgflags *getFlags(m_slot slot)
		{
		return (&(getMsgTab(slot)->mtmsgflags));
		}

	inline ulong getLocation(m_slot slot)
		{
		return (getMsgTab(slot)->mtmsgLoc);
		}

	inline r_slot getRoomNum(m_slot slot)
		{
		return (getMsgTab(slot)->mtroomno);
		}

	inline int getToHash(m_slot slot)
		{
		return (getMsgTab(slot)->mttohash);
		}

	inline int getAuthHash(m_slot slot)
		{
		return (getMsgTab(slot)->mtauthhash);
		}

	inline uint getOriginID(m_slot slot)
		{
		return (getMsgTab(slot)->mtomesg);
		}

	inline m_slot getPrevRoomMsg(m_slot slot)
		{
		return (getMsgTab(slot)->PrevRoomMsg);
		}

	inline m_slot getNextRoomMsg(m_slot slot)
		{
		return (getMsgTab(slot)->NextRoomMsg);
		}

#endif

#define getCopyLMT(i)			long_JOIN(lmt->mttohash, lmt->mtauthhash)
#define getFlagsLMT(i)			(&(lmt->mtmsgflags))
#define getToHashLMT(i) 		lmt->mttohash
#define getAuthHashLMT(i)		lmt->mtauthhash
#define getOriginIDLMT(i)		lmt->mtomesg
#define getRoomNumLMT(i)		lmt->mtroomno
#define getLocationLMT(i)		lmt->mtmsgLoc
#define getNextRoomMsgLMT(i)	lmt->NextRoomMsg
#define getPrevRoomMsgLMT(i)	lmt->PrevRoomMsg

#else
	#ifdef WINCIT
		#define getRoomNum(i)		msgTabWin[(m_slot)i].mtroomno
		#define getLocation(i)		msgTabWin[(m_slot)i].mtmsgLoc
		#define getFlags(i) 		(&msgTabWin[(m_slot)i].mtmsgflags)
		#define getToHash(i)		msgTabWin[(m_slot)i].mttohash
		#define getAuthHash(i)		msgTabWin[(m_slot)i].mtauthhash
		#define getOriginID(i)		msgTabWin[(m_slot)i].mtomesg
		#define getNextRoomMsg(i)	msgTabWin[(m_slot)i].NextRoomMsg
		#define getPrevRoomMsg(i)	msgTabWin[(m_slot)i].PrevRoomMsg
	#else
		#define getRoomNum(i)	msgTab_mtroomno[(m_slot)i]
		#define getLocation(i)	long_JOIN(msgTab_mtmsgLocLO[(m_slot)i], \
										msgTab_mtmsgLocHI[(m_slot)i])
		#define getFlags(i) 	(&msgTab_mtmsgflags[(m_slot)i])
		#define getToHash(i)	msgTab_mttohash[(m_slot)i]
		#define getAuthHash(i)	msgTab_mtauthhash[(m_slot)i]
		#define getOriginID(i)	msgTab_mtomesg[(m_slot)i]
	#endif

	#define getCopyLMT(i)		getCopy(i)
	#define getFlagsLMT(i)		getFlags(i)
	#define getToHashLMT(i) 	getToHash(i)
	#define getAuthHashLMT(i)	getAuthHash(i)
	#define getOriginIDLMT(i)	getOriginID(i)
	#define getRoomNumLMT(i)	getRoomNum(i)
	#define getLocationLMT(i)	getLocation(i)
#endif
