PennMUSH Community

root/1.8.3/trunk/src/extchat.c

Revision 1278, 103.7 kB (checked in by shawnw, 2 weeks ago)

Prep for 1.8.3p7

</
Line 
1 /**
2  * \file extchat.c
3  *
4  * \brief The PennMUSH chat system
5  *
6  *
7  */
8 #include "copyrite.h"
9 #include "config.h"
10 #include <ctype.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stddef.h>
14 #ifdef I_SYS_TYPES
15 #include <sys/types.h>
16 #endif
17 #include <stdarg.h>
18 #include "conf.h"
19 #include "externs.h"
20 #include "attrib.h"
21 #include "mushdb.h"
22 #include "match.h"
23 #include "flags.h"
24 #include "extchat.h"
25 #include "ansi.h"
26 #include "privtab.h"
27 #include "mymalloc.h"
28 #include "pueblo.h"
29 #include "parse.h"
30 #include "lock.h"
31 #include "log.h"
32 #include "game.h"
33 #include "dbdefs.h"
34 #include "function.h"
35 #include "command.h"
36 #include "dbio.h"
37 #include "confmagic.h"
38
39
40 static CHAN *new_channel(void);
41 static CHANLIST *new_chanlist(const void *hint);
42 static CHANUSER *new_user(dbref who, const void *hint);
43 static void free_channel(CHAN *c);
44 static void free_chanlist(CHANLIST *cl);
45 static void free_user(CHANUSER *u);
46 static int load_chatdb_oldstyle(FILE * fp);
47 static int load_channel(FILE * fp, CHAN *ch);
48 static int load_chanusers(FILE * fp, CHAN *ch);
49 static int load_labeled_channel(FILE * fp, CHAN *ch);
50 static int load_labeled_chanusers(FILE * fp, CHAN *ch);
51 static void insert_channel(CHAN **ch);
52 static void remove_channel(CHAN *ch);
53 static void insert_obj_chan(dbref who, CHAN **ch);
54 static void remove_obj_chan(dbref who, CHAN *ch);
55 void remove_all_obj_chan(dbref thing);
56 static void chan_chown(CHAN *c, dbref victim);
57 void chan_chownall(dbref old, dbref new);
58 static int insert_user(CHANUSER *user, CHAN *ch);
59 static int remove_user(CHANUSER *u, CHAN *ch);
60 static int save_channel(FILE * fp, CHAN *ch);
61 static int save_chanuser(FILE * fp, CHANUSER *user);
62 static void channel_wipe(dbref player, CHAN *chan);
63 static int yesno(const char *str);
64 static int canstilladd(dbref player);
65 static enum cmatch_type find_channel_partial_on(const char *name, CHAN **chan,
66                                                 dbref player);
67 static enum cmatch_type find_channel_partial_off(const char *name, CHAN **chan,
68                                                  dbref player);
69 static char *list_cuflags(CHANUSER *u, int verbose);
70 static void channel_join_self(dbref player, const char *name);
71 static void channel_leave_self(dbref player, const char *name);
72 static void do_channel_who(dbref player, CHAN *chan);
73 void chat_player_announce(dbref player, char *msg, int ungag);
74 static int ok_channel_name(const char *n);
75 static void format_channel_chat(CHAN *chan, CHANUSER *u, dbref victim,
76                                 int flags, const char *msg, const char *extra);
77 static void list_partial_matches(dbref player, const char *name,
78                                  enum chan_match_type type);
79
80 const char *chan_speak_lock = "ChanSpeakLock";  /**< Name of speak lock */
81 const char *chan_join_lock = "ChanJoinLock";    /**< Name of join lock */
82 const char *chan_mod_lock = "ChanModLock";      /**< Name of modify lock */
83 const char *chan_see_lock = "ChanSeeLock";      /**< Name of see lock */
84 const char *chan_hide_lock = "ChanHideLock";    /**< Name of hide lock */
85
86 slab *channel_slab; /**< slab for 'struct channel' allocations */
87 slab *chanlist_slab; /**< slab for 'struct chanlist' allocations */
88 slab *chanuser_slab; /**< slab for 'struct chanuser' allocations */
89
90 #define YES 1     /**< An affirmative. */
91 #define NO 0      /**< A negative. */
92 #define ERR -1    /**< An error. Clever, eh? */
93
94 /** Wrapper for insert_user() that generates a new CHANUSER and inserts it */
95 #define insert_user_by_dbref(who,chan) \
96   insert_user(new_user(who, ChanUsers(chan)),chan)
97 /** Wrapper for remove_user() that searches for the CHANUSER to remove */
98 #define remove_user_by_dbref(who,chan) \
99         remove_user(onchannel(who,chan),chan)
100
101 int num_channels;  /**< Number of channels defined */
102
103 CHAN *channels;    /**< Pointer to channel list */
104
105 extern int rhs_present;         /* from command.c */
106
107 static PRIV priv_table[] = {
108   {"Disabled", 'D', CHANNEL_DISABLED, CHANNEL_DISABLED},
109   {"Admin", 'A', CHANNEL_ADMIN | CHANNEL_PLAYER, CHANNEL_ADMIN},
110   {"Wizard", 'W', CHANNEL_WIZARD | CHANNEL_PLAYER, CHANNEL_WIZARD},
111   {"Player", 'P', CHANNEL_PLAYER, CHANNEL_PLAYER},
112   {"Object", 'O', CHANNEL_OBJECT, CHANNEL_OBJECT},
113   {"Quiet", 'Q', CHANNEL_QUIET, CHANNEL_QUIET},
114   {"Open", 'o', CHANNEL_OPEN, CHANNEL_OPEN},
115   {"Hide_Ok", 'H', CHANNEL_CANHIDE, CHANNEL_CANHIDE},
116   {"NoTitles", 'T', CHANNEL_NOTITLES, CHANNEL_NOTITLES},
117   {"NoNames", 'N', CHANNEL_NONAMES, CHANNEL_NONAMES},
118   {"NoCemit", 'C', CHANNEL_NOCEMIT, CHANNEL_NOCEMIT},
119   {"Interact", 'I', CHANNEL_INTERACT, CHANNEL_INTERACT},
120   {NULL, '\0', 0, 0}
121 };
122
123 static PRIV chanuser_priv[] = {
124   {"Quiet", 'Q', CU_QUIET, CU_QUIET},
125   {"Hide", 'H', CU_HIDE, CU_HIDE},
126   {"Gag", 'G', CU_GAG, CU_GAG},
127   {NULL, '\0', 0, 0}
128 };
129
130
131 /** Get a player's CHANUSER entry if they're on a channel.
132  * This function checks to see if a given player is on a given channel.
133  * If so, it returns a pointer to their CHANUSER structure. If not,
134  * returns NULL.
135  * \param who player to test channel membership of.
136  * \param ch pointer to channel to test membership on.
137  * \return player's CHANUSER entry on the channel, or NULL.
138  */
139 CHANUSER *
140 onchannel(dbref who, CHAN *ch)
141 {
142   static CHANUSER *u;
143   for (u = ChanUsers(ch); u; u = u->next) {
144     if (CUdbref(u) == who) {
145       return u;
146     }
147   }
148   return NULL;
149 }
150
151 /** A macro to test if a channel exists and, if not, to notify. */
152 #define test_channel(player,name,chan) \
153    do { \
154     chan = NULL; \
155     switch (find_channel(name,&chan,player)) { \
156     case CMATCH_NONE: \
157       notify(player, T ("CHAT: I don't recognize that channel.")); \
158       return; \
159     case CMATCH_AMBIG: \
160       notify(player, T("CHAT: I don't know which channel you mean.")); \
161       list_partial_matches(player, name, PMATCH_ALL); \
162       return; \
163     case CMATCH_EXACT: \
164     case CMATCH_PARTIAL: \
165     default: \
166       break; \
167      } \
168     } while (0)
169
170 /** A macro to test if a channel exists and player's on it, and,
171  * if not, to notify. */
172 #define test_channel_on(player,name,chan) \
173    do { \
174     chan = NULL; \
175     switch (find_channel_partial_on(name,&chan,player)) { \
176     case CMATCH_NONE: \
177       notify(player, T ("CHAT: I don't recognize that channel.")); \
178       return; \
179     case CMATCH_AMBIG: \
180       notify(player, T("CHAT: I don't know which channel you mean.")); \
181       list_partial_matches(player, name, PMATCH_ALL); \
182       return; \
183     case CMATCH_EXACT: \
184     case CMATCH_PARTIAL: \
185     default: \
186       break; \
187      } \
188     } while (0)
189
190 /*----------------------------------------------------------
191  * Loading and saving the chatdb
192  * The chatdb's format is pretty straightforward
193  * Return 1 on success, 0 on failure
194  */
195
196 /** Initialize the chat database .*/
197 void
198 init_chatdb(void)
199 {
200   static bool init_called = 0;
201   if (!init_called) {
202     init_called = 1;
203     num_channels = 0;
204     channel_slab = slab_create("channels", sizeof(struct channel));
205     chanuser_slab = slab_create("channel users", sizeof(struct chanuser));
206     chanlist_slab = slab_create("channel lists", sizeof(struct chanlist));
207     slab_set_opt(chanuser_slab, SLAB_ALLOC_BEST_FIT, 1);
208     slab_set_opt(chanlist_slab, SLAB_ALLOC_BEST_FIT, 1);
209     channels = NULL;
210   }
211 }
212
213 /** Load the chat database from a file.
214  * \param fp pointer to file to read from.
215  * \retval 1 success
216  * \retval 0 failure
217  */
218 static int
219 load_chatdb_oldstyle(FILE * fp)
220 {
221   int i;
222   CHAN *ch;
223   char buff[20];
224
225   /* How many channels? */
226   num_channels = getref(fp);
227   if (num_channels > MAX_CHANNELS)
228     return 0;
229
230   /* Load all channels */
231   for (i = 0; i < num_channels; i++) {
232     if (feof(fp))
233       break;
234     ch = new_channel();
235     if (!ch)
236       return 0;
237     if (!load_channel(fp, ch)) {
238       do_rawlog(LT_ERR, T("Unable to load channel %d."), i);
239       free_channel(ch);
240       return 0;
241     }
242     insert_channel(&ch);
243   }
244   num_channels = i;
245
246   /* Check for **END OF DUMP*** */
247   if (!fgets(buff, sizeof buff, fp))
248     do_rawlog(LT_ERR, T("CHAT: No end-of-dump marker in the chat database."));
249   else if (strcmp(buff, EOD) != 0)
250     do_rawlog(LT_ERR, T("CHAT: Trailing garbage in the chat database."));
251
252   return 1;
253 }
254
255 extern char db_timestamp[];
256
257 /** Load the chat database from a file.
258  * \param fp pointer to file to read from.
259  * \retval 1 success
260  * \retval 0 failure
261  */
262 int
263 load_chatdb(FILE * fp)
264 {
265   int i, flags;
266   CHAN *ch;
267   char buff[20];
268   char *chat_timestamp;
269
270   i = fgetc(fp);
271   if (i == EOF) {
272     do_rawlog(LT_ERR, T("CHAT: Invalid database format!"));
273     longjmp(db_err, 1);
274   } else if (i != '+') {
275     ungetc(i, fp);
276     return load_chatdb_oldstyle(fp);
277   }
278
279   i = fgetc(fp);
280
281   if (i != 'V') {
282     do_rawlog(LT_ERR, T("CHAT: Invalid database format!"));
283     longjmp(db_err, 1);
284   }
285
286   flags = getref(fp);
287
288   db_read_this_labeled_string(fp, "savedtime", &chat_timestamp);
289
290   if (strcmp(chat_timestamp, db_timestamp))
291     do_rawlog(LT_ERR,
292               T
293               ("CHAT: warning: chatdb and game db were saved at different times!"));
294
295   /* How many channels? */
296   db_read_this_labeled_int(fp, "channels", &num_channels);
297   if (num_channels > MAX_CHANNELS)
298     return 0;
299
300   /* Load all channels */
301   for (i = 0; i < num_channels; i++) {
302     ch = new_channel();
303     if (!ch)
304       return 0;
305     if (!load_labeled_channel(fp, ch)) {
306       do_rawlog(LT_ERR, T("Unable to load channel %d."), i);
307       free_channel(ch);
308       return 0;
309     }
310     insert_channel(&ch);
311   }
312   num_channels = i;
313
314   /* Check for **END OF DUMP*** */
315   if (!fgets(buff, sizeof buff, fp))
316     do_rawlog(LT_ERR, T("CHAT: No end-of-dump marker in the chat database."));
317   else if (strcmp(buff, EOD) != 0)
318     do_rawlog(LT_ERR, T("CHAT: Trailing garbage in the chat database."));
319
320   return 1;
321 }
322
323
324 /* Malloc memory for a new channel, and initialize it */
325 static CHAN *
326 new_channel(void)
327 {
328   CHAN *ch;
329
330   ch = slab_malloc(channel_slab, NULL);
331   if (!ch)
332     return NULL;
333   ch->name[0] = '\0';
334   ch->title[0] = '\0';
335   ChanType(ch) = CHANNEL_DEFAULT_FLAGS;
336   ChanCreator(ch) = NOTHING;
337   ChanCost(ch) = CHANNEL_COST;
338   ChanNext(ch) = NULL;
339   ChanNumMsgs(ch) = 0;
340   /* By default channels are public but mod-lock'd to the creator */
341   ChanJoinLock(ch) = TRUE_BOOLEXP;
342   ChanSpeakLock(ch) = TRUE_BOOLEXP;
343   ChanSeeLock(ch) = TRUE_BOOLEXP;
344   ChanHideLock(ch) = TRUE_BOOLEXP;
345   ChanModLock(ch) = TRUE_BOOLEXP;
346   ChanNumUsers(ch) = 0;
347   ChanMaxUsers(ch) = 0;
348   ChanUsers(ch) = NULL;
349   ChanBufferQ(ch) = NULL;
350   return ch;
351 }
352
353
354
355 /* Malloc memory for a new user, and initialize it */
356 static CHANUSER *
357 new_user(dbref who, const void *hint)
358 {
359   CHANUSER *u;
360   u = slab_malloc(chanuser_slab, hint);
361   if (!u)
362     mush_panic("Couldn't allocate memory in new_user in extchat.c");
363   CUdbref(u) = who;
364   CUtype(u) = CU_DEFAULT_FLAGS;
365   u->title[0] = '\0';
366   CUnext(u) = NULL;
367   return u;
368 }
369
370 /* Free memory from a channel */
371 static void
372 free_channel(CHAN *c)
373 {
374   CHANUSER *u, *unext;
375   if (!c)
376     return;
377   free_boolexp(ChanJoinLock(c));
378   free_boolexp(ChanSpeakLock(c));
379   free_boolexp(ChanHideLock(c));
380   free_boolexp(ChanSeeLock(c));
381   free_boolexp(ChanModLock(c));
382   u = ChanUsers(c);
383   while (u) {
384     unext = u->next;
385     free_user(u);
386     u = unext;
387   }
388   return;
389 }
390
391 /* Free memory from a channel user */
392 static void
393 free_user(CHANUSER *u)
394 {
395   if (u)
396     slab_free(chanuser_slab, u);
397 }
398
399 /* Load in a single channel into position i. Return 1 if
400  * successful, 0 otherwise.
401  */
402 static int
403 load_channel(FILE * fp, CHAN *ch)
404 {
405   mush_strncpy(ChanName(ch), getstring_noalloc(fp), CHAN_NAME_LEN);
406   if (feof(fp))
407     return 0;
408   mush_strncpy(ChanTitle(ch), getstring_noalloc(fp), CHAN_TITLE_LEN);
409   ChanType(ch) = (privbits) getref(fp);
410   ChanCreator(ch) = getref(fp);
411   ChanCost(ch) = getref(fp);
412   ChanNumMsgs(ch) = 0;
413   ChanJoinLock(ch) = getboolexp(fp, chan_join_lock);
414   ChanSpeakLock(ch) = getboolexp(fp, chan_speak_lock);
415   ChanModLock(ch) = getboolexp(fp, chan_mod_lock);
416   ChanSeeLock(ch) = getboolexp(fp, chan_see_lock);
417   ChanHideLock(ch) = getboolexp(fp, chan_hide_lock);
418   ChanNumUsers(ch) = getref(fp);
419   ChanMaxUsers(ch) = ChanNumUsers(ch);
420   ChanUsers(ch) = NULL;
421   if (ChanNumUsers(ch) > 0)
422     ChanNumUsers(ch) = load_chanusers(fp, ch);
423   return 1;
424 }
425
426 /* Load in a single channel into position i. Return 1 if
427  * successful, 0 otherwise.
428  */
429 static int
430 load_labeled_channel(FILE * fp, CHAN *ch)
431 {
432   char *tmp;
433   int i;
434   dbref d;
435   char *label, *value;
436
437   db_read_this_labeled_string(fp, "name", &tmp);
438   mush_strncpy(ChanName(ch), tmp, CHAN_NAME_LEN);
439   db_read_this_labeled_string(fp, "description", &tmp);
440   mush_strncpy(ChanTitle(ch), tmp, CHAN_TITLE_LEN);
441   db_read_this_labeled_int(fp, "flags", &i);
442   ChanType(ch) = (privbits) i;
443   db_read_this_labeled_dbref(fp, "creator", &d);
444   ChanCreator(ch) = d;
445   db_read_this_labeled_int(fp, "cost", &i);
446   ChanCost(ch) = i;
447   ChanNumMsgs(ch) = 0;
448   while (1) {
449     db_read_labeled_string(fp, &label, &value);
450     if (strcmp(label, "lock"))
451       break;
452     else if (strcmp(value, "join") == 0)
453       ChanJoinLock(ch) = getboolexp(fp, chan_join_lock);
454     else if (strcmp(value, "speak") == 0)
455       ChanSpeakLock(ch) = getboolexp(fp, chan_speak_lock);
456     else if (strcmp(value, "modify") == 0)
457       ChanModLock(ch) = getboolexp(fp, chan_mod_lock);
458     else if (strcmp(value, "see") == 0)
459       ChanSeeLock(ch) = getboolexp(fp, chan_see_lock);
460     else if (strcmp(value, "hide") == 0)
461       ChanHideLock(ch) = getboolexp(fp, chan_hide_lock);
462   }
463   ChanNumUsers(ch) = parse_integer(value);
464   ChanMaxUsers(ch) = ChanNumUsers(ch);
465   ChanUsers(ch) = NULL;
466   if (ChanNumUsers(ch) > 0)
467     ChanNumUsers(ch) = load_labeled_chanusers(fp, ch);
468   return 1;
469 }
470
471
472 /* Load the *channel's user list. Return number of users on success, or 0 */
473 static int
474 load_chanusers(FILE * fp, CHAN *ch)
475 {
476   int i, num = 0;
477   CHANUSER *user;
478   dbref player;
479   for (i = 0; i < ChanNumUsers(ch); i++) {
480     player = getref(fp);
481     /* Don't bother if the player isn't a valid dbref or the wrong type */
482     if (GoodObject(player) && Chan_Ok_Type(ch, player)) {
483       user = new_user(player, ChanUsers(ch));
484       CUtype(user) = getref(fp);
485       strcpy(CUtitle(user), getstring_noalloc(fp));
486       CUnext(user) = NULL;
487       if (insert_user(user, ch))
488         num++;
489     } else {
490       /* But be sure to read (and discard) the player's info */
491       do_log(LT_ERR, 0, 0, T("Bad object #%d removed from channel %s"),
492              player, ChanName(ch));
493       (void) getref(fp);
494       (void) getstring_noalloc(fp);
495     }
496   }
497   return num;
498 }
499
500 /* Load the *channel's user list. Return number of users on success, or 0 */
501 static int
502 load_labeled_chanusers(FILE * fp, CHAN *ch)
503 {
504   int i, num = 0, n;
505   char *tmp;
506   CHANUSER *user;
507   dbref player;
508   for (i = 0; i < ChanNumUsers(ch); i++) {
509     db_read_this_labeled_dbref(fp, "dbref", &player);
510     /* Don't bother if the player isn't a valid dbref or the wrong type */
511     if (GoodObject(player) && Chan_Ok_Type(ch, player)) {
512       user = new_user(player, ChanUsers(ch));