// mail.cpp // // $Id: mail.cpp,v 1.11 2000/09/20 22:49:28 sdennis Exp $ // // This code was taken from Kalkin's DarkZone code, which was // originally taken from PennMUSH 1.50 p10, and has been heavily modified // since being included in MUX. // #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include <sys/types.h> #include "db.h" #include "interface.h" #include "attrs.h" #include "mail.h" #include "alloc.h" #include "htab.h" #include "match.h" #include "functions.h" #ifdef RADIX_COMPRESSION #include "radix.h" // Buffers used for RADIX_COMPRESSION. // char msgbuff[LBUF_SIZE + (LBUF_SIZE >> 1) + 1]; char subbuff[LBUF_SIZE + (LBUF_SIZE >> 1) + 1]; char timebuff[LBUF_SIZE + (LBUF_SIZE >> 1) + 1]; #endif // RADIX_COMPRESSION static int FDECL(sign, (int)); static void FDECL(do_mail_flags, (dbref, char *, mail_flag, int)); static void FDECL(send_mail, (dbref, dbref, const char *, const char *, int, mail_flag, int)); static int FDECL(player_folder, (dbref)); static int FDECL(parse_msglist, (char *, struct mail_selector *, dbref)); static int FDECL(mail_match, (struct mail *, struct mail_selector, int)); static int FDECL(parse_folder, (dbref, char *)); static char *FDECL(status_chars, (struct mail *)); static char *FDECL(status_string, (struct mail *)); void FDECL(add_folder_name, (dbref, int, char *)); static int FDECL(get_folder_number, (dbref, char *)); static char *FDECL(get_folder_name, (dbref, int)); static char *FDECL(mail_list_time, (const char *)); static char *FDECL(make_numlist, (dbref, char *)); static char *FDECL(make_namelist, (dbref, char *)); static void FDECL(mail_to_list, (dbref, char *, char *, char *, int, int)); static void FDECL(do_edit_msg, (dbref, char *, char *)); static void FDECL(do_mail_proof, (dbref)); void FDECL(do_mail_cc, (dbref, char *)); void FDECL(do_expmail_abort, (dbref)); #define SIZEOF_MALIAS 13 #define WIDTHOF_MALIASDESC 40 #define SIZEOF_MALIASDESC (WIDTHOF_MALIASDESC*2) #define MAX_MALIAS_MEMBERSHIP 100 struct malias { int owner; char *name; char *desc; int desc_width; // The visual width of the Mail Alias Description. int numrecep; dbref list[MAX_MALIAS_MEMBERSHIP]; }; int ma_size = 0; int ma_top = 0; struct malias **malias; /* * Handling functions for the database of mail messages. */ /* * mail_db_grow - We keep a database of mail text, so if we send a message to * * more than one player, we won't have to duplicate the text. */ static void mail_db_grow(int newtop) { int newsize, i; MENT *newdb; if (newtop <= mudstate.mail_db_top) { return; } if (newtop <= mudstate.mail_db_size) { for (i = mudstate.mail_db_top; i < newtop; i++) { mudstate.mail_list[i].count = 0; mudstate.mail_list[i].message = NULL; } mudstate.mail_db_top = newtop; return; } if (newtop <= mudstate.mail_db_size + 100) { newsize = mudstate.mail_db_size + 100; } else { newsize = newtop; } newdb = (MENT *)MEMALLOC((newsize + 1) * sizeof(MENT)); ISOUTOFMEMORY(newdb); if (mudstate.mail_list) { mudstate.mail_list -= 1; memcpy(newdb, mudstate.mail_list, (mudstate.mail_db_top + 1) * sizeof(MENT)); MEMFREE(mudstate.mail_list); } mudstate.mail_list = newdb + 1; newdb = NULL; for (i = mudstate.mail_db_top; i < newtop; i++) { mudstate.mail_list[i].count = 0; mudstate.mail_list[i].message = NULL; } mudstate.mail_db_top = newtop; mudstate.mail_db_size = newsize; } /* * make_mail_freelist - move the freelist to the first empty slot in the mail * * database. */ static void make_mail_freelist() { int i; for (i = 0; i < mudstate.mail_db_top; i++) { if (mudstate.mail_list[i].message == NULL) { mudstate.mail_freelist = i; return; } } mail_db_grow(i + 1); mudstate.mail_freelist = i; } /* * add_mail_message - adds a new text message to the mail database, and returns * * a unique number for that message. */ static int add_mail_message(dbref player, char *message) { int number; char *atrstr, *execstr, *msg, *bp, *str; int aflags; dbref aowner; if (!_stricmp(message, "clear")) { notify(player, "MAIL: You probably don't wanna send mail saying 'clear'."); return -1; } if (!mudstate.mail_list) { mail_db_grow(1); } // Add an extra bit of protection here. // while (mudstate.mail_list[mudstate.mail_freelist].message != NULL) { make_mail_freelist(); } number = mudstate.mail_freelist; atrstr = atr_get(player, A_SIGNATURE, &aowner, &aflags); execstr = bp = alloc_lbuf("add_mail_message"); str = atrstr; TinyExec(execstr, &bp, 0, player, player, EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, (char **)NULL, 0); *bp = '\0'; msg = bp = alloc_lbuf("add_mail_message.2"); str = message; TinyExec(msg, &bp, 0, player, player, EV_EVAL | EV_FCHECK | EV_NO_COMPRESS, &str, (char **)NULL, 0); *bp = '\0'; char *pMessageBody = tprintf("%s %s", msg, execstr); #ifdef RADIX_COMPRESSION int len = string_compress(pMessageBody, msgbuff); mudstate.mail_list[number].message = BufferCloneLen(msgbuff, len); #else mudstate.mail_list[number].message = StringClone(pMessageBody); #endif // RADIX_COMPRESSION free_lbuf(atrstr); free_lbuf(execstr); free_lbuf(msg); make_mail_freelist(); return number; } /* * add_mail_message_nosig - used for reading in old style messages from disk */ static int add_mail_message_nosig(char *message) { int number = mudstate.mail_freelist; if (!mudstate.mail_list) { mail_db_grow(1); } if (strlen(message) > LBUF_SIZE-1) { STARTLOG(LOG_BUGS, "BUG", "MAIL"); log_text(tprintf("add_mail_message_nosig: Mail message %d truncated.", number)); ENDLOG; message[LBUF_SIZE-1] = '\0'; } #ifdef RADIX_COMPRESSION int len = string_compress(message, msgbuff); mudstate.mail_list[number].message = BufferCloneLen(msgbuff, len); #else mudstate.mail_list[number].message = StringClone(message); #endif // RADIX_COMPRESSION make_mail_freelist(); return number; } /* * new_mail_message - used for reading messages in from disk which already have * * a number assigned to them. */ #ifdef RADIX_COMPRESSION static void new_mail_message(char *message, int number, int len) #else static void new_mail_message(char *message, int number) #endif { if (strlen(message) > LBUF_SIZE-1) { STARTLOG(LOG_BUGS, "BUG", "MAIL"); log_text(tprintf("new_mail_message: Mail message %d truncated.", number)); ENDLOG; message[LBUF_SIZE-1] = '\0'; } #ifdef RADIX_COMPRESSION mudstate.mail_list[number].message = BufferCloneLen(message, len); #else mudstate.mail_list[number].message = StringClone(message); #endif } /* * add_count - increments the reference count for any particular message */ static DCL_INLINE void add_count(int number) { mudstate.mail_list[number].count++; } /* * delete_mail_message - decrements the reference count for a message, and * * deletes the message if the counter reaches 0. */ static void delete_mail_message(int number) { mudstate.mail_list[number].count--; if (mudstate.mail_list[number].count < 1) { MEMFREE(mudstate.mail_list[number].message); mudstate.mail_list[number].message = NULL; mudstate.mail_list[number].count = 0; } } /* * get_mail_message - returns the text for a particular number. This text * * should NOT be modified. */ char *get_mail_message(int number) { #ifdef RADIX_COMPRESSION static char buff[LBUF_SIZE]; #endif if (mudstate.mail_list[number].message != NULL) { return mudstate.mail_list[number].message; } else { delete_mail_message(number); #ifdef RADIX_COMPRESSION string_compress("MAIL: This mail message does not exist in the database. Please alert your admin.", msgbuff); strcpy(buff, msgbuff); return buff; #else return "MAIL: This mail message does not exist in the database. Please alert your admin."; #endif } } /*-------------------------------------------------------------------------* * User mail functions (these are called from game.c) * * do_mail - cases * do_mail_read - read messages * do_mail_list - list messages * do_mail_flags - tagging, untagging, clearing, unclearing of messages * do_mail_file - files messages into a new folder * do_mail_fwd - forward messages to another player(s) * do_mail_reply - reply to a message * do_mail_count - count messages * do_mail_purge - purge cleared messages * do_mail_change_folder - change current folder *-------------------------------------------------------------------------*/ /* * Change or rename a folder */ void do_mail_change_folder(dbref player, char *fld, char *newname) { int pfld; char *p; if (!fld || !*fld) { /* * Check mail in all folders */ for (pfld = MAX_FOLDERS; pfld >= 0; pfld--) check_mail(player, pfld, 1); pfld = player_folder(player); notify(player, tprintf("MAIL: Current folder is %d [%s].", pfld, get_folder_name(player, pfld))); return; } pfld = parse_folder(player, fld); if (pfld < 0) { notify(player, "MAIL: What folder is that?"); return; } if (newname && *newname) { /* * We're changing a folder name here */ if (strlen(newname) > FOLDER_NAME_LEN) { notify(player, "MAIL: Folder name too long"); return; } for (p = newname; Tiny_IsAlphaNumeric[(unsigned char)*p]; p++) ; if (*p != '\0') { notify(player, "MAIL: Illegal folder name"); return; } add_folder_name(player, pfld, newname); notify(player, tprintf("MAIL: Folder %d now named '%s'", pfld, newname)); } else { /* * Set a new folder */ set_player_folder(player, pfld); notify(player, tprintf("MAIL: Current folder set to %d [%s].", pfld, get_folder_name(player, pfld))); } } void do_mail_tag(dbref player, char *msglist) { do_mail_flags(player, msglist, M_TAG, 0); } void do_mail_safe(dbref player, char *msglist) { do_mail_flags(player, msglist, M_SAFE, 0); } void do_mail_clear(dbref player, char *msglist) { do_mail_flags(player, msglist, M_CLEARED, 0); } void do_mail_untag(dbref player, char *msglist) { do_mail_flags(player, msglist, M_TAG, 1); } void do_mail_unclear(dbref player, char *msglist) { do_mail_flags(player, msglist, M_CLEARED, 1); } /* * adjust the flags of a set of messages */ // if 1, clear the flag static void do_mail_flags(dbref player, char *msglist, mail_flag flag, int negate) { struct mail *mp; struct mail_selector ms; int i = 0, j = 0, folder; if (!parse_msglist(msglist, &ms, player)) { return; } folder = player_folder(player); for (mp = (struct mail *)hashfindLEN(&player, sizeof(player), &mudstate.mail_htab); mp; mp = mp->next) { if (All(ms) || (Folder(mp) == folder)) { i++; if (mail_match(mp, ms, i)) { j++; if (negate) { mp->read &= ~flag; } else { mp->read |= flag; } switch (flag) { case M_TAG: notify(player, tprintf("MAIL: Msg #%d %s.", i, negate ? "untagged" : "tagged")); break; case M_CLEARED: if (Unread(mp) && !negate) { notify(player, tprintf("MAIL: Unread Msg #%d cleared! Use @mail/unclear %d to recover.", i, i)); } else { notify(player, tprintf("MAIL: Msg #%d %s.", i, negate ? "uncleared" : "cleared")); } break; case M_SAFE: notify(player, tprintf("MAIL: Msg #%d marked safe.", i)); break; } } } } if (!j) { // Ran off the end of the list without finding anything. // notify(player, "MAIL: You don't have any matching messages!"); } } /* * Change a message's folder */ void do_mail_file(dbref player, char *msglist, char *folder) { struct mail *mp; struct mail_selector ms; int foldernum, origfold; int i = 0, j = 0; if (!parse_msglist(msglist, &ms, player)) { return; } if ((foldernum = parse_folder(player, folder)) == -1) { notify(player, "MAIL: Invalid folder specification"); return; } origfold = player_folder(player); for (mp = (struct mail *)hashfindLEN(&player, sizeof(player), &mudstate.mail_htab); mp; mp = mp->next) { if (All(ms) || (Folder(mp) == origfold)) { i++; if (mail_match(mp, ms, i)) { j++; // Clear the folder. // mp->read &= M_FMASK; mp->read |= FolderBit(foldernum); notify(player, tprintf("MAIL: Msg %d filed in folder %d", i, foldernum)); } } } if (!j) { // Ran off the end of the list without finding anything. // notify(player, "MAIL: You don't have any matching messages!"); } } /* * print a mail message(s) */ void do_mail_read(dbref player, char *msglist) { struct mail *mp; char *buff, *status, *names; struct mail_selector ms; int i = 0, j = 0, folder; if (!parse_msglist(msglist, &ms, player)) { return; } buff = alloc_lbuf("do_mail_read.1"); folder = player_folder(player); mp = (struct mail *)hashfindLEN( &player, sizeof(player), &mudstate.mail_htab); for ( ; mp; mp = mp->next) { if (Folder(mp) == folder) { i++; if (mail_match(mp, ms, i)) { // Read it. // j++; #ifdef RADIX_COMPRESSION string_decompress(get_mail_message(mp->number), buff); #else buff[LBUF_SIZE-1] = '\0'; strncpy(buff, get_mail_message(mp->number), LBUF_SIZE); if (buff[LBUF_SIZE-1] != '\0') { STARTLOG(LOG_BUGS, "BUG", "MAIL"); log_text(tprintf("do_mail_read: %s: Mail message %d truncated.", Name(player), mp->number)); ENDLOG; buff[LBUF_SIZE-1] = '\0'; } #endif // RADIX_COMPRESSION notify(player, DASH_LINE); status = status_string(mp); names = make_namelist(player, (char *)mp->tolist); #ifdef RADIX_COMPRESSION string_decompress(mp->subject, subbuff); string_decompress(mp->time, timebuff); notify(player, tprintf("%-3d From: %-*s At: %-25s %s\r\nFldr : %-2d Status: %s\r\nTo : %-65s\r\nSubject: %-65s", i, PLAYER_NAME_LIMIT - 6, Name(mp->from), timebuff, (Connected(mp->from) && (!Hidden(mp->from) || Hasprivs(player))) ? " (Conn)" : " ", folder, status, names, subbuff)); #else notify(player, tprintf("%-3d From: %-*s At: %-25s %s\r\nFldr : %-2d Status: %s\r\nTo : %-65s\r\nSubject: %-65s", i, PLAYER_NAME_LIMIT - 6, Name(mp->from), mp->time, (Connected(mp->from) && (!Hidden(mp->from) || Hasprivs(player))) ? " (Conn)" : " ", folder, status, names, mp->subject)); #endif // RADIX_COMPRESSION free_lbuf(names); free_lbuf(status); notify(player, DASH_LINE); notify(player, buff); notify(player, DASH_LINE); if (Unread(mp)) { // Mark message as read. // mp->read |= M_ISREAD; } } } } free_lbuf(buff); if (!j) { // Ran off the end of the list without finding anything. // notify(player, "MAIL: You don't have that many matching messages!"); } } void do_mail_retract(dbref player, char *name, char *msglist) { dbref target; struct mail *mp, *nextp; struct mail_selector ms; int i = 0, j = 0; target = lookup_player(player, name, 1); if (target == NOTHING) { notify(player, "MAIL: No such player."); return; } if (!parse_msglist(msglist, &ms, target)) { return; } for (mp = (struct mail *)hashfindLEN(&target, sizeof(target), &mudstate.mail_htab); mp; mp = nextp) { if (mp->from == player) { i++; if (mail_match(mp, ms, i)) { j++; if (Unread(mp)) { if (mp->prev == NULL) { if (mp->next == NULL) { hashdeleteLEN(&target, sizeof(target), &mudstate.mail_htab); } else { hashreplLEN(&target, sizeof(target), (int *)(mp->next), &mudstate.mail_htab); } } else if (mp->next == NULL) mp->prev->next = NULL; if (mp->prev != NULL) mp->prev->next = mp->next; if (mp->next != NULL) mp->next->prev = mp->prev; nextp = mp->next; MEMFREE((char *)mp->subject); delete_mail_message(mp->number); MEMFREE((char *)mp->time); MEMFREE((char *)mp->tolist); MEMFREE(mp); notify(player, "MAIL: Mail retracted."); } else { notify(player, "MAIL: That message has been read."); nextp = mp->next; } } else { nextp = mp->next; } } else { nextp = mp->next; } } if (!j) { /* * ran off the end of the list without finding anything */ notify(player, "MAIL: No matching messages."); } } void do_mail_review(dbref player, char *name, char *msglist) { struct mail *mp; struct mail_selector ms; int i = 0, j = 0; dbref target; char *tbuf1, *msg, *status, *bp, *str; target = lookup_player(player, name, 1); if (target == NOTHING) { notify(player, "MAIL: No such player."); return; } if (!msglist || !*msglist) { notify(player, tprintf("-------------------- MAIL: %-25s ------------------", Name(target))); for (mp = (struct mail *)hashfindLEN(&target, sizeof(target), &mudstate.mail_htab); mp; mp = mp->next) { if (mp->from == player) { i++; /* * list it */ #ifdef RADIX_COMPRESSION string_decompress(get_mail_message(mp->number), msgbuff); string_decompress(mp->time, timebuff); string_decompress(mp->subject, subbuff); notify(player, tprintf("[%s] %-3d (%4d) From: %-*s Sub: %.25s", status_chars(mp), i, strlen(msgbuff), PLAYER_NAME_LIMIT - 6, Name(mp->from), subbuff)); #else notify(player, tprintf("[%s] %-3d (%4d) From: %-*s Sub: %.25s", status_chars(mp), i, strlen(get_mail_message(mp->number)), PLAYER_NAME_LIMIT - 6, Name(mp->from), mp->subject)); #endif /* * RADIX_COMPRESSION */ } } notify(player, DASH_LINE); } else { tbuf1 = alloc_lbuf("do_mail_read"); if (!parse_msglist(msglist, &ms, target)) { free_lbuf(tbuf1); return; } for (mp = (struct mail *)hashfindLEN(&target, sizeof(target), &mudstate.mail_htab); mp; mp = mp->next) { if (mp->from == player) { i++; if (mail_match(mp, ms, i)) { /* * Read it */ j++; status = status_string(mp); #ifdef RADIX_COMPRESSION string_decompress(get_mail_message(mp->number), msgbuff); string_decompress(mp->time, timebuff); string_decompress(mp->subject, subbuff); msg = bp = alloc_lbuf("do_mail_review"); str = msgbuff; TinyExec(msg, &bp, 0, player, player, EV_EVAL | EV_FCHECK | EV_NO_COMPRESS, &str, (char **)NULL, 0); *bp = '\0'; notify(player, DASH_LINE); notify(player, tprintf("%-3d From: %-*s At: %-25s %s\r\nFldr : %-2d Status: %s\r\nSubject: %-65s", i, PLAYER_NAME_LIMIT - 6, Name(mp->from), timebuff, (Connected(mp->from) && (!Hidden(mp->from) || Hasprivs(player))) ? " (Conn)" : " ", 0, status, subbuff)); #else msg = bp = alloc_lbuf("do_mail_review"); str = get_mail_message(mp->number); TinyExec(msg, &bp, 0, player, player, EV_EVAL | EV_FCHECK | EV_NO_COMPRESS, &str, (char **)NULL, 0); *bp = '\0'; notify(player, DASH_LINE); notify(player, tprintf("%-3d From: %-*s At: %-25s %s\r\nFldr : %-2d Status: %s\r\nSubject: %-65s", i, PLAYER_NAME_LIMIT - 6, Name(mp->from), mp->time, (Connected(mp->from) && (!Hidden(mp->from) || Hasprivs(player))) ? " (Conn)" : " ", 0, status, mp->subject)); #endif // RADIX_COMPRESSION free_lbuf(status); notify(player, DASH_LINE); strcpy(tbuf1, msg); notify(player, tbuf1); notify(player, DASH_LINE); free_lbuf(msg); } } } if (!j) { /* * ran off the end of the list without finding * * * * anything */ notify(player, "MAIL: You don't have that many matching messages!"); } free_lbuf(tbuf1); } } void do_mail_list(dbref player, char *msglist, int sub) { struct mail *mp; struct mail_selector ms; int i = 0, folder; char *time; if (!parse_msglist(msglist, &ms, player)) { return; } folder = player_folder(player); notify(player, tprintf( "--------------------------- MAIL: Folder %d ----------------------------", folder)); for (mp = (struct mail *)hashfindLEN(&player, sizeof(player), &mudstate.mail_htab); mp; mp = mp->next) { if (Folder(mp) == folder) { i++; if (mail_match(mp, ms, i)) { /* * list it */ #ifdef RADIX_COMPRESSION string_decompress(get_mail_message(mp->number), msgbuff); string_decompress(mp->subject, subbuff); string_decompress(mp->time, timebuff); time = mail_list_time(timebuff); if (sub) notify(player, tprintf("[%s] %-3d (%4d) From: %-*s Sub: %.25s", status_chars(mp), i, strlen(msgbuff), PLAYER_NAME_LIMIT - 6, Name(mp->from), subbuff)); else notify(player, tprintf("[%s] %-3d (%4d) From: %-*s At: %s %s", status_chars(mp), i, strlen(msgbuff), PLAYER_NAME_LIMIT - 6, Name(mp->from), time, ((Connected(mp->from) && (!Hidden(mp->from) || Hasprivs(player))) ? "Conn" : " "))); #else time = mail_list_time(mp->time); if (sub) notify(player, tprintf("[%s] %-3d (%4d) From: %-*s Sub: %.25s", status_chars(mp), i, strlen(get_mail_message(mp->number)), PLAYER_NAME_LIMIT - 6, Name(mp->from), mp->subject)); else notify(player, tprintf("[%s] %-3d (%4d) From: %-*s At: %s %s", status_chars(mp), i, strlen(get_mail_message(mp->number)), PLAYER_NAME_LIMIT - 6, Name(mp->from), time, ((Connected(mp->from) && (!Hidden(mp->from) || Hasprivs(player))) ? "Conn" : " "))); #endif /* * RADIX_COMPRESSION */ free_lbuf(time); } } } notify(player, DASH_LINE); } static char *mail_list_time(const char *the_time) { char *new0; char *p, *q; int i; p = (char *)the_time; q = new0 = alloc_lbuf("mail_list_time"); if (!p || !*p) { *new0 = '\0'; return new0; } /* * Format of the_time is: day mon dd hh:mm:ss yyyy */ /* * Chop out :ss */ for (i = 0; i < 16; i++) { if (*p) *q++ = *p++; } for (i = 0; i < 3; i++) { if (*p) p++; } for (i = 0; i < 5; i++) { if (*p) *q++ = *p++; } *q = '\0'; return new0; } void do_mail_purge(dbref player) { struct mail *mp, *nextp; /* * Go through player's mail, and remove anything marked cleared */ for (mp = (struct mail *)hashfindLEN(&player, sizeof(player), &mudstate.mail_htab); mp; mp = nextp) { if (Cleared(mp)) { /* * Delete this one */ /* * head and tail of the list are special */ if (mp->prev == NULL) { if (mp->next == NULL) { hashdeleteLEN(&player, sizeof(player), &mudstate.mail_htab); } else { hashreplLEN(&player, sizeof(player), (int *)(mp->next), &mudstate.mail_htab); } } else if (mp->next == NULL) { mp->prev->next = NULL; } /* * relink the list */ if (mp->prev != NULL) mp->prev->next = mp->next; if (mp->next != NULL) mp->next->prev = mp->prev; /* * save the pointer */ nextp = mp->next; /* * then wipe */ MEMFREE((char *)mp->subject); delete_mail_message(mp->number); MEMFREE((char *)mp->time); MEMFREE((char *)mp->tolist); MEMFREE(mp); } else { nextp = mp->next; } } notify(player, "MAIL: Mailbox purged."); } void do_mail_fwd(dbref player, char *msg, char *tolist) { struct mail *mp; int num; if (Flags2(player) & PLAYER_MAILS) { notify(player, "MAIL: Mail message already in progress."); return; } if (!msg || !*msg) { notify(player, "MAIL: No message list."); return; } if (!tolist || !*tolist) { notify(player, "MAIL: To whom should I forward?"); return; } num = Tiny_atol(msg); if (!num) { notify(player, "MAIL: I don't understand that message number."); return; } mp = mail_fetch(player, num); if (!mp) { notify(player, "MAIL: You can't forward non-existent messages."); return; } #ifdef RADIX_COMPRESSION string_decompress(mp->subject, subbuff); string_decompress(get_mail_message(mp->number), msgbuff); do_expmail_start(player, tolist, tprintf("%s (fwd from %s)", subbuff, Name(mp->from))); atr_add_raw(player, A_MAILMSG, msgbuff); #else do_expmail_start(player, tolist, tprintf("%s (fwd from %s)", mp->subject, Name(mp->from))); atr_add_raw(player, A_MAILMSG, get_mail_message(mp->number)); #endif char *pValue = atr_get_raw(player, A_MAILFLAGS); int iFlag = M_FORWARD; if (pValue) { iFlag |= Tiny_atol(pValue); } atr_add_raw(player, A_MAILFLAGS, Tiny_ltoa_t(iFlag)); } void do_mail_reply(dbref player, char *msg, int all, int key) { char *tolist, *bp, *p, *names, *oldlist; if (Flags2(player) & PLAYER_MAILS) { notify(player, "MAIL: Mail message already in progress."); return; } if (!msg || !*msg) { notify(player, "MAIL: No message list."); return; } int num = Tiny_atol(msg); if (!num) { notify(player, "MAIL: I don't understand that message number."); return; } struct mail *mp = mail_fetch(player, num); if (!mp) { notify(player, "MAIL: You can't reply to non-existent messages."); return; } if (all) { names = alloc_lbuf("do_mail_reply.names"); oldlist = alloc_lbuf("do_mail_reply.oldlist"); bp = names; *bp = '\0'; strcpy(oldlist, (char *)mp->tolist); TINY_STRTOK_STATE tts; Tiny_StrTokString(&tts, oldlist); Tiny_StrTokControl(&tts, " "); for (p = Tiny_StrTokParse(&tts); p; p = Tiny_StrTokParse(&tts)) { if (*p == '*') { safe_str(p, names, &bp); safe_chr(' ', names, &bp); } else if (Tiny_atol(p) != mp->from) { safe_chr('"', names, &bp); safe_str(Name(Tiny_atol(p)), names, &bp); safe_chr('"', names, &bp); safe_chr(' ', names, &bp); } } free_lbuf(oldlist); safe_chr(' ', names, &bp); safe_chr('"', names, &bp); safe_str(Name(mp->from), names, &bp); safe_chr('"', names, &bp); *bp = '\0'; tolist = names; } else { tolist = msg; } const char *pSubject; char *pMessage; const char *pTime; #ifdef RADIX_COMPRESSION string_decompress(mp->subject, subbuff); pSubject = subbuff; string_decompress(get_mail_message(mp->number), msgbuff); pMessage = msgbuff; string_decompress(mp->time, timebuff); pTime = timebuff; #else pSubject = mp->subject; pMessage = get_mail_message(mp->number); pTime = mp->time; #endif if (strncmp(pSubject, "Re:", 3)) { do_expmail_start(player, tolist, tprintf("Re: %s", pSubject)); } else { do_expmail_start(player, tolist, tprintf("%s", pSubject)); } if (key & MAIL_QUOTE) { char *pMessageBody = tprintf("On %s, %s wrote:\r\n\r\n%s\r\n\r\n********** End of included message from %s\r\n", pTime, Name(mp->from), pMessage, Name(mp->from)); atr_add_raw(player, A_MAILMSG, pMessageBody); } char *pValue = atr_get_raw(player, A_MAILFLAGS); int iFlag = M_REPLY; if (pValue) { iFlag |= Tiny_atol(pValue); } atr_add_raw(player, A_MAILFLAGS, Tiny_ltoa_t(iFlag)); if (all) { free_lbuf(names); } } /*-------------------------------------------------------------------------* * Admin mail functions * * do_mail_nuke - clear & purge mail for a player, or all mail in db. * do_mail_stat - stats on mail for a player, or for all db. * do_mail_debug - fix mail with a sledgehammer *-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------* * Basic mail functions *-------------------------------------------------------------------------*/ struct mail *mail_fetch(dbref player, int num) { struct mail *mp; int i = 0; for (mp = (struct mail *)hashfindLEN(&player, sizeof(player), &mudstate.mail_htab); mp; mp = mp->next) { if (Folder(mp) == player_folder(player)) { i++; if (i == num) return mp; } } return NULL; } /* * returns count of read, unread, & cleared messages as rcount, ucount, * * ccount */ void count_mail(dbref player, int folder, int *rcount, int *ucount, int *ccount) { struct mail *mp; int rc, uc, cc; cc = rc = uc = 0; for (mp = (struct mail *)hashfindLEN(&player, sizeof(player), &mudstate.mail_htab); mp; mp = mp->next) { if (Folder(mp) == folder) { if (Read(mp)) rc++; else uc++; if (Cleared(mp)) cc++; } } *rcount = rc; *ucount = uc; *ccount = cc; } void urgent_mail(dbref player, int folder, int *ucount) { struct mail *mp; int uc; uc = 0; for (mp = (struct mail *)hashfindLEN(&player, sizeof(player), &mudstate.mail_htab); mp; mp = mp->next) { if (Folder(mp) == folder) { if (!(Read(mp)) && (Urgent(mp))) uc++; } } *ucount = uc; } static void send_mail(dbref player, dbref target, const char *tolist, const char *subject, int number, mail_flag flags, int silent) { char tbuf1[30]; if (Typeof(target) != TYPE_PLAYER) { notify(player, "MAIL: You cannot send mail to non-existent people."); delete_mail_message(number); return; } CLinearTimeAbsolute ltaNow; ltaNow.GetLocal(); char *temp = ltaNow.ReturnDateString(); strcpy(tbuf1, temp); // Initialize the appropriate fields. // struct mail *newp = (struct mail *)MEMALLOC(sizeof(struct mail)); ISOUTOFMEMORY(newp); newp->to = target; newp->from = player; newp->tolist = StringClone(tolist); newp->number = number; add_count(number); #ifdef RADIX_COMPRESSION int len = string_compress(tbuf1, timebuff); newp->time = BufferCloneLen(timebuff, len); len = string_compress(subject, subbuff); newp->subject = BufferCloneLen(subbuff, len); #else newp->time = StringClone(tbuf1); newp->subject = StringClone(subject); #endif // RADIX_COMPRESSION // Send to folder 0 // newp->read = flags & M_FMASK; // If this is the first message, it is the head and the tail. // BOOL bFailed = FALSE; struct mail *mptr = (struct mail *)hashfindLEN(&target, sizeof(target), &mudstate.mail_htab); if (mptr) { while (mptr->next != NULL) { mptr = mptr->next; } mptr->next = newp; newp->next = NULL; newp->prev = mptr; } else { hashaddLEN(&target, sizeof(target), (int *)newp, &mudstate.mail_htab); newp->next = NULL; newp->prev = NULL; } if (!bFailed) { /* * notify people */ if (!silent) { notify(player, tprintf("MAIL: You sent your message to %s.", Name(target))); } notify(target, tprintf("MAIL: You have a new message from %s.", Name(player))); did_it(player, target, A_MAIL, NULL, 0, NULL, A_AMAIL, NULL, NOTHING); } } void do_mail_nuke(dbref player) { struct mail *mp, *nextp; dbref thing; if (!God(player)) { notify(player, "The postal service issues a warrant for your arrest."); return; } /* * walk the list */ MAIL_ITER_SAFE(mp, thing, nextp) { nextp = mp->next; delete_mail_message(mp->number); MEMFREE((char *)mp->subject); MEMFREE((char *)mp->tolist); MEMFREE((char *)mp->time); MEMFREE(mp); } log_text(tprintf("** MAIL PURGE ** done by %s(#%d).", Name(player), (int)player)); notify(player, "You annihilate the post office. All messages cleared."); } void do_mail_debug(dbref player, char *action, char *victim) { dbref target, thing; struct mail *mp, *nextp; if (!Wizard(player)) { notify(player, "Go get some bugspray."); return; } if (string_prefix("clear", action)) { target = lookup_player(player, victim, 1); if (target == NOTHING) { init_match(player, victim, NOTYPE); match_absolute(); target = match_result(); } if (target == NOTHING) { notify(player, tprintf("%s: no such player.", victim)); return; } do_mail_clear(target, NULL); do_mail_purge(target); notify(player, tprintf("Mail cleared for %s(#%d).", Name(target), target)); return; } else if (string_prefix("sanity", action)) { MAIL_ITER_ALL(mp, thing) { if (!Good_obj(mp->to)) notify(player, tprintf("Bad object #%d has mail.", mp->to)); else if (Typeof(mp->to) != TYPE_PLAYER) notify(player, tprintf("%s(#%d) has mail but is not a player.", Name(mp->to), mp->to)); } notify(player, "Mail sanity check completed."); } else if (string_prefix("fix", action)) { MAIL_ITER_SAFE(mp, thing, nextp) { if (!Good_obj(mp->to) || (Typeof(mp->to) != TYPE_PLAYER)) { notify(player, tprintf("Fixing mail for #%d.", mp->to)); /* * Delete this one */ /* * head and tail of the list are * special */ if (mp->prev == NULL) { if (mp->next == NULL) { hashdeleteLEN(&player, sizeof(player), &mudstate.mail_htab); } else { hashreplLEN(&player, sizeof(player), (int *)(mp->next), &mudstate.mail_htab); } } else if (mp->next == NULL) { mp->prev->next = NULL; } /* * relink the list */ if (mp->prev != NULL) mp->prev->next = mp->next; if (mp->next != NULL) mp->next->prev = mp->prev; /* * save the pointer */ nextp = mp->next; /* * then wipe */ MEMFREE((char *)mp->subject); delete_mail_message(mp->number); MEMFREE((char *)mp->time); MEMFREE((char *)mp->tolist); MEMFREE(mp); } else nextp = mp->next; } notify(player, "Mail sanity fix completed."); } else { notify(player, "That is not a debugging option."); return; } } void do_mail_stats(dbref player, char *name, int full) { dbref target, thing; int fc, fr, fu, tc, tr, tu, fchars, tchars, cchars, count; char last[50]; struct mail *mp; #ifdef RADIX_COMPRESSION int len; #endif fc = fr = fu = tc = tr = tu = cchars = fchars = tchars = count = 0; /* * find player */ if ((*name == '\0') || !name) { if Wizard (player) target = AMBIGUOUS; else target = player; } else if (*name == NUMBER_TOKEN) { target = Tiny_atol(&name[1]); if (!Good_obj(target) || (Typeof(target) != TYPE_PLAYER)) target = NOTHING; } else if (!_stricmp(name, "me")) { target = player; } else { target = lookup_player(player, name, 1); } if (target == NOTHING) { init_match(player, name, NOTYPE); match_absolute(); target = match_result(); } if (target == NOTHING) { notify(player, tprintf("%s: No such player.", name)); return; } if (!Wizard(player) && (target != player)) { notify(player, "The post office protects privacy!"); return; } /* * this comand is computationally expensive */ if (!payfor(player, mudconf.searchcost)) { notify(player, tprintf("Finding mail stats costs %d %s.", mudconf.searchcost, (mudconf.searchcost == 1) ? mudconf.one_coin : mudconf.many_coins)); return; } if (target == AMBIGUOUS) { /* * stats for all */ if (full == 0) { MAIL_ITER_ALL(mp, thing) { count++; } notify(player, tprintf("There are %d messages in the mail spool.", count)); return; } else if (full == 1) { MAIL_ITER_ALL(mp, thing) { if (Cleared(mp)) fc++; else if (Read(mp)) fr++; else fu++; } notify(player, tprintf("MAIL: There are %d msgs in the mail spool, %d unread, %d cleared.", fc + fr + fu, fu, fc)); return; } else { MAIL_ITER_ALL(mp, thing) { #ifdef RADIX_COMPRESSION if (Cleared(mp)) { fc++; len = string_decompress(get_mail_message(mp->number), msgbuff); cchars += len; } else if (Read(mp)) { fr++; len = string_decompress(get_mail_message(mp->number), msgbuff); fchars += len; } else { fu++; len = string_decompress(get_mail_message(mp->number), msgbuff); tchars += len; } #else if (Cleared(mp)) { fc++; cchars += strlen(get_mail_message(mp->number)); } else if (Read(mp)) { fr++; fchars += strlen(get_mail_message(mp->number)); } else { fu++; tchars += strlen(get_mail_message(mp->number)); } #endif /* * RADIX_COMPRESSION */ } notify(player, tprintf("MAIL: There are %d old msgs in the mail spool, totalling %d characters.", fr, fchars)); notify(player, tprintf("MAIL: There are %d new msgs in the mail spool, totalling %d characters.", fu, tchars)); notify(player, tprintf("MAIL: There are %d cleared msgs in the mail spool, totalling %d characters.", fc, cchars)); return; } } /* * individual stats */ if (full == 0) { /* * just count number of messages */ MAIL_ITER_ALL(mp, thing) { if (mp->from == target) fr++; if (mp->to == target) tr++; } notify(player, tprintf("%s sent %d messages.", Name(target), fr)); notify(player, tprintf("%s has %d messages.", Name(target), tr)); return; } /* * more detailed message count */ MAIL_ITER_ALL(mp, thing) { if (mp->from == target) { if (Cleared(mp)) fc++; else if (Read(mp)) fr++; else fu++; if (full == 2) #ifdef RADIX_COMPRESSION len = string_compress(get_mail_message(mp->number), msgbuff); fchars += len; #else fchars += strlen(get_mail_message(mp->number)); #endif /* * RADIX_COMPRESSION */ } if (mp->to == target) { if (!tr && !tu) { #ifdef RADIX_COMPRESSION string_decompress(mp->time, timebuff); strcpy(last, timebuff); #else strcpy(last, mp->time); #endif /* * RADIX_COMPRESSION */ } if (Cleared(mp)) tc++; else if (Read(mp)) tr++; else tu++; if (full == 2) { #ifdef RADIX_COMPRESSION len = string_decompress(get_mail_message(mp->number), msgbuff); tchars += len; #else tchars += strlen(get_mail_message(mp->number)); #endif /* * RADIX_COMPRESSION */ } } } notify(player, tprintf("Mail statistics for %s:", Name(target))); if (full == 1) { notify(player, tprintf("%d messages sent, %d unread, %d cleared.", fc + fr + fu, fu, fc)); notify(player, tprintf("%d messages received, %d unread, %d cleared.", tc + tr + tu, tu, tc)); } else { notify(player, tprintf("%d messages sent, %d unread, %d cleared, totalling %d characters.", fc + fr + fu, fu, fc, fchars)); notify(player, tprintf("%d messages received, %d unread, %d cleared, totalling %d characters.", tc + tr + tu, tu, tc, tchars)); } if (tc + tr + tu > 0) notify(player, tprintf("Last is dated %s", last)); } /*-------------------------------------------------------------------------* * Main mail routine for @mail w/o a switch *-------------------------------------------------------------------------*/ void do_mail_stub(dbref player, char *arg1, char *arg2) { if (!arg1 || !*arg1) { if (arg2 && *arg2) { notify(player, "MAIL: Invalid mail command."); return; } /* * just the "@mail" command */ do_mail_list(player, arg1, 1); return; } /* * purge a player's mailbox */ if (!_stricmp(arg1, "purge")) { do_mail_purge(player); return; } /* * clear message */ if (!_stricmp(arg1, "clear")) { do_mail_clear(player, arg2); return; } if (!_stricmp(arg1, "unclear")) { do_mail_unclear(player, arg2); return; } if (arg2 && *arg2) { /* * Sending mail */ do_expmail_start(player, arg1, arg2); return; } else { /* * Must be reading or listing mail - no arg2 */ if (Tiny_IsDigit[(unsigned char)*arg1] && !strchr(arg1, '-')) do_mail_read(player, arg1); else do_mail_list(player, arg1, 1); return; } } void do_mail(dbref player, dbref cause, int key, char *arg1, char *arg2) { if (!mudconf.have_mailer) { notify(player, "Mailer is disabled."); return; } switch (key & ~MAIL_QUOTE) { case 0: do_mail_stub(player, arg1, arg2); break; case MAIL_STATS: do_mail_stats(player, arg1, 0); break; case MAIL_DSTATS: do_mail_stats(player, arg1, 1); break; case MAIL_FSTATS: do_mail_stats(player, arg1, 2); break; case MAIL_DEBUG: do_mail_debug(player, arg1, arg2); break; case MAIL_NUKE: do_mail_nuke(player); break; case MAIL_FOLDER: do_mail_change_folder(player, arg1, arg2); break; case MAIL_LIST: do_mail_list(player, arg1, 0); break; case MAIL_READ: do_mail_read(player, arg1); break; case MAIL_CLEAR: do_mail_clear(player, arg1); break; case MAIL_UNCLEAR: do_mail_unclear(player, arg1); break; case MAIL_PURGE: do_mail_purge(player); break; case MAIL_FILE: do_mail_file(player, arg1, arg2); break; case MAIL_TAG: do_mail_tag(player, arg1); break; case MAIL_UNTAG: do_mail_untag(player, arg1); break; case MAIL_FORWARD: do_mail_fwd(player, arg1, arg2); break; case MAIL_REPLY: do_mail_reply(player, arg1, 0, key); break; case MAIL_REPLYALL: do_mail_reply(player, arg1, 1, key); break; case MAIL_SEND: do_expmail_stop(player, 0); break; case MAIL_EDIT: do_edit_msg(player, arg1, arg2); break; case MAIL_URGENT: do_expmail_stop(player, M_URGENT); break; case MAIL_ALIAS: do_malias_create(player, arg1, arg2); break; case MAIL_ALIST: do_malias_list_all(player); break; case MAIL_PROOF: do_mail_proof(player); break; case MAIL_ABORT: do_expmail_abort(player); break; case MAIL_QUICK: do_mail_quick(player, arg1, arg2); break; case MAIL_REVIEW: do_mail_review(player, arg1, arg2); break; case MAIL_RETRACT: do_mail_retract(player, arg1, arg2); break; case MAIL_CC: do_mail_cc(player, arg1); break; case MAIL_SAFE: do_mail_safe(player, arg1); break; } } int dump_mail(FILE *fp) { struct mail *mp, *mptr; dbref thing; int count = 0, i; /* * Write out version number */ fprintf(fp, "+V5\n"); putref(fp, mudstate.mail_db_top); DO_WHOLE_DB(thing) { if (isPlayer(thing)) { mptr = (struct mail *)hashfindLEN(&thing, sizeof(thing), &mudstate.mail_htab); if (mptr != NULL) { for (mp = mptr; mp; mp = mp->next) { putref(fp, mp->to); putref(fp, mp->from); putref(fp, mp->number); putstring(fp, mp->tolist); #ifdef RADIX_COMPRESSION string_decompress(mp->time, timebuff); string_decompress(mp->subject, subbuff); putstring(fp, timebuff); putstring(fp, subbuff); #else putstring(fp, mp->time); putstring(fp, mp->subject); #endif /* * RADIX_COMPRESSION */ putref(fp, mp->read); count++; } } } } fprintf(fp, "*** END OF DUMP ***\n"); /* * Add the db of mail messages */ for (i = 0; i < mudstate.mail_db_top; i++) { if (mudstate.mail_list[i].count > 0) { putref(fp, i); #ifdef RADIX_COMPRESSION string_decompress(get_mail_message(i),msgbuff); putstring(fp, msgbuff); #else putstring(fp, get_mail_message(i)); #endif } } fprintf(fp, "+++ END OF DUMP +++\n"); save_malias(fp); return count; } int load_mail(FILE *fp) { char nbuf1[8]; int mail_top = 0; int new0 = 0; int pennsub = 0; int read_tolist = 0; int read_newdb = 0; int read_new_strings = 0; #ifdef RADIX_COMPRESSION int len, number; #endif // Read the version number. // fgets(nbuf1, sizeof(nbuf1), fp); if (!strncmp(nbuf1, "+V2", 3)) { new0 = 1; } else if (!strncmp(nbuf1, "+V3", 3)) { new0 = 1; read_tolist = 1; } else if (!strncmp(nbuf1, "+V4", 3)) { new0 = 1; read_tolist = 1; read_newdb = 1; } else if (!strncmp(nbuf1, "+V5", 3)) { new0 = 1; read_tolist = 1; read_newdb = 1; read_new_strings = 1; } else if (!strncmp(nbuf1, "+1", 2)) { pennsub = 1; } if (pennsub) { // Toss away the number of messages. // fgets(nbuf1, sizeof(nbuf1), fp); } if (read_newdb) { mail_top = getref(fp); mail_db_grow(mail_top + 1); } else { mail_db_grow(1); } fgets(nbuf1, sizeof(nbuf1), fp); while (strncmp(nbuf1, "***", 3) != 0) { struct mail *mp = (struct mail *)MEMALLOC(sizeof(struct mail)); ISOUTOFMEMORY(mp); dbref nTo = Tiny_atol(nbuf1); struct mail *mptr = (struct mail *)hashfindLEN(&nTo, sizeof(nTo), &mudstate.mail_htab); if (mptr) { // Find the end of the list the hard way. // while (mptr->next != NULL) { mptr = mptr->next; } mptr->next = mp; mp->prev = mptr; } else { mp->prev = NULL; hashaddLEN(&nTo, sizeof(nTo), (int *)mp, &mudstate.mail_htab); } mp->next = NULL; mp->to = nTo; mp->from = getref(fp); if (read_newdb) { mp->number = getref(fp); add_count(mp->number); } if (read_tolist) { mp->tolist = StringClone(getstring_noalloc(fp, read_new_strings)); } else { mp->tolist = StringClone(Tiny_ltoa_t(mp->to)); } #ifdef RADIX_COMPRESSION len = string_compress(getstring_noalloc(fp, read_new_strings), timebuff); mp->time = BufferCloneLen(timebuff, len); if (pennsub) { len = string_compress(getstring_noalloc(fp, read_new_strings), subbuff); mp->subject = BufferCloneLen(subbuff, len); } else if (!new0) { len = string_compress("No subject", subbuff); mp->subject = BufferCloneLen(subbuff, len); } if (!read_newdb) { string_compress(getstring_noalloc(fp, read_new_strings), msgbuff); number = add_mail_message_nosig(msgbuff); add_count(number); mp->number = number; } if (new0) { len = string_compress(getstring_noalloc(fp, read_new_strings), subbuff); mp->subject = BufferCloneLen(subbuff, len); } else if (!pennsub) { len = string_compress("No subject", subbuff); mp->subject = BufferCloneLen(subbuff, len); } #else mp->time = StringClone(getstring_noalloc(fp, read_new_strings)); if (pennsub) { mp->subject = StringClone(getstring_noalloc(fp, read_new_strings)); } else if (!new0) { mp->subject = StringClone("No subject"); } if (!read_newdb) { int number = add_mail_message_nosig(getstring_noalloc(fp, read_new_strings)); add_count(number); mp->number = number; } if (new0) { mp->subject = StringClone(getstring_noalloc(fp, read_new_strings)); } else if (!pennsub) { mp->subject = StringClone("No subject"); } #endif // RADIX_COMPRESSION mp->read = getref(fp); fgets(nbuf1, sizeof(nbuf1), fp); } if (read_newdb) { fgets(nbuf1, sizeof(nbuf1), fp); while (strncmp(nbuf1, "+++", 3)) { int number = Tiny_atol(nbuf1); #ifdef RADIX_COMPRESSION len = string_compress(getstring_noalloc(fp, read_new_strings), msgbuff); new_mail_message(msgbuff, number, len); #else new_mail_message(getstring_noalloc(fp, read_new_strings), number); #endif fgets(nbuf1, sizeof(nbuf1), fp); } } load_malias(fp); return 1; } static int get_folder_number(dbref player, char *name) { int aflags; dbref aowner; char *atrstr; char *str, *pat, *res, *p, *bp; /* Look up a folder name and return the appopriate number */ atrstr = atr_get(player, A_MAILFOLDERS, &aowner, &aflags); if (!*atrstr) { free_lbuf(atrstr); return -1; } str = alloc_lbuf("get_folder_num_str"); bp = pat = alloc_lbuf("get_folder_num_pat"); strcpy(str, atrstr); safe_tprintf_str(pat, &bp, ":%s:", upcasestr(name)); res = (char *)strstr(str, pat); if (!res) { free_lbuf(str); free_lbuf(pat); free_lbuf(atrstr); return -1; } res += 2 + strlen(name); p = res; while (!Tiny_IsSpace[(unsigned char)*p]) { p++; } p = '\0'; free_lbuf(atrstr); free_lbuf(str); free_lbuf(pat); return Tiny_atol(res); } static char *get_folder_name(dbref player, int fld) { static char str[LBUF_SIZE]; char *pat; static char *old; char *r, *atrstr; int flags, len; /* * Get the name of the folder, or "nameless" */ pat = alloc_lbuf("get_folder_name"); sprintf(pat, "%d:", fld); old = NULL; atrstr = atr_get(player, A_MAILFOLDERS, &player, &flags); if (!*atrstr) { strcpy(str, "unnamed"); free_lbuf(pat); free_lbuf(atrstr); return str; } strcpy(str, atrstr); old = (char *)string_match(str, pat); free_lbuf(atrstr); if (old) { r = old + strlen(pat); while (*r != ':') { r++; } *r = '\0'; len = strlen(pat); free_lbuf(pat); return old + len; } else { strcpy(str, "unnamed"); free_lbuf(pat); return str; } } void add_folder_name(dbref player, int fld, char *name) { char *old, *res, *r, *atrstr; char *new0, *pat, *str, *tbuf; int aflags; /* * Muck with the player's MAILFOLDERS attrib to add a string of the * * * * * * form: number:name:number to it, replacing any such string * with a * * * * matching number. */ new0 = alloc_lbuf("add_folder_name.new"); pat = alloc_lbuf("add_folder_name.pat"); str = alloc_lbuf("add_folder_name.str"); tbuf = alloc_lbuf("add_folder_name.tbuf"); sprintf(new0, "%d:%s:%d ", fld, upcasestr(name), fld); sprintf(pat, "%d:", fld); /* * get the attrib and the old string, if any */ old = NULL; atrstr = atr_get(player, A_MAILFOLDERS, &player, &aflags); if (*atrstr) { strcpy(str, atrstr); old = (char *)string_match(str, pat); } if (old && *old) { strcpy(tbuf, str); r = old; while (!Tiny_IsSpace[(unsigned char)*r]) { r++; } *r = '\0'; res = (char *)replace_string(old, new0, tbuf); } else { r = res = alloc_lbuf("mail_folders"); if (*atrstr) { safe_str(str, res, &r); } safe_str(new0, res, &r); *r = '\0'; } /* * put the attrib back */ atr_add(player, A_MAILFOLDERS, res, player, AF_MDARK | AF_WIZARD | AF_NOPROG | AF_LOCK); free_lbuf(str); free_lbuf(pat); free_lbuf(new0); free_lbuf(tbuf); free_lbuf(atrstr); free_lbuf(res); } static int player_folder(dbref player) { /* * Return the player's current folder number. If they don't have one, * * * * * * * set * it to 0 */ int flags, number; char *atrstr; atrstr = atr_pget(player, A_MAILCURF, &player, &flags); if (!*atrstr) { free_lbuf(atrstr); set_player_folder(player, 0); return 0; } number = Tiny_atol(atrstr); free_lbuf(atrstr); return number; } void set_player_folder(dbref player, int fnum) { /* * Set a player's folder to fnum */ ATTR *a; char *tbuf1; tbuf1 = alloc_lbuf("set_player_folder"); Tiny_ltoa(fnum, tbuf1); a = (ATTR *)atr_num(A_MAILCURF); if (a) { atr_add(player, A_MAILCURF, tbuf1, GOD, a->flags); } else { // Shouldn't happen, but... // atr_add(player, A_MAILCURF, tbuf1, GOD, AF_ODARK | AF_WIZARD | AF_NOPROG | AF_LOCK); } free_lbuf(tbuf1); } static int parse_folder(dbref player, char *folder_string) { int fnum; /* * Given a string, return a folder #, or -1 The string is just a * * * * number, * for now. Later, this will be where named folders are * * * * handled */ if (!folder_string || !*folder_string) return -1; if (Tiny_IsDigit[(unsigned char)*folder_string]) { fnum = Tiny_atol(folder_string); if ((fnum < 0) || (fnum > MAX_FOLDERS)) return -1; else return fnum; } /* * Handle named folders here */ return get_folder_number(player, folder_string); } static int mail_match(struct mail *mp, struct mail_selector ms, int num) { mail_flag mpflag; // Does a piece of mail match the mail_selector? // if (ms.low && num < ms.low) return 0; if (ms.high && num > ms.high) return 0; if (ms.player && mp->from != ms.player) return 0; mpflag = Read(mp) ? mp->read : (mp->read | M_MSUNREAD); if (!(ms.flags & M_ALL) && !(ms.flags & mpflag)) return 0; if (ms.days == -1) return 1; // Get the time now, subtract mp->time, and compare the results with // ms.days (in manner of ms.day_comp) // CLinearTimeAbsolute ltaNow; ltaNow.GetLocal(); #ifdef RADIX_COMPRESSION string_decompress(mp->time, timebuff); const char *pMailTimeStr = timebuff; #else const char *pMailTimeStr = mp->time; #endif CLinearTimeAbsolute ltaMail; if (ltaMail.SetString(pMailTimeStr)) { CLinearTimeDelta ltd(ltaMail, ltaNow); int iDiffDays = ltd.ReturnDays(); if (sign(iDiffDays - ms.days) == ms.day_comp) { return 1; } } return 0; } static int parse_msglist(char *msglist, struct mail_selector *ms, dbref player) { // Take a message list, and return the appropriate mail_selector setup. // For now, msglists are quite restricted. That'll change once all this // is working. Returns 0 if couldn't parse, and also notifies the player // why. // Initialize the mail selector - this matches all messages. // ms->low = 0; ms->high = 0; ms->flags = 0x0FFF | M_MSUNREAD; ms->player = 0; ms->days = -1; ms->day_comp = 0; // Now, parse the message list. // if (!msglist || !*msglist) { // All messages // return 1; } char *p = msglist; while (Tiny_IsSpace[(unsigned char)*p]) { p++; } if (*p == '\0') { return 1; } if (Tiny_IsDigit[(unsigned char)*p]) { // Message or range. // char *q = strchr(p, '-'); if (q) { // We have a subrange, split it up and test to see if it is valid. // q++; ms->low = Tiny_atol(p); if (ms->low <= 0) { notify(player, "MAIL: Invalid message range"); return 0; } if (*q == '\0') { // Unbounded range. // ms->high = 0; } else { ms->high = Tiny_atol(q); if (ms->low > ms->high) { notify(player, "MAIL: Invalid message range"); return 0; } } } else { // A single message. // ms->low = ms->high = Tiny_atol(p); if (ms->low <= 0) { notify(player, "MAIL: Invalid message number"); return 0; } } } else { switch (Tiny_ToUpper[(unsigned char)*p]) { case '-': // Range with no start. // p++; if (*p == '\0') { notify(player, "MAIL: Invalid message range"); return 0; } ms->high = Tiny_atol(p); if (ms->high <= 0) { notify(player, "MAIL: Invalid message range"); return 0; } break; case '~': // Exact # of days old. // p++; if (*p == '\0') { notify(player, "MAIL: Invalid age"); return 0; } ms->day_comp = 0; ms->days = Tiny_atol(p); if (ms->days < 0) { notify(player, "MAIL: Invalid age"); return 0; } break; case '<': // Less than # of days old. // p++; if (*p == '\0') { notify(player, "MAIL: Invalid age"); return 0; } ms->day_comp = -1; ms->days = Tiny_atol(p); if (ms->days < 0) { notify(player, "MAIL: Invalid age"); return 0; } break; case '>': // Greater than # of days old. // p++; if (*p == '\0') { notify(player, "MAIL: Invalid age"); return 0; } ms->day_comp = 1; ms->days = Tiny_atol(p); if (ms->days < 0) { notify(player, "MAIL: Invalid age"); return 0; } break; case '#': // From db#. // p++; if (*p == '\0') { notify(player, "MAIL: Invalid dbref #"); return 0; } ms->player = Tiny_atol(p); if (!Good_obj(ms->player) || !(ms->player)) { notify(player, "MAIL: Invalid dbref #"); return 0; } break; case '*': // From player name. // p++; if (*p == '\0') { notify(player, "MAIL: Invalid player"); return 0; } ms->player = lookup_player(player, p, 1); if (ms->player == NOTHING) { notify(player, "MAIL: Invalid player"); return 0; } break; #if 0 case 'A': // All messages, all folders // ms->flags = M_ALL; break; #endif case 'U': // Urgent, Unread // p++; if (*p == '\0') { notify(player, "MAIL: U is ambiguous (urgent or unread?)"); return 0; } switch (Tiny_ToUpper[(unsigned char)*p]) { case 'R': // Urgent // ms->flags = M_URGENT; break; case 'N': // Unread // ms->flags = M_MSUNREAD; break; default: // Bad // notify(player, "MAIL: Invalid message specification"); return 0; break; } break; case 'R': // Read // ms->flags = M_ISREAD; break; case 'C': // Cleared. // ms->flags = M_CLEARED; break; case 'T': // Tagged. // ms->flags = M_TAG; break; case 'M': // Mass, me. // p++; if (*p == '\0') { notify(player, "MAIL: M is ambiguous (mass or me?)"); return 0; } switch (Tiny_ToUpper[(unsigned char)*p]) { case 'A': ms->flags = M_MASS; break; case 'E': ms->player = player; break; default: notify(player, "MAIL: Invalid message specification"); return 0; break; } break; default: // Bad news. // notify(player, "MAIL: Invalid message specification"); return 0; break; } } return 1; } void check_mail_expiration(void) { struct mail *mp, *nextp; dbref thing; /* * negative values for expirations never expire */ if (0 > mudconf.mail_expiration) return; int expire_secs = mudconf.mail_expiration * 86400; CLinearTimeAbsolute ltaNow; ltaNow.GetLocal(); CLinearTimeAbsolute ltaMail; MAIL_ITER_SAFE(mp, thing, nextp) { if (M_Safe(mp)) { nextp = mp->next; continue; } #ifdef RADIX_COMPRESSION string_decompress(mp->time, timebuff); const char *pMailTimeStr = timebuff; #else const char *pMailTimeStr = mp->time; #endif if (!ltaMail.SetString(pMailTimeStr)) { nextp = mp->next; continue; } CLinearTimeDelta ltd(ltaMail, ltaNow); if (ltd.ReturnSeconds() <= expire_secs) { nextp = mp->next; continue; } // Delete this one. // // head and tail of the list are special. // dbref nTo = mp->to; if (mp->prev == NULL) { if (mp->next == NULL) { hashdeleteLEN(&nTo, sizeof(nTo), &mudstate.mail_htab); } else { hashreplLEN(&nTo, sizeof(nTo), (int *)(mp->next), &mudstate.mail_htab); } } else if (mp->next == NULL) { mp->prev->next = NULL; } /* * relink the list */ if (mp->prev != NULL) mp->prev->next = mp->next; if (mp->next != NULL) mp->next->prev = mp->prev; /* * save the pointer */ nextp = mp->next; /* * then wipe */ MEMFREE((char *)mp->subject); delete_mail_message(mp->number); MEMFREE((char *)mp->tolist); MEMFREE((char *)mp->time); MEMFREE(mp); } } static char *status_chars(struct mail *mp) { /* * Return a short description of message flags */ static char res[10]; char *p = res; *p++ = Read(mp) ? '-' : 'N'; *p++ = M_Safe(mp) ? 'S' : '-'; *p++ = Cleared(mp) ? 'C' : '-'; *p++ = Urgent(mp) ? 'U' : '-'; *p++ = Mass(mp) ? 'M' : '-'; *p++ = Forward(mp) ? 'F' : '-'; *p++ = Tagged(mp) ? '+' : '-'; *p = '\0'; return res; } #define NUM_MAILSTATUSTABLE 7 struct tag_mailstatusentry { int nMask; char *pYes; int nYes; char *pNo; int nNo; } aMailStatusTable[NUM_MAILSTATUSTABLE] = { { M_ISREAD, "Read", 4, "Unread", 6 }, { M_CLEARED, "Cleared", 7, 0, 0 }, { M_URGENT, "Urgent", 6, 0, 0 }, { M_MASS, "Mass", 4, 0, 0 }, { M_FORWARD, "Fwd", 3, 0, 0 }, { M_TAG, "Tagged", 6, 0, 0 }, { M_SAFE, "Safe", 4, 0, 0 } }; static char *status_string(struct mail *mp) { // Return a longer description of message flags. // char *tbuf1 = alloc_lbuf("status_string"); char *p = tbuf1; struct tag_mailstatusentry *mse = aMailStatusTable; for (int i = 0; i < NUM_MAILSTATUSTABLE; i++, mse++) { if (mp->read & mse->nMask) { if (p != tbuf1) *p++ = ' '; memcpy(p, mse->pYes, mse->nYes); p += mse->nYes; } else if (mse->pNo) { if (p != tbuf1) *p++ = ' '; memcpy(p, mse->pNo, mse->nNo); p += mse->nNo; } } *p++ = '\0'; return tbuf1; } void check_mail(dbref player, int folder, int silent) { // Check for new @mail // int rc; // Read messages. int uc; // Unread messages. int cc; // Cleared messages. int gc; // urgent messages. // Just count messages // count_mail(player, folder, &rc, &uc, &cc); urgent_mail(player, folder, &gc); #ifdef MAIL_ALL_FOLDERS notify(player, tprintf("MAIL: %d messages in folder %d [%s] (%d unread, %d cleared).\r\n", rc + uc, folder, get_folder_name(player, folder), uc, cc)); #else if (rc + uc > 0) { notify(player, tprintf("MAIL: %d messages in folder %d [%s] (%d unread, %d cleared).", rc + uc, folder, get_folder_name(player, folder), uc, cc)); } else if (!silent) { notify(player, tprintf("\r\nMAIL: You have no mail.\r\n")); } if (gc > 0) { notify(player, tprintf("URGENT MAIL: You have %d urgent messages in folder %d [%s].", gc, folder, get_folder_name(player, folder))); } #endif } static int sign(int x) { if (x == 0) { return 0; } else if (x < 0) { return -1; } else { return 1; } } void do_malias_switch(dbref player, char *a1, char *a2) { if ((!a2 || !*a2) && !(!a1 || !*a1)) { do_malias_list(player, a1); } else if ((!*a1 || !a1) && (!*a2 || !a2)) { do_malias_list_all(player); } else { do_malias_create(player, a1, a2); } } void do_malias(dbref player, dbref cause, int key, char *arg1, char *arg2) { if (!mudconf.have_mailer) { notify(player, "Mailer is disabled."); return; } switch (key) { case 0: do_malias_switch(player, arg1, arg2); break; case 1: do_malias_desc(player, arg1, arg2); break; case 2: do_malias_chown(player, arg1, arg2); break; case 3: do_malias_add(player, arg1, arg2); break; case 4: do_malias_remove(player, arg1, arg2); break; case 5: do_malias_delete(player, arg1); break; case 6: do_malias_rename(player, arg1, arg2); break; case 7: /* * empty */ break; case 8: do_malias_adminlist(player); break; case 9: do_malias_status(player); } } // A mail alias can be any combination of upper-case letters, lower-case // letters, and digits. No leading digits. No symbols. No ANSI. Length is // limited to SIZEOF_MALIAS-1. Case is preserved. // char *MakeCanonicalMailAlias ( char *pMailAlias, int *pnValidMailAlias, BOOL *pbValidMailAlias ) { static char Buffer[SIZEOF_MALIAS]; *pnValidMailAlias = 0; *pbValidMailAlias = FALSE; if ( !pMailAlias || !Tiny_IsAlpha[(unsigned char)pMailAlias[0]]) { return NULL; } char *p = Buffer; *p++ = pMailAlias[0]; pMailAlias += 1; int nLeft = (sizeof(Buffer)-1) - 1; while (*pMailAlias && nLeft) { if ( !Tiny_IsAlpha[(unsigned char)*pMailAlias] && !Tiny_IsDigit[(unsigned char)*pMailAlias]) { return Buffer; } *p = *pMailAlias; p++; pMailAlias++; nLeft--; } *p = '\0'; *pnValidMailAlias = p - Buffer; *pbValidMailAlias = TRUE; return Buffer; } // A mail alias description can be any combination of upper-case letters, // lower-case letters, digits, blanks, and symbols. ANSI is permitted. // Length is limited to SIZEOF_MALIASDESC-1. Visual width is limited to // WIDTHOF_MALIASDESC. Case is preserved. // char *MakeCanonicalMailAliasDesc ( char *pMailAliasDesc, int *pnValidMailAliasDesc, BOOL *pbValidMailAliasDesc, int *pnVisualWidth ) { *pnValidMailAliasDesc = 0; *pbValidMailAliasDesc = FALSE; *pnVisualWidth = 0; if (!pMailAliasDesc) { return NULL; } // First, remove all '\r\n\t' from the string. // char *Buffer = RemoveSetOfCharacters(pMailAliasDesc, "\r\n\t"); // Optimize/terminate any ANSI in the string. // static char szFittedMailAliasDesc[SIZEOF_MALIASDESC]; *pnValidMailAliasDesc = ANSI_TruncateToField ( Buffer, SIZEOF_MALIASDESC, szFittedMailAliasDesc, WIDTHOF_MALIASDESC, pnVisualWidth, FALSE ); *pbValidMailAliasDesc = TRUE; return szFittedMailAliasDesc; } #define GMA_NOTFOUND 1 #define GMA_FOUND 2 #define GMA_INVALIDFORM 3 struct malias *get_malias(dbref player, char *alias, int *pnResult) { *pnResult = GMA_INVALIDFORM; if (!alias) { return NULL; } if (alias[0] == '#') { if (ExpMail(player)) { int x = Tiny_atol(alias + 1); if (x < 0 || x >= ma_top) { *pnResult = GMA_NOTFOUND; return NULL; } *pnResult = GMA_FOUND; return malias[x]; } } else if (alias[0] == '*') { int nValidMailAlias; BOOL bValidMailAlias; char *pValidMailAlias = MakeCanonicalMailAlias ( alias+1, &nValidMailAlias, &bValidMailAlias ); if (bValidMailAlias) { for (int i = 0; i < ma_top; i++) { struct malias *m = malias[i]; if ( m->owner == player || m->owner == GOD || ExpMail(player)) { if (!strcmp(pValidMailAlias, m->name)) { // Found it! // *pnResult = GMA_FOUND; return m; } } } *pnResult = GMA_NOTFOUND; } } if (*pnResult == GMA_INVALIDFORM) { if (ExpMail(player)) { notify(player, "MAIL: Mail aliases must be of the form *<name> or #<num>."); } else { notify(player, "MAIL: Mail aliases must be of the form *<name>."); } } return NULL; } void do_malias_send(dbref player, char *tolist, char *listto, char *subject, int number, mail_flag flags, int silent) { dbref vic; int nResult; struct malias *m = get_malias(player, tolist, &nResult); if (nResult == GMA_INVALIDFORM) { char *p; p = tprintf ( "MAIL: I can't figure out from '%s' who you want to mail to.", tolist ); notify(player, p); return; } else if (nResult == GMA_NOTFOUND) { notify(player, tprintf("MAIL: Alias '%s' not found.", tolist)); return; } // Parse the player list. // int k; for (k = 0; k < m->numrecep; k++) { vic = m->list[k]; if (Typeof(vic) == TYPE_PLAYER) { send_mail(player, m->list[k], listto, subject, number, flags, silent); } else { // Complain about it. // char *pMail = tprintf("Alias Error: Bad Player %d for %s", vic, tolist); int iMail = add_mail_message(player, pMail); send_mail(GOD, GOD, listto, subject, iMail, 0, silent); } } } void do_malias_create(dbref player, char *alias, char *tolist) { char *head, *tail, spot; struct malias **nm; char *buff; int i = 0; dbref target; int nResult; struct malias *m = get_malias(player, alias, &nResult); if (nResult == GMA_INVALIDFORM) { notify(player, "MAIL: What alias do you want to create?."); return; } else if (nResult == GMA_FOUND) { notify(player, tprintf("MAIL: Mail Alias '%s' already exists.", alias)); return; } if (!ma_size) { ma_size = MA_INC; malias = (struct malias **)MEMALLOC(sizeof(struct malias *) * ma_size); ISOUTOFMEMORY(malias); } else if (ma_top >= ma_size) { ma_size += MA_INC; nm = (struct malias **)MEMALLOC(sizeof(struct malias *) * (ma_size)); ISOUTOFMEMORY(nm); for (i = 0; i < ma_top; i++) { nm[i] = malias[i]; } MEMFREE(malias); malias = nm; } malias[ma_top] = (struct malias *)MEMALLOC(sizeof(struct malias)); ISOUTOFMEMORY(malias[ma_top]); i = 0; // Parse the player list. // head = (char *)tolist; while (head && *head && (i < (MAX_MALIAS_MEMBERSHIP - 1))) { while (*head == ' ') head++; tail = head; while (*tail && (*tail != ' ')) { if (*tail == '"') { head++; tail++; while (*tail && (*tail != '"')) tail++; } if (*tail) tail++; } tail--; if (*tail != '"') tail++; spot = *tail; *tail = '\0'; // Now locate a target. // if (!_stricmp(head, "me")) target = player; else if (*head == '#') { target = Tiny_atol(head + 1); if (!Good_obj(target)) target = NOTHING; } else { target = lookup_player(player, head, 1); } if ((target == NOTHING) || (Typeof(target) != TYPE_PLAYER)) { notify(player, "MAIL: No such player."); } else { buff = unparse_object(player, target, 0); notify(player, tprintf("MAIL: %s added to alias %s", buff, alias)); malias[ma_top]->list[i] = target; i++; free_lbuf(buff); } // Get the next recip. // *tail = spot; head = tail; if (*head == '"') head++; } int nValidMailAlias; BOOL bValidMailAlias; char *pValidMailAlias = MakeCanonicalMailAlias ( alias+1, &nValidMailAlias, &bValidMailAlias ); if (!bValidMailAlias) { notify(player, "MAIL: Invalid mail alias."); return; } // The Mail Alias Description is a superset of the Mail Alias, // so, the following code is not necessary unless the specification // of the Mail Alias Description becomes more restrictive at some // future time. // #if 0 int nValidMailAliasDesc; BOOL bValidMailAliasDesc; char *pValidMailAliasDesc = MakeCanonicalMailAliasDesc ( alias+1, &nValidMailAliasDesc, &bValidMailAliasDesc ); if (!bValidMailAliasDesc) { notify(player, "MAIL: Invalid mail alias description."); break; } #else char *pValidMailAliasDesc = pValidMailAlias; int nValidMailAliasDesc = nValidMailAlias; #endif malias[ma_top]->list[i] = NOTHING; malias[ma_top]->name = StringCloneLen(pValidMailAlias, nValidMailAlias); malias[ma_top]->numrecep = i; malias[ma_top]->owner = player; malias[ma_top]->desc = StringCloneLen(pValidMailAliasDesc, nValidMailAliasDesc); ma_top++; notify(player, tprintf("MAIL: Alias set '%s' defined.", alias)); } void do_malias_list(dbref player, char *alias) { int nResult; struct malias *m = get_malias(player, alias, &nResult); if (nResult == GMA_NOTFOUND) { notify(player, tprintf("MAIL: Alias '%s' not found.", alias)); return; } if (nResult != GMA_FOUND) { return; } if (!ExpMail(player) && (player != m->owner) && !(God(m->owner))) { notify(player, "MAIL: Permission denied."); return; } char *buff; char *bp; bp = buff = alloc_lbuf("do_malias_list"); safe_tprintf_str(buff, &bp, "MAIL: Alias *%s: ", m->name); for (int i = m->numrecep - 1; i > -1; i--) { char *p = Name(m->list[i]); if (strchr(p, ' ')) { safe_chr('"', buff, &bp); safe_str(p, buff, &bp); safe_chr('"', buff, &bp); } else { safe_str(p, buff, &bp); } safe_chr(' ', buff, &bp); } *bp = '\0'; notify(player, buff); free_lbuf(buff); } char *Spaces(int n) { static char buffer[42] = " "; static int nLast = 0; buffer[nLast] = ' '; if (0 <= n && n < sizeof(buffer)-1) { buffer[n] = '\0'; nLast = n; } return buffer; } void do_malias_list_all(dbref player) { int i = 0; int notified = 0; for (i = 0; i < ma_top; i++) { struct malias *m = malias[i]; if ( m->owner == GOD || m->owner == player || God(player)) { if (!notified) { notify(player, "Name Description Owner"); notified++; } char *p = tprintf( "%-12s %s%s %-15.15s", m->name, m->desc, Spaces(40 - m->desc_width), Name(m->owner)); notify(player, p); } } notify(player, "***** End of Mail Aliases *****"); } void load_malias(FILE *fp) { char buffer[200]; getref(fp); if ( fscanf(fp, "*** Begin %s ***\n", buffer) == 1 && !strcmp(buffer, "MALIAS")) { malias_read(fp); } else { Log.WriteString("ERROR: Couldn't find Begin MALIAS.\n"); return; } } void save_malias(FILE *fp) { fprintf(fp, "*** Begin MALIAS ***\n"); malias_write(fp); } // This function acts like fgets except that any data on the end of the // line past the buffer size is truncated instead of being returned on // the next call. // int GetLineTrunc(char *Buffer, size_t nBuffer, FILE *fp) { fgets(Buffer, nBuffer, fp); int lenBuffer = strlen(Buffer); if (Buffer[lenBuffer-1] != '\n') { // The line was too long for the buffer. Continue reading until the // end of the line. // char TruncBuffer[SBUF_SIZE]; int lenTruncBuffer; do { fgets(TruncBuffer, sizeof(TruncBuffer), fp); lenTruncBuffer = strlen(TruncBuffer); } while (TruncBuffer[lenTruncBuffer-1] != '\n'); } return lenBuffer; } void malias_read(FILE *fp) { int i, j; char buffer[1000]; struct malias *m; ma_top = getref(fp); if (ma_top <= 0) { ma_size = ma_top = 0; malias = NULL; return; } ma_size = ma_top; malias = (struct malias **)MEMALLOC(sizeof(struct malias *) * ma_size); ISOUTOFMEMORY(malias); for (i = 0; i < ma_top; i++) { m = (struct malias *)MEMALLOC(sizeof(struct malias)); ISOUTOFMEMORY(m); malias[i] = m; // Format is: "%d %d\n", &(m->owner), &(m->numrecep) // fgets(buffer, sizeof(buffer), fp); char *p = strchr(buffer, ' '); m->owner = m->numrecep = 0; if (p) { m->owner = Tiny_atol(buffer); m->numrecep = Tiny_atol(p+1); } // The format of @malias name is "N:<name>\n". // int nLen = GetLineTrunc(buffer, sizeof(buffer), fp); buffer[nLen-1] = '\0'; // Get rid of trailing '\n'. int nMailAlias; BOOL bMailAlias; char *pMailAlias = MakeCanonicalMailAlias( buffer+2, &nMailAlias, &bMailAlias); if (bMailAlias) { m->name = StringCloneLen(pMailAlias, nMailAlias); } else { m->name = StringCloneLen("Invalid", 7); } // The format of the description is "D:<description>\n" // nLen = GetLineTrunc(buffer, sizeof(buffer), fp); int nMailAliasDesc; BOOL bMailAliasDesc; int nVisualWidth; char *pMailAliasDesc = MakeCanonicalMailAliasDesc( buffer+2, &nMailAliasDesc, &bMailAliasDesc, &nVisualWidth); if (bMailAliasDesc) { m->desc = StringCloneLen(pMailAliasDesc, nMailAliasDesc); m->desc_width = nVisualWidth; } else { m->desc = StringCloneLen("Invalid Desc", 12); m->desc_width = 12; } if (m->numrecep > 0) { for (j = 0; j < m->numrecep; j++) { m->list[j] = getref(fp); } } else { m->list[0] = 0; } } } void malias_write(FILE *fp) { int i, j; struct malias *m; putref(fp, ma_top); for (i = 0; i < ma_top; i++) { m = malias[i]; fprintf(fp, "%d %d\n", m->owner, m->numrecep); fprintf(fp, "N:%s\n", m->name); fprintf(fp, "D:%s\n", m->desc); for (j = 0; j < m->numrecep; j++) { putref(fp, m->list[j]); } } } void do_expmail_start(dbref player, char *arg, char *subject) { if (!arg || !*arg) { notify(player, "MAIL: I do not know whom you want to mail."); return; } if (!subject || !*subject) { notify(player, "MAIL: No subject."); return; } if (Flags2(player) & PLAYER_MAILS) { notify(player, "MAIL: Mail message already in progress."); return; } char *tolist = make_numlist(player, arg); if (!tolist) return; atr_add_raw(player, A_MAILTO, tolist); atr_add_raw(player, A_MAILSUB, subject); atr_add_raw(player, A_MAILFLAGS, "0"); atr_clr(player, A_MAILMSG); Flags2(player) |= PLAYER_MAILS; char *names = make_namelist(player, tolist); notify(player, tprintf("MAIL: You are sending mail to '%s'.", names)); free_lbuf(names); free_lbuf(tolist); } void do_mail_cc(dbref player, char *arg) { char *fulllist, *bp; char *names; if (!(Flags2(player) & PLAYER_MAILS)) { notify(player, "MAIL: No mail message in progress."); return; } if (!arg || !*arg) { notify(player, "MAIL: I do not know whom you want to mail."); return; } char *tolist = make_numlist(player, arg); if (!tolist) return; fulllist = alloc_lbuf("do_mail_cc"); bp = fulllist; safe_str(tolist, fulllist, &bp); char *pPlayerMailTo = atr_get_raw(player, A_MAILTO); if (pPlayerMailTo) { safe_chr(' ', fulllist, &bp); safe_str(pPlayerMailTo, fulllist, &bp); } *bp = '\0'; atr_add_raw(player, A_MAILTO, fulllist); names = make_namelist(player, fulllist); notify(player, tprintf("MAIL: You are sending mail to '%s'.", names)); free_lbuf(names); free_lbuf(tolist); free_lbuf(fulllist); } static char *make_namelist(dbref player, char *arg) { char *names, *oldarg; char *bp, *p; oldarg = alloc_lbuf("make_namelist.oldarg"); names = alloc_lbuf("make_namelist.names"); bp = names; strcpy(oldarg, arg); TINY_STRTOK_STATE tts; Tiny_StrTokString(&tts, oldarg); Tiny_StrTokControl(&tts, " "); for (p = Tiny_StrTokParse(&tts); p; p = Tiny_StrTokParse(&tts)) { if (*p == '*') { safe_str(p, names, &bp); safe_str(", ", names, &bp); } else { safe_str(Name(Tiny_atol(p)), names, &bp); safe_str(", ", names, &bp); } } *(bp - 2) = '\0'; free_lbuf(oldarg); return names; } static char *make_numlist(dbref player, char *arg) { char *head, *tail, spot; char *numbuf; char buf[MBUF_SIZE]; char *numbp; struct malias *m; struct mail *temp; dbref target; int num; numbuf = alloc_lbuf("make_numlist"); numbp = numbuf; *numbp = '\0'; head = (char *)arg; while (head && *head) { while (*head == ' ') { head++; } tail = head; while (*tail && (*tail != ' ')) { if (*tail == '"') { head++; tail++; while (*tail && (*tail != '"')) { tail++; } } if (*tail) { tail++; } } tail--; if (*tail != '"') { tail++; } spot = *tail; *tail = 0; num = Tiny_atol(head); if (num) { temp = mail_fetch(player, num); if (!temp) { notify(player, "MAIL: You can't reply to nonexistent mail."); free_lbuf(numbuf); return NULL; } sprintf(buf, "%d ", temp->from); safe_str(buf, numbuf, &numbp); } else if (*head == '*') { int nResult; m = get_malias(player, head, &nResult); if (nResult == GMA_NOTFOUND) { notify(player, tprintf("MAIL: Alias '%s' does not exist.", head)); free_lbuf(numbuf); return NULL; } else if (nResult == GMA_INVALIDFORM) { notify(player, tprintf("MAIL: '%s' is a badly-formed alias.", head)); free_lbuf(numbuf); return NULL; } safe_str(head, numbuf, &numbp); safe_chr(' ', numbuf, &numbp); } else { target = lookup_player(player, head, 1); if (target != NOTHING) { sprintf(buf, "%d ", target); safe_str(buf, numbuf, &numbp); } else { notify(player, tprintf("MAIL: '%s' does not exist.", head)); free_lbuf(numbuf); return NULL; } } // Get the next recip. // *tail = spot; head = tail; if (*head == '"') { head++; } } if (!*numbuf) { notify(player, "MAIL: No players specified."); free_lbuf(numbuf); return NULL; } else { *(numbp - 1) = '\0'; return numbuf; } } void do_mail_quick(dbref player, char *arg1, char *arg2) { char *buf, *bp; if (!arg1 || !*arg1) { notify(player, "MAIL: I don't know who you want to mail."); return; } if (!arg2 || !*arg2) { notify(player, "MAIL: No message."); return; } if (Flags2(player) & PLAYER_MAILS) { notify(player, "MAIL: Mail message already in progress."); return; } buf = alloc_lbuf("do_mail_quick"); bp = buf; strcpy(bp, arg1); parse_to(&bp, '/', 1); if (!bp) { notify(player, "MAIL: No subject."); free_lbuf(buf); return; } mail_to_list(player, make_numlist(player, buf), bp, arg2, 0, 0); free_lbuf(buf); } void mail_to_list(dbref player, char *list, char *subject, char *message, int flags, int silent) { char *head, *tail, spot, *tolist; dbref target; int number; if (!list) { return; } if (!*list) { free_lbuf(list); return; } tolist = alloc_lbuf("mail_to_list"); strcpy(tolist, list); number = add_mail_message(player, message); head = list; while (head && *head) { while (*head == ' ') { head++; } tail = head; while (*tail && (*tail != ' ')) { if (*tail == '"') { head++; tail++; while (*tail && (*tail != '"')) tail++; } if (*tail) tail++; } tail--; if (*tail != '"') { tail++; } spot = *tail; *tail = 0; if (*head == '*') { do_malias_send(player, head, tolist, subject, number, flags, silent); } else { target = Tiny_atol(head); if (target != NOTHING) { send_mail(player, target, tolist, subject, number, flags, silent); } } // Get the next recip. // *tail = spot; head = tail; if (*head == '"') { head++; } } free_lbuf(tolist); free_lbuf(list); } void do_expmail_stop(dbref player, int flags) { char *tolist, *mailsub, *mailmsg, *mailflags; dbref aowner; dbref aflags; tolist = atr_get(player, A_MAILTO, &aowner, &aflags); mailmsg = atr_get(player, A_MAILMSG, &aowner, &aflags); mailsub = atr_get(player, A_MAILSUB, &aowner, &aflags); mailflags = atr_get(player, A_MAILFLAGS, &aowner, &aflags); if (!*tolist || !*mailmsg || !(Flags2(player) & PLAYER_MAILS)) { notify(player, "MAIL: No such message to send."); free_lbuf(tolist); } else { mail_to_list(player, tolist, mailsub, mailmsg, flags | Tiny_atol(mailflags), 0); } free_lbuf(mailflags); free_lbuf(mailmsg); free_lbuf(mailsub); Flags2(player) &= ~PLAYER_MAILS; } void do_expmail_abort(dbref player) { Flags2(player) &= ~PLAYER_MAILS; notify(player, "MAIL: Message aborted."); } void do_prepend(dbref player, dbref cause, int key, char *text) { char *oldmsg, *newmsg, *bp, *attr; dbref aowner; int aflags; if (!mudconf.have_mailer) { return; } if (Flags2(player) & PLAYER_MAILS) { oldmsg = atr_get(player, A_MAILMSG, &aowner, &aflags); if (*oldmsg) { bp = newmsg = alloc_lbuf("do_prepend"); safe_str(text + 1, newmsg, &bp); safe_chr(' ', newmsg, &bp); safe_str(oldmsg, newmsg, &bp); *bp = '\0'; atr_add_raw(player, A_MAILMSG, newmsg); free_lbuf(newmsg); } else { atr_add_raw(player, A_MAILMSG, text + 1); } free_lbuf(oldmsg); int nLen; attr = atr_get_raw_LEN(player, A_MAILMSG, &nLen); notify(player, tprintf("%d/%d characters prepended.", nLen, LBUF_SIZE-1)); } else { notify(player, "MAIL: No message in progress."); } } void do_postpend(dbref player, dbref cause, int key, char *text) { char *oldmsg, *newmsg, *bp, *attr; dbref aowner; int aflags; if (!mudconf.have_mailer) { return; } if ((*(text + 1) == '-') && !(*(text + 2))) { do_expmail_stop(player, 0); return; } if (Flags2(player) & PLAYER_MAILS) { oldmsg = atr_get(player, A_MAILMSG, &aowner, &aflags); if (*oldmsg) { bp = newmsg = alloc_lbuf("do_postpend"); safe_str(oldmsg, newmsg, &bp); safe_chr(' ', newmsg, &bp); safe_str(text + 1, newmsg, &bp); *bp = '\0'; atr_add_raw(player, A_MAILMSG, newmsg); free_lbuf(newmsg); } else { atr_add_raw(player, A_MAILMSG, text + 1); } free_lbuf(oldmsg); int nLen; attr = atr_get_raw_LEN(player, A_MAILMSG, &nLen); notify(player, tprintf("%d/%d characters added.", nLen, LBUF_SIZE-1)); } else { notify(player, "MAIL: No message in progress."); } } static void do_edit_msg(dbref player, char *from, char *to) { char *result, *msg; dbref aowner; int aflags; if (Flags2(player) & PLAYER_MAILS) { msg = atr_get(player, A_MAILMSG, &aowner, &aflags); result = replace_string(from, to, msg); atr_add(player, A_MAILMSG, result, aowner, aflags); notify(player, "Text edited."); free_lbuf(result); free_lbuf(msg); } else { notify(player, "MAIL: No message in progress."); } } static void do_mail_proof(dbref player) { char *mailto, *names; char *mailmsg, *msg, *bp, *str; dbref aowner; int aflags; mailto = atr_get(player, A_MAILTO, &aowner, &aflags); if (!atr_get_raw(player, A_MAILMSG)) { notify(player, "MAIL: No text."); free_lbuf(mailto); return; } else { str = mailmsg = atr_get(player, A_MAILMSG, &aowner, &aflags); bp = msg = alloc_lbuf("do_mail_proof"); TinyExec(msg, &bp, 0, player, player, EV_EVAL | EV_FCHECK, &str, (char **)NULL, 0); *bp = '\0'; free_lbuf(mailmsg); } if (Flags2(player) & PLAYER_MAILS) { names = make_namelist(player, mailto); notify(player, DASH_LINE); notify(player, tprintf("From: %-*s Subject: %-35s\nTo: %s", PLAYER_NAME_LIMIT - 6, Name(player), atr_get_raw(player, A_MAILSUB), names)); notify(player, DASH_LINE); notify(player, msg); notify(player, DASH_LINE); free_lbuf(names); } else { notify(player, "MAIL: No message in progress."); } free_lbuf(mailto); free_lbuf(msg); } void do_malias_desc(dbref player, char *alias, char *desc) { int nResult; struct malias *m = get_malias(player, alias, &nResult); if (nResult == GMA_NOTFOUND) { notify(player, tprintf("MAIL: Alias '%s' not found.", alias)); return; } if (nResult != GMA_FOUND) { return; } if ( m->owner != GOD || ExpMail(player)) { int nValidMailAliasDesc; BOOL bValidMailAliasDesc; int nVisualWidth; char *pValidMailAliasDesc = MakeCanonicalMailAliasDesc ( desc, &nValidMailAliasDesc, &bValidMailAliasDesc, &nVisualWidth ); if (bValidMailAliasDesc) { MEMFREE(m->desc); m->desc = StringCloneLen(pValidMailAliasDesc, nValidMailAliasDesc); m->desc_width = nVisualWidth; notify(player, "MAIL: Description changed."); } else { notify(player, "MAIL: Description is not valid."); } } else { notify(player, "MAIL: Permission denied."); } } void do_malias_chown(dbref player, char *alias, char *owner) { dbref no = NOTHING; int nResult; struct malias *m = get_malias(player, alias, &nResult); if (nResult == GMA_NOTFOUND) { notify(player, tprintf("MAIL: Alias '%s' not found.", alias)); return; } if (nResult != GMA_FOUND) { return; } if (!ExpMail(player)) { notify(player, "MAIL: You cannot do that!"); return; } else { if ((no = lookup_player(player, owner, 1)) == NOTHING) { notify(player, "MAIL: I do not see that here."); return; } m->owner = no; notify(player, "MAIL: Owner changed for alias."); } } void do_malias_add(dbref player, char *alias, char *person) { int nResult; struct malias *m = get_malias(player, alias, &nResult); if (nResult == GMA_NOTFOUND) { notify(player, tprintf("MAIL: Alias '%s' not found.", alias)); return; } else if (nResult != GMA_FOUND) { return; } dbref thing = NOTHING; if (*person == '#') { thing = parse_dbref(person + 1); if (!isPlayer(thing)) { notify(player, "MAIL: Only players may be added."); return; } } if (thing == NOTHING) { thing = lookup_player(player, person, 1); } if (thing == NOTHING) { notify(player, "MAIL: I do not see that person here."); return; } if ((m->owner == GOD) && !ExpMail(player)) { notify(player, "MAIL: Permission denied."); return; } int i; for (i = 0; i < m->numrecep; i++) { if (m->list[i] == thing) { notify(player, "MAIL: That person is already on the list."); return; } } if (i >= (MAX_MALIAS_MEMBERSHIP - 1)) { notify(player, "MAIL: The list is full."); return; } m->list[m->numrecep] = thing; m->numrecep = m->numrecep + 1; notify(player, tprintf("MAIL: %s added to %s", Name(thing), m->name)); } void do_malias_remove(dbref player, char *alias, char *person) { int nResult; struct malias *m = get_malias(player, alias, &nResult); if (nResult == GMA_NOTFOUND) { notify(player, tprintf("MAIL: Alias '%s' not found.", alias)); return; } if (nResult != GMA_FOUND) { return; } if ((m->owner == GOD) && !ExpMail(player)) { notify(player, "MAIL: Permission denied."); return; } dbref thing = NOTHING; if (*person == '#') { thing = parse_dbref(person + 1); } if (thing == NOTHING) { thing = lookup_player(player, person, 1); } if (thing == NOTHING) { notify(player, "MAIL: I do not see that person here."); return; } BOOL ok = FALSE; for (int i = 0; i < m->numrecep; i++) { if (ok) m->list[i] = m->list[i + 1]; else if ((m->list[i] == thing) && !ok) { m->list[i] = m->list[i + 1]; ok = TRUE; } } if (ok) { m->numrecep--; } notify(player, tprintf("MAIL: %s removed from alias %s.", Name(thing), alias)); } void do_malias_rename(dbref player, char *alias, char *newname) { int nResult; struct malias *m = get_malias(player, newname, &nResult); if (nResult == GMA_FOUND) { notify(player, "MAIL: That name already exists!"); return; } if (nResult != GMA_NOTFOUND) { return; } m = get_malias(player, alias, &nResult); if (nResult == GMA_NOTFOUND) { notify(player, "MAIL: I cannot find that alias!"); return; } if (nResult != GMA_FOUND) { return; } if (!ExpMail(player) && !(m->owner == player)) { notify(player, "MAIL: Permission denied."); return; } int nValidMailAlias; BOOL bValidMailAlias; char *pValidMailAlias = MakeCanonicalMailAlias ( newname+1, &nValidMailAlias, &bValidMailAlias ); if (bValidMailAlias) { MEMFREE(m->name); m->name = StringClone(newname+1); notify(player, "MAIL: Mailing Alias renamed."); } else { notify(player, "MAIL: Alias is not valid."); } } void do_malias_delete(dbref player, char *alias) { int nResult; struct malias *m = get_malias(player, alias, &nResult); if (nResult == GMA_NOTFOUND) { notify(player, tprintf("MAIL: Alias '%s' not found.", alias)); return; } if (nResult != GMA_FOUND) { return; } BOOL done = FALSE; for (int i = 0; i < ma_top; i++) { if (done) { malias[i] = malias[i + 1]; } else { if ((m->owner == player) || ExpMail(player)) { if (m == malias[i]) { done = 1; notify(player, "MAIL: Alias Deleted."); malias[i] = malias[i + 1]; } } } } if (!done) { notify(player, tprintf("MAIL: Alias '%s' not found.", alias)); } else { ma_top--; } } void do_malias_adminlist(dbref player) { struct malias *m; int i; if (!ExpMail(player)) { do_malias_list_all(player); return; } notify(player, "Num Name Description Owner"); for (i = 0; i < ma_top; i++) { m = malias[i]; notify(player, tprintf("%-4d %-12s %s%s %-15.15s", i, m->name, m->desc, Spaces(40 - m->desc_width), Name(m->owner))); } notify(player, "***** End of Mail Aliases *****"); } void do_malias_status(dbref player) { if (!ExpMail(player)) notify(player, "MAIL: Permission denied."); else { notify(player, tprintf("MAIL: Number of mail aliases defined: %d", ma_top)); notify(player, tprintf("MAIL: Allocated slots %d", ma_size)); } }