mux/src/mail.cpp

Go to the documentation of this file.
00001 // mail.cpp
00002 //
00003 // $Id: mail.cpp,v 1.55 2007/03/07 00:27:59 sdennis Exp $
00004 //
00005 // This code was taken from Kalkin's DarkZone code, which was
00006 // originally taken from PennMUSH 1.50 p10, and has been heavily modified
00007 // since being included in MUX.
00008 //
00009 
00010 #include "copyright.h"
00011 #include "autoconf.h"
00012 #include "config.h"
00013 #include "externs.h"
00014 
00015 #include <sys/types.h>
00016 
00017 #include "attrs.h"
00018 #include "command.h"
00019 #include "powers.h"
00020 #include "mail.h"
00021 
00022 #define SIZEOF_MALIAS 13
00023 #define WIDTHOF_MALIASDESC 40
00024 #define SIZEOF_MALIASDESC (WIDTHOF_MALIASDESC*2)
00025 
00026 #define MAX_MALIAS_MEMBERSHIP 100
00027 struct malias
00028 {
00029     int owner;
00030     char *name;
00031     char *desc;
00032     int desc_width; // The visual width of the Mail Alias Description.
00033     int numrecep;
00034     dbref list[MAX_MALIAS_MEMBERSHIP];
00035 };
00036 
00037 static int ma_size = 0;
00038 static int ma_top = 0;
00039 
00040 static struct malias **malias   = NULL;
00041 static MAILBODY      *mail_list = NULL;
00042 
00043 // Handling functions for the database of mail messages.
00044 //
00045 
00046 // mail_db_grow - We keep a database of mail text, so if we send a
00047 // message to more than one player, we won't have to duplicate the
00048 // text.
00049 //
00050 #define MAIL_FUDGE 1
00051 static void mail_db_grow(int newtop)
00052 {
00053     if (newtop <= mudstate.mail_db_top)
00054     {
00055         return;
00056     }
00057     if (mudstate.mail_db_size <= newtop)
00058     {
00059         // We need to make the mail bag bigger.
00060         //
00061         int newsize = mudstate.mail_db_size + 100;
00062         if (newtop > newsize)
00063         {
00064             newsize = newtop;
00065         }
00066 
00067         MAILBODY *newdb = (MAILBODY *)MEMALLOC((newsize + MAIL_FUDGE) * sizeof(MAILBODY));
00068         ISOUTOFMEMORY(newdb);
00069         if (mail_list)
00070         {
00071             mail_list -= MAIL_FUDGE;
00072             memcpy( newdb,
00073                     mail_list,
00074                     (mudstate.mail_db_top + MAIL_FUDGE) * sizeof(MAILBODY));
00075             MEMFREE(mail_list);
00076             mail_list = NULL;
00077         }
00078         mail_list = newdb + MAIL_FUDGE;
00079         newdb = NULL;
00080         mudstate.mail_db_size = newsize;
00081     }
00082 
00083     // Initialize new parts of the mail bag.
00084     //
00085     for (int i = mudstate.mail_db_top; i < newtop; i++)
00086     {
00087         mail_list[i].m_nRefs = 0;
00088         mail_list[i].m_pMessage = NULL;
00089     }
00090     mudstate.mail_db_top = newtop;
00091 }
00092 
00093 // MessageReferenceInc - Increments the reference count for any
00094 // particular message.
00095 //
00096 static DCL_INLINE void MessageReferenceInc(int number)
00097 {
00098     mail_list[number].m_nRefs++;
00099 }
00100 
00101 // MessageReferenceCheck - Checks whether the reference count for
00102 // any particular message indicates that the message body should be
00103 // freed. Also checks that if a message point is null, that the
00104 // reference count is zero.
00105 //
00106 static void MessageReferenceCheck(int number)
00107 {
00108     MAILBODY &m = mail_list[number];
00109     if (m.m_nRefs <= 0)
00110     {
00111         if (m.m_pMessage)
00112         {
00113             MEMFREE(m.m_pMessage);
00114             m.m_pMessage = NULL;
00115         }
00116     }
00117     if (m.m_pMessage == NULL)
00118     {
00119         m.m_nRefs = 0;
00120     }
00121 }
00122 
00123 // MessageReferenceDec - Decrements the reference count for a message, and
00124 // will also delete the message if the counter reaches 0.
00125 //
00126 static void MessageReferenceDec(int number)
00127 {
00128     mail_list[number].m_nRefs--;
00129     MessageReferenceCheck(number);
00130 }
00131 
00132 // MessageFetch - returns the text for a particular message number. This
00133 // text should not be modified.
00134 //
00135 static const char *MessageFetch(int number)
00136 {
00137     MessageReferenceCheck(number);
00138     if (mail_list[number].m_pMessage)
00139     {
00140         return mail_list[number].m_pMessage;
00141     }
00142     else
00143     {
00144         return "MAIL: This mail message does not exist in the database. Please alert your admin.";
00145     }
00146 }
00147 
00148 // This function returns a reference to the message and the the
00149 // reference count is increased to reflect that.
00150 //
00151 static int MessageAdd(char *pMessage)
00152 {
00153     if (!mail_list)
00154     {
00155         mail_db_grow(1);
00156     }
00157 
00158     int i;
00159     MAILBODY *pm;
00160     bool bFound = false;
00161     for (i = 0; i < mudstate.mail_db_top; i++)
00162     {
00163         pm = &mail_list[i];
00164         if (pm->m_pMessage == NULL)
00165         {
00166             pm->m_nRefs = 0;
00167             bFound = true;
00168             break;
00169         }
00170     }
00171     if (!bFound)
00172     {
00173         mail_db_grow(i + 1);
00174     }
00175 
00176     pm = &mail_list[i];
00177     pm->m_pMessage = StringClone(pMessage);
00178     MessageReferenceInc(i);
00179     return i;
00180 }
00181 
00182 // add_mail_message - adds a new text message to the mail database, and returns
00183 // a unique number for that message.
00184 //
00185 // IF return value is !NOTHING, you have a reference to the message,
00186 // and the reference count reflects that.
00187 //
00188 static int add_mail_message(dbref player, char *message)
00189 {
00190     if (!mux_stricmp(message, "clear"))
00191     {
00192         notify(player, "MAIL: You probably did not intend to send a @mail saying 'clear'.");
00193         return NOTHING;
00194     }
00195 
00196     // Evaluate signature.
00197     //
00198     int   aflags;
00199     dbref aowner;
00200     char *bp = alloc_lbuf("add_mail_message");
00201     char *atrstr = atr_get(player, A_SIGNATURE, &aowner, &aflags);
00202     char *execstr = bp;
00203     char *str = atrstr;
00204     mux_exec(execstr, &bp, player, player, player,
00205              EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, (char **)NULL, 0);
00206     *bp = '\0';
00207 
00208     // Save message body and return a reference to it.
00209     //
00210     int number = MessageAdd(tprintf("%s %s", message, execstr));
00211     free_lbuf(atrstr);
00212     free_lbuf(execstr);
00213     return number;
00214 }
00215 
00216 // This function is -only- used from reading from the disk, and so
00217 // it does -not- manage the reference counts.
00218 //
00219 static bool MessageAddWithNumber(int i, char *pMessage)
00220 {
00221     mail_db_grow(i+1);
00222 
00223     MAILBODY *pm = &mail_list[i];
00224     pm->m_pMessage = StringClone(pMessage);
00225     return true;
00226 }
00227 
00228 // new_mail_message - used for reading messages in from disk which
00229 // already have a number assigned to them.
00230 //
00231 // This function is -only- used from reading from the disk, and so
00232 // it does -not- manage the reference counts.
00233 //
00234 static void new_mail_message(char *message, int number)
00235 {
00236     bool bTruncated = false;
00237     if (strlen(message) > LBUF_SIZE-1)
00238     {
00239         bTruncated = true;
00240         message[LBUF_SIZE-1] = '\0';
00241     }
00242     MessageAddWithNumber(number, message);
00243     if (bTruncated)
00244     {
00245         STARTLOG(LOG_BUGS, "BUG", "MAIL");
00246         log_text(tprintf("new_mail_message: Mail message %d truncated.", number));
00247         ENDLOG;
00248     }
00249 }
00250 
00251 /*-------------------------------------------------------------------------*
00252  *   User mail functions (these are called from game.c)
00253  *
00254  * do_mail - cases
00255  * do_mail_read - read messages
00256  * do_mail_list - list messages
00257  * do_mail_flags - tagging, untagging, clearing, unclearing of messages
00258  * do_mail_file - files messages into a new folder
00259  * do_mail_fwd - forward messages to another player(s)
00260  * do_mail_reply - reply to a message
00261  * do_mail_count - count messages
00262  * do_mail_purge - purge cleared messages
00263  * do_mail_change_folder - change current folder
00264  *-------------------------------------------------------------------------*/
00265 
00266 static void set_player_folder(dbref player, int fnum)
00267 {
00268     // Set a player's folder to fnum.
00269     //
00270     char *tbuf1 = alloc_lbuf("set_player_folder");
00271     mux_ltoa(fnum, tbuf1);
00272     ATTR *a = atr_num(A_MAILCURF);
00273     if (a)
00274     {
00275         atr_add(player, A_MAILCURF, tbuf1, GOD, a->flags);
00276     }
00277     else
00278     {
00279         // Shouldn't happen, but...
00280         //
00281         atr_add(player, A_MAILCURF, tbuf1, GOD, AF_ODARK | AF_WIZARD | AF_NOPROG | AF_LOCK);
00282     }
00283     free_lbuf(tbuf1);
00284 }
00285 
00286 static void add_folder_name(dbref player, int fld, char *name)
00287 {
00288     // Fetch current list of folders
00289     //
00290     int aflags;
00291     size_t nFolders;
00292     dbref aowner;
00293     char *aFolders = alloc_lbuf("add_folder_name.str");
00294     atr_get_str_LEN(aFolders, player, A_MAILFOLDERS, &aowner, &aflags,
00295         &nFolders);
00296 
00297     // Build new record ("%d:%s:%d", fld, uppercase(name), fld);
00298     //
00299     char *aNew = alloc_lbuf("add_folder_name.new");
00300     char *q = aNew;
00301     q += mux_ltoa(fld, q);
00302     *q++ = ':';
00303     char *p = name;
00304     while (*p)
00305     {
00306         *q++ = mux_toupper(*p);
00307         p++;
00308     }
00309     *q++ = ':';
00310     q += mux_ltoa(fld, q);
00311     *q = '\0';
00312     size_t nNew = q - aNew;
00313 
00314     if (nFolders != 0)
00315     {
00316         // Build pattern ("%d:", fld)
00317         //
00318         char *aPattern = alloc_lbuf("add_folder_name.pat");
00319         q = aPattern;
00320         q += mux_ltoa(fld, q);
00321         *q++ = ':';
00322         *q = '\0';
00323         size_t nPattern = q - aPattern;
00324 
00325         BMH_State bmhs;
00326         BMH_Prepare(&bmhs, nPattern, aPattern);
00327         for (;;)
00328         {
00329             int i = BMH_Execute(&bmhs, nPattern, aPattern, nFolders, aFolders);
00330             if (i < 0)
00331             {
00332                 break;
00333             }
00334 
00335             // Remove old record.
00336             //
00337             q = aFolders + i;
00338             p = q + nPattern;
00339 
00340             // Eat leading spaces.
00341             //
00342             while (  aFolders < q
00343                   && mux_isspace(q[-1]))
00344             {
00345                 q--;
00346             }
00347 
00348             // Skip past old record and trailing spaces.
00349             //
00350             while (  *p
00351                   && *p != ':')
00352             {
00353                 p++;
00354             }
00355             while (  *p
00356                   && !mux_isspace(*p))
00357             {
00358                 p++;
00359             }
00360             while (mux_isspace(*p))
00361             {
00362                 p++;
00363             }
00364 
00365             if (q != aFolders)
00366             {
00367                 *q++ = ' ';
00368             }
00369             while (*p)
00370             {
00371                 *q++ = *p++;
00372             }
00373             *q = '\0';
00374             nFolders = q - aFolders;
00375         }
00376         free_lbuf(aPattern);
00377     }
00378     if (nFolders + 1 + nNew < LBUF_SIZE)
00379     {
00380         // It will fit. Append new record.
00381         //
00382         q = aFolders + nFolders;
00383         if (nFolders)
00384         {
00385             *q++ = ' ';
00386         }
00387         memcpy(q, aNew, nNew);
00388         q += nNew;
00389         *q = '\0';
00390 
00391         atr_add(player, A_MAILFOLDERS, aFolders, player,
00392             AF_MDARK | AF_WIZARD | AF_NOPROG | AF_LOCK);
00393     }
00394     free_lbuf(aFolders);
00395     free_lbuf(aNew);
00396 }
00397 
00398 static char *get_folder_name(dbref player, int fld)
00399 {
00400     // Get the name of the folder, or return "unnamed".
00401     //
00402     int aflags;
00403     size_t nFolders;
00404     dbref aowner;
00405     static char aFolders[LBUF_SIZE];
00406     atr_get_str_LEN(aFolders, player, A_MAILFOLDERS, &aowner, &aflags,
00407         &nFolders);
00408     char *p;
00409     if (nFolders != 0)
00410     {
00411         char *aPattern = alloc_lbuf("get_folder_name");
00412         p = aPattern;
00413         p += mux_ltoa(fld, p);
00414         *p++ = ':';
00415         *p = '\0';
00416         size_t nPattern = p - aPattern;
00417 
00418         int i = BMH_StringSearch(nPattern, aPattern, nFolders, aFolders);
00419         free_lbuf(aPattern);
00420 
00421         if (0 <= i)
00422         {
00423             p = aFolders + i + nPattern;
00424             char *q = p;
00425             while (  *q
00426                   && *q != ':')
00427             {
00428                 q++;
00429             }
00430             *q = '\0';
00431             return p;
00432         }
00433     }
00434     p = "unnamed";
00435     return p;
00436 }
00437 
00438 static int get_folder_number(dbref player, char *name)
00439 {
00440     // Look up a folder name and return the corresponding folder number.
00441     //
00442     int aflags;
00443     size_t nFolders;
00444     dbref aowner;
00445     char *aFolders = alloc_lbuf("get_folder_num_str");
00446     atr_get_str_LEN(aFolders, player, A_MAILFOLDERS, &aowner, &aflags,
00447         &nFolders);
00448     if (nFolders != 0)
00449     {
00450         char *aPattern = alloc_lbuf("get_folder_num_pat");
00451         char *q = aPattern;
00452         *q++ = ':';
00453         char *p = name;
00454         while (*p)
00455         {
00456             *q++ = mux_toupper(*p);
00457             p++;
00458         }
00459         *q++ = ':';
00460         *q = '\0';
00461         size_t nPattern = q - aPattern;
00462 
00463         int i = BMH_StringSearch(nPattern, aPattern, nFolders, aFolders);
00464         free_lbuf(aPattern);
00465         if (0 <= i)
00466         {
00467             p = aFolders + i + nPattern;
00468             q = p;
00469             while (  *q
00470                   && !mux_isspace(*q))
00471             {
00472                 q++;
00473             }
00474             *q = '\0';
00475             i = mux_atol(p);
00476             free_lbuf(aFolders);
00477             return i;
00478         }
00479     }
00480     free_lbuf(aFolders);
00481     return -1;
00482 }
00483 
00484 static int parse_folder(dbref player, char *folder_string)
00485 {
00486     // Given a string, return a folder #, or -1.
00487     //
00488     if (  !folder_string
00489        || !*folder_string)
00490     {
00491         return -1;
00492     }
00493     if (mux_isdigit(*folder_string))
00494     {
00495         int fnum = mux_atol(folder_string);
00496         if (  fnum < 0
00497            || fnum > MAX_FOLDERS)
00498         {
00499             return -1;
00500         }
00501         else
00502         {
00503             return fnum;
00504         }
00505     }
00506 
00507     // Handle named folders here
00508     //
00509     return get_folder_number(player, folder_string);
00510 }
00511 
00512 #define MAIL_INVALID_RANGE  0
00513 #define MAIL_INVALID_NUMBER 1
00514 #define MAIL_INVALID_AGE    2
00515 #define MAIL_INVALID_DBREF  3
00516 #define MAIL_INVALID_PLAYER 4
00517 #define MAIL_INVALID_SPEC   5
00518 #define MAIL_INVALID_PLAYER_OR_USING_MALIAS 6
00519 
00520 static char *mailmsg[] =
00521 {
00522     "MAIL: Invalid message range",
00523     "MAIL: Invalid message number",
00524     "MAIL: Invalid age",
00525     "MAIL: Invalid dbref #",
00526     "MAIL: Invalid player",
00527     "MAIL: Invalid message specification",
00528     "MAIL: Invalid player or trying to send @mail to a @malias without a subject",
00529 };
00530 
00531 static bool parse_msglist(char *msglist, struct mail_selector *ms, dbref player)
00532 {
00533     // Take a message list, and return the appropriate mail_selector setup.
00534     // For now, msglists are quite restricted. That'll change once all this
00535     // is working. Returns 0 if couldn't parse, and also notifies the player
00536     // why.
00537 
00538     // Initialize the mail selector - this matches all messages.
00539     //
00540     ms->low = 0;
00541     ms->high = 0;
00542     ms->flags = 0x0FFF | M_MSUNREAD;
00543     ms->player = 0;
00544     ms->days = -1;
00545     ms->day_comp = 0;
00546 
00547     // Now, parse the message list.
00548     //
00549     if (!msglist || !*msglist)
00550     {
00551         // All messages
00552         //
00553         return true;
00554     }
00555 
00556     char *p = msglist;
00557     while (mux_isspace(*p))
00558     {
00559         p++;
00560     }
00561 
00562     if (*p == '\0')
00563     {
00564         return true;
00565     }
00566 
00567     if (mux_isdigit(*p))
00568     {
00569         // Message or range.
00570         //
00571         char *q = strchr(p, '-');
00572         if (q)
00573         {
00574             // We have a subrange, split it up and test to see if it is valid.
00575             //
00576             q++;
00577             ms->low = mux_atol(p);
00578             if (ms->low <= 0)
00579             {
00580                 notify(player, mailmsg[MAIL_INVALID_RANGE]);
00581                 return false;
00582             }
00583             if (*q == '\0')
00584             {
00585                 // Unbounded range.
00586                 //
00587                 ms->high = 0;
00588             }
00589             else
00590             {
00591                 ms->high = mux_atol(q);
00592                 if (ms->low > ms->high)
00593                 {
00594                     notify(player, mailmsg[MAIL_INVALID_RANGE]);
00595                     return false;
00596                 }
00597             }
00598         }
00599         else
00600         {
00601             // A single message.
00602             //
00603             ms->low = ms->high = mux_atol(p);
00604             if (ms->low <= 0)
00605             {
00606                 notify(player, mailmsg[MAIL_INVALID_NUMBER]);
00607                 return false;
00608             }
00609         }
00610     }
00611     else
00612     {
00613         switch (mux_toupper(*p))
00614         {
00615         case '-':
00616 
00617             // Range with no start.
00618             //
00619             p++;
00620             if (*p == '\0')
00621             {
00622                 notify(player, mailmsg[MAIL_INVALID_RANGE]);
00623                 return false;
00624             }
00625             ms->high = mux_atol(p);
00626             if (ms->high <= 0)
00627             {
00628                 notify(player, mailmsg[MAIL_INVALID_RANGE]);
00629                 return false;
00630             }
00631             break;
00632 
00633         case '~':
00634 
00635             // Exact # of days old.
00636             //
00637             p++;
00638             if (*p == '\0')
00639             {
00640                 notify(player, mailmsg[MAIL_INVALID_AGE]);
00641                 return false;
00642             }
00643             ms->day_comp = 0;
00644             ms->days = mux_atol(p);
00645             if (ms->days < 0)
00646             {
00647                 notify(player, mailmsg[MAIL_INVALID_AGE]);
00648                 return false;
00649             }
00650             break;
00651 
00652         case '<':
00653 
00654             // Less than # of days old.
00655             //
00656             p++;
00657             if (*p == '\0')
00658             {
00659                 notify(player, mailmsg[MAIL_INVALID_AGE]);
00660                 return false;
00661             }
00662             ms->day_comp = -1;
00663             ms->days = mux_atol(p);
00664             if (ms->days < 0)
00665             {
00666                 notify(player, mailmsg[MAIL_INVALID_AGE]);
00667                 return false;
00668             }
00669             break;
00670 
00671         case '>':
00672 
00673             // Greater than # of days old.
00674             //
00675             p++;
00676             if (*p == '\0')
00677             {
00678                 notify(player, mailmsg[MAIL_INVALID_AGE]);
00679                 return false;
00680             }
00681             ms->day_comp = 1;
00682             ms->days = mux_atol(p);
00683             if (ms->days < 0)
00684             {
00685                 notify(player, mailmsg[MAIL_INVALID_AGE]);
00686                 return false;
00687             }
00688             break;
00689 
00690         case '#':
00691 
00692             // From db#.
00693             //
00694             p++;
00695             if (*p == '\0')
00696             {
00697                 notify(player, mailmsg[MAIL_INVALID_DBREF]);
00698                 return false;
00699             }
00700             ms->player = mux_atol(p);
00701             if (!Good_obj(ms->player) || !(ms->player))
00702             {
00703                 notify(player, mailmsg[MAIL_INVALID_DBREF]);
00704                 return false;
00705             }
00706             break;
00707 
00708         case '*':
00709 
00710             // From player name.
00711             //
00712             p++;
00713             if (*p == '\0')
00714             {
00715                 notify(player, mailmsg[MAIL_INVALID_PLAYER]);
00716                 return false;
00717             }
00718             ms->player = lookup_player(player, p, true);
00719             if (ms->player == NOTHING)
00720             {
00721                 notify(player, mailmsg[MAIL_INVALID_PLAYER_OR_USING_MALIAS]);
00722                 return false;
00723             }
00724             break;
00725 
00726         case 'A':
00727 
00728             // All messages, all folders
00729             //
00730             p++;
00731             switch (mux_toupper(*p))
00732             {
00733             case '\0':
00734                 notify(player, "MAIL: A isn't enough (all?)");
00735                 return false;
00736 
00737             case 'L':
00738 
00739                 // All messages, all folders
00740                 //
00741                 p++;
00742                 switch (mux_toupper(*p))
00743                 {
00744                 case '\0':
00745                     notify(player, "MAIL: AL isn't enough (all?)");
00746                     return false;
00747 
00748                 case 'L':
00749 
00750                     // All messages, all folders
00751                     //
00752                     p++;
00753                     if (*p == '\0')
00754                     {
00755                         ms->flags = M_ALL;
00756                     }
00757                     else
00758                     {
00759                         notify(player, mailmsg[MAIL_INVALID_SPEC]);
00760                         return false;
00761                     }
00762                     break;
00763 
00764                 default:
00765 
00766                     // Bad
00767                     //
00768                     notify(player, mailmsg[MAIL_INVALID_SPEC]);
00769                     return false;
00770                 }
00771                 break;
00772 
00773             default:
00774 
00775                 // Bad
00776                 //
00777                 notify(player, mailmsg[MAIL_INVALID_SPEC]);
00778                 return false;
00779             }
00780             break;
00781 
00782         case 'U':
00783 
00784             // Urgent, Unread
00785             //
00786             p++;
00787             if (*p == '\0')
00788             {
00789                 notify(player, "MAIL: U is ambiguous (urgent or unread?)");
00790                 return false;
00791             }
00792             switch (mux_toupper(*p))
00793             {
00794             case 'R':
00795 
00796                 // Urgent
00797                 //
00798                 ms->flags = M_URGENT;
00799                 break;
00800 
00801             case 'N':
00802 
00803                 // Unread
00804                 //
00805                 ms->flags = M_MSUNREAD;
00806                 break;
00807 
00808             default:
00809 
00810                 // Bad
00811                 //
00812                 notify(player, mailmsg[MAIL_INVALID_SPEC]);
00813                 return false;
00814             }
00815             break;
00816 
00817         case 'R':
00818 
00819             // Read
00820             //
00821             ms->flags = M_ISREAD;
00822             break;
00823 
00824         case 'C':
00825 
00826             // Cleared.
00827             //
00828             ms->flags = M_CLEARED;
00829             break;
00830 
00831         case 'T':
00832 
00833             // Tagged.
00834             //
00835             ms->flags = M_TAG;
00836             break;
00837 
00838         case 'M':
00839 
00840             // Mass, me.
00841             //
00842             p++;
00843             if (*p == '\0')
00844             {
00845                 notify(player, "MAIL: M is ambiguous (mass or me?)");
00846                 return false;
00847             }
00848             switch (mux_toupper(*p))
00849             {
00850             case 'A':
00851 
00852                 ms->flags = M_MASS;
00853                 break;
00854 
00855             case 'E':
00856 
00857                 ms->player = player;
00858                 break;
00859 
00860             default:
00861 
00862                 notify(player, mailmsg[MAIL_INVALID_SPEC]);
00863                 return false;
00864             }
00865             break;
00866 
00867         default:
00868 
00869             // Bad news.
00870             //
00871             notify(player, mailmsg[MAIL_INVALID_SPEC]);
00872             return false;
00873         }
00874     }
00875     return true;
00876 }
00877 
00878 static int player_folder(dbref player)
00879 {
00880     // Return the player's current folder number. If they don't have one, set
00881     // it to 0.
00882     //
00883     int flags;
00884     char *atrstr = atr_pget(player, A_MAILCURF, &player, &flags);
00885     if (!*atrstr)
00886     {
00887         free_lbuf(atrstr);
00888         set_player_folder(player, 0);
00889         return 0;
00890     }
00891     int number = mux_atol(atrstr);
00892     free_lbuf(atrstr);
00893     return number;
00894 }
00895 
00896 // Change or rename a folder
00897 //
00898 static void do_mail_change_folder(dbref player, char *fld, char *newname)
00899 {
00900     int pfld;
00901 
00902     if (!fld || !*fld)
00903     {
00904         // Check mail in all folders
00905         //
00906         for (pfld = 0; pfld <= MAX_FOLDERS; pfld++)
00907         {
00908             check_mail(player, pfld, true);
00909         }
00910         pfld = player_folder(player);
00911         notify(player, tprintf("MAIL: Current folder is %d [%s].",
00912                        pfld, get_folder_name(player, pfld)));
00913         return;
00914     }
00915     pfld = parse_folder(player, fld);
00916     if (pfld < 0)
00917     {
00918         notify(player, "MAIL: What folder is that?");
00919         return;
00920     }
00921     if (newname && *newname)
00922     {
00923         // We're changing a folder name here
00924         //
00925         if (strlen(newname) > FOLDER_NAME_LEN)
00926         {
00927             notify(player, "MAIL: Folder name too long");
00928             return;
00929         }
00930         char *p;
00931         for (p = newname; mux_isalnum(*p); p++) ;
00932         if (*p != '\0')
00933         {
00934             notify(player, "MAIL: Illegal folder name");
00935             return;
00936         }
00937 
00938         add_folder_name(player, pfld, newname);
00939         notify(player, tprintf("MAIL: Folder %d now named '%s'", pfld, newname));
00940     }
00941     else
00942     {
00943         // Set a new folder
00944         //
00945         set_player_folder(player, pfld);
00946         notify(player, tprintf("MAIL: Current folder set to %d [%s].",
00947                        pfld, get_folder_name(player, pfld)));
00948     }
00949 }
00950 
00951 static int sign(int x)
00952 {
00953     if (x == 0)
00954     {
00955         return 0;
00956     }
00957     else if (x < 0)
00958     {
00959         return -1;
00960     }
00961     else
00962     {
00963         return 1;
00964     }
00965 }
00966 
00967 static bool mail_match(struct mail *mp, struct mail_selector ms, int num)
00968 {
00969     // Does a piece of mail match the mail_selector?
00970     //
00971     if (ms.low && num < ms.low)
00972     {
00973         return false;
00974     }
00975     if (ms.high && ms.high < num)
00976     {
00977         return false;
00978     }
00979     if (ms.player && mp->from != ms.player)
00980     {
00981         return false;
00982     }
00983 
00984     mail_flag mpflag = Read(mp)
00985         ? (mp->read | M_ALL)
00986         : (mp->read | M_ALL | M_MSUNREAD);
00987 
00988     if ((ms.flags & mpflag) == 0)
00989     {
00990         return false;
00991     }
00992 
00993     if (ms.days == -1)
00994     {
00995         return true;
00996     }
00997 
00998     // Get the time now, subtract mp->time, and compare the results with
00999     // ms.days (in manner of ms.day_comp)
01000     //
01001     CLinearTimeAbsolute ltaNow;
01002     ltaNow.GetLocal();
01003 
01004     const char *pMailTimeStr = mp->time;
01005 
01006     CLinearTimeAbsolute ltaMail;
01007     if (ltaMail.SetString(pMailTimeStr))
01008     {
01009         CLinearTimeDelta ltd(ltaMail, ltaNow);
01010         int iDiffDays = ltd.ReturnDays();
01011         if (sign(iDiffDays - ms.days) == ms.day_comp)
01012         {
01013             return true;
01014         }
01015     }
01016     return false;
01017 }
01018 
01019 // Adjust the flags of a set of messages.
01020 // If negate is true, clear the flag.
01021 static void do_mail_flags(dbref player, char *msglist, mail_flag flag, bool negate)
01022 {
01023     struct mail_selector ms;
01024 
01025     if (!parse_msglist(msglist, &ms, player))
01026     {
01027         return;
01028     }
01029     int i = 0, j = 0;
01030     int folder = player_folder(player);
01031 
01032     MailList ml(player);
01033     struct mail *mp;
01034     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
01035     {
01036         if (  All(ms)
01037            || Folder(mp) == folder)
01038         {
01039             i++;
01040             if (mail_match(mp, ms, i))
01041             {
01042                 j++;
01043                 if (negate)
01044                 {
01045                     mp->read &= ~flag;
01046                 }
01047                 else
01048                 {
01049                     mp->read |= flag;
01050                 }
01051 
01052                 switch (flag)
01053                 {
01054                 case M_TAG:
01055                     notify(player, tprintf("MAIL: Msg #%d %s.", i, negate ? "untagged" : "tagged"));
01056                     break;
01057 
01058                 case M_CLEARED:
01059                     if (Unread(mp) && !negate)
01060                     {
01061                         notify(player, tprintf("MAIL: Unread Msg #%d cleared! Use @mail/unclear %d to recover.", i, i));
01062                     }
01063                     else
01064                     {
01065                         notify(player, tprintf("MAIL: Msg #%d %s.", i, negate ? "uncleared" : "cleared"));
01066                     }
01067                     break;
01068 
01069                 case M_SAFE:
01070                     notify(player, tprintf("MAIL: Msg #%d marked safe.", i));
01071                     break;
01072                 }
01073             }
01074         }
01075     }
01076 
01077     if (!j)
01078     {
01079         // Ran off the end of the list without finding anything.
01080         //
01081         notify(player, "MAIL: You don't have any matching messages!");
01082     }
01083 }
01084 
01085 static void do_mail_tag(dbref player, char *msglist)
01086 {
01087     do_mail_flags(player, msglist, M_TAG, false);
01088 }
01089 
01090 static void do_mail_safe(dbref player, char *msglist)
01091 {
01092     do_mail_flags(player, msglist, M_SAFE, false);
01093 }
01094 
01095 void do_mail_clear(dbref player, char *msglist)
01096 {
01097     do_mail_flags(player, msglist, M_CLEARED, false);
01098 }
01099 
01100 static void do_mail_untag(dbref player, char *msglist)
01101 {
01102     do_mail_flags(player, msglist, M_TAG, true);
01103 }
01104 
01105 static void do_mail_unclear(dbref player, char *msglist)
01106 {
01107     do_mail_flags(player, msglist, M_CLEARED, true);
01108 }
01109 
01110 // Change a message's folder.
01111 //
01112 static void do_mail_file(dbref player, char *msglist, char *folder)
01113 {
01114     struct mail_selector ms;
01115     if (!parse_msglist(msglist, &ms, player))
01116     {
01117         return;
01118     }
01119     int foldernum;
01120     if ((foldernum = parse_folder(player, folder)) == -1)
01121     {
01122         notify(player, "MAIL: Invalid folder specification");
01123         return;
01124     }
01125     int i = 0, j = 0;
01126     int origfold = player_folder(player);
01127 
01128     MailList ml(player);
01129     struct mail *mp;
01130     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
01131     {
01132         if (  All(ms)
01133            || (Folder(mp) == origfold))
01134         {
01135             i++;
01136             if (mail_match(mp, ms, i))
01137             {
01138                 j++;
01139 
01140                 // Clear the folder.
01141                 //
01142                 mp->read &= M_FMASK;
01143                 mp->read |= FolderBit(foldernum);
01144                 notify(player, tprintf("MAIL: Msg %d filed in folder %d", i, foldernum));
01145             }
01146         }
01147     }
01148 
01149     if (!j)
01150     {
01151         // Ran off the end of the list without finding anything.
01152         //
01153         notify(player, "MAIL: You don't have any matching messages!");
01154     }
01155 }
01156 
01157 // A mail alias can be any combination of upper-case letters, lower-case
01158 // letters, and digits. No leading digits. No symbols. No ANSI. Length is
01159 // limited to SIZEOF_MALIAS-1. Case is preserved.
01160 //
01161 static char *MakeCanonicalMailAlias
01162 (
01163     char *pMailAlias,
01164     int *pnValidMailAlias,
01165     bool *pbValidMailAlias
01166 )
01167 {
01168     static char Buffer[SIZEOF_MALIAS];
01169     size_t nLeft = sizeof(Buffer)-1;
01170     char *q = Buffer;
01171     char *p = pMailAlias;
01172 
01173     if (  !p
01174        || !mux_isalpha(*p))
01175     {
01176         *pnValidMailAlias = 0;
01177         *pbValidMailAlias = false;
01178         return NULL;
01179     }
01180     *q++ = *p++;
01181     nLeft--;
01182 
01183     while (  *p
01184           && nLeft)
01185     {
01186         if (  !mux_isalpha(*p)
01187            && !mux_isdigit(*p)
01188            && *p != '_')
01189         {
01190             break;
01191         }
01192         *q++ = *p++;
01193         nLeft--;
01194     }
01195     *q = '\0';
01196 
01197     *pnValidMailAlias = q - Buffer;
01198     *pbValidMailAlias = true;
01199     return Buffer;
01200 }
01201 
01202 #define GMA_NOTFOUND    1
01203 #define GMA_FOUND       2
01204 #define GMA_INVALIDFORM 3
01205 
01206 static struct malias *get_malias(dbref player, char *alias, int *pnResult)
01207 {
01208     *pnResult = GMA_INVALIDFORM;
01209     if (!alias)
01210     {
01211         return NULL;
01212     }
01213     if (alias[0] == '#')
01214     {
01215         if (ExpMail(player))
01216         {
01217             int x = mux_atol(alias + 1);
01218             if (x < 0 || x >= ma_top)
01219             {
01220                 *pnResult = GMA_NOTFOUND;
01221                 return NULL;
01222             }
01223             *pnResult = GMA_FOUND;
01224             return malias[x];
01225         }
01226     }
01227     else if (alias[0] == '*')
01228     {
01229         int  nValidMailAlias;
01230         bool bValidMailAlias;
01231         char *pValidMailAlias = MakeCanonicalMailAlias
01232                                 (   alias+1,
01233                                     &nValidMailAlias,
01234                                     &bValidMailAlias
01235                                 );
01236 
01237         if (bValidMailAlias)
01238         {
01239             for (int i = 0; i < ma_top; i++)
01240             {
01241                 struct malias *m = malias[i];
01242                 if (  m->owner == player
01243                    || m->owner == GOD
01244                    || ExpMail(player))
01245                 {
01246                     if (!strcmp(pValidMailAlias, m->name))
01247                     {
01248                         // Found it!
01249                         //
01250                         *pnResult = GMA_FOUND;
01251                         return m;
01252                     }
01253                 }
01254             }
01255             *pnResult = GMA_NOTFOUND;
01256         }
01257     }
01258     if (*pnResult == GMA_INVALIDFORM)
01259     {
01260         if (ExpMail(player))
01261         {
01262             notify(player, "MAIL: Mail aliases must be of the form *<name> or #<num>.");
01263         }
01264         else
01265         {
01266             notify(player, "MAIL: Mail aliases must be of the form *<name>.");
01267         }
01268     }
01269     return NULL;
01270 }
01271 
01272 static char *make_namelist(dbref player, char *arg)
01273 {
01274     UNUSED_PARAMETER(player);
01275 
01276     char *p;
01277     char *oldarg = alloc_lbuf("make_namelist.oldarg");
01278     char *names = alloc_lbuf("make_namelist.names");
01279     char *bp = names;
01280 
01281     strcpy(oldarg, arg);
01282 
01283     MUX_STRTOK_STATE tts;
01284     mux_strtok_src(&tts, oldarg);
01285     mux_strtok_ctl(&tts, " ");
01286     bool bFirst = true;
01287     for (p = mux_strtok_parse(&tts); p; p = mux_strtok_parse(&tts))
01288     {
01289         if (!bFirst)
01290         {
01291             safe_str(", ", names, &bp);
01292         }
01293         bFirst = false;
01294 
01295         if (  mux_isdigit(p[0])
01296            || (  p[0] == '!'
01297               && mux_isdigit(p[1])))
01298         {
01299             char ch = p[0];
01300             if (ch == '!')
01301             {
01302                 p++;
01303             }
01304             dbref target = mux_atol(p);
01305             if (  Good_obj(target)
01306                && isPlayer(target))
01307             {
01308                 if (ch == '!')
01309                 {
01310                     safe_chr('!', names, &bp);
01311                 }
01312                 safe_str(Name(target), names, &bp);
01313             }
01314         }
01315         else
01316         {
01317             safe_str(p, names, &bp);
01318         }
01319     }
01320     *bp = '\0';
01321     free_lbuf(oldarg);
01322     return names;
01323 }
01324 
01325 #define NUM_MAILSTATUSTABLE 7
01326 static struct tag_mailstatusentry
01327 {
01328     int nMask;
01329     char *pYes;
01330     int   nYes;
01331     char *pNo;
01332     int   nNo;
01333 }
01334 aMailStatusTable[NUM_MAILSTATUSTABLE] =
01335 {
01336     { M_ISREAD,  "Read",    4, "Unread", 6 },
01337     { M_CLEARED, "Cleared", 7,  0,       0 },
01338     { M_URGENT,  "Urgent",  6,  0,       0 },
01339     { M_MASS,    "Mass",    4,  0,       0 },
01340     { M_FORWARD, "Fwd",     3,  0,       0 },
01341     { M_TAG,     "Tagged",  6,  0,       0 },
01342     { M_SAFE,    "Safe",    4,  0,       0 }
01343 };
01344 
01345 static char *status_string(struct mail *mp)
01346 {
01347     // Return a longer description of message flags.
01348     //
01349     char *tbuf1 = alloc_lbuf("status_string");
01350     char *p = tbuf1;
01351     struct tag_mailstatusentry *mse = aMailStatusTable;
01352     for (int i = 0; i < NUM_MAILSTATUSTABLE; i++, mse++)
01353     {
01354         if (mp->read & mse->nMask)
01355         {
01356             if (p != tbuf1) *p++ = ' ';
01357             memcpy(p, mse->pYes, mse->nYes);
01358             p += mse->nYes;
01359         }
01360         else if (mse->pNo)
01361         {
01362             if (p != tbuf1) *p++ = ' ';
01363             memcpy(p, mse->pNo, mse->nNo);
01364             p += mse->nNo;
01365         }
01366     }
01367     *p++ = '\0';
01368     return tbuf1;
01369 }
01370 
01371 static void do_mail_read(dbref player, char *msglist)
01372 {
01373     struct mail_selector ms;
01374     if (!parse_msglist(msglist, &ms, player))
01375     {
01376         return;
01377     }
01378 
01379     char *status, *names;
01380     int i = 0, j = 0;
01381     char *buff = alloc_lbuf("do_mail_read.1");
01382     int folder = player_folder(player);
01383 
01384     MailList ml(player);
01385     struct mail *mp;
01386     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
01387     {
01388         if (Folder(mp) == folder)
01389         {
01390             i++;
01391             if (mail_match(mp, ms, i))
01392             {
01393                 // Read it.
01394                 //
01395                 j++;
01396                 buff[LBUF_SIZE-1] = '\0';
01397                 strncpy(buff, MessageFetch(mp->number), LBUF_SIZE);
01398                 if (buff[LBUF_SIZE-1] != '\0')
01399                 {
01400                     STARTLOG(LOG_BUGS, "BUG", "MAIL");
01401                     log_text(tprintf("do_mail_read: %s: Mail message %d truncated.", Name(player), mp->number));
01402                     ENDLOG;
01403                     buff[LBUF_SIZE-1] = '\0';
01404                 }
01405                 notify(player, DASH_LINE);
01406                 status = status_string(mp);
01407                 names = make_namelist(player, mp->tolist);
01408                 char szSubjectBuffer[MBUF_SIZE];
01409                 int iRealVisibleWidth;
01410                 ANSI_TruncateToField(mp->subject, sizeof(szSubjectBuffer),
01411                     szSubjectBuffer, 65, &iRealVisibleWidth, ANSI_ENDGOAL_NORMAL);
01412                 notify(player, tprintf("%-3d         From:  %-*s  At: %-25s  %s\r\nFldr   : %-2d Status: %s\r\nTo     : %-65s\r\nSubject: %s",
01413                                i, PLAYER_NAME_LIMIT - 6, Name(mp->from),
01414                                mp->time,
01415                                (Connected(mp->from) &&
01416                                (!Hidden(mp->from) || See_Hidden(player))) ?
01417                                " (Conn)" : "      ", folder,
01418                                status,
01419                                names,
01420                                szSubjectBuffer));
01421                 free_lbuf(names);
01422                 free_lbuf(status);
01423                 notify(player, DASH_LINE);
01424                 notify(player, buff);
01425                 notify(player, DASH_LINE);
01426                 if (Unread(mp))
01427                 {
01428                     // Mark message as read.
01429                     //
01430                     mp->read |= M_ISREAD;
01431                 }
01432             }
01433         }
01434     }
01435     free_lbuf(buff);
01436 
01437     if (!j)
01438     {
01439         // Ran off the end of the list without finding anything.
01440         //
01441         notify(player, "MAIL: You don't have that many matching messages!");
01442     }
01443 }
01444 
01445 static char *status_chars(struct mail *mp)
01446 {
01447     // Return a short description of message flags.
01448     //
01449     static char res[10];
01450 
01451     char *p = res;
01452     *p++ = Read(mp)     ? '-' : 'N';
01453     *p++ = M_Safe(mp)   ? 'S' : '-';
01454     *p++ = Cleared(mp)  ? 'C' : '-';
01455     *p++ = Urgent(mp)   ? 'U' : '-';
01456     *p++ = Mass(mp)     ? 'M' : '-';
01457     *p++ = Forward(mp)  ? 'F' : '-';
01458     *p++ = Tagged(mp)   ? '+' : '-';
01459     *p = '\0';
01460     return res;
01461 }
01462 
01463 static void do_mail_review(dbref player, char *name, char *msglist)
01464 {
01465     dbref target = lookup_player(player, name, true);
01466     if (target == NOTHING)
01467     {
01468         notify(player, "MAIL: No such player.");
01469         return;
01470     }
01471 
01472     struct mail *mp;
01473     struct mail_selector ms;
01474     int i = 0, j = 0;
01475     int iRealVisibleWidth;
01476     char szSubjectBuffer[MBUF_SIZE];
01477 
01478     if (  !msglist
01479        || !*msglist)
01480     {
01481         notify(player, tprintf("--------------------   MAIL: %-25s   ------------------", Name(target)));
01482         MailList ml(target);
01483         for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
01484         {
01485             if (mp->from == player)
01486             {
01487                 i++;
01488                 ANSI_TruncateToField(mp->subject, sizeof(szSubjectBuffer),
01489                     szSubjectBuffer, 25, &iRealVisibleWidth, ANSI_ENDGOAL_NORMAL);
01490                 size_t nSize = strlen(MessageFetch(mp->number));
01491                 const char *pFromName = Name(mp->from);
01492                 notify(player, tprintf("[%s] %-3d (%4d) From: %-*s Sub: %s",
01493                                status_chars(mp),
01494                                i, nSize,
01495                                PLAYER_NAME_LIMIT - 6, pFromName,
01496                                szSubjectBuffer));
01497             }
01498         }
01499         notify(player, DASH_LINE);
01500     }
01501     else
01502     {
01503         if (!parse_msglist(msglist, &ms, target))
01504         {
01505             return;
01506         }
01507         MailList ml(target);
01508         for (mp = ml.FirstItem(); !ml.IsEnd() && !MuxAlarm.bAlarmed; mp = ml.NextItem())
01509         {
01510             if (mp->from == player)
01511             {
01512                 i++;
01513                 if (mail_match(mp, ms, i))
01514                 {
01515                     j++;
01516                     char *status = status_string(mp);
01517                     const char *str = MessageFetch(mp->number);
01518                     ANSI_TruncateToField(mp->subject, sizeof(szSubjectBuffer),
01519                         szSubjectBuffer, 65, &iRealVisibleWidth, ANSI_ENDGOAL_NORMAL);
01520                     notify(player, DASH_LINE);
01521                     notify(player, tprintf("%-3d         From:  %-*s  At: %-25s  %s\r\nFldr   : %-2d Status: %s\r\nSubject: %s",
01522                                    i, PLAYER_NAME_LIMIT - 6, Name(mp->from),
01523                                    mp->time,
01524                                    (Connected(mp->from) &&
01525                                    (!Hidden(mp->from) || See_Hidden(player))) ?
01526                                    " (Conn)" : "      ", 0,
01527                                    status, szSubjectBuffer));
01528                     free_lbuf(status);
01529                     notify(player, DASH_LINE);
01530                     notify(player, str);
01531                     notify(player, DASH_LINE);
01532                 }
01533             }
01534         }
01535 
01536         if (!j)
01537         {
01538             // Ran off the end of the list without finding anything.
01539             //
01540             notify(player, "MAIL: You don't have that many matching messages!");
01541         }
01542     }
01543 }
01544 
01545 static char *mail_list_time(const char *the_time)
01546 {
01547     char *p = (char *)the_time;
01548     char *new0 = alloc_lbuf("mail_list_time");
01549     char *q = new0;
01550     if (!p || !*p)
01551     {
01552         *new0 = '\0';
01553         return new0;
01554     }
01555 
01556     // Format of the_time is: day mon dd hh:mm:ss yyyy
01557     // Chop out :ss
01558     //
01559     int i;
01560     for (i = 0; i < 16; i++)
01561     {
01562         if (*p)
01563         {
01564             *q++ = *p++;
01565         }
01566     }
01567 
01568     for (i = 0; i < 3; i++)
01569     {
01570         if (*p)
01571         {
01572             p++;
01573         }
01574     }
01575 
01576     for (i = 0; i < 5; i++)
01577     {
01578         if (*p)
01579         {
01580             *q++ = *p++;
01581         }
01582     }
01583 
01584     *q = '\0';
01585     return new0;
01586 }
01587 
01588 static void do_mail_list(dbref player, char *msglist, bool sub)
01589 {
01590     struct mail_selector ms;
01591 
01592     if (!parse_msglist(msglist, &ms, player))
01593     {
01594         return;
01595     }
01596     int i = 0;
01597     char *time;
01598     int iRealVisibleWidth;
01599     char szSubjectBuffer[MBUF_SIZE];
01600     int folder = player_folder(player);
01601 
01602     notify(player,
01603         tprintf("---------------------------   MAIL: Folder %d   ----------------------------", folder));
01604 
01605     MailList ml(player);
01606     struct mail *mp;
01607     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
01608     {
01609         if (Folder(mp) == folder)
01610         {
01611             i++;
01612             if (mail_match(mp, ms, i))
01613             {
01614                 time = mail_list_time(mp->time);
01615                 size_t nSize = strlen(MessageFetch(mp->number));
01616                 const char *pFromName = Name(mp->from);
01617                 if (sub)
01618                 {
01619                     ANSI_TruncateToField(mp->subject, sizeof(szSubjectBuffer),
01620                         szSubjectBuffer, 25, &iRealVisibleWidth, ANSI_ENDGOAL_NORMAL);
01621 
01622                     notify(player, tprintf("[%s] %-3d (%4d) From: %-*s Sub: %s",
01623                         status_chars(mp), i, nSize, PLAYER_NAME_LIMIT - 6, pFromName, szSubjectBuffer));
01624                 }
01625                 else
01626                 {
01627                     notify(player, tprintf("[%s] %-3d (%4d) From: %-*s At: %s %s",
01628                         status_chars(mp), i, nSize, PLAYER_NAME_LIMIT - 6, pFromName, time,
01629                             ((Connected(mp->from) && (!Hidden(mp->from) || See_Hidden(player))) ? "Conn" : " ")));
01630                 }
01631                 free_lbuf(time);
01632             }
01633         }
01634     }
01635     notify(player, DASH_LINE);
01636 }
01637 
01638 void do_mail_purge(dbref player)
01639 {
01640     // Go through player's mail, and remove anything marked cleared.
01641     //
01642     MailList ml(player);
01643     struct mail *mp;
01644     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
01645     {
01646         if (Cleared(mp))
01647         {
01648             ml.RemoveItem();
01649         }
01650     }
01651     notify(player, "MAIL: Mailbox purged.");
01652 }
01653 
01654 static char *make_numlist(dbref player, char *arg, bool bBlind)
01655 {
01656     char *tail, spot;
01657     struct malias *m;
01658     dbref target;
01659     int nRecip = 0;
01660     dbref aRecip[(LBUF_SIZE+1)/2];
01661 
01662     char *head = arg;
01663 
01664     while (  head
01665           && *head)
01666     {
01667         while (*head == ' ')
01668         {
01669             head++;
01670         }
01671 
01672         tail = head;
01673         while (  *tail
01674               && *tail != ' ')
01675         {
01676             if (*tail == '"')
01677             {
01678                 head++;
01679                 tail++;
01680                 while (  *tail
01681                       && *tail != '"')
01682                 {
01683                     tail++;
01684                 }
01685             }
01686             if (*tail)
01687             {
01688                 tail++;
01689             }
01690         }
01691         tail--;
01692         if (*tail != '"')
01693         {
01694             tail++;
01695         }
01696         spot = *tail;
01697         *tail = '\0';
01698 
01699         if (*head == '*')
01700         {
01701             int nResult;
01702             m = get_malias(player, head, &nResult);
01703             if (nResult == GMA_NOTFOUND)
01704             {
01705                 notify(player, tprintf("MAIL: Alias '%s' does not exist.", head));
01706                 return NULL;
01707             }
01708             else if (nResult == GMA_INVALIDFORM)
01709             {
01710                 notify(player, tprintf("MAIL: '%s' is a badly-formed alias.", head));
01711                 return NULL;
01712             }
01713             for (int i = 0; i < m->numrecep; i++)
01714             {
01715                  aRecip[nRecip++] = m->list[i];
01716             }
01717         }
01718         else
01719         {
01720             target = lookup_player(player, head, true);
01721             if (Good_obj(target))
01722             {
01723                 aRecip[nRecip++] = target;
01724             }
01725             else
01726             {
01727                 notify(player, tprintf("MAIL: '%s' does not exist.", head));
01728                 return NULL;
01729             }
01730         }
01731 
01732         // Get the next recip.
01733         //
01734         *tail = spot;
01735         head = tail;
01736         if (*head == '"')
01737         {
01738             head++;
01739         }
01740     }
01741 
01742     if (nRecip <= 0)
01743     {
01744         notify(player, "MAIL: No players specified.");
01745         return NULL;
01746     }
01747     else
01748     {
01749         ITL itl;
01750         char *numbuf, *numbp;
01751         numbp = numbuf = alloc_lbuf("mail.make_numlist");
01752         ItemToList_Init(&itl, numbuf, &numbp, bBlind ? '!' : '\0');
01753         int i;
01754         for (i = 0; i < nRecip; i++)
01755         {
01756             if (aRecip[i] != NOTHING)
01757             {
01758                 for (int j = i + 1; j < nRecip; j++)
01759                 {
01760                     if (aRecip[i] == aRecip[j])
01761                     {
01762                         aRecip[j] = NOTHING;
01763                     }
01764                 }
01765                 if (Good_obj(aRecip[i]))
01766                 {
01767                     ItemToList_AddInteger(&itl, aRecip[i]);
01768                 }
01769             }
01770         }
01771         ItemToList_Final(&itl);
01772         return numbuf;
01773     }
01774 }
01775 
01776 static void do_expmail_start(dbref player, char *arg, char *subject)
01777 {
01778     if (!arg || !*arg)
01779     {
01780         notify(player, "MAIL: I do not know whom you want to mail.");
01781         return;
01782     }
01783     if (!subject || !*subject)
01784     {
01785         notify(player, "MAIL: No subject.");
01786         return;
01787     }
01788     if (Flags2(player) & PLAYER_MAILS)
01789     {
01790         notify(player, "MAIL: Mail message already in progress.");
01791         return;
01792     }
01793     if (  !Wizard(player)
01794        && ThrottleMail(player))
01795     {
01796         notify(player, "MAIL: Too much @mail sent recently.");
01797         return;
01798     }
01799     char *tolist = make_numlist(player, arg, false);
01800     if (!tolist)
01801     {
01802         return;
01803     }
01804 
01805     atr_add_raw(player, A_MAILTO, tolist);
01806     atr_add_raw(player, A_MAILSUB, subject);
01807     atr_add_raw(player, A_MAILFLAGS, "0");
01808     atr_clr(player, A_MAILMSG);
01809     Flags2(player) |= PLAYER_MAILS;
01810     char *names = make_namelist(player, tolist);
01811     notify(player, tprintf("MAIL: You are sending mail to '%s'.", names));
01812     free_lbuf(names);
01813     free_lbuf(tolist);
01814 }
01815 
01816 static void do_mail_fwd(dbref player, char *msg, char *tolist)
01817 {
01818     if (Flags2(player) & PLAYER_MAILS)
01819     {
01820         notify(player, "MAIL: Mail message already in progress.");
01821         return;
01822     }
01823     if (!msg || !*msg)
01824     {
01825         notify(player, "MAIL: No message list.");
01826         return;
01827     }
01828     if (!tolist || !*tolist)
01829     {
01830         notify(player, "MAIL: To whom should I forward?");
01831         return;
01832     }
01833     if (  !Wizard(player)
01834        && ThrottleMail(player))
01835     {
01836         notify(player, "MAIL: Too much @mail sent recently.");
01837         return;
01838     }
01839     int num = mux_atol(msg);
01840     if (!num)
01841     {
01842         notify(player, "MAIL: I don't understand that message number.");
01843         return;
01844     }
01845     struct mail *mp = mail_fetch(player, num);
01846     if (!mp)
01847     {
01848         notify(player, "MAIL: You can't forward non-existent messages.");
01849         return;
01850     }
01851     do_expmail_start(player, tolist, tprintf("%s (fwd from %s)", mp->subject, Name(mp->from)));
01852     atr_add_raw(player, A_MAILMSG, MessageFetch(mp->number));
01853     const char *pValue = atr_get_raw(player, A_MAILFLAGS);
01854     int iFlag = M_FORWARD;
01855     if (pValue)
01856     {
01857         iFlag |= mux_atol(pValue);
01858     }
01859     atr_add_raw(player, A_MAILFLAGS, mux_ltoa_t(iFlag));
01860 }
01861 
01862 static void do_mail_reply(dbref player, char *msg, bool all, int key)
01863 {
01864     if (Flags2(player) & PLAYER_MAILS)
01865     {
01866         notify(player, "MAIL: Mail message already in progress.");
01867         return;
01868     }
01869     if (!msg || !*msg)
01870     {
01871         notify(player, "MAIL: No message list.");
01872         return;
01873     }
01874     if (  !Wizard(player)
01875        && ThrottleMail(player))
01876     {
01877         notify(player, "MAIL: Too much @mail sent recently.");
01878         return;
01879     }
01880     int num = mux_atol(msg);
01881     if (!num)
01882     {
01883         notify(player, "MAIL: I don't understand that message number.");
01884         return;
01885     }
01886     struct mail *mp = mail_fetch(player, num);
01887     if (!mp)
01888     {
01889         notify(player, "MAIL: You can't reply to non-existent messages.");
01890         return;
01891     }
01892     char *tolist = alloc_lbuf("do_mail_reply.tolist");
01893     char *bp = tolist;
01894     if (all)
01895     {
01896         char *names = alloc_lbuf("do_mail_reply.names");
01897         char *oldlist = alloc_lbuf("do_mail_reply.oldlist");
01898         bp = names;
01899         *bp = '\0';
01900 
01901         strcpy(oldlist, mp->tolist);
01902 
01903         MUX_STRTOK_STATE tts;
01904         mux_strtok_src(&tts, oldlist);
01905         mux_strtok_ctl(&tts, " ");
01906         char *p;
01907         for (p = mux_strtok_parse(&tts); p; p = mux_strtok_parse(&tts))
01908         {
01909             if (mux_atol(p) != mp->from)
01910             {
01911                 safe_chr('#', names, &bp);
01912                 safe_str(p, names, &bp);
01913                 safe_chr(' ', names, &bp);
01914             }
01915         }
01916         free_lbuf(oldlist);
01917         safe_chr('#', names, &bp);
01918         safe_ltoa(mp->from, names, &bp);
01919         *bp = '\0';
01920         strcpy(tolist, names);
01921         free_lbuf(names);
01922     }
01923     else
01924     {
01925         safe_chr('#', tolist, &bp);
01926         safe_ltoa(mp->from, tolist, &bp);
01927         *bp = '\0';
01928     }
01929 
01930     const char *pSubject = mp->subject;
01931     const char *pMessage = MessageFetch(mp->number);
01932     const char *pTime = mp->time;
01933     if (strncmp(pSubject, "Re:", 3))
01934     {
01935         do_expmail_start(player, tolist, tprintf("Re: %s", pSubject));
01936     }
01937     else
01938     {
01939         do_expmail_start(player, tolist, tprintf("%s", pSubject));
01940     }
01941     if (key & MAIL_QUOTE)
01942     {
01943         const char *pFromName = Name(mp->from);
01944         char *pMessageBody =
01945             tprintf("On %s, %s wrote:\r\n\r\n%s\r\n\r\n********** End of included message from %s\r\n",
01946                 pTime, pFromName, pMessage, pFromName);
01947         atr_add_raw(player, A_MAILMSG, pMessageBody);
01948     }
01949 
01950     // The following combination of atr_get_raw() with atr_add_raw() is OK
01951     // because we are not passing a pointer to atr_add_raw() that came
01952     // directly from atr_get_raw().
01953     //
01954     const char *pValue = atr_get_raw(player, A_MAILFLAGS);
01955     int iFlag = M_REPLY;
01956     if (pValue)
01957     {
01958         iFlag |= mux_atol(pValue);
01959     }
01960     atr_add_raw(player, A_MAILFLAGS, mux_ltoa_t(iFlag));
01961 
01962     free_lbuf(tolist);
01963 }
01964 
01965 /*-------------------------------------------------------------------------*
01966  *   Admin mail functions
01967  *
01968  * do_mail_nuke - clear & purge mail for a player, or all mail in db.
01969  * do_mail_stat - stats on mail for a player, or for all db.
01970  * do_mail_debug - fix mail with a sledgehammer
01971  *-------------------------------------------------------------------------*/
01972 
01973 /*-------------------------------------------------------------------------*
01974  *   Basic mail functions
01975  *-------------------------------------------------------------------------*/
01976 struct mail *mail_fetch(dbref player, int num)
01977 {
01978     int i = 0;
01979     MailList ml(player);
01980     struct mail *mp;
01981     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
01982     {
01983         if (Folder(mp) == player_folder(player))
01984         {
01985             i++;
01986             if (i == num)
01987             {
01988                 return mp;
01989             }
01990         }
01991     }
01992     return NULL;
01993 }
01994 
01995 const char *mail_fetch_message(dbref player, int num)
01996 {
01997     struct mail *mp = mail_fetch(player, num);
01998     if (mp)
01999     {
02000         return MessageFetch(mp->number);
02001     }
02002     return NULL;
02003 }
02004 
02005 int mail_fetch_from(dbref player, int num)
02006 {
02007     struct mail *mp = mail_fetch(player, num);
02008     if (mp)
02009     {
02010         return mp->from;
02011     }
02012     return NOTHING;
02013 }
02014 
02015 // Returns count of read, unread, and cleared messages as rcount, ucount, ccount.
02016 //
02017 void count_mail(dbref player, int folder, int *rcount, int *ucount, int *ccount)
02018 {
02019     int rc = 0;
02020     int uc = 0;
02021     int cc = 0;
02022 
02023     MailList ml(player);
02024     struct mail *mp;
02025     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02026     {
02027         if (Folder(mp) == folder)
02028         {
02029             if (Read(mp))
02030             {
02031                 rc++;
02032             }
02033             else
02034             {
02035                 uc++;
02036             }
02037 
02038             if (Cleared(mp))
02039             {
02040                 cc++;
02041             }
02042         }
02043     }
02044     *rcount = rc;
02045     *ucount = uc;
02046     *ccount = cc;
02047 }
02048 
02049 static void urgent_mail(dbref player, int folder, int *ucount)
02050 {
02051     int uc = 0;
02052 
02053     MailList ml(player);
02054     struct mail *mp;
02055     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02056     {
02057         if (Folder(mp) == folder)
02058         {
02059             if (Unread(mp) && Urgent(mp))
02060             {
02061                 uc++;
02062             }
02063         }
02064     }
02065     *ucount = uc;
02066 }
02067 
02068 static void mail_return(dbref player, dbref target)
02069 {
02070     dbref aowner;
02071     int aflags;
02072 
02073     char *str = atr_pget(target, A_MFAIL, &aowner, &aflags);
02074     if (*str)
02075     {
02076         char *str2, *buf, *bp;
02077         str2 = bp = alloc_lbuf("mail_return");
02078         buf = str;
02079         mux_exec(str2, &bp, target, player, player,
02080                  EV_FCHECK | EV_EVAL | EV_TOP | EV_NO_LOCATION, &buf,
02081                  (char **)NULL, 0);
02082         *bp = '\0';
02083         if (*str2)
02084         {
02085             CLinearTimeAbsolute ltaNow;
02086             ltaNow.GetLocal();
02087             FIELDEDTIME ft;
02088             ltaNow.ReturnFields(&ft);
02089 
02090             notify_with_cause_ooc(player, target, tprintf("MAIL: Reject message from %s: %s",
02091                 Moniker(target), str2));
02092             notify_with_cause_ooc(target, player, tprintf("[%d:%02d] MAIL: Reject message sent to %s.",
02093                 ft.iHour, ft.iMinute, Moniker(player)));
02094         }
02095         free_lbuf(str2);
02096     }
02097     else
02098     {
02099         notify_with_cause_ooc(player, target, tprintf("Sorry, %s is not accepting mail.", Moniker(target)));
02100     }
02101     free_lbuf(str);
02102 }
02103 
02104 static bool mail_check(dbref player, dbref target)
02105 {
02106     if (!could_doit(player, target, A_LMAIL))
02107     {
02108         mail_return(player, target);
02109     }
02110     else if (!could_doit(target, player, A_LMAIL))
02111     {
02112         if (Wizard(player))
02113         {
02114             notify(player, tprintf("Warning: %s can't return your mail.", Moniker(target)));
02115             return true;
02116         }
02117         else
02118         {
02119             notify(player, tprintf("Sorry, %s can't return your mail.", Moniker(target)));
02120             return false;
02121         }
02122     }
02123     else
02124     {
02125         return true;
02126     }
02127     return false;
02128 }
02129 
02130 static void send_mail
02131 (
02132     dbref player,
02133     dbref target,
02134     const char *tolist,
02135     const char *subject,
02136     int number,
02137     mail_flag flags,
02138     bool silent
02139 )
02140 {
02141     if (!isPlayer(target))
02142     {
02143         notify(player, "MAIL: You cannot send mail to non-existent people.");
02144         return;
02145     }
02146     if (!mail_check(player, target))
02147     {
02148         return;
02149     }
02150     CLinearTimeAbsolute ltaNow;
02151     ltaNow.GetLocal();
02152 
02153     char *pTimeStr = ltaNow.ReturnDateString(0);
02154 
02155     // Initialize the appropriate fields.
02156     //
02157     struct mail *newp = (struct mail *)MEMALLOC(sizeof(struct mail));
02158     ISOUTOFMEMORY(newp);
02159     newp->to = target;
02160 
02161     // HACK: Allow @mail/quick, if player is an object, then the
02162     // object's owner is the sender, if the owner is a wizard, then
02163     // we allow the object to be the sender.
02164     //
02165     if (isPlayer(player))
02166     {
02167         newp->from = player;
02168     }
02169     else
02170     {
02171         dbref mailbag = Owner(player);
02172         if (Wizard(mailbag))
02173         {
02174             newp->from = player;
02175         }
02176         else
02177         {
02178             newp->from = mailbag;
02179         }
02180     }
02181     if (  !tolist
02182        || tolist[0] == '\0')
02183     {
02184         newp->tolist = StringClone("*HIDDEN*");
02185     }
02186     else
02187     {
02188         newp->tolist = StringClone(tolist);
02189     }
02190 
02191     newp->number = number;
02192     MessageReferenceInc(number);
02193     newp->time = StringClone(pTimeStr);
02194     newp->subject = StringClone(subject);
02195 
02196     // Send to folder 0
02197     //
02198     newp->read = flags & M_FMASK;
02199 
02200     // If this is the first message, it is the head and the tail.
02201     //
02202     MailList ml(target);
02203     ml.AppendItem(newp);
02204 
02205     // Notify people.
02206     //
02207     if (!silent)
02208     {
02209         notify(player, tprintf("MAIL: You sent your message to %s.", Name(target)));
02210     }
02211 
02212     notify(target, tprintf("MAIL: You have a new message from %s.", Name(player)));
02213     did_it(player, target, A_MAIL, NULL, 0, NULL, A_AMAIL, NULL, NOTHING);
02214 }
02215 
02216 static void do_mail_nuke(dbref player)
02217 {
02218     if (!God(player))
02219     {
02220         notify(player, "The postal service issues a warrant for your arrest.");
02221         return;
02222     }
02223 
02224     // Walk the list.
02225     //
02226     dbref thing;
02227     DO_WHOLE_DB(thing)
02228     {
02229         MailList ml(thing);
02230         ml.RemoveAll();
02231     }
02232     log_text(tprintf("** MAIL PURGE ** done by %s(#%d)." ENDLINE, Name(player), player));
02233     notify(player, "You annihilate the post office. All messages cleared.");
02234 }
02235 
02236 static void do_mail_debug(dbref player, char *action, char *victim)
02237 {
02238     if (!ExpMail(player))
02239     {
02240         notify(player, "Go get some bugspray.");
02241         return;
02242     }
02243 
02244     dbref thing;
02245     if (string_prefix("clear", action))
02246     {
02247         dbref target = lookup_player(player, victim, true);
02248         if (target == NOTHING)
02249         {
02250             init_match(player, victim, NOTYPE);
02251             match_absolute();
02252             target = match_result();
02253         }
02254         if (target == NOTHING)
02255         {
02256             notify(player, tprintf("%s: no such player.", victim));
02257             return;
02258         }
02259         if (Wizard(target))
02260         {
02261             notify(player, tprintf("Let %s clear their own @mail.", Name(target)));
02262             return;
02263         }
02264         do_mail_clear(target, NULL);
02265         do_mail_purge(target);
02266         notify(player, tprintf("Mail cleared for %s(#%d).", Name(target), target));
02267         return;
02268     }
02269     else if (string_prefix("sanity", action))
02270     {
02271         int *ai = (int *)MEMALLOC(mudstate.mail_db_top * sizeof(int));
02272         ISOUTOFMEMORY(ai);
02273         memset(ai, 0, mudstate.mail_db_top * sizeof(int));
02274 
02275         DO_WHOLE_DB(thing)
02276         {
02277             MailList ml(thing);
02278             struct mail *mp;
02279             for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02280             {
02281                 bool bGoodReference;
02282                 if (0 <= mp->number && mp->number < mudstate.mail_db_top)
02283                 {
02284                     ai[mp->number]++;
02285                     bGoodReference = true;
02286                 }
02287                 else
02288                 {
02289                     bGoodReference = false;
02290                 }
02291                 if (!Good_obj(mp->to))
02292                 {
02293                     if (bGoodReference)
02294                     {
02295                         notify(player, tprintf("Bad object #%d has mail.", mp->to));
02296                     }
02297                     else
02298                     {
02299                         notify(player, tprintf("Bad object #%d has mail which refers to a non-existent mailbag item.", mp->to));
02300                     }
02301                 }
02302                 else if (!isPlayer(mp->to))
02303                 {
02304                     if (bGoodReference)
02305                     {
02306                         notify(player, tprintf("%s(#%d) has mail, but is not a player.",
02307                                  Name(mp->to), mp->to));
02308                     }
02309                     else
02310                     {
02311                         notify(player, tprintf("%s(#%d) is not a player, but has mail which refers to a non-existent mailbag item.",
02312                              Name(mp->to), mp->to));
02313                     }
02314                 }
02315                 else if (!bGoodReference)
02316                 {
02317                     notify(player, tprintf("%s(#%d) has mail which refers to a non-existent mailbag item.", Name(mp->to), mp->to));
02318                 }
02319             }
02320         }
02321 
02322         // Check ref counts.
02323         //
02324         if (mail_list)
02325         {
02326             int i;
02327             int nCountHigher = 0;
02328             int nCountLower  = 0;
02329             for (i = 0; i < mudstate.mail_db_top; i++)
02330             {
02331                 if (mail_list[i].m_nRefs < ai[i])
02332                 {
02333                     nCountLower++;
02334                 }
02335                 else if (mail_list[i].m_nRefs > ai[i])
02336                 {
02337                     nCountHigher++;
02338                 }
02339             }
02340             if (nCountLower)
02341             {
02342                 notify(player, "Some mailbag items are referred to more often than the mailbag item indicates.");
02343             }
02344             if (nCountHigher)
02345             {
02346                 notify(player, "Some mailbag items are referred to less often than the mailbag item indicates.");
02347             }
02348         }
02349         MEMFREE(ai);
02350         ai = NULL;
02351         notify(player, "Mail sanity check completed.");
02352     }
02353     else if (string_prefix("fix", action))
02354     {
02355         // First, we should fixup the reference counts.
02356         //
02357         if (mail_list)
02358         {
02359             notify(player, tprintf("Re-counting mailbag reference counts."));
02360             int *ai = (int *)MEMALLOC(mudstate.mail_db_top * sizeof(int));
02361             ISOUTOFMEMORY(ai);
02362             memset(ai, 0, mudstate.mail_db_top * sizeof(int));
02363 
02364             DO_WHOLE_DB(thing)
02365             {
02366                 MailList ml(thing);
02367                 struct mail *mp;
02368                 for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02369                 {
02370                     if (  0 <= mp->number
02371                        && mp->number < mudstate.mail_db_top)
02372                     {
02373                         ai[mp->number]++;
02374                     }
02375                     else
02376                     {
02377                         mp->number = NOTHING;
02378                     }
02379                 }
02380             }
02381             int i;
02382             int nCountWrong = 0;
02383             for (i = 0; i < mudstate.mail_db_top; i++)
02384             {
02385                 if (mail_list[i].m_nRefs != ai[i])
02386                 {
02387                     mail_list[i].m_nRefs = ai[i];
02388                     nCountWrong++;
02389                 }
02390             }
02391             if (nCountWrong)
02392             {
02393                 notify(player, "Some reference counts were wrong [FIXED].");
02394             }
02395             MEMFREE(ai);
02396             ai = NULL;
02397         }
02398 
02399         notify(player, tprintf("Removing @mail that is associated with non-players."));
02400 
02401         // Now, remove all mail to non-good or non-players, or mail that
02402         // points to non-existent mailbag items.
02403         //
02404         DO_WHOLE_DB(thing)
02405         {
02406             MailList ml(thing);
02407             struct mail *mp;
02408             for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02409             {
02410                 if (  !Good_obj(mp->to)
02411                    || !isPlayer(mp->to)
02412                    || NOTHING == mp->number)
02413                 {
02414                     // Delete this item.
02415                     //
02416                     notify(player, tprintf("Fixing mail for #%d.", mp->to));
02417                     ml.RemoveItem();
02418                 }
02419             }
02420         }
02421         notify(player, "Mail sanity fix completed.");
02422     }
02423     else
02424     {
02425         notify(player, "That is not a debugging option.");
02426         return;
02427     }
02428 }
02429 
02430 static void do_mail_stats(dbref player, char *name, int full)
02431 {
02432     dbref target, thing;
02433     int fc, fr, fu, tc, tr, tu, fchars, tchars, cchars, count;
02434     fc = fr = fu = tc = tr = tu = fchars = tchars = cchars = count = 0;
02435 
02436     // Find player.
02437     //
02438     if (  !name
02439        || *name == '\0')
02440     {
02441         if (Wizard(player))
02442         {
02443             target = AMBIGUOUS;
02444         }
02445         else
02446         {
02447             target = player;
02448         }
02449     }
02450     else if (*name == NUMBER_TOKEN)
02451     {
02452         target = mux_atol(&name[1]);
02453         if (!Good_obj(target) || !isPlayer(target))
02454         {
02455             target = NOTHING;
02456         }
02457     }
02458     else if (!mux_stricmp(name, "me"))
02459     {
02460         target = player;
02461     }
02462     else
02463     {
02464         target = lookup_player(player, name, true);
02465     }
02466 
02467     if (target == NOTHING)
02468     {
02469         init_match(player, name, NOTYPE);
02470         match_absolute();
02471         target = match_result();
02472     }
02473     if (target == NOTHING)
02474     {
02475         notify(player, tprintf("%s: No such player.", name));
02476         return;
02477     }
02478     if (!ExpMail(player) && (target != player))
02479     {
02480         notify(player, "The post office protects privacy!");
02481         return;
02482     }
02483 
02484     // This comand is computationally expensive.
02485     //
02486     if (!payfor(player, mudconf.searchcost))
02487     {
02488         notify(player, tprintf("Finding mail stats costs %d %s.",
02489                        mudconf.searchcost,
02490                        (mudconf.searchcost == 1) ? mudconf.one_coin : mudconf.many_coins));
02491         return;
02492     }
02493     if (AMBIGUOUS == target)
02494     {
02495         // Stats for all.
02496         //
02497         if (full == 0)
02498         {
02499             DO_WHOLE_DB(thing)
02500             {
02501                 MailList ml(thing);
02502                 for ((void)ml.FirstItem(); !ml.IsEnd(); (void)ml.NextItem())
02503                 {
02504                     count++;
02505                 }
02506             }
02507             notify(player, tprintf("There are %d messages in the mail spool.", count));
02508             return;
02509         }
02510         else if (full == 1)
02511         {
02512             DO_WHOLE_DB(thing)
02513             {
02514                 MailList ml(thing);
02515                 struct mail *mp;
02516                 for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02517                 {
02518                     if (Cleared(mp))
02519                     {
02520                         fc++;
02521                     }
02522                     else if (Read(mp))
02523                     {
02524                         fr++;
02525                     }
02526                     else
02527                     {
02528                         fu++;
02529                     }
02530                 }
02531             }
02532             notify(player,
02533                    tprintf("MAIL: There are %d msgs in the mail spool, %d unread, %d cleared.",
02534                        fc + fr + fu, fu, fc));
02535             return;
02536         }
02537         else
02538         {
02539             DO_WHOLE_DB(thing)
02540             {
02541                 MailList ml(thing);
02542                 struct mail *mp;
02543                 for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02544                 {
02545                     if (Cleared(mp))
02546                     {
02547                         fc++;
02548                         cchars += strlen(MessageFetch(mp->number));
02549                     }
02550                     else if (Read(mp))
02551                     {
02552                         fr++;
02553                         fchars += strlen(MessageFetch(mp->number));
02554                     }
02555                     else
02556                     {
02557                         fu++;
02558                         tchars += strlen(MessageFetch(mp->number));
02559                     }
02560                 }
02561             }
02562             notify(player, tprintf("MAIL: There are %d old msgs in the mail spool, totalling %d characters.", fr, fchars));
02563             notify(player, tprintf("MAIL: There are %d new msgs in the mail spool, totalling %d characters.", fu, tchars));
02564             notify(player, tprintf("MAIL: There are %d cleared msgs in the mail spool, totalling %d characters.", fc, cchars));
02565             return;
02566         }
02567     }
02568 
02569     // individual stats
02570     //
02571     if (full == 0)
02572     {
02573         // Just count the number of messages.
02574         //
02575         DO_WHOLE_DB(thing)
02576         {
02577             MailList ml(thing);
02578             struct mail *mp;
02579             for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02580             {
02581                 if (mp->from == target)
02582                 {
02583                     fr++;
02584                 }
02585                 if (mp->to == target)
02586                 {
02587                     tr++;
02588                 }
02589             }
02590         }
02591         notify(player, tprintf("%s sent %d messages.", Name(target), fr));
02592         notify(player, tprintf("%s has %d messages.", Name(target), tr));
02593         return;
02594     }
02595 
02596     // More detailed message count.
02597     //
02598     char last[50];
02599     DO_WHOLE_DB(thing)
02600     {
02601         MailList ml(thing);
02602         struct mail *mp;
02603         for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02604         {
02605             if (mp->from == target)
02606             {
02607                 if (Cleared(mp))
02608                 {
02609                     fc++;
02610                 }
02611                 else if (Read(mp))
02612                 {
02613                     fr++;
02614                 }
02615                 else
02616                 {
02617                     fu++;
02618                 }
02619                 if (full == 2)
02620                 {
02621                     fchars += strlen(MessageFetch(mp->number));
02622                 }
02623             }
02624             if (mp->to == target)
02625             {
02626                 if (!tr && !tu)
02627                 {
02628                     strcpy(last, mp->time);
02629                 }
02630                 if (Cleared(mp))
02631                 {
02632                     tc++;
02633                 }
02634                 else if (Read(mp))
02635                 {
02636                     tr++;
02637                 }
02638                 else
02639                 {
02640                     tu++;
02641                 }
02642                 if (full == 2)
02643                 {
02644                     tchars += strlen(MessageFetch(mp->number));
02645                 }
02646             }
02647         }
02648     }
02649 
02650     notify(player, tprintf("Mail statistics for %s:", Name(target)));
02651 
02652     if (full == 1)
02653     {
02654         notify(player, tprintf("%d messages sent, %d unread, %d cleared.",
02655                        fc + fr + fu, fu, fc));
02656         notify(player, tprintf("%d messages received, %d unread, %d cleared.",
02657                        tc + tr + tu, tu, tc));
02658     }
02659     else
02660     {
02661         notify(player,
02662                tprintf("%d messages sent, %d unread, %d cleared, totalling %d characters.",
02663                    fc + fr + fu, fu, fc, fchars));
02664         notify(player,
02665                tprintf("%d messages received, %d unread, %d cleared, totalling %d characters.",
02666                    tc + tr + tu, tu, tc, tchars));
02667     }
02668 
02669     if (tc + tr + tu > 0)
02670     {
02671         notify(player, tprintf("Last is dated %s", last));
02672     }
02673 }
02674 
02675 /*-------------------------------------------------------------------------*
02676  *   Main mail routine for @mail w/o a switch
02677  *-------------------------------------------------------------------------*/
02678 
02679 static void do_mail_stub(dbref player, char *arg1, char *arg2)
02680 {
02681     if (!arg1 || !*arg1)
02682     {
02683         if (arg2 && *arg2)
02684         {
02685             notify(player, "MAIL: Invalid mail command.");
02686             return;
02687         }
02688 
02689         // Just the "@mail" command.
02690         //
02691         do_mail_list(player, arg1, true);
02692         return;
02693     }
02694 
02695     // purge a player's mailbox
02696     //
02697     if (!mux_stricmp(arg1, "purge"))
02698     {
02699         do_mail_purge(player);
02700         return;
02701     }
02702 
02703     // clear message
02704     //
02705     if (!mux_stricmp(arg1, "clear"))
02706     {
02707         do_mail_clear(player, arg2);
02708         return;
02709     }
02710     if (!mux_stricmp(arg1, "unclear"))
02711     {
02712         do_mail_unclear(player, arg2);
02713         return;
02714     }
02715     if (arg2 && *arg2)
02716     {
02717         // Sending mail
02718         //
02719         do_expmail_start(player, arg1, arg2);
02720         return;
02721     }
02722     else
02723     {
02724         // Must be reading or listing mail - no arg2
02725         //
02726         if (  mux_isdigit(*arg1)
02727            && !strchr(arg1, '-'))
02728         {
02729             do_mail_read(player, arg1);
02730         }
02731         else
02732         {
02733             do_mail_list(player, arg1, true);
02734         }
02735         return;
02736     }
02737 }
02738 
02739 static void malias_write(FILE *fp)
02740 {
02741     int i, j;
02742     struct malias *m;
02743 
02744     putref(fp, ma_top);
02745     for (i = 0; i < ma_top; i++)
02746     {
02747         m = malias[i];
02748         fprintf(fp, "%d %d\n", m->owner, m->numrecep);
02749         fprintf(fp, "N:%s\n", m->name);
02750         fprintf(fp, "D:%s\n", m->desc);
02751         for (j = 0; j < m->numrecep; j++)
02752         {
02753             putref(fp, m->list[j]);
02754         }
02755     }
02756 }
02757 
02758 static void save_malias(FILE *fp)
02759 {
02760     fprintf(fp, "*** Begin MALIAS ***\n");
02761     malias_write(fp);
02762 }
02763 
02764 int dump_mail(FILE *fp)
02765 {
02766     dbref thing;
02767     int count = 0, i;
02768 
02769     // Write out version number
02770     //
02771     fprintf(fp, "+V5\n");
02772     putref(fp, mudstate.mail_db_top);
02773     DO_WHOLE_DB(thing)
02774     {
02775         if (isPlayer(thing))
02776         {
02777             MailList ml(thing);
02778             struct mail *mp;
02779             for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
02780             {
02781                 putref(fp, mp->to);
02782                 putref(fp, mp->from);
02783                 putref(fp, mp->number);
02784                 putstring(fp, mp->tolist);
02785                 putstring(fp, mp->time);
02786                 putstring(fp, mp->subject);
02787                 putref(fp, mp->read);
02788                 count++;
02789             }
02790         }
02791     }
02792 
02793     fprintf(fp, "*** END OF DUMP ***\n");
02794 
02795     // Add the db of mail messages
02796     //
02797     for (i = 0; i < mudstate.mail_db_top; i++)
02798     {
02799         if (0 < mail_list[i].m_nRefs)
02800         {
02801             putref(fp, i);
02802             putstring(fp, MessageFetch(i));
02803         }
02804     }
02805     fprintf(fp, "+++ END OF DUMP +++\n");
02806     save_malias(fp);
02807 
02808     return count;
02809 }
02810 
02811 static void load_mail_V5(FILE *fp)
02812 {
02813     int mail_top = getref(fp);
02814     mail_db_grow(mail_top + 1);
02815 
02816     char nbuf1[8];
02817     char *p = fgets(nbuf1, sizeof(nbuf1), fp);
02818     while (p && strncmp(nbuf1, "***", 3) != 0)
02819     {
02820         struct mail *mp = (struct mail *)MEMALLOC(sizeof(struct mail));
02821         ISOUTOFMEMORY(mp);
02822 
02823         mp->to      = mux_atol(nbuf1);
02824         mp->from    = getref(fp);
02825 
02826         mp->number  = getref(fp);
02827         MessageReferenceInc(mp->number);
02828         mp->tolist  = StringClone(getstring_noalloc(fp, true));
02829 
02830         mp->time    = StringClone(getstring_noalloc(fp, true));
02831         mp->subject = StringClone(getstring_noalloc(fp, true));
02832         mp->read    = getref(fp);
02833 
02834         MailList ml(mp->to);
02835         ml.AppendItem(mp);
02836 
02837         p = fgets(nbuf1, sizeof(nbuf1), fp);
02838     }
02839 
02840     p = fgets(nbuf1, sizeof(nbuf1), fp);
02841     while (p && strncmp(nbuf1, "+++", 3))
02842     {
02843         int number = mux_atol(nbuf1);
02844         new_mail_message(getstring_noalloc(fp, true), number);
02845         p = fgets(nbuf1, sizeof(nbuf1), fp);
02846     }
02847 }
02848 
02849 // A mail alias description can be any combination of upper-case letters,
02850 // lower-case letters, digits, blanks, and symbols. ANSI is permitted.
02851 // Length is limited to SIZEOF_MALIASDESC-1. Visual width is limited to
02852 // WIDTHOF_MALIASDESC. Case is preserved.
02853 //
02854 static char *MakeCanonicalMailAliasDesc
02855 (
02856     char *pMailAliasDesc,
02857     int *pnValidMailAliasDesc,
02858     bool *pbValidMailAliasDesc,
02859     int *pnVisualWidth
02860 )
02861 {
02862     if (!pMailAliasDesc)
02863     {
02864         return NULL;
02865     }
02866 
02867     // First, remove all '\r\n\t' from the string.
02868     //
02869     char *Buffer = RemoveSetOfCharacters(pMailAliasDesc, "\r\n\t");
02870 
02871     // Optimize/terminate any ANSI in the string.
02872     //
02873     *pnVisualWidth = 0;
02874     static char szFittedMailAliasDesc[SIZEOF_MALIASDESC];
02875     *pnValidMailAliasDesc = ANSI_TruncateToField
02876                             ( Buffer,
02877                               SIZEOF_MALIASDESC,
02878                               szFittedMailAliasDesc,
02879                               WIDTHOF_MALIASDESC,
02880                               pnVisualWidth,
02881                               ANSI_ENDGOAL_NORMAL
02882                              );
02883     *pbValidMailAliasDesc = true;
02884     return szFittedMailAliasDesc;
02885 }
02886 
02887 static void malias_read(FILE *fp)
02888 {
02889     int i, j;
02890 
02891     i = getref(fp);
02892     if (i <= 0)
02893     {
02894         return;
02895     }
02896     char buffer[LBUF_SIZE];
02897     struct malias *m;
02898 
02899     ma_size = ma_top = i;
02900 
02901     malias = (struct malias **)MEMALLOC(sizeof(struct malias *) * ma_size);
02902     ISOUTOFMEMORY(malias);
02903 
02904     for (i = 0; i < ma_top; i++)
02905     {
02906         // Format is: "%d %d\n", &(m->owner), &(m->numrecep)
02907         //
02908         if (!fgets(buffer, sizeof(buffer), fp))
02909         {
02910             // We've hit the end of the file. Set the last recognized
02911             // @malias, and give up.
02912             //
02913             ma_top = i;
02914             return;
02915         }
02916 
02917         m = (struct malias *)MEMALLOC(sizeof(struct malias));
02918         ISOUTOFMEMORY(m);
02919         malias[i] = m;
02920 
02921         char *p = strchr(buffer, ' ');
02922         m->owner = m->numrecep = 0;
02923         if (p)
02924         {
02925             m->owner = mux_atol(buffer);
02926             m->numrecep = mux_atol(p+1);
02927         }
02928 
02929         // The format of @malias name is "N:<name>\n".
02930         //
02931         int nLen = GetLineTrunc(buffer, sizeof(buffer), fp);
02932         buffer[nLen-1] = '\0'; // Get rid of trailing '\n'.
02933         int  nMailAlias;
02934         bool bMailAlias;
02935         char *pMailAlias = MakeCanonicalMailAlias( buffer+2,
02936                                                    &nMailAlias,
02937                                                    &bMailAlias);
02938         if (bMailAlias)
02939         {
02940             m->name = StringCloneLen(pMailAlias, nMailAlias);
02941         }
02942         else
02943         {
02944             m->name = StringCloneLen("Invalid", 7);
02945         }
02946 
02947         // The format of the description is "D:<description>\n"
02948         //
02949         nLen = GetLineTrunc(buffer, sizeof(buffer), fp);
02950         int  nMailAliasDesc;
02951         bool bMailAliasDesc;
02952         int  nVisualWidth;
02953         char *pMailAliasDesc = MakeCanonicalMailAliasDesc( buffer+2,
02954                                                            &nMailAliasDesc,
02955                                                            &bMailAliasDesc,
02956                                                            &nVisualWidth);
02957         if (bMailAliasDesc)
02958         {
02959             m->desc = StringCloneLen(pMailAliasDesc, nMailAliasDesc);
02960             m->desc_width = nVisualWidth;
02961         }
02962         else
02963         {
02964             m->desc = StringCloneLen("Invalid Desc", 12);
02965             m->desc_width = 12;
02966         }
02967 
02968         if (m->numrecep > 0)
02969         {
02970             for (j = 0; j < m->numrecep; j++)
02971             {
02972                 int k = getref(fp);
02973                 if (j < MAX_MALIAS_MEMBERSHIP)
02974                 {
02975                     m->list[j] = k;
02976                 }
02977             }
02978         }
02979         else
02980         {
02981             m->list[0] = 0;
02982         }
02983     }
02984 }
02985 
02986 static void load_malias(FILE *fp)
02987 {
02988     char buffer[200];
02989 
02990     getref(fp);
02991     if (  fscanf(fp, "*** Begin %s ***\n", buffer) == 1
02992        && !strcmp(buffer, "MALIAS"))
02993     {
02994         malias_read(fp);
02995     }
02996     else
02997     {
02998         Log.WriteString("ERROR: Couldn't find Begin MALIAS." ENDLINE);
02999         return;
03000     }
03001 }
03002 
03003 void load_mail(FILE *fp)
03004 {
03005     char nbuf1[8];
03006 
03007     // Read the version number.
03008     //
03009     if (!fgets(nbuf1, sizeof(nbuf1), fp))
03010     {
03011         return;
03012     }
03013     if (strncmp(nbuf1, "+V5", 3) == 0)
03014     {
03015         load_mail_V5(fp);
03016     }
03017     else
03018     {
03019         return;
03020     }
03021     load_malias(fp);
03022 }
03023 
03024 void check_mail_expiration(void)
03025 {
03026     // Negative values for expirations never expire.
03027     //
03028     if (0 > mudconf.mail_expiration)
03029     {
03030         return;
03031     }
03032 
03033     dbref thing;
03034     int expire_secs = mudconf.mail_expiration * 86400;
03035 
03036     CLinearTimeAbsolute ltaNow;
03037     ltaNow.GetLocal();
03038 
03039     CLinearTimeAbsolute ltaMail;
03040     DO_WHOLE_DB(thing)
03041     {
03042         MailList ml(thing);
03043         struct mail *mp;
03044         for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
03045         {
03046             if (M_Safe(mp))
03047             {
03048                 continue;
03049             }
03050 
03051             const char *pMailTimeStr = mp->time;
03052             if (!ltaMail.SetString(pMailTimeStr))
03053             {
03054                 continue;
03055             }
03056 
03057             CLinearTimeDelta ltd(ltaMail, ltaNow);
03058             if (ltd.ReturnSeconds() <= expire_secs)
03059             {
03060                 continue;
03061             }
03062 
03063             // Delete this one.
03064             //
03065             ml.RemoveItem();
03066         }
03067     }
03068 }
03069 
03070 void check_mail(dbref player, int folder, bool silent)
03071 {
03072     // Check for new @mail
03073     //
03074     int rc;     // Read messages.
03075     int uc;     // Unread messages.
03076     int cc;     // Cleared messages.
03077     int gc;     // urgent messages.
03078 
03079     // Just count messages
03080     //
03081     count_mail(player, folder, &rc, &uc, &cc);
03082     urgent_mail(player, folder, &gc);
03083 #ifdef MAIL_ALL_FOLDERS
03084     notify(player,
03085            tprintf("MAIL: %d messages in folder %d [%s] (%d unread, %d cleared).\r\n",
03086                rc + uc, folder, get_folder_name(player, folder), uc, cc));
03087 #else // MAIL_ALL_FOLDERS
03088     if (rc + uc > 0)
03089     {
03090         notify(player, tprintf("MAIL: %d messages in folder %d [%s] (%d unread, %d cleared).", rc + uc, folder, get_folder_name(player, folder), uc, cc));
03091     }
03092     else if (!silent)
03093     {
03094         notify(player, tprintf("\r\nMAIL: You have no mail.\r\n"));
03095     }
03096     if (gc > 0)
03097     {
03098         notify(player, tprintf("URGENT MAIL: You have %d urgent messages in folder %d [%s].", gc, folder, get_folder_name(player, folder)));
03099     }
03100 #endif // MAIL_ALL_FOLDERS
03101 }
03102 
03103 static void do_malias_send
03104 (
03105     dbref player,
03106     char *tolist,
03107     char *listto,
03108     char *subject,
03109     int   number,
03110     mail_flag flags,
03111     bool  silent
03112 )
03113 {
03114     int nResult;
03115     struct malias *m = get_malias(player, tolist, &nResult);
03116     if (nResult == GMA_INVALIDFORM)
03117     {
03118         notify(player, tprintf("MAIL: I can't figure out from '%s' who you want to mail to.", tolist));
03119         return;
03120     }
03121     else if (nResult == GMA_NOTFOUND)
03122     {
03123         notify(player, tprintf("MAIL: Alias '%s' not found.", tolist));
03124         return;
03125     }
03126 
03127     // Parse the player list.
03128     //
03129     dbref vic;
03130     int k;
03131     for (k = 0; k < m->numrecep; k++)
03132     {
03133         vic = m->list[k];
03134 
03135         if (isPlayer(vic))
03136         {
03137             send_mail(player, m->list[k], listto, subject, number, flags, silent);
03138         }
03139         else
03140         {
03141             // Complain about it.
03142             //
03143             char *pMail = tprintf("Alias Error: Bad Player %d for %s", vic, tolist);
03144             int iMail = add_mail_message(player, pMail);
03145             if (iMail != NOTHING)
03146             {
03147                 send_mail(GOD, GOD, listto, subject, iMail, 0, silent);
03148                 MessageReferenceDec(iMail);
03149             }
03150         }
03151     }
03152 }
03153 
03154 static void do_malias_create(dbref player, char *alias, char *tolist)
03155 {
03156     struct malias **nm;
03157     int nResult;
03158     get_malias(player, alias, &nResult);
03159 
03160     if (nResult == GMA_INVALIDFORM)
03161     {
03162         notify(player, "MAIL: What alias do you want to create?.");
03163         return;
03164     }
03165     else if (nResult == GMA_FOUND)
03166     {
03167         notify(player, tprintf("MAIL: Mail Alias '%s' already exists.", alias));
03168         return;
03169     }
03170 
03171     int i = 0;
03172     if (!ma_size)
03173     {
03174         ma_size = MA_INC;
03175         malias = (struct malias **)MEMALLOC(sizeof(struct malias *) * ma_size);
03176         ISOUTOFMEMORY(malias);
03177     }
03178     else if (ma_top >= ma_size)
03179     {
03180         ma_size += MA_INC;
03181         nm = (struct malias **)MEMALLOC(sizeof(struct malias *) * (ma_size));
03182         ISOUTOFMEMORY(nm);
03183 
03184         for (i = 0; i < ma_top; i++)
03185         {
03186             nm[i] = malias[i];
03187         }
03188         MEMFREE(malias);
03189         malias = nm;
03190     }
03191     malias[ma_top] = (struct malias *)MEMALLOC(sizeof(struct malias));
03192     ISOUTOFMEMORY(malias[ma_top]);
03193 
03194 
03195     // Parse the player list.
03196     //
03197     char *head = tolist;
03198     char *tail, spot;
03199     char *buff;
03200     dbref target;
03201     i = 0;
03202     while (  head
03203           && *head
03204           && i < (MAX_MALIAS_MEMBERSHIP - 1))
03205     {
03206         while (*head == ' ')
03207         {
03208             head++;
03209         }
03210         tail = head;
03211         while (  *tail
03212               && *tail != ' ')
03213         {
03214             if (*tail == '"')
03215             {
03216                 head++;
03217                 tail++;
03218                 while (  *tail
03219                       && *tail != '"')
03220                 {
03221                     tail++;
03222                 }
03223             }
03224             if (*tail)
03225             {
03226                 tail++;
03227             }
03228         }
03229         tail--;
03230         if (*tail != '"')
03231         {
03232             tail++;
03233         }
03234         spot = *tail;
03235         *tail = '\0';
03236 
03237         // Now locate a target.
03238         //
03239         if (!mux_stricmp(head, "me"))
03240         {
03241             target = player;
03242         }
03243         else if (*head == '#')
03244         {
03245             target = mux_atol(head + 1);
03246         }
03247         else
03248         {
03249             target = lookup_player(player, head, true);
03250         }
03251 
03252         if (  !Good_obj(target)
03253            || !isPlayer(target))
03254         {
03255             notify(player, "MAIL: No such player.");
03256         }
03257         else
03258         {
03259             buff = unparse_object(player, target, false);
03260             notify(player, tprintf("MAIL: %s added to alias %s", buff, alias));
03261             malias[ma_top]->list[i] = target;
03262             i++;
03263             free_lbuf(buff);
03264         }
03265 
03266         // Get the next recip.
03267         //
03268         *tail = spot;
03269         head = tail;
03270         if (*head == '"')
03271         {
03272             head++;
03273         }
03274     }
03275     int  nValidMailAlias;
03276     bool bValidMailAlias;
03277     char *pValidMailAlias = MakeCanonicalMailAlias
03278                             (   alias+1,
03279                                 &nValidMailAlias,
03280                                 &bValidMailAlias
03281                             );
03282 
03283     if (!bValidMailAlias)
03284     {
03285         notify(player, "MAIL: Invalid mail alias.");
03286         return;
03287     }
03288 
03289     // The Mail Alias Description is a superset of the Mail Alias,
03290     // so, the following code is not necessary unless the specification
03291     // of the Mail Alias Description becomes more restrictive at some
03292     // future time.
03293     //
03294 #if 0
03295     int  nValidMailAliasDesc;
03296     bool bValidMailAliasDesc;
03297     char *pValidMailAliasDesc = MakeCanonicalMailAliasDesc
03298                                 (   alias+1,
03299                                     &nValidMailAliasDesc,
03300                                     &bValidMailAliasDesc
03301                                 );
03302 
03303     if (!bValidMailAliasDesc)
03304     {
03305         notify(player, "MAIL: Invalid mail alias description.");
03306         break;
03307     }
03308 #else
03309     char *pValidMailAliasDesc = pValidMailAlias;
03310     int nValidMailAliasDesc = nValidMailAlias;
03311 #endif
03312 
03313     malias[ma_top]->list[i] = NOTHING;
03314     malias[ma_top]->name = StringCloneLen(pValidMailAlias, nValidMailAlias);
03315     malias[ma_top]->numrecep = i;
03316     malias[ma_top]->owner = player;
03317     malias[ma_top]->desc = StringCloneLen(pValidMailAliasDesc, nValidMailAliasDesc);
03318     malias[ma_top]->desc_width = nValidMailAliasDesc;
03319     ma_top++;
03320 
03321     notify(player, tprintf("MAIL: Alias set '%s' defined.", alias));
03322 }
03323 
03324 static void do_malias_list(dbref player, char *alias)
03325 {
03326     int nResult;
03327     struct malias *m = get_malias(player, alias, &nResult);
03328     if (nResult == GMA_NOTFOUND)
03329     {
03330         notify(player, tprintf("MAIL: Alias '%s' not found.", alias));
03331         return;
03332     }
03333     if (nResult != GMA_FOUND)
03334     {
03335         return;
03336     }
03337     if (!ExpMail(player) && (player != m->owner) && !(God(m->owner)))
03338     {
03339         notify(player, "MAIL: Permission denied.");
03340         return;
03341     }
03342     char *buff = alloc_lbuf("do_malias_list");
03343     char *bp = buff;
03344 
03345     safe_tprintf_str(buff, &bp, "MAIL: Alias *%s: ", m->name);
03346     for (int i = m->numrecep - 1; i > -1; i--)
03347     {
03348         const char *p = Name(m->list[i]);
03349         if (strchr(p, ' '))
03350         {
03351             safe_chr('"', buff, &bp);
03352             safe_str(p, buff, &bp);
03353             safe_chr('"', buff, &bp);
03354         }
03355         else
03356         {
03357             safe_str(p, buff, &bp);
03358         }
03359         safe_chr(' ', buff, &bp);
03360     }
03361     *bp = '\0';
03362 
03363     notify(player, buff);
03364     free_lbuf(buff);
03365 }
03366 
03367 static char *Spaces(unsigned int n)
03368 {
03369     static char buffer[42] = "                                         ";
03370     static unsigned int nLast = 0;
03371     buffer[nLast] = ' ';
03372     if (n < sizeof(buffer)-1)
03373     {
03374         buffer[n] = '\0';
03375         nLast = n;
03376     }
03377     return buffer;
03378 }
03379 
03380 static void do_malias_list_all(dbref player)
03381 {
03382     bool notified = false;
03383     for (int i = 0; i < ma_top; i++)
03384     {
03385         struct malias *m = malias[i];
03386         if (  m->owner == GOD
03387            || m->owner == player
03388            || God(player))
03389         {
03390             if (!notified)
03391             {
03392                 notify(player, "Name         Description                              Owner");
03393                 notified = true;
03394             }
03395             char *pSpaces = Spaces(40 - m->desc_width);
03396             char *p = tprintf( "%-12s %s%s %-15.15s",
03397                                m->name,
03398                                m->desc,
03399                                pSpaces,
03400                                Name(m->owner));
03401             notify(player, p);
03402         }
03403     }
03404     notify(player, "*****  End of Mail Aliases *****");
03405 }
03406 
03407 static void do_malias_switch(dbref player, char *a1, char *a2)
03408 {
03409     if (a1 && *a1)
03410     {
03411         if (a2 && *a2)
03412         {
03413             do_malias_create(player, a1, a2);
03414         }
03415         else
03416         {
03417             do_malias_list(player, a1);
03418         }
03419     }
03420     else
03421     {
03422         do_malias_list_all(player);
03423     }
03424 }
03425 
03426 static void do_mail_cc(dbref player, char *arg, bool bBlind)
03427 {
03428     if (!(Flags2(player) & PLAYER_MAILS))
03429     {
03430         notify(player, "MAIL: No mail message in progress.");
03431         return;
03432     }
03433     if (!arg || !*arg)
03434     {
03435         notify(player, "MAIL: I do not know whom you want to mail.");
03436         return;
03437     }
03438 
03439     char *tolist = make_numlist(player, arg, bBlind);
03440     if (!tolist)
03441     {
03442         return;
03443     }
03444     char *fulllist = alloc_lbuf("do_mail_cc");
03445     char *bp = fulllist;
03446 
03447     safe_str(tolist, fulllist, &bp);
03448     const char *pPlayerMailTo = atr_get_raw(player, A_MAILTO);
03449     if (pPlayerMailTo)
03450     {
03451         safe_chr(' ', fulllist, &bp);
03452         safe_str(pPlayerMailTo, fulllist, &bp);
03453     }
03454     *bp = '\0';
03455 
03456     atr_add_raw(player, A_MAILTO, fulllist);
03457     char *names = make_namelist(player, fulllist);
03458     notify(player, tprintf("MAIL: You are sending mail to '%s'.", names));
03459     free_lbuf(names);
03460     free_lbuf(tolist);
03461     free_lbuf(fulllist);
03462 }
03463 
03464 static void mail_to_list(dbref player, char *list, char *subject, char *message, int flags, bool silent)
03465 {
03466     if (!list)
03467     {
03468         return;
03469     }
03470     if (!*list)
03471     {
03472         free_lbuf(list);
03473         return;
03474     }
03475 
03476     // Construct a tolist which excludes all the Blind Carbon Copy (BCC)
03477     // recipients.
03478     //
03479     char *tolist = alloc_lbuf("mail_to_list");
03480     char *p = tolist;
03481     char *tail;
03482     char *head = list;
03483     while (*head)
03484     {
03485         while (*head == ' ')
03486         {
03487             head++;
03488         }
03489 
03490         tail = head;
03491         while (  *tail
03492               && *tail != ' ')
03493         {
03494             if (*tail == '"')
03495             {
03496                 head++;
03497                 tail++;
03498                 while (  *tail
03499                       && *tail != '"')
03500                 {
03501                     tail++;
03502                 }
03503             }
03504             if (*tail)
03505             {
03506                 tail++;
03507             }
03508         }
03509         tail--;
03510         if (*tail != '"')
03511         {
03512             tail++;
03513         }
03514 
03515         if (*head != '!')
03516         {
03517             if (p != tolist)
03518             {
03519                 *p++ = ' ';
03520             }
03521             memcpy(p, head, tail-head);
03522             p += tail-head;
03523         }
03524 
03525         // Get the next recipient.
03526         //
03527         head = tail;
03528         if (*head == '"')
03529         {
03530             head++;
03531         }
03532     }
03533     *p = '\0';
03534 
03535     int number = add_mail_message(player, message);
03536     if (number != NOTHING)
03537     {
03538         char spot;
03539         head = list;
03540         while (*head)
03541         {
03542             while (*head == ' ')
03543             {
03544                 head++;
03545             }
03546 
03547             tail = head;
03548             while (  *tail
03549                   && *tail != ' ')
03550             {
03551                 if (*tail == '"')
03552                 {
03553                     head++;
03554                     tail++;
03555                     while (  *tail
03556                           && *tail != '"')
03557                     {
03558                         tail++;
03559                     }
03560                 }
03561                 if (*tail)
03562                 {
03563                     tail++;
03564                 }
03565             }
03566             tail--;
03567             if (*tail != '"')
03568             {
03569                 tail++;
03570             }
03571             spot = *tail;
03572             *tail = '\0';
03573 
03574             if (*head == '!')
03575             {
03576                 head++;
03577             }
03578 
03579             if (*head == '*')
03580             {
03581                 do_malias_send(player, head, tolist, subject, number, flags, silent);
03582             }
03583             else
03584             {
03585                 dbref target = mux_atol(head);
03586                 if (  Good_obj(target)
03587                    && isPlayer(target))
03588                 {
03589                     send_mail(player, target, tolist, subject, number, flags, silent);
03590                 }
03591             }
03592 
03593             // Get the next recipient.
03594             //
03595             *tail = spot;
03596             head = tail;
03597             if (*head == '"')
03598             {
03599                 head++;
03600             }
03601         }
03602         MessageReferenceDec(number);
03603     }
03604     free_lbuf(tolist);
03605     free_lbuf(list);
03606 }
03607 
03608 static void do_mail_quick(dbref player, char *arg1, char *arg2)
03609 {
03610     if (!arg1 || !*arg1)
03611     {
03612         notify(player, "MAIL: I don't know who you want to mail.");
03613         return;
03614     }
03615     if (!arg2 || !*arg2)
03616     {
03617         notify(player, "MAIL: No message.");
03618         return;
03619     }
03620     if (Flags2(player) & PLAYER_MAILS)
03621     {
03622         notify(player, "MAIL: Mail message already in progress.");
03623         return;
03624     }
03625     if (  !Wizard(player)
03626        && ThrottleMail(player))
03627     {
03628         notify(player, "MAIL: Too much @mail sent recently.");
03629         return;
03630     }
03631     char *bufDest = alloc_lbuf("do_mail_quick");
03632     char *bpSubject = bufDest;
03633 
03634     strcpy(bpSubject, arg1);
03635     parse_to(&bpSubject, '/', 1);
03636 
03637     if (!bpSubject)
03638     {
03639         notify(player, "MAIL: No subject.");
03640         free_lbuf(bufDest);
03641         return;
03642     }
03643 
03644     mail_to_list(player, make_numlist(player, bufDest, false), bpSubject, arg2, 0, false);
03645     free_lbuf(bufDest);
03646 }
03647 
03648 static void do_expmail_stop(dbref player, int flags)
03649 {
03650     if ((Flags2(player) & PLAYER_MAILS) != PLAYER_MAILS)
03651     {
03652         notify(player, "MAIL: No message started.");
03653         return;
03654     }
03655 
03656     dbref aowner;
03657     dbref aflags;
03658     char *tolist = atr_get(player, A_MAILTO, & aowner, &aflags);
03659     if (*tolist == '\0')
03660     {
03661         notify(player, "MAIL: No recipients.");
03662         free_lbuf(tolist);
03663     }
03664     else
03665     {
03666         char *mailmsg = atr_get(player, A_MAILMSG, &aowner, &aflags);
03667         if (*mailmsg == '\0')
03668         {
03669             notify(player, "MAIL: The body of this message is empty.  Use - to add to the message.");
03670             free_lbuf(tolist);
03671         }
03672         else
03673         {
03674             char *mailsub   = atr_get(player, A_MAILSUB, &aowner, &aflags);
03675             char *mailflags = atr_get(player, A_MAILFLAGS, &aowner, &aflags);
03676             mail_to_list(player, tolist, mailsub, mailmsg, flags | mux_atol(mailflags), false);
03677             free_lbuf(mailflags);
03678             free_lbuf(mailsub);
03679 
03680             Flags2(player) &= ~PLAYER_MAILS;
03681         }
03682         free_lbuf(mailmsg);
03683     }
03684 }
03685 
03686 static void do_expmail_abort(dbref player)
03687 {
03688     Flags2(player) &= ~PLAYER_MAILS;
03689     notify(player, "MAIL: Message aborted.");
03690 }
03691 
03692 void do_prepend(dbref executor, dbref caller, dbref enactor, int key, char *text)
03693 {
03694     UNUSED_PARAMETER(key);
03695 
03696     if (!mudconf.have_mailer)
03697     {
03698         return;
03699     }
03700 
03701     if (Flags2(executor) & PLAYER_MAILS)
03702     {
03703         if (  !text
03704            || !*text)
03705         {
03706             notify(executor, "No text prepended.");
03707             return;
03708         }
03709 
03710         char *bufText = alloc_lbuf("do_prepend");
03711         char *bpText = bufText;
03712         char *strText = text+1;
03713         mux_exec(bufText, &bpText, executor, caller, enactor,
03714                  EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &strText, (char **)NULL, 0);
03715         *bpText = '\0';
03716 
03717         dbref aowner;
03718         int aflags;
03719 
03720         char *oldmsg = atr_get(executor, A_MAILMSG, &aowner, &aflags);
03721         if (*oldmsg)
03722         {
03723             char *newmsg = alloc_lbuf("do_prepend");
03724             char *bp = newmsg;
03725             safe_str(bufText, newmsg, &bp);
03726             safe_chr(' ', newmsg, &bp);
03727             safe_str(oldmsg, newmsg, &bp);
03728             *bp = '\0';
03729             atr_add_raw(executor, A_MAILMSG, newmsg);
03730             free_lbuf(newmsg);
03731         }
03732         else
03733         {
03734             atr_add_raw(executor, A_MAILMSG, bufText);
03735         }
03736 
03737         free_lbuf(bufText);
03738         free_lbuf(oldmsg);
03739         size_t nLen;
03740 
03741         atr_get_raw_LEN(executor, A_MAILMSG, &nLen);
03742         notify(executor, tprintf("%d/%d characters prepended.", nLen, LBUF_SIZE-1));
03743     }
03744     else
03745     {
03746         notify(executor, "MAIL: No message in progress.");
03747     }
03748 }
03749 
03750 void do_postpend(dbref executor, dbref caller, dbref enactor, int key, char *text)
03751 {
03752     UNUSED_PARAMETER(key);
03753 
03754     if (!mudconf.have_mailer)
03755     {
03756         return;
03757     }
03758     if (  text[1] == '-'
03759        && text[2] == '\0')
03760     {
03761         do_expmail_stop(executor, 0);
03762         return;
03763     }
03764 
03765     if (Flags2(executor) & PLAYER_MAILS)
03766     {
03767         if (  !text
03768            || !*text)
03769         {
03770             notify(executor, "No text added.");
03771             return;
03772         }
03773 
03774         char *bufText = alloc_lbuf("do_prepend");
03775         char *bpText = bufText;
03776         char *strText = text+1;
03777         mux_exec(bufText, &bpText, executor, caller, enactor,
03778                  EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &strText, (char **)NULL, 0);
03779         *bpText = '\0';
03780 
03781         dbref aowner;
03782         int aflags;
03783 
03784         char *oldmsg = atr_get(executor, A_MAILMSG, &aowner, &aflags);
03785         if (*oldmsg)
03786         {
03787             char *newmsg = alloc_lbuf("do_postpend");
03788             char *bp = newmsg;
03789             safe_str(oldmsg, newmsg, &bp);
03790             safe_chr(' ', newmsg, &bp);
03791             safe_str(bufText, newmsg, &bp);
03792             *bp = '\0';
03793             atr_add_raw(executor, A_MAILMSG, newmsg);
03794             free_lbuf(newmsg);
03795         }
03796         else
03797         {
03798             atr_add_raw(executor, A_MAILMSG, bufText);
03799         }
03800 
03801         free_lbuf(bufText);
03802         free_lbuf(oldmsg);
03803         size_t nLen;
03804 
03805         atr_get_raw_LEN(executor, A_MAILMSG, &nLen);
03806         notify(executor, tprintf("%d/%d characters added.", nLen, LBUF_SIZE-1));
03807     }
03808     else
03809     {
03810         notify(executor, "MAIL: No message in progress.");
03811     }
03812 }
03813 
03814 static void do_edit_msg(dbref player, char *from, char *to)
03815 {
03816     if (Flags2(player) & PLAYER_MAILS)
03817     {
03818         dbref aowner;
03819         int aflags;
03820         char *msg = atr_get(player, A_MAILMSG, &aowner, &aflags);
03821         char *result = replace_string(from, to, msg);
03822         atr_add(player, A_MAILMSG, result, aowner, aflags);
03823         notify(player, "Text edited.");
03824         free_lbuf(result);
03825         free_lbuf(msg);
03826     }
03827     else
03828     {
03829         notify(player, "MAIL: No message in progress.");
03830     }
03831 }
03832 
03833 static void do_mail_proof(dbref player)
03834 {
03835     if (!(Flags2(player) & PLAYER_MAILS))
03836     {
03837         notify(player, "MAIL: No message in progress.");
03838         return;
03839     }
03840 
03841     dbref aowner;
03842     int aflags;
03843 
03844     char *mailto  = atr_get(player, A_MAILTO, &aowner, &aflags);
03845     char *mailmsg = atr_get(player, A_MAILMSG, &aowner, &aflags);
03846     char *names   = make_namelist(player, mailto);
03847 
03848     int iRealVisibleWidth;
03849     char szSubjectBuffer[MBUF_SIZE];
03850     ANSI_TruncateToField(atr_get_raw(player, A_MAILSUB),
03851         sizeof(szSubjectBuffer), szSubjectBuffer, 35,
03852         &iRealVisibleWidth, ANSI_ENDGOAL_NORMAL);
03853 
03854     notify(player, DASH_LINE);
03855     notify(player, tprintf("From:  %-*s  Subject: %s\nTo: %s",
03856             PLAYER_NAME_LIMIT - 6, Name(player), szSubjectBuffer, names));
03857     notify(player, DASH_LINE);
03858     notify(player, mailmsg);
03859     notify(player, DASH_LINE);
03860     free_lbuf(mailmsg);
03861     free_lbuf(names);
03862     free_lbuf(mailto);
03863 }
03864 
03865 static void do_malias_desc(dbref player, char *alias, char *desc)
03866 {
03867     int nResult;
03868     struct malias *m = get_malias(player, alias, &nResult);
03869     if (nResult == GMA_NOTFOUND)
03870     {
03871         notify(player, tprintf("MAIL: Alias '%s' not found.", alias));
03872         return;
03873     }
03874     if (nResult != GMA_FOUND)
03875     {
03876         return;
03877     }
03878     if (  m->owner != GOD
03879        || ExpMail(player))
03880     {
03881         int  nValidMailAliasDesc;
03882         bool bValidMailAliasDesc;
03883         int  nVisualWidth;
03884         char *pValidMailAliasDesc = MakeCanonicalMailAliasDesc
03885                                     (   desc,
03886                                         &nValidMailAliasDesc,
03887                                         &bValidMailAliasDesc,
03888                                         &nVisualWidth
03889                                     );
03890 
03891         if (bValidMailAliasDesc)
03892         {
03893             MEMFREE(m->desc);
03894             m->desc = StringCloneLen(pValidMailAliasDesc, nValidMailAliasDesc);
03895             m->desc_width = nVisualWidth;
03896             notify(player, "MAIL: Description changed.");
03897         }
03898         else
03899         {
03900             notify(player, "MAIL: Description is not valid.");
03901         }
03902     }
03903     else
03904     {
03905         notify(player, "MAIL: Permission denied.");
03906     }
03907 }
03908 
03909 static void do_malias_chown(dbref player, char *alias, char *owner)
03910 {
03911     if (!ExpMail(player))
03912     {
03913         notify(player, "MAIL: You cannot do that!");
03914         return;
03915     }
03916 
03917     int nResult;
03918     struct malias *m = get_malias(player, alias, &nResult);
03919     if (nResult == GMA_NOTFOUND)
03920     {
03921         notify(player, tprintf("MAIL: Alias '%s' not found.", alias));
03922         return;
03923     }
03924     if (nResult != GMA_FOUND)
03925     {
03926         return;
03927     }
03928     dbref no = lookup_player(player, owner, true);
03929     if (no == NOTHING)
03930     {
03931         notify(player, "MAIL: I do not see that here.");
03932         return;
03933     }
03934     m->owner = no;
03935     notify(player, "MAIL: Owner changed for alias.");
03936 }
03937 
03938 static void do_malias_add(dbref player, char *alias, char *person)
03939 {
03940     int nResult;
03941     struct malias *m = get_malias(player, alias, &nResult);
03942     if (nResult == GMA_NOTFOUND)
03943     {
03944         notify(player, tprintf("MAIL: Alias '%s' not found.", alias));
03945         return;
03946     }
03947     else if (nResult != GMA_FOUND)
03948     {
03949         return;
03950     }
03951     dbref thing = NOTHING;
03952     if (*person == '#')
03953     {
03954         thing = parse_dbref(person + 1);
03955         if (!isPlayer(thing))
03956         {
03957             notify(player, "MAIL: Only players may be added.");
03958             return;
03959         }
03960     }
03961 
03962     if (thing == NOTHING)
03963     {
03964         thing = lookup_player(player, person, true);
03965     }
03966 
03967     if (thing == NOTHING)
03968     {
03969         notify(player, "MAIL: I do not see that person here.");
03970         return;
03971     }
03972 
03973     if ((m->owner == GOD) && !ExpMail(player))
03974     {
03975         notify(player, "MAIL: Permission denied.");
03976         return;
03977     }
03978     int i;
03979     for (i = 0; i < m->numrecep; i++)
03980     {
03981         if (m->list[i] == thing)
03982         {
03983             notify(player, "MAIL: That person is already on the list.");
03984             return;
03985         }
03986     }
03987 
03988     if (i >= (MAX_MALIAS_MEMBERSHIP - 1))
03989     {
03990         notify(player, "MAIL: The list is full.");
03991         return;
03992     }
03993 
03994     m->list[m->numrecep] = thing;
03995     m->numrecep = m->numrecep + 1;
03996     notify(player, tprintf("MAIL: %s added to %s", Name(thing), m->name));
03997 }
03998 
03999 static void do_malias_remove(dbref player, char *alias, char *person)
04000 {
04001     int nResult;
04002     struct malias *m = get_malias(player, alias, &nResult);
04003     if (nResult == GMA_NOTFOUND)
04004     {
04005         notify(player, tprintf("MAIL: Alias '%s' not found.", alias));
04006         return;
04007     }
04008     if (nResult != GMA_FOUND)
04009     {
04010         return;
04011     }
04012     if ((m->owner == GOD) && !ExpMail(player))
04013     {
04014         notify(player, "MAIL: Permission denied.");
04015         return;
04016     }
04017 
04018     dbref thing = NOTHING;
04019     if (*person == '#')
04020     {
04021         thing = parse_dbref(person + 1);
04022     }
04023     if (thing == NOTHING)
04024     {
04025         thing = lookup_player(player, person, true);
04026     }
04027     if (thing == NOTHING)
04028     {
04029         notify(player, "MAIL: I do not see that person here.");
04030         return;
04031     }
04032 
04033     bool ok = false;
04034     for (int i = 0; i < m->numrecep; i++)
04035     {
04036         if (ok)
04037         {
04038             m->list[i] = m->list[i + 1];
04039         }
04040         else if (m->list[i] == thing)
04041         {
04042             m->list[i] = m->list[i + 1];
04043             ok = true;
04044         }
04045     }
04046 
04047     if (ok)
04048     {
04049         m->numrecep--;
04050         notify(player, tprintf("MAIL: %s removed from alias %s.",
04051                    Name(thing), alias));
04052     }
04053     else
04054     {
04055         notify(player, tprintf("MAIL: %s is not a member of alias %s.",
04056                    Name(thing), alias));
04057     }
04058 }
04059 
04060 static void do_malias_rename(dbref player, char *alias, char *newname)
04061 {
04062     int nResult;
04063     struct malias *m = get_malias(player, newname, &nResult);
04064     if (nResult == GMA_FOUND)
04065     {
04066         notify(player, "MAIL: That name already exists!");
04067         return;
04068     }
04069     if (nResult != GMA_NOTFOUND)
04070     {
04071         return;
04072     }
04073     m = get_malias(player, alias, &nResult);
04074     if (nResult == GMA_NOTFOUND)
04075     {
04076         notify(player, "MAIL: I cannot find that alias!");
04077         return;
04078     }
04079     if (nResult != GMA_FOUND)
04080     {
04081         return;
04082     }
04083     if (!ExpMail(player) && !(m->owner == player))
04084     {
04085         notify(player, "MAIL: Permission denied.");
04086         return;
04087     }
04088 
04089     int  nValidMailAlias;
04090     bool bValidMailAlias;
04091     char *pValidMailAlias = MakeCanonicalMailAlias
04092                             (   newname+1,
04093                                 &nValidMailAlias,
04094                                 &bValidMailAlias
04095                             );
04096     if (bValidMailAlias)
04097     {
04098         MEMFREE(m->name);
04099         m->name = StringCloneLen(pValidMailAlias, nValidMailAlias);
04100         notify(player, "MAIL: Mailing Alias renamed.");
04101     }
04102     else
04103     {
04104         notify(player, "MAIL: Alias is not valid.");
04105     }
04106 }
04107 
04108 static void do_malias_delete(dbref player, char *alias)
04109 {
04110     int nResult;
04111     struct malias *m = get_malias(player, alias, &nResult);
04112     if (nResult == GMA_NOTFOUND)
04113     {
04114         notify(player, tprintf("MAIL: Alias '%s' not found.", alias));
04115         return;
04116     }
04117     if (nResult != GMA_FOUND)
04118     {
04119         return;
04120     }
04121     bool done = false;
04122     for (int i = 0; i < ma_top; i++)
04123     {
04124         if (done)
04125         {
04126             malias[i] = malias[i + 1];
04127         }
04128         else
04129         {
04130             if ((m->owner == player) || ExpMail(player))
04131             {
04132                 if (m == malias[i])
04133                 {
04134                     done = true;
04135                     notify(player, "MAIL: Alias Deleted.");
04136                     malias[i] = malias[i + 1];
04137                 }
04138             }
04139         }
04140     }
04141 
04142     if (!done)
04143     {
04144         notify(player, tprintf("MAIL: Alias '%s' not found.", alias));
04145     }
04146     else
04147     {
04148         ma_top--;
04149     }
04150 }
04151 
04152 static void do_malias_adminlist(dbref player)
04153 {
04154     if (!ExpMail(player))
04155     {
04156         do_malias_list_all(player);
04157         return;
04158     }
04159     notify(player,
04160       "Num  Name         Description                              Owner");
04161 
04162     struct malias *m;
04163     int i;
04164 
04165     for (i = 0; i < ma_top; i++)
04166     {
04167         m = malias[i];
04168         char *pSpaces = Spaces(40 - m->desc_width);
04169         notify(player, tprintf("%-4d %-12s %s%s %-15.15s",
04170                        i, m->name, m->desc, pSpaces,
04171                        Name(m->owner)));
04172     }
04173 
04174     notify(player, "***** End of Mail Aliases *****");
04175 }
04176 
04177 static void do_malias_status(dbref player)
04178 {
04179     if (!ExpMail(player))
04180     {
04181         notify(player, "MAIL: Permission denied.");
04182     }
04183     else
04184     {
04185         notify(player, tprintf("MAIL: Number of mail aliases defined: %d", ma_top));
04186         notify(player, tprintf("MAIL: Allocated slots %d", ma_size));
04187     }
04188 }
04189 
04190 static void malias_cleanup1(struct malias *m, dbref target)
04191 {
04192     int count = 0;
04193     dbref j;
04194     for (int i = 0; i < m->numrecep; i++)
04195     {
04196          j = m->list[i];
04197          if (  !Good_obj(j)
04198             || j == target)
04199          {
04200             count++;
04201          }
04202          if (count)
04203          {
04204             m->list[i] = m->list[i + count];
04205          }
04206     }
04207     m->numrecep -= count;
04208 }
04209 
04210 void malias_cleanup(dbref player)
04211 {
04212     for (int i = 0; i < ma_top; i++)
04213     {
04214         malias_cleanup1(malias[i], player);
04215     }
04216 }
04217 
04218 static void do_mail_retract1(dbref player, char *name, char *msglist)
04219 {
04220     dbref target = lookup_player(player, name, true);
04221     if (target == NOTHING)
04222     {
04223         notify(player, "MAIL: No such player.");
04224         return;
04225     }
04226     struct mail_selector ms;
04227     if (!parse_msglist(msglist, &ms, target))
04228     {
04229         return;
04230     }
04231 
04232     int i = 0, j = 0;
04233     MailList ml(target);
04234     struct mail *mp;
04235     for (mp = ml.FirstItem(); !ml.IsEnd(); mp = ml.NextItem())
04236     {
04237         if (mp->from == player)
04238         {
04239             i++;
04240             if (mail_match(mp, ms, i))
04241             {
04242                 j++;
04243                 if (Unread(mp))
04244                 {
04245                     ml.RemoveItem();
04246                     notify(player, "MAIL: Mail retracted.");
04247                 }
04248                 else
04249                 {
04250                     notify(player, "MAIL: That message has been read.");
04251                 }
04252             }
04253         }
04254     }
04255 
04256     if (!j)
04257     {
04258         // Ran off the end of the list without finding anything.
04259         //
04260         notify(player, "MAIL: No matching messages.");
04261     }
04262 }
04263 
04264 static void do_mail_retract(dbref player, char *name, char *msglist)
04265 {
04266     if (*name == '*')
04267     {
04268         int pnResult;
04269         struct malias *m = get_malias(player, name, &pnResult);
04270         if (pnResult == GMA_NOTFOUND)
04271         {
04272             notify(player, tprintf("MAIL: Mail alias %s not found.", name));
04273             return;
04274         }
04275         if (pnResult == GMA_FOUND)
04276         {
04277             for (int i = 0; i < m->numrecep; i++)
04278             {
04279                 do_mail_retract1(player, tprintf("#%d", m->list[i]), msglist);
04280             }
04281         }
04282     }
04283     else
04284     {
04285         do_mail_retract1(player, name, msglist);
04286     }
04287 }
04288 
04289 void do_malias
04290 (
04291     dbref executor,
04292     dbref caller,
04293     dbref enactor,
04294     int   key,
04295     int   nargs,
04296     char *arg1,
04297     char *arg2
04298 )
04299 {
04300     UNUSED_PARAMETER(caller);
04301     UNUSED_PARAMETER(enactor);
04302     UNUSED_PARAMETER(nargs);
04303 
04304     if (!mudconf.have_mailer)
04305     {
04306         notify(executor, "Mailer is disabled.");
04307         return;
04308     }
04309     switch (key)
04310     {
04311     case 0:
04312         do_malias_switch(executor, arg1, arg2);
04313         break;
04314     case MALIAS_DESC:
04315         do_malias_desc(executor, arg1, arg2);
04316         break;
04317     case MALIAS_CHOWN:
04318         do_malias_chown(executor, arg1, arg2);
04319         break;
04320     case MALIAS_ADD:
04321         do_malias_add(executor, arg1, arg2);
04322         break;
04323     case MALIAS_REMOVE:
04324         do_malias_remove(executor, arg1, arg2);
04325         break;
04326     case MALIAS_DELETE:
04327         do_malias_delete(executor, arg1);
04328         break;
04329     case MALIAS_RENAME:
04330         do_malias_rename(executor, arg1, arg2);
04331         break;
04332     case 7:
04333         // empty
04334         break;
04335     case MALIAS_LIST:
04336         do_malias_adminlist(executor);
04337         break;
04338     case MALIAS_STATUS:
04339         do_malias_status(executor);
04340     }
04341 }
04342 
04343 void do_mail
04344 (
04345     dbref executor,
04346     dbref caller,
04347     dbref enactor,
04348     int   key,
04349     int   nargs,
04350     char *arg1,
04351     char *arg2
04352 )
04353 {
04354     UNUSED_PARAMETER(caller);
04355     UNUSED_PARAMETER(enactor);
04356     UNUSED_PARAMETER(nargs);
04357 
04358     if (!mudconf.have_mailer)
04359     {
04360         notify(executor, "Mailer is disabled.");
04361         return;
04362     }
04363 
04364     // HACK: Fix to allow @mail/quick from objects.
04365     //
04366     if (  (key & ~MAIL_QUOTE) != MAIL_QUICK
04367        && !isPlayer(executor))
04368     {
04369         return;
04370     }
04371 
04372     switch (key & ~MAIL_QUOTE)
04373     {
04374     case 0:
04375         do_mail_stub(executor, arg1, arg2);
04376         break;
04377     case MAIL_STATS:
04378         do_mail_stats(executor, arg1, 0);
04379         break;
04380     case MAIL_DSTATS:
04381         do_mail_stats(executor, arg1, 1);
04382         break;
04383     case MAIL_FSTATS:
04384         do_mail_stats(executor, arg1, 2);
04385         break;
04386     case MAIL_DEBUG:
04387         do_mail_debug(executor, arg1, arg2);
04388         break;
04389     case MAIL_NUKE:
04390         do_mail_nuke(executor);
04391         break;
04392     case MAIL_FOLDER:
04393         do_mail_change_folder(executor, arg1, arg2);
04394         break;
04395     case MAIL_LIST:
04396         do_mail_list(executor, arg1, false);
04397         break;
04398     case MAIL_READ:
04399         do_mail_read(executor, arg1);
04400         break;
04401     case MAIL_CLEAR:
04402         do_mail_clear(executor, arg1);
04403         break;
04404     case MAIL_UNCLEAR:
04405         do_mail_unclear(executor, arg1);
04406         break;
04407     case MAIL_PURGE:
04408         do_mail_purge(executor);
04409         break;
04410     case MAIL_FILE:
04411         do_mail_file(executor, arg1, arg2);
04412         break;
04413     case MAIL_TAG:
04414         do_mail_tag(executor, arg1);
04415         break;
04416     case MAIL_UNTAG:
04417         do_mail_untag(executor, arg1);
04418         break;
04419     case MAIL_FORWARD:
04420         do_mail_fwd(executor, arg1, arg2);
04421         break;
04422     case MAIL_REPLY:
04423         do_mail_reply(executor, arg1, false, key);
04424         break;
04425     case MAIL_REPLYALL:
04426         do_mail_reply(executor, arg1, true, key);
04427         break;
04428     case MAIL_SEND:
04429         do_expmail_stop(executor, 0);
04430         break;
04431     case MAIL_EDIT:
04432         do_edit_msg(executor, arg1, arg2);
04433         break;
04434     case MAIL_URGENT:
04435         do_expmail_stop(executor, M_URGENT);
04436         break;
04437     case MAIL_ALIAS:
04438         do_malias_create(executor, arg1, arg2);
04439         break;
04440     case MAIL_ALIST:
04441         do_malias_list_all(executor);
04442         break;
04443     case MAIL_PROOF:
04444         do_mail_proof(executor);
04445         break;
04446     case MAIL_ABORT:
04447         do_expmail_abort(executor);
04448         break;
04449     case MAIL_QUICK:
04450         do_mail_quick(executor, arg1, arg2);
04451         break;
04452     case MAIL_REVIEW:
04453         do_mail_review(executor, arg1, arg2);
04454         break;
04455     case MAIL_RETRACT:
04456         do_mail_retract(executor, arg1, arg2);
04457         break;
04458     case MAIL_CC:
04459         do_mail_cc(executor, arg1, false);
04460         break;
04461     case MAIL_SAFE:
04462         do_mail_safe(executor, arg1);
04463         break;
04464     case MAIL_BCC:
04465         do_mail_cc(executor, arg1, true);
04466         break;
04467     }
04468 }
04469 
04470 struct mail *MailList::FirstItem(void)
04471 {
04472     m_miHead = (struct mail *)hashfindLEN(&m_player, sizeof(m_player), &mudstate.mail_htab);
04473     m_mi = m_miHead;
04474     m_bRemoved = false;
04475     return m_mi;
04476 }
04477 
04478 struct mail *MailList::NextItem(void)
04479 {
04480     if (!m_bRemoved)
04481     {
04482         if (NULL != m_mi)
04483         {
04484             m_mi = m_mi->next;
04485             if (m_mi == m_miHead)
04486             {
04487                 m_mi = NULL;
04488             }
04489         }
04490     }
04491     m_bRemoved = false;
04492     return m_mi;
04493 }
04494 
04495 bool MailList::IsEnd(void)
04496 {
04497     return (NULL == m_mi);
04498 }
04499 
04500 MailList::MailList(dbref player)
04501 {
04502     m_mi       = NULL;
04503     m_miHead   = NULL;
04504     m_player   = player;
04505     m_bRemoved = false;
04506 }
04507 
04508 void MailList::RemoveItem(void)
04509 {
04510     if (  NULL == m_mi
04511        || NOTHING == m_player)
04512     {
04513         return;
04514     }
04515 
04516     struct mail *miNext = m_mi->next;
04517 
04518     if (m_mi == m_miHead)
04519     {
04520         if (miNext == m_miHead)
04521         {
04522             hashdeleteLEN(&m_player, sizeof(m_player), &mudstate.mail_htab);
04523             miNext   = NULL;
04524         }
04525         else
04526         {
04527             hashreplLEN(&m_player, sizeof(m_player), miNext, &mudstate.mail_htab);
04528         }
04529         m_miHead = miNext;
04530     }
04531 
04532     // Relink the list
04533     //
04534     m_mi->prev->next = m_mi->next;
04535     m_mi->next->prev = m_mi->prev;
04536 
04537     m_mi->next = NULL;
04538     m_mi->prev = NULL;
04539     MessageReferenceDec(m_mi->number);
04540     MEMFREE(m_mi->subject);
04541     m_mi->subject = NULL;
04542     MEMFREE(m_mi->time);
04543     m_mi->time = NULL;
04544     MEMFREE(m_mi->tolist);
04545     m_mi->tolist = NULL;
04546     MEMFREE(m_mi);
04547 
04548     m_mi = miNext;
04549     m_bRemoved = true;
04550 }
04551 
04552 void MailList::AppendItem(struct mail *miNew)
04553 {
04554     struct mail *miHead = (struct mail *)
04555         hashfindLEN(&m_player, sizeof(m_player), &mudstate.mail_htab);
04556 
04557     if (miHead)
04558     {
04559         // Add new item to the end of the list.
04560         //
04561         struct mail *miEnd = miHead->prev;
04562 
04563         miNew->next = miHead;
04564         miNew->prev = miEnd;
04565 
04566         miHead->prev = miNew;
04567         miEnd->next  = miNew;
04568     }
04569     else
04570     {
04571         hashaddLEN(&m_player, sizeof(m_player), miNew, &mudstate.mail_htab);
04572         miNew->next = miNew;
04573         miNew->prev = miNew;
04574     }
04575 }
04576 
04577 void MailList::RemoveAll(void)
04578 {
04579     struct mail *miHead = (struct mail *)
04580         hashfindLEN(&m_player, sizeof(m_player), &mudstate.mail_htab);
04581 
04582     if (NULL != miHead)
04583     {
04584         hashdeleteLEN(&m_player, sizeof(m_player), &mudstate.mail_htab);
04585     }
04586 
04587     struct mail *mi;
04588     struct mail *miNext;
04589     for (mi = miHead; NULL != mi; mi = miNext)
04590     {
04591         if (mi == miHead)
04592         {
04593             miNext = NULL;
04594         }
04595         else
04596         {
04597             miNext = mi->next;
04598         }
04599         MessageReferenceDec(mi->number);
04600         MEMFREE(mi->subject);
04601         mi->subject = NULL;
04602         MEMFREE(mi->tolist);
04603         mi->tolist = NULL;
04604         MEMFREE(mi->time);
04605         mi->time = NULL;
04606         MEMFREE(mi);
04607     }
04608     m_mi = NULL;
04609 }

Generated on Mon May 28 04:40:10 2007 for MUX by  doxygen 1.4.7