/* 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"); }