mux/src/comsys.cpp

Go to the documentation of this file.
00001 // comsys.cpp
00002 //
00003 // $Id: comsys.cpp,v 1.45 2007/03/17 04:25:08 sdennis Exp $
00004 //
00005 #include "copyright.h"
00006 #include "autoconf.h"
00007 #include "config.h"
00008 #include "externs.h"
00009 
00010 #include <sys/types.h>
00011 
00012 #include "ansi.h"
00013 #include "attrs.h"
00014 #include "command.h"
00015 #include "comsys.h"
00016 #include "functions.h"
00017 #include "interface.h"
00018 #include "powers.h"
00019 
00020 static int num_channels;
00021 static comsys_t *comsys_table[NUM_COMSYS];
00022 
00023 #define DFLT_MAX_LOG        0
00024 #define MIN_RECALL_REQUEST  1
00025 #define DFLT_RECALL_REQUEST 10
00026 #define MAX_RECALL_REQUEST  200
00027 
00028 // Return value must be free_lbuf'ed.
00029 //
00030 static char *RestrictTitleValue(char *pTitleRequest)
00031 {
00032     // First, remove all '\r\n\t' from the string.
00033     //
00034     char *pNewTitle = RemoveSetOfCharacters(pTitleRequest, "\r\n\t");
00035 
00036     // Optimize/terminate any ANSI in the string.
00037     //
00038     char NewTitle_ANSI[MAX_TITLE_LEN+1];
00039     int nVisualWidth;
00040     int nLen = ANSI_TruncateToField(pNewTitle, sizeof(NewTitle_ANSI),
00041         NewTitle_ANSI, sizeof(NewTitle_ANSI), &nVisualWidth,
00042         ANSI_ENDGOAL_NORMAL);
00043     memcpy(pNewTitle, NewTitle_ANSI, nLen+1);
00044     return pNewTitle;
00045 }
00046 
00047 static void do_setcomtitlestatus(dbref player, struct channel *ch, bool status)
00048 {
00049     struct comuser *user = select_user(ch,player);
00050     if (ch && user)
00051     {
00052         user->ComTitleStatus = status;
00053     }
00054 }
00055 
00056 static void do_setnewtitle(dbref player, struct channel *ch, char *pValidatedTitle)
00057 {
00058     struct comuser *user = select_user(ch, player);
00059 
00060     if (ch && user)
00061     {
00062         if (user->title)
00063         {
00064             MEMFREE(user->title);
00065             user->title = NULL;
00066         }
00067         user->title = StringClone(pValidatedTitle);
00068     }
00069 }
00070 
00071 void load_comsys(char *filename)
00072 {
00073     int i;
00074     char buffer[200];
00075 
00076     for (i = 0; i < NUM_COMSYS; i++)
00077     {
00078         comsys_table[i] = NULL;
00079     }
00080 
00081     FILE *fp = fopen(filename, "rb");
00082     if (!fp)
00083     {
00084         Log.tinyprintf("Error: Couldn't find %s." ENDLINE, filename);
00085     }
00086     else
00087     {
00088         DebugTotalFiles++;
00089         Log.tinyprintf("LOADING: %s" ENDLINE, filename);
00090         if (fscanf(fp, "*** Begin %s ***\n", buffer) == 1 && !strcmp(buffer, "CHANNELS"))
00091         {
00092             load_channels(fp);
00093         }
00094         else
00095         {
00096             Log.tinyprintf("Error: Couldn't find Begin CHANNELS in %s.", filename);
00097             if (fclose(fp) == 0)
00098             {
00099                 DebugTotalFiles--;
00100             }
00101             return;
00102         }
00103 
00104         if (fscanf(fp, "*** Begin %s ***\n", buffer) == 1 && !strcmp(buffer, "COMSYS"))
00105         {
00106             load_comsystem(fp);
00107         }
00108         else
00109         {
00110             Log.tinyprintf("Error: Couldn't find Begin COMSYS in %s.", filename);
00111             if (fclose(fp) == 0)
00112             {
00113                 DebugTotalFiles--;
00114             }
00115             return;
00116         }
00117 
00118         if (fclose(fp) == 0)
00119         {
00120             DebugTotalFiles--;
00121         }
00122         Log.tinyprintf("LOADING: %s (done)" ENDLINE, filename);
00123     }
00124 }
00125 
00126 void save_comsys(char *filename)
00127 {
00128     char buffer[500];
00129 
00130     sprintf(buffer, "%s.#", filename);
00131     FILE *fp = fopen(buffer, "wb");
00132     if (!fp)
00133     {
00134         Log.tinyprintf("Unable to open %s for writing." ENDLINE, buffer);
00135         return;
00136     }
00137     DebugTotalFiles++;
00138     fprintf(fp, "*** Begin CHANNELS ***\n");
00139     save_channels(fp);
00140 
00141     fprintf(fp, "*** Begin COMSYS ***\n");
00142     save_comsystem(fp);
00143 
00144     if (fclose(fp) == 0)
00145     {
00146         DebugTotalFiles--;
00147     }
00148     ReplaceFile(buffer, filename);
00149 }
00150 
00151 // Aliases must be between 1 and 5 characters. No spaces. No ANSI.
00152 //
00153 static char *MakeCanonicalComAlias
00154 (
00155     const char *pAlias,
00156     int *nValidAlias,
00157     bool *bValidAlias
00158 )
00159 {
00160     static char Buffer[ALIAS_SIZE];
00161     *nValidAlias = 0;
00162     *bValidAlias = false;
00163 
00164     if (!pAlias)
00165     {
00166         return NULL;
00167     }
00168     const char *p = pAlias;
00169     char *q = Buffer;
00170     int n = 0;
00171     while (*p)
00172     {
00173         if (  !mux_isprint(*p)
00174            || *p == ' ')
00175         {
00176             return NULL;
00177         }
00178         if (  n <= MAX_ALIAS_LEN
00179            && q < Buffer + ALIAS_SIZE)
00180         {
00181             n++;
00182             *q++ = *p;
00183         }
00184         p++;
00185     }
00186     *q = '\0';
00187     if (  n < 1
00188        || MAX_ALIAS_LEN < n)
00189     {
00190         return NULL;
00191     }
00192     *nValidAlias = n;
00193     *bValidAlias = true;
00194     return Buffer;
00195 }
00196 
00197 static bool ParseChannelLine(char *pBuffer, char *pAlias5, char **ppChannelName)
00198 {
00199     // Fetch alias portion. We need to find the first space.
00200     //
00201     char *p = strchr(pBuffer, ' ');
00202     if (!p)
00203     {
00204         return false;
00205     }
00206 
00207     *p = '\0';
00208     bool bValidAlias;
00209     int  nValidAlias;
00210     char *pValidAlias = MakeCanonicalComAlias(pBuffer, &nValidAlias, &bValidAlias);
00211     if (!bValidAlias)
00212     {
00213         return false;
00214     }
00215     strcpy(pAlias5, pValidAlias);
00216 
00217     // Skip any leading space before the channel name.
00218     //
00219     p++;
00220     while (mux_isspace(*p))
00221     {
00222         p++;
00223     }
00224 
00225     if (*p == '\0')
00226     {
00227         return false;
00228     }
00229 
00230     // The rest of the line is the channel name.
00231     //
00232     *ppChannelName = StringClone(p);
00233     return true;
00234 }
00235 
00236 void load_channels(FILE *fp)
00237 {
00238     int i, j;
00239     char buffer[LBUF_SIZE];
00240     comsys_t *c;
00241 
00242     int np = 0;
00243     int cc = fscanf(fp, "%d\n", &np);
00244     mux_assert(1 == cc);
00245     for (i = 0; i < np; i++)
00246     {
00247         c = create_new_comsys();
00248         c->who = 0;
00249         c->numchannels = 0;
00250         cc = fscanf(fp, "%d %d\n", &(c->who), &(c->numchannels));
00251         mux_assert(2 == cc);
00252         c->maxchannels = c->numchannels;
00253         if (c->maxchannels > 0)
00254         {
00255             c->alias = (char *)MEMALLOC(c->maxchannels * ALIAS_SIZE);
00256             ISOUTOFMEMORY(c->alias);
00257             c->channels = (char **)MEMALLOC(sizeof(char *) * c->maxchannels);
00258             ISOUTOFMEMORY(c->channels);
00259 
00260             for (j = 0; j < c->numchannels; j++)
00261             {
00262                 int n = GetLineTrunc(buffer, sizeof(buffer), fp);
00263                 if (buffer[n-1] == '\n')
00264                 {
00265                     // Get rid of trailing '\n'.
00266                     //
00267                     n--;
00268                     buffer[n] = '\0';
00269                 }
00270                 if (!ParseChannelLine(buffer, c->alias + j * ALIAS_SIZE, c->channels+j))
00271                 {
00272                     c->numchannels--;
00273                     j--;
00274                 }
00275             }
00276             sort_com_aliases(c);
00277         }
00278         else
00279         {
00280             c->alias = NULL;
00281             c->channels = NULL;
00282         }
00283         if (Good_obj(c->who))
00284         {
00285             add_comsys(c);
00286         }
00287         else
00288         {
00289             Log.tinyprintf("Invalid dbref %d." ENDLINE, c->who);
00290         }
00291         purge_comsystem();
00292     }
00293 }
00294 
00295 void purge_comsystem(void)
00296 {
00297 #ifdef ABORT_PURGE_COMSYS
00298     return;
00299 #endif // ABORT_PURGE_COMSYS
00300 
00301     comsys_t *c;
00302     comsys_t *d;
00303     int i;
00304     for (i = 0; i < NUM_COMSYS; i++)
00305     {
00306         c = comsys_table[i];
00307         while (c)
00308         {
00309             d = c;
00310             c = c->next;
00311             if (d->numchannels == 0)
00312             {
00313                 del_comsys(d->who);
00314                 continue;
00315             }
00316             if (isPlayer(d->who))
00317             {
00318                 continue;
00319             }
00320             if (  God(Owner(d->who))
00321                && Going(d->who))
00322             {
00323                 del_comsys(d->who);
00324                 continue;
00325             }
00326         }
00327     }
00328 }
00329 
00330 void save_channels(FILE *fp)
00331 {
00332     purge_comsystem();
00333 
00334     comsys_t *c;
00335     int i, j;
00336     int np = 0;
00337     for (i = 0; i < NUM_COMSYS; i++)
00338     {
00339         c = comsys_table[i];
00340         while (c)
00341         {
00342             np++;
00343             c = c->next;
00344         }
00345     }
00346 
00347     fprintf(fp, "%d\n", np);
00348     for (i = 0; i < NUM_COMSYS; i++)
00349     {
00350         c = comsys_table[i];
00351         while (c)
00352         {
00353             fprintf(fp, "%d %d\n", c->who, c->numchannels);
00354             for (j = 0; j < c->numchannels; j++)
00355             {
00356                 fprintf(fp, "%s %s\n", c->alias + j * ALIAS_SIZE, c->channels[j]);
00357             }
00358             c = c->next;
00359         }
00360     }
00361 }
00362 
00363 comsys_t *create_new_comsys(void)
00364 {
00365     comsys_t *c = (comsys_t *)MEMALLOC(sizeof(comsys_t));
00366     ISOUTOFMEMORY(c);
00367 
00368     c->who         = NOTHING;
00369     c->numchannels = 0;
00370     c->maxchannels = 0;
00371     c->alias       = NULL;
00372     c->channels    = NULL;
00373     c->next        = NULL;
00374     return c;
00375 }
00376 
00377 static comsys_t *get_comsys(dbref which)
00378 {
00379     if (which < 0)
00380     {
00381         return NULL;
00382     }
00383 
00384     comsys_t *c = comsys_table[which % NUM_COMSYS];
00385 
00386     while (c && (c->who != which))
00387         c = c->next;
00388 
00389     if (!c)
00390     {
00391         c = create_new_comsys();
00392         c->who = which;
00393         add_comsys(c);
00394     }
00395     return c;
00396 }
00397 
00398 void add_comsys(comsys_t *c)
00399 {
00400     if (c->who < 0 || c->who >= mudstate.db_top)
00401     {
00402         Log.tinyprintf("add_comsys: dbref %d out of range [0, %d)" ENDLINE, c->who, mudstate.db_top);
00403         return;
00404     }
00405 
00406     c->next = comsys_table[c->who % NUM_COMSYS];
00407     comsys_table[c->who % NUM_COMSYS] = c;
00408 }
00409 
00410 void del_comsys(dbref who)
00411 {
00412     if (who < 0 || who >= mudstate.db_top)
00413     {
00414         Log.tinyprintf("del_comsys: dbref %d out of range [0, %d)" ENDLINE, who, mudstate.db_top);
00415         return;
00416     }
00417 
00418     comsys_t *c = comsys_table[who % NUM_COMSYS];
00419 
00420     if (c == NULL)
00421     {
00422         return;
00423     }
00424 
00425     if (c->who == who)
00426     {
00427         comsys_table[who % NUM_COMSYS] = c->next;
00428         destroy_comsys(c);
00429         return;
00430     }
00431     comsys_t *last = c;
00432     c = c->next;
00433     while (c)
00434     {
00435         if (c->who == who)
00436         {
00437             last->next = c->next;
00438             destroy_comsys(c);
00439             return;
00440         }
00441         last = c;
00442         c = c->next;
00443     }
00444 }
00445 
00446 void destroy_comsys(comsys_t *c)
00447 {
00448     int i;
00449 
00450     if (c->alias)
00451     {
00452         MEMFREE(c->alias);
00453         c->alias = NULL;
00454     }
00455     for (i = 0; i < c->numchannels; i++)
00456     {
00457         MEMFREE(c->channels[i]);
00458         c->channels[i] = NULL;
00459     }
00460     if (c->channels)
00461     {
00462         MEMFREE(c->channels);
00463         c->channels = NULL;
00464     }
00465     MEMFREE(c);
00466     c = NULL;
00467 }
00468 
00469 void sort_com_aliases(comsys_t *c)
00470 {
00471     int i;
00472     char buffer[10];
00473     char *s;
00474     bool cont = true;
00475 
00476     while (cont)
00477     {
00478         cont = false;
00479         for (i = 0; i < c->numchannels - 1; i++)
00480         {
00481             if (strcmp(c->alias + i * ALIAS_SIZE, c->alias + (i + 1) * ALIAS_SIZE) > 0)
00482             {
00483                 strcpy(buffer, c->alias + i * ALIAS_SIZE);
00484                 strcpy(c->alias + i * ALIAS_SIZE, c->alias + (i + 1) * ALIAS_SIZE);
00485                 strcpy(c->alias + (i + 1) * ALIAS_SIZE, buffer);
00486                 s = c->channels[i];
00487                 c->channels[i] = c->channels[i + 1];
00488                 c->channels[i + 1] = s;
00489                 cont = true;
00490             }
00491         }
00492     }
00493 }
00494 
00495 static char *get_channel_from_alias(dbref player, char *alias)
00496 {
00497     int first, last, current, dir;
00498 
00499     comsys_t *c = get_comsys(player);
00500 
00501     current = first = 0;
00502     last = c->numchannels - 1;
00503     dir = 1;
00504 
00505     while (dir && (first <= last))
00506     {
00507         current = (first + last) / 2;
00508         dir = strcmp(alias, c->alias + ALIAS_SIZE * current);
00509         if (dir < 0)
00510             last = current - 1;
00511         else
00512             first = current + 1;
00513     }
00514 
00515     if (!dir)
00516     {
00517         return c->channels[current];
00518     }
00519     else
00520     {
00521         return "";
00522     }
00523 }
00524 
00525 void load_comsystem(FILE *fp)
00526 {
00527     int i, j, dummy;
00528     int ver = 0;
00529     struct channel *ch;
00530     char temp[LBUF_SIZE];
00531 
00532     num_channels = 0;
00533 
00534     int nc = 0;
00535     fgets(temp, sizeof(temp), fp);
00536     if (!strncmp(temp, "+V", 2))
00537     {
00538         // +V2 has colored headers
00539         //
00540         ver = mux_atol(temp + 2);
00541         if (ver < 1 || 3 < ver)
00542         {
00543             return;
00544         }
00545         int cc;
00546         cc = fscanf(fp, "%d\n", &nc);
00547         mux_assert(1 == cc);
00548     }
00549     else
00550     {
00551         nc = mux_atol(temp);
00552     }
00553 
00554     num_channels = nc;
00555 
00556     for (i = 0; i < nc; i++)
00557     {
00558         ch = (struct channel *)MEMALLOC(sizeof(struct channel));
00559         ISOUTOFMEMORY(ch);
00560 
00561         int nChannel = GetLineTrunc(temp, sizeof(temp), fp);
00562         if (nChannel > MAX_CHANNEL_LEN)
00563         {
00564             nChannel = MAX_CHANNEL_LEN;
00565         }
00566         if (temp[nChannel-1] == '\n')
00567         {
00568             // Get rid of trailing '\n'.
00569             //
00570             nChannel--;
00571         }
00572         memcpy(ch->name, temp, nChannel);
00573         ch->name[nChannel] = '\0';
00574 
00575         if (ver >= 2)
00576         {
00577             int nHeader = GetLineTrunc(temp, sizeof(temp), fp);
00578             if (nHeader > MAX_HEADER_LEN)
00579             {
00580                 nHeader = MAX_HEADER_LEN;
00581             }
00582             if (temp[nHeader-1] == '\n')
00583             {
00584                 nHeader--;
00585             }
00586             memcpy(ch->header, temp, nHeader);
00587             ch->header[nHeader] = '\0';
00588         }
00589 
00590         ch->on_users = NULL;
00591 
00592         hashaddLEN(ch->name, nChannel, ch, &mudstate.channel_htab);
00593 
00594         ch->type         = 127;
00595         ch->temp1        = 0;
00596         ch->temp2        = 0;
00597         ch->charge       = 0;
00598         ch->charge_who   = NOTHING;
00599         ch->amount_col   = 0;
00600         ch->num_messages = 0;
00601         ch->chan_obj     = NOTHING;
00602 
00603         int cc;
00604         if (ver >= 1)
00605         {
00606             cc = fscanf(fp, "%d %d %d %d %d %d %d %d\n",
00607                 &(ch->type), &(ch->temp1), &(ch->temp2),
00608                 &(ch->charge), &(ch->charge_who),
00609                 &(ch->amount_col), &(ch->num_messages), &(ch->chan_obj));
00610             mux_assert(8 == cc);
00611         }
00612         else
00613         {
00614             cc = fscanf(fp, "%d %d %d %d %d %d %d %d %d %d\n",
00615                 &(ch->type), &(dummy), &(ch->temp1), &(ch->temp2),
00616                 &(dummy), &(ch->charge), &(ch->charge_who),
00617                 &(ch->amount_col), &(ch->num_messages), &(ch->chan_obj));
00618             mux_assert(10 == cc);
00619         }
00620 
00621         if (ver <= 1)
00622         {
00623             // Build colored header if not +V2 or later db.
00624             //
00625             if (ch->type & CHANNEL_PUBLIC)
00626             {
00627                 sprintf(temp, "%s[%s%s%s%s%s]%s", ANSI_CYAN, ANSI_HILITE,
00628                     ANSI_BLUE, ch->name, ANSI_NORMAL, ANSI_CYAN, ANSI_NORMAL);
00629             }
00630             else
00631             {
00632                 sprintf(temp, "%s[%s%s%s%s%s]%s", ANSI_MAGENTA, ANSI_HILITE,
00633                     ANSI_RED, ch->name, ANSI_NORMAL, ANSI_MAGENTA,
00634                     ANSI_NORMAL);
00635             }
00636             int vwVisual;
00637             ANSI_TruncateToField(temp, MAX_HEADER_LEN+1, ch->header,
00638                 MAX_HEADER_LEN+1, &vwVisual, ANSI_ENDGOAL_NORMAL);
00639         }
00640 
00641         ch->num_users = 0;
00642         cc =fscanf(fp, "%d\n", &(ch->num_users));
00643         mux_assert(1 == cc);
00644         ch->max_users = ch->num_users;
00645         if (ch->num_users > 0)
00646         {
00647             ch->users = (struct comuser **)calloc(ch->max_users, sizeof(struct comuser *));
00648             ISOUTOFMEMORY(ch->users);
00649 
00650             int jAdded = 0;
00651             for (j = 0; j < ch->num_users; j++)
00652             {
00653                 struct comuser t_user;
00654                 memset(&t_user, 0, sizeof(t_user));
00655 
00656                 t_user.who = NOTHING;
00657                 t_user.bUserIsOn = false;
00658                 t_user.ComTitleStatus = false;
00659 
00660                 int iUserIsOn;
00661                 if (ver == 3)
00662                 {
00663                     int iComTitleStatus;
00664                     cc = fscanf(fp, "%d %d %d\n", &(t_user.who), &iUserIsOn,
00665                         &iComTitleStatus);
00666                     mux_assert(3 == cc);
00667                     t_user.bUserIsOn = (iUserIsOn ? true : false);
00668                     t_user.ComTitleStatus = (iComTitleStatus ? true : false);
00669                 }
00670                 else
00671                 {
00672                     t_user.ComTitleStatus = true;
00673                     if (ver)
00674                     {
00675                         cc = fscanf(fp, "%d %d\n", &(t_user.who), &iUserIsOn);
00676                         mux_assert(2 == cc);
00677                         t_user.bUserIsOn = (iUserIsOn ? true : false);
00678                     }
00679                     else
00680                     {
00681                         cc = fscanf(fp, "%d %d %d", &(t_user.who), &(dummy), &(dummy));
00682                         mux_assert(3 == cc);
00683                         cc = fscanf(fp, "%d\n", &iUserIsOn);
00684                         mux_assert(1 == cc);
00685                         t_user.bUserIsOn = (iUserIsOn ? true : false);
00686                     }
00687                 }
00688 
00689                 // Read Comtitle.
00690                 //
00691                 int nTitle = GetLineTrunc(temp, sizeof(temp), fp);
00692                 char *pTitle = temp;
00693 
00694                 if (!Good_dbref(t_user.who))
00695                 {
00696                     Log.tinyprintf("load_comsystem: dbref %d out of range [0, %d)." ENDLINE, t_user.who, mudstate.db_top);
00697                 }
00698                 else if (isGarbage(t_user.who))
00699                 {
00700                     Log.tinyprintf("load_comsystem: dbref is GARBAGE." ENDLINE, t_user.who);
00701                 }
00702                 else
00703                 {
00704                     // Validate comtitle
00705                     //
00706                     if (3 < nTitle && temp[0] == 't' && temp[1] == ':')
00707                     {
00708                         pTitle = temp+2;
00709                         nTitle -= 2;
00710                         if (pTitle[nTitle-1] == '\n')
00711                         {
00712                             // Get rid of trailing '\n'.
00713                             //
00714                             nTitle--;
00715                         }
00716                         if (nTitle <= 0 || MAX_TITLE_LEN < nTitle)
00717                         {
00718                             nTitle = 0;
00719                             pTitle = temp;
00720                         }
00721                     }
00722                     else
00723                     {
00724                         nTitle = 0;
00725                     }
00726 
00727                     struct comuser *user = (struct comuser *)MEMALLOC(sizeof(struct comuser));
00728                     ISOUTOFMEMORY(user);
00729                     memcpy(user, &t_user, sizeof(struct comuser));
00730 
00731                     user->title = StringCloneLen(pTitle, nTitle);
00732                     ch->users[jAdded++] = user;
00733 
00734                     if (  !(isPlayer(user->who))
00735                        && !(Going(user->who)
00736                        && (God(Owner(user->who)))))
00737                     {
00738                         do_joinchannel(user->who, ch);
00739                     }
00740                     user->on_next = ch->on_users;
00741                     ch->on_users = user;
00742                 }
00743             }
00744             ch->num_users = jAdded;
00745             sort_users(ch);
00746         }
00747         else
00748         {
00749             ch->users = NULL;
00750         }
00751     }
00752 }
00753 
00754 void save_comsystem(FILE *fp)
00755 {
00756     struct channel *ch;
00757     struct comuser *user;
00758     int j;
00759 
00760     fprintf(fp, "+V3\n");
00761     fprintf(fp, "%d\n", num_channels);
00762     for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
00763          ch;
00764          ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
00765     {
00766         fprintf(fp, "%s\n", ch->name);
00767         fprintf(fp, "%s\n", ch->header);
00768 
00769         fprintf(fp, "%d %d %d %d %d %d %d %d\n", ch->type, ch->temp1,
00770             ch->temp2, ch->charge, ch->charge_who, ch->amount_col,
00771             ch->num_messages, ch->chan_obj);
00772 
00773         // Count the number of 'valid' users to dump.
00774         //
00775         int nUsers = 0;
00776         for (j = 0; j < ch->num_users; j++)
00777         {
00778             user = ch->users[j];
00779             if (user->who >= 0 && user->who < mudstate.db_top)
00780             {
00781                 nUsers++;
00782             }
00783         }
00784 
00785         fprintf(fp, "%d\n", nUsers);
00786         for (j = 0; j < ch->num_users; j++)
00787         {
00788             user = ch->users[j];
00789             if (user->who >= 0 && user->who < mudstate.db_top)
00790             {
00791                 user = ch->users[j];
00792                 fprintf(fp, "%d %d %d\n", user->who, user->bUserIsOn, user->ComTitleStatus);
00793                 if (user->title[0] != '\0')
00794                 {
00795                     fprintf(fp, "t:%s\n", user->title);
00796                 }
00797                 else
00798                 {
00799                     fprintf(fp, "t:\n");
00800                 }
00801             }
00802         }
00803     }
00804 }
00805 
00806 static void BuildChannelMessage
00807 (
00808     bool bSpoof,
00809     const char *pHeader,
00810     struct comuser *user,
00811     char *pPose,
00812     char **messNormal,
00813     char **messNoComtitle
00814 )
00815 {
00816     // Allocate necessary buffers.
00817     //
00818     *messNormal = alloc_lbuf("BCM.messNormal");
00819     *messNoComtitle = NULL;
00820     if (!bSpoof)
00821     {
00822         *messNoComtitle = alloc_lbuf("BCM.messNoComtitle");
00823     }
00824 
00825     // Comtitle Check
00826     //
00827     bool hasComTitle = (user->title[0] != '\0');
00828 
00829     char *mnptr  = *messNormal;     // Message without comtitle removal
00830     char *mncptr = *messNoComtitle; // Message with comtitle removal
00831 
00832     safe_str(pHeader, *messNormal, &mnptr);
00833     safe_chr(' ', *messNormal, &mnptr);
00834     if (!bSpoof)
00835     {
00836         safe_str(pHeader, *messNoComtitle, &mncptr);
00837         safe_chr(' ', *messNoComtitle, &mncptr);
00838     }
00839 
00840     // Don't evaluate a title if there isn't one to parse or evaluation of
00841     // comtitles is disabled.
00842     // If they're set spoof, ComTitleStatus doesn't matter.
00843     if (hasComTitle && (user->ComTitleStatus || bSpoof))
00844     {
00845         if (mudconf.eval_comtitle)
00846         {
00847             // Evaluate the comtitle as code.
00848             //
00849             char TempToEval[LBUF_SIZE];
00850             strcpy(TempToEval, user->title);
00851             char *q = TempToEval;
00852             mux_exec(*messNormal, &mnptr, user->who, user->who, user->who,
00853                 EV_FCHECK | EV_EVAL | EV_TOP, &q, (char **)NULL, 0);
00854         }
00855         else
00856         {
00857             safe_str(user->title, *messNormal, &mnptr);
00858         }
00859         if (!bSpoof)
00860         {
00861             safe_chr(' ', *messNormal, &mnptr);
00862             safe_str(Moniker(user->who), *messNormal, &mnptr);
00863             safe_str(Moniker(user->who), *messNoComtitle, &mncptr);
00864         }
00865     }
00866     else
00867     {
00868         safe_str(Moniker(user->who), *messNormal, &mnptr);
00869         if (!bSpoof)
00870         {
00871             safe_str(Moniker(user->who), *messNoComtitle, &mncptr);
00872         }
00873     }
00874 
00875     char *saystring = NULL;
00876     char *newPose = NULL;
00877 
00878     switch(pPose[0])
00879     {
00880     case ':':
00881         pPose++;
00882         newPose = modSpeech(user->who, pPose, true, "channel/pose");
00883         if (newPose)
00884         {
00885             pPose = newPose;
00886         }
00887         safe_chr(' ', *messNormal, &mnptr);
00888         safe_str(pPose, *messNormal, &mnptr);
00889         if (!bSpoof)
00890         {
00891             safe_chr(' ', *messNoComtitle, &mncptr);
00892             safe_str(pPose, *messNoComtitle, &mncptr);
00893         }
00894         break;
00895 
00896     case ';':
00897         pPose++;
00898         newPose = modSpeech(user->who, pPose, true, "channel/pose");
00899         if (newPose)
00900         {
00901             pPose = newPose;
00902         }
00903         safe_str(pPose, *messNormal, &mnptr);
00904         if (!bSpoof)
00905         {
00906             safe_str(pPose, *messNoComtitle, &mncptr);
00907         }
00908         break;
00909 
00910     default:
00911         newPose = modSpeech(user->who, pPose, true, "channel");
00912         if (newPose)
00913         {
00914             pPose = newPose;
00915         }
00916         saystring = modSpeech(user->who, pPose, false, "channel");
00917         if (saystring)
00918         {
00919             safe_chr(' ', *messNormal, &mnptr);
00920             safe_str(saystring, *messNormal, &mnptr);
00921             safe_str(" \"", *messNormal, &mnptr);
00922         }
00923         else
00924         {
00925             safe_str(" says, \"", *messNormal, &mnptr);
00926         }
00927         safe_str(pPose, *messNormal, &mnptr);
00928         safe_chr('"', *messNormal, &mnptr);
00929         if (!bSpoof)
00930         {
00931             if (saystring)
00932             {
00933                 safe_chr(' ', *messNoComtitle, &mncptr);
00934                 safe_str(saystring, *messNoComtitle, &mncptr);
00935                 safe_str(" \"", *messNoComtitle, &mncptr);
00936             }
00937             else
00938             {
00939                 safe_str(" says, \"", *messNoComtitle, &mncptr);
00940             }
00941             safe_str(pPose, *messNoComtitle, &mncptr);
00942             safe_chr('"', *messNoComtitle, &mncptr);
00943         }
00944         break;
00945     }
00946     *mnptr = '\0';
00947     if (!bSpoof)
00948     {
00949         *mncptr = '\0';
00950     }
00951     if (newPose)
00952     {
00953         free_lbuf(newPose);
00954     }
00955     if (saystring)
00956     {
00957         free_lbuf(saystring);
00958     }
00959 }
00960 
00961 static void do_processcom(dbref player, char *arg1, char *arg2)
00962 {
00963     if (!*arg2)
00964     {
00965         raw_notify(player, "No message.");
00966         return;
00967     }
00968     if (3500 < strlen(arg2))
00969     {
00970         arg2[3500] = '\0';
00971     }
00972     struct channel *ch = select_channel(arg1);
00973     if (!ch)
00974     {
00975         raw_notify(player, tprintf("Unknown channel %s.", arg1));
00976         return;
00977     }
00978     struct comuser *user = select_user(ch, player);
00979     if (!user)
00980     {
00981         raw_notify(player, "You are not listed as on that channel.  Delete this alias and readd.");
00982         return;
00983     }
00984     if (  Gagged(player)
00985        && !Wizard(player))
00986     {
00987         raw_notify(player, "GAGGED players may not speak on channels.");
00988         return;
00989     }
00990     if (!strcmp(arg2, "on"))
00991     {
00992         do_joinchannel(player, ch);
00993     }
00994     else if (!strcmp(arg2, "off"))
00995     {
00996         do_leavechannel(player, ch);
00997     }
00998     else if (!user->bUserIsOn)
00999     {
01000         raw_notify(player, tprintf("You must be on %s to do that.", arg1));
01001         return;
01002     }
01003     else if (!strcmp(arg2, "who"))
01004     {
01005         do_comwho(player, ch);
01006     }
01007     else if (  !strncmp(arg2, "last", 4)
01008             && (  arg2[4] == '\0'
01009                || (  arg2[4] == ' '
01010                   && is_integer(arg2 + 5, NULL))))
01011     {
01012         // Parse optional number after the 'last' command.
01013         //
01014         int nRecall = DFLT_RECALL_REQUEST;
01015         if (arg2[4] == ' ')
01016         {
01017             nRecall = mux_atol(arg2 + 5);
01018         }
01019         do_comlast(player, ch, nRecall);
01020     }
01021     else if (!test_transmit_access(player, ch))
01022     {
01023         raw_notify(player, "That channel type cannot be transmitted on.");
01024         return;
01025     }
01026     else
01027     {
01028         if (!payfor(player, Guest(player) ? 0 : ch->charge))
01029         {
01030             notify(player, tprintf("You don't have enough %s.", mudconf.many_coins));
01031             return;
01032         }
01033         else
01034         {
01035             ch->amount_col += ch->charge;
01036             giveto(ch->charge_who, ch->charge);
01037         }
01038 
01039         // BuildChannelMessage allocates messNormal and messNoComtitle,
01040         // SendChannelMessage frees them.
01041         //
01042         char *messNormal;
01043         char *messNoComtitle;
01044         BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, ch->header, user,
01045             arg2, &messNormal, &messNoComtitle);
01046         SendChannelMessage(player, ch, messNormal, messNoComtitle);
01047     }
01048 }
01049 
01050 void SendChannelMessage
01051 (
01052     dbref executor,
01053     struct channel *ch,
01054     char *msgNormal,
01055     char *msgNoComtitle
01056 )
01057 {
01058     bool bSpoof = ((ch->type & CHANNEL_SPOOF) != 0);
01059     ch->num_messages++;
01060 
01061     struct comuser *user;
01062     for (user = ch->on_users; user; user = user->on_next)
01063     {
01064         if (  user->bUserIsOn
01065            && test_receive_access(user->who, ch))
01066         {
01067             if (  user->ComTitleStatus
01068                || bSpoof
01069                || msgNoComtitle == NULL)
01070             {
01071                 notify_with_cause_ooc(user->who, executor, msgNormal);
01072             }
01073             else
01074             {
01075                 notify_with_cause_ooc(user->who, executor, msgNoComtitle);
01076             }
01077         }
01078     }
01079 
01080     dbref obj = ch->chan_obj;
01081     if (Good_obj(obj))
01082     {
01083         dbref aowner;
01084         int aflags;
01085         int logmax = DFLT_MAX_LOG;
01086         char *maxbuf;
01087         ATTR *pattr = atr_str("MAX_LOG");
01088         if (  pattr
01089            && pattr->number)
01090         {
01091             maxbuf = atr_get(obj, pattr->number, &aowner, &aflags);
01092             logmax = mux_atol(maxbuf);
01093             free_lbuf(maxbuf);
01094         }
01095         if (logmax > 0)
01096         {
01097             if (logmax > MAX_RECALL_REQUEST)
01098             {
01099                 logmax = MAX_RECALL_REQUEST;
01100                 atr_add(ch->chan_obj, pattr->number, mux_ltoa_t(logmax), GOD,
01101                     AF_CONST|AF_NOPROG|AF_NOPARSE);
01102             }
01103             char *p = tprintf("HISTORY_%d", iMod(ch->num_messages, logmax));
01104             int atr = mkattr(GOD, p);
01105             if (0 < atr)
01106             {
01107                 atr_add(ch->chan_obj, atr, msgNormal, GOD, AF_CONST|AF_NOPROG|AF_NOPARSE);
01108             }
01109         }
01110     }
01111     else if (ch->chan_obj != NOTHING)
01112     {
01113         ch->chan_obj = NOTHING;
01114     }
01115 
01116     // Since msgNormal and msgNoComTitle are no longer needed, free them here.
01117     //
01118     if (msgNormal)
01119     {
01120         free_lbuf(msgNormal);
01121     }
01122     if (  msgNoComtitle
01123        && msgNoComtitle != msgNormal)
01124     {
01125         free_lbuf(msgNoComtitle);
01126     }
01127 }
01128 
01129 void do_joinchannel(dbref player, struct channel *ch)
01130 {
01131     struct comuser **cu;
01132     int i;
01133 
01134     struct comuser *user = select_user(ch, player);
01135 
01136     if (!user)
01137     {
01138         ch->num_users++;
01139         if (ch->num_users >= ch->max_users)
01140         {
01141             ch->max_users += 10;
01142             cu = (struct comuser **)MEMALLOC(sizeof(struct comuser *) * ch->max_users);
01143             ISOUTOFMEMORY(cu);
01144 
01145             for (i = 0; i < (ch->num_users - 1); i++)
01146             {
01147                 cu[i] = ch->users[i];
01148             }
01149             MEMFREE(ch->users);
01150             ch->users = cu;
01151         }
01152         user = (struct comuser *)MEMALLOC(sizeof(struct comuser));
01153         ISOUTOFMEMORY(user);
01154 
01155         for (i = ch->num_users - 1; i > 0 && ch->users[i - 1]->who > player; i--)
01156         {
01157             ch->users[i] = ch->users[i - 1];
01158         }
01159         ch->users[i] = user;
01160 
01161         user->who            = player;
01162         user->bUserIsOn      = true;
01163         user->ComTitleStatus = true;
01164         user->title          = StringClone("");
01165 
01166         // if (Connected(player))&&(isPlayer(player))
01167         //
01168         if (UNDEAD(player))
01169         {
01170             user->on_next = ch->on_users;
01171             ch->on_users  = user;
01172         }
01173     }
01174     else if (!user->bUserIsOn)
01175     {
01176         user->bUserIsOn = true;
01177     }
01178     else
01179     {
01180         raw_notify(player, tprintf("You are already on channel %s.", ch->name));
01181         return;
01182     }
01183 
01184     if (!Hidden(player))
01185     {
01186         char *messNormal, *messNoComtitle;
01187         BuildChannelMessage((ch->type & CHANNEL_SPOOF) != 0, ch->header, user,
01188             ":has joined this channel.", &messNormal, &messNoComtitle);
01189         SendChannelMessage(player, ch, messNormal, messNoComtitle);
01190     }
01191 }
01192 
01193 void do_leavechannel(dbref player, struct channel *ch)
01194 {
01195     struct comuser *user = select_user(ch, player);
01196     raw_notify(player, tprintf("You have left channel %s.", ch->name));
01197     if (  user->bUserIsOn
01198        && !Hidden(player))
01199     {
01200         char *messNormal, *messNoComtitle;
01201         BuildChannelMessage((ch->type & CHANNEL_SPOOF) !=