/* iDIRT Message System Version 1.03/PE (Public Edition)
* by Illusion (1995)
*
* Derived from the DYRT mailer from DYRT 1.1c
* by Thrace [Valentin Popescu] (vpopesc@calstatela.edu)
* -------------------------------------------------------------------------
* *** NOTE ***
* DO NOT CHANGE THE VERSION INFORMATION IN THIS MAILER!!
* IF YOU CHANGE THE MAILER CONSIDERABLY, GIVE CREDIT TO
* BOTH ILLUSION AND THRACE. THRACE WORKED HARD ON THIS
* MAILER, AND ILLUSION WORKED HARD TO DEBUG IT, THEY
* DESERVE CREDIT FOR DOING SO.
*
* Basically what I did was take this source code from the Dyrt 1.1c
* source code and started rewriting it and trying to make it work with
* the iDIRT (or any other Dirt) code. I had to fix a few bugs that the
* mailer had which were:
*
* 1.00
* ----
* - A bug in the dump_headers() function that made the whole MUD crash
* when you scan your mail when you typed 'mail' to read it (read
* the headers). What was happening was this: the MUD was calling a
* bflush() that seemed to be crashing the MUD. Removing this bflush()
* still killed it. After trying out many different techniques I
* figured (basically guessed *grin*) that the error was coming from
* the bflush() calling fflush() when the mail file was at the EOF (or
* past it, that was my idea). So I had the dump_headers() check to see
* if the pointer that accesses the mail file is located past the EOF
* or not. Well, this fixed it and it no longer crashes.
*
* - A bug in the write_mail() function that didn't let you abort the
* message if you had a blank subject.
*
* - Fixed a bug in the mail menu that would cause it to stay in an
* infinite mail loop. This bug was caused by a user running the
* mailer from within the mailer again (*mail).
*
* - Players can now no longer alias from the mail menu.
*
* 1.01
* ----
* - Fixed the bug that allowed players to put the EOM text in the
* message. Change the EOM_MARKER #define to whatever you see fit
* and keep it a secret so that nobody can kill players mail files.
* You may even wish to use control characters for the EOM marker,
* your decision.
*
* - Added some color to the mailer.
*
* 1.02
* ----
* - Fixed a bug in which if users entered any format character into
* the subject, it crashed the MUD.
*
* 1.03
* ----
* - Fixed a bug in which if users entered any format character into
* the text, it crashed the MUD.
*
* - Now the mailer saves the player's prompt before entering the mailer.
*
* - Released a Public Version removing the iDIRT specific code.
*/
#define MAIL_C
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#ifdef RS6000
#include <strings.h>
#endif
#include "kernel.h"
#include "mobile.h"
#include "lflags.h"
#include "sflags.h"
#include "pflags.h"
#include "mflags.h"
#include "cflags.h"
#include "sendsys.h"
#include "levels.h"
#include "parse.h"
#include "rooms.h"
#include "mail.h"
#include "bprintf.h"
#include "uaf.h"
#include "log.h"
#include "mud.h"
#include "misc.h"
/* Mail system defines */
#define EOM_MARKER "\001\002\003"
#define MAX_LETTERS 150
#define READ 1
#define WRITE 0
#define SUBJECT "&+wSubject: &+w"
#define SENT "\n&+wMessage sent.\n"
#define MAILP "&+w:&+w> &+w"
#define PROMPT "&+w[&+wMail&+w] &+w:&+w "
#define NOTICE1 "\
&+w=============================================================================\n\
&+wEnter your message. To save your message type &+w'=s' &+wor &+w'**' &+won \
a blank line.\nTo abort your message type &+w'=a' &+wor &+w'*abort' &+won \
a blank line.\n\
&+w=============================================================================\n"
int count;
/* mailcom(): Main loop for the mail program. This function checks to see if
* the person trying to access the mailer is aliased, and then checks if a
* player's name was passed through the command line and will startup the
* mail sender if they are found.
*/
A_COMMAND(mailcom)
{
PERSONA p;
if (ltstflg(ploc(mynum),LFL_JAIL) && (plev(mynum) < LVL_ARCHWIZARD) )
{ bprintf("Sorry.... we have jailed the postman as well.\n");
return;
}
/* Do not let aliased players use the mailer */
if (cur_player->aliased || cur_player->polymorphed != -1)
{
bprintf ("You cannot use the mailer while you are aliased.\n");
return;
}
/* Save the mail header */
cur_player->mails.old_handler = cur_player->inp_handler->inp_handler;
/* Save the prompt */
strcpy(cur_player->mails.prompt, cur_player->prompt);
if (brkword () != -1)
{
if (!getuaf (wordbuf, &p))
{
bprintf ("&+wPlayer not found in database.\n");
return;
}
strcpy (cur_player->mails.w_name, p.p_name);
bprintf ("&+wTo: &+w%s\n", cur_player->mails.w_name);
bprintf (SUBJECT);
cur_player->inmailer = True;
cur_player->mails.old_handler = cur_player->inp_handler->inp_handler;
strcpy (cur_player->cprompt, SUBJECT);
replace_input_handler(write_mail);
return;
}
read_mail (pname(mynum));
return;
}
/* write_mail(): Gets the message subject and checks to see if to abort
* the message (if subject is a blank) and creates the header in the
* temporary mail file. Also, now you can abort the message here (before
* it just asked you for the subject again).
*/
void write_mail (char *u)
{
if (EMPTY (u) || u[0] == '\n')
{
bprintf ("&+wMessage Aborted.\n");
replace_input_handler (cur_player->mails.old_handler);
strcpy(cur_player->prompt, cur_player->mails.prompt);
strcpy (cur_player->cprompt, build_prompt (mynum));
cur_player->inmailer= False;
bprintf("\r%s", cur_player->cprompt);
setup_globals (mynum);
return;
}
else
{
ssetflg(mynum,SFL_NOCOMM);
bprintf (NOTICE1);
bprintf (MAILP);
open_mail (WRITE, True);
if (cur_player->mails.write == (FILE *) 0)
return;
fprintf (cur_player->mails.write, "%s\n%s\n%d\n", pname (mynum), u, (int)time (0));
fflush (cur_player->mails.write);
strcpy (cur_player->prompt, MAILP);
replace_input_handler (input_mail);
}
return;
}
/* input_mail(): Handles the input of the message text */
void input_mail (char *x)
{ int p;
char to[256];
char from[256];
Boolean abort = False;
if (!strcasecmp(x,"=s") || !strcasecmp(x,"=a") ||
!strcasecmp(x,"**") || !strcasecmp(x,"*abort"))
{
fprintf (cur_player->mails.write, "\n%s\n", EOM_MARKER);
close_mail (WRITE);
strcpy(cur_player->prompt, cur_player->mails.prompt);
strcpy (cur_player->cprompt, (char *) build_prompt (mynum));
sprintf (to, "%s/%s", MAIL_DIR, cur_player->mails.w_name);
sprintf (from, "%s/%s.from-%s", MAIL_DIR, cur_player->mails.w_name,pname (mynum));
if (!strcasecmp(x,"=a") || !strcasecmp(x, "*abort"))
{
bprintf("&+wMail aborted.\n");
abort = True;
}
else
concat_files (to, from);
unlink (from);
if (!abort) bprintf (SENT);
if ((p = fpbns (cur_player->mails.w_name)) >= 0)
{
if (p < max_players && !abort)
sendf (p, "&+w* &+wYou have new mail from %s &+w*\n", pname (mynum));
}
if (cur_player->mails.level)
replace_input_handler (mail_menu);
else
{ sclrflg(mynum,SFL_NOCOMM);
cur_player->inmailer = False;
replace_input_handler (cur_player->mails.old_handler);
strcpy(cur_player->prompt, cur_player->mails.prompt);
strcpy (cur_player->cprompt, build_prompt (mynum));
bprintf("\r%s", cur_player->cprompt);
}
return;
}
fprintf (cur_player->mails.write, "%s\n", x);
bprintf (MAILP);
fflush (cur_player->mails.write);
return;
}
/* read_mail(): Lets the player read a message */
void read_mail (char *u)
{
strcpy (cur_player->mails.r_name, pname (mynum));
open_mail (READ, False);
if (!(cur_player->mails.read))
{
bprintf ("&+wSorry, you have no mail.\n");
return;
}
/*
strcpy (cur_player->cprompt, PROMPT);
*/
strcpy (cur_player->prompt, PROMPT);
dump_headers (cur_player->mails.read);
bprintf (PROMPT);
replace_input_handler (mail_menu);
return;
}
/* dump_headers(): Displays the message headers for your message file.
* There was a bug with the original code that caused the MUD to crash when
* the bflush() was called and if the EOF of the mail file had been reached.
* This was fixed by Illusion for iDIRT.
*/
void dump_headers (FILE * m)
{ char b[MAX_COM_LEN];
char from[MAX_COM_LEN];
time_t time_sent;
char s[MAX_COM_LEN];
count = 1;
rewind (cur_player->mails.read);
bprintf ("&+w No. From Date Sent Subject\n");
bprintf ("&+w----- ------------ ------------------------ ---------------------------\n");
while (!feof (cur_player->mails.read))
{
fgets (b, MAX_COM_LEN, cur_player->mails.read); /* From Field */
if (feof(cur_player->mails.read))
return;
b[strlen (b) - 1] = '\0';
strncpy (from, b, 12);
fgets (s, MAX_COM_LEN, cur_player->mails.read); /* Subject Field */
fgets (b, 40, cur_player->mails.read); /* Date Send Field */
time_sent = atol (b);
for (;;) /* Find the EOF text */
{
if (feof (cur_player->mails.read))
break;
fgets (b, MAX_COM_LEN, cur_player->mails.read);
if (strncmp (b, EOM_MARKER, 3) == 0)
break;
}
if (strlen (s) > 27) /* Dump Header */
{
s[24] = ' ';
s[25] = s[26] = s[27] = '.';
s[28] = '\n';
s[29] = '\0';
}
bprintf("&+w[%3.3d] %-12.12s %24.24s %s",
count, from, ctime (&time_sent),
(cur_player->mails.data)[count - 1] ? "&+w[ To Be Deleted ]\n" : s);
count++;
if (!feof(cur_player->mails.read)) /* [iDIRT] Bug fix. The MUD used */
bflush(); /* to crash here until the mail */
} /* file was checked for EOF. */
return;
}
/* mail_menu(): The menuing system for the mailer */
void mail_menu (char *u)
{ time_t tim;
char buffer[MAX_COM_LEN + 5];
int quitting_mail = False;
cur_player->inmailer = True;
if (cur_player->mails.level)
{
bprintf (PROMPT);
cur_player->mails.level = 0;
}
/* strcpy (cur_player->cprompt, PROMPT);*/
strcpy(cur_player->prompt, PROMPT);
if (isupper (u[0]))
u[0] = tolower (u[0]);
switch (u[0]) {
case 'r': /* Reply */
tim = time (0);
if (EMPTY (cur_player->mails.last))
bprintf ("&+wYou haven't read any messages yet.\n");
else
{
if (strncasecmp (cur_player->mails.subject, "Re: ", 4) != 0)
sprintf (buffer, "&+wRe: &+w%s", cur_player->mails.subject);
else
strcpy (buffer, cur_player->mails.subject);
strcpy (cur_player->mails.w_name, cur_player->mails.last);
open_mail (WRITE, True);
if (!cur_player->mails.write)
{
close_mail (READ);
return;
}
bprintf ("&+wTo: &+w%s\n&+wSubject: &+w%s\n",
cur_player->mails.last, buffer);
bprintf(NOTICE1);
bprintf (MAILP);
fprintf (cur_player->mails.write, "%s\n%s%d\n",
pname (mynum), buffer, (int)tim);
cur_player->mails.level = 1;
replace_input_handler (input_mail);
return;
}
break;
case 'd': /* Delete */
if (u[1] != ' ' || !isdigit (u[2]))
bprintf ("\nUsage: d [message number]\n");
else
/*or statement added by jwn*/
if ((atoi(&u[2]) - 1) > MAX_LETTERS || atoi(&u[2]) > (count-1) )
{
bprintf("Message number cannot be greater than %d\n", (count-1));
mudlog("Bad &+wMAIL&* by %s, Number was %d",
pname(mynum), (atoi(&u[2]) - 1));
bprintf (PROMPT);
return;
}
(cur_player->mails.data)[atoi (&u[2]) - 1] = True;
break;
case 'u': /* Undelete */
if (u[1] != ' ' || !isdigit (u[2]))
bprintf ("\nUsage: u [message number]\n");
else
if ((atoi(&u[2]) - 1) > MAX_LETTERS || atoi(&u[2]) > (count-1) )
{
bprintf("Message number cannot be greater than %d\n", MAX_LETTERS);
mudlog("Bad &+wMAIL&* by %s, Number was %d",
pname(mynum), (atoi(&u[2]) - 1));
bprintf (PROMPT);
return;
}
(cur_player->mails.data)[atoi (&u[2]) - 1] = False;
break;
case '*': /* Game command */
Game_Com (&u[1], False);
break;
case 'i': /* Info */
bprintf ("\001fHELP/%s\003\n", "mail.info");
break;
case '?':
bprintf ("\001fHELP/%s\003\n", "mail.help");
break;
case 'h':
bprintf ("\n");
dump_headers (cur_player->mails.read);
break;
case 'v':
bprintf("iDIRT Mailer Version 1.03/PE (Public Edition)\n");
bprintf("1994, 95 by Illusion\n\n");
bprintf("Derived from the DYRT mailer from DYRT 1.1c\n");
bprintf("by Valentin Popescu (vpopesc@calstatela.edu)\n");
break;
case '\0':
case '\n':
break;
case 'x':
case 'q': /* quit the mailer */
replace_input_handler (cur_player->mails.old_handler);
close_mail (WRITE);
delete_mail ();
close_mail (READ);
strcpy(cur_player->prompt, cur_player->mails.prompt);
strcpy (cur_player->cprompt, build_prompt (mynum));
setup_globals (mynum);
cur_player->inmailer = False;
quitting_mail = True;
break;
default:
/* Check to see if a number was inputed */
{
int number;
number = atoi (u);
if (number < 1 || number > MAX_LETTERS) {
bprintf ("\nUnknown command. Type '?' for help.\n");
break;
}
read_msg (number);
}
break;
} /* End switch */
if (!quitting_mail)
bprintf (PROMPT);
else
{ bprintf("&+wQuitting Mailer.\n");
strcpy(cur_player->prompt, cur_player->mails.prompt);
bprintf ((char *) build_prompt (mynum));
}
return;
}
void abort_mail(void)
{
replace_input_handler (cur_player->mails.old_handler);
close_mail (WRITE);
close_mail (READ);
strcpy(cur_player->prompt, cur_player->mails.prompt);
strcpy (cur_player->cprompt, build_prompt (mynum));
setup_globals (mynum);
cur_player->inmailer = False;
}
/* read_msg(): Reads in a message number, taking the number as a parameter */
void read_msg (int num)
{
/* to get to a particular message num, we must skip exactly num - 1 EOFs */
char buffer[MAX_COM_LEN];
time_t time_sent;
if (num <= 0 || num > MAX_LETTERS)
return;
rewind (cur_player->mails.read);
for (num = num - 1; num != 0; num--)
{
for (;;)
{
fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
if (feof (cur_player->mails.read))
break;
if (strncmp (buffer, EOM_MARKER, 3) == 0)
break;
}
}
/* Now we should be at the right place, and we dump the letter */
fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
if (feof (cur_player->mails.read))
{
bprintf ("\n&+wMessage out of range.\n", num);
return;
}
buffer[strlen (buffer) - 1] = '\0';
strncpy (cur_player->mails.last, buffer, 12);
if ((cur_player->mails.data)[num] == True)
bprintf ("\n&+w* Note: This letter is marked for deletion *\n");
/* dump the header */
bprintf ("\n&+wFrom: &+w%s\n", buffer);
fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
strncpy (cur_player->mails.subject, buffer, MAX_COM_LEN);
bprintf ("&+wSubject: &+w%s", buffer);
fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
time_sent = atoi (buffer);
bprintf ("&+wSent: &+w%s", ctime (&time_sent));
bprintf ("\n");
/* Dump the message body */
for (;;)
{
fgets (buffer, MAX_COM_LEN, cur_player->mails.read);
if (strncmp (buffer, EOM_MARKER, 3) == 0)
break;
bprintf("%s", buffer);
if (feof (cur_player->mails.read))
break;
}
bflush ();
return;
}
/* open_mail(): Handles the opening of a mail file. */
void open_mail (char mode, Boolean complain)
{ char buf[1024];
FILE *tf;
Boolean fail = False;
switch (mode) {
case READ:
sprintf (buf, "%s/%s", MAIL_DIR, cur_player->mails.r_name);
tf = fopen (buf, "r");
if (!tf)
fail = True;
else
cur_player->mails.read = tf;
break;
case WRITE:
sprintf (buf, "%s/%s.from-%s", MAIL_DIR, cur_player->mails.w_name, pname (mynum));
tf = fopen (buf, "w");
if (!tf)
{
progerror ("mail-write");
fail = True;
}
cur_player->mails.write = tf;
break;
} /* End switch */
if (complain && fail)
{
bprintf ("We are experiencing technical difficulties. Try again later.\n");
progerror ("open_mail");
if (cur_player->mails.read != NULL)
{ fclose(cur_player->mails.read);
cur_player->mails.read = NULL;
}
if (cur_player->mails.write != NULL)
{ fclose(cur_player->mails.write);
cur_player->mails.write = NULL;
}
replace_input_handler (cur_player->mails.old_handler);
}
return;
}
/* close_mail(): Handles the closing of the mail files */
void close_mail (char mode)
{
switch (mode) {
case READ:
if (cur_player->mails.read)
fclose (cur_player->mails.read);
cur_player->mails.read = 0;
break;
case WRITE:
if (cur_player->mails.write)
fclose (cur_player->mails.write);
cur_player->mails.write = 0;
break;
}
}
/* delete_mail(): Deletes messages that have been marked for removal */
void delete_mail (void)
{ char tempfile[128];
char tempfile2[128];
char b[MAX_COM_LEN];
FILE *fout;
FILE *fin;
struct stat s;
register int t;
int pid;
extern int errno;
pid = getpid ();
sprintf (tempfile, "%s/TEMP.%s.%d", MAIL_DIR, pname (mynum), pid);
sprintf (tempfile2, "%s/%s", MAIL_DIR, pname (mynum));
(void) unlink (tempfile);
/* copy the file, filtering out the offending message */
fout = fopen (tempfile, "w");
if (!fout)
{
bprintf ("System error detected, mail NOT deleted.\n");
progerror ("delete_file write");
return;
}
fin = fopen (tempfile2, "r");
if (!fin)
{
bprintf ("System error detected, mail NOT deleted.\n");
progerror ("delete_file read");
fclose (fout);
unlink (tempfile);
errno = 0; /* unlink may cause an errorr */
return;
}
/* now we have both files open */
for (t = 0; t != MAX_LETTERS; t++)
{
if (feof (fin) != 0)
break;
/* Should we delete messages? */
if ((cur_player->mails.data)[t] != 0) /* Yes, delete messages */
{ while (1) /* Search for 'EOF' */
{ bzero (b, MAX_COM_LEN);
fgets (b, MAX_COM_LEN, fin);
if (feof (fin) != 0)
break;
if (strncmp (b, EOM_MARKER, 3) == 0)
break;
}
}
else /* No, do not delete */
{ while (1)
{
bzero (b, MAX_COM_LEN);
fgets (b, MAX_COM_LEN, fin);
fprintf (fout, "%s", b);
if (feof (fin) != 0)
break;
if (strncmp (b, EOM_MARKER, 3) == 0)
break;
}
}
/* unset the marker */
(cur_player->mails.data)[t] = 0;
}
rewind (fin);
fclose (fin);
fclose (fout);
/* copy the file over */
unlink (tempfile2);
rename (tempfile, tempfile2);
if (stat (tempfile2, &s) != -1)
if (s.st_size < 4)
unlink (tempfile2);
return;
}
/* check_files(): Checks if any files have been updated since login. A
* problem with iDIRT (and any DIRT for that matter) has been fixed here and
* it uses all of the correct DIRT user variables now.
*/
void check_files (void)
{ PERSONA me;
struct stat s;
time_t last;
char buff[512];
getuaf(pname(mynum), &me);
last = me.p_last_on;
sprintf (buff, "%s/%s", MAIL_DIR, pname (mynum));
/* Check boards for new messages */
if (stat("bboard_ply", &s) != -1)
{
if (s.st_mtime > last)
bprintf ("There are &+wNEW&* messages on the mortal bulletin board.\n");
}
if ((stat ("bboard_wiz", &s) != -1) && plev (mynum) >= LVL_WIZARD)
{
if (s.st_mtime > last)
bprintf ("There are &+wNEW&* messages on the Wizard board.\n");
}
if ((stat("bboard_awiz", &s) != -1) && plev(mynum) >= LVL_ARCHWIZARD)
{ if (s.st_mtime > last)
bprintf("There are &+wNEW&* messages on the Archwizard board.\n");
}
if ((stat("bboard_god",&s) != -1) && plev(mynum) >= LVL_GOD)
{ if (s.st_mtime > last)
bprintf("There are &+wNEW&* messages on the God board.\n");
}
if (stat(BUGSYSLOG,&s) != -1 && ptstflg(mynum,PFL_SYSLOG))
{ if (s.st_mtime > last)
bprintf("&+wNew &+wBugs &+wreported in buglog.\n");
}
if (stat(SUGGESTLOG,&s) != -1 && ptstflg(mynum,PFL_SYSLOG))
{ if (s.st_mtime > last)
bprintf("&+wNew &+wSuggestions &+wreported in suggestlog.\n");
}
#ifdef USE_BULLETIN
if (stat (BULLETIN1, &s) != -1) {
if (s.st_mtime > last)
bprintf ("New Mortal bulletin as of %19.19s.\nType 'bull' to read it.\n", ctime (&s.st_mtime));
}
if ((stat (BULLETIN2, &s) != -1) && plev (mynum) >= LVL_WIZARD) {
if (s.st_mtime > last)
bprintf ("New Wizard+ bulletin as of %19.19s.\nType 'bull' to read it.\n", ctime (&s.st_mtime));
}
if ((stat (BULLETIN3, &s) != -1) && plev (mynum) >= LVL_ARCHWIZARD) {
if (s.st_mtime > last)
bprintf ("New Arch-Wizard+ bulletin as of %19.19s.\nType 'bull' to read it.\n", ctime (&s.st_mtime));
}
#endif
if (stat (buff, &s) != -1)
{
if (s.st_size > 3)
{
if (s.st_mtime > last)
bprintf ("&+wYou have &B&+wNEW&* mail.\n");
else
bprintf ("&+wYou have old mail in your mailbox.\n");
}
}
bprintf("\n");
}