ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/modules/operserv/news.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (11 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 16543 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# Content
1 /* News module.
2 * Based on code by Andrew Kempe (TheShadow).
3 *
4 * IRC Services is copyright (c) 1996-2009 Andrew Church.
5 * E-mail: <achurch@achurch.org>
6 * Parts written by Andrew Kempe and others.
7 * This program is free but copyrighted software; see the file GPL.txt for
8 * details.
9 */
10
11 #include "services.h"
12 #include "modules.h"
13 #include "conffile.h"
14 #include "commands.h"
15 #include "databases.h"
16 #include "language.h"
17
18 #include "operserv.h"
19 #include "news.h"
20
21 /*************************************************************************/
22
23 static Module *module_operserv;
24
25 static void do_logonnews(User *u);
26 static void do_opernews(User *u);
27
28 static Command cmds[] = {
29 /* Anyone can use *NEWS LIST, but *NEWS {ADD,DEL} are reserved for
30 * Services operators. (The command routines check permissions.) */
31 {"LOGONNEWS", do_logonnews, NULL, NEWS_HELP_LOGON, -1,-1},
32 {"OPERNEWS", do_opernews, NULL, NEWS_HELP_OPER, -1,-1},
33 {NULL}
34 };
35
36 /*************************************************************************/
37
38 /* List of messages for each news type. This simplifies message sending. */
39
40 #define MSG_SYNTAX 0
41 #define MSG_LIST_HEADER 1
42 #define MSG_LIST_ENTRY 2
43 #define MSG_LIST_NONE 3
44 #define MSG_ADD_SYNTAX 4
45 #define MSG_ADD_FULL 5
46 #define MSG_ADDED 6
47 #define MSG_DEL_SYNTAX 7
48 #define MSG_DEL_NOT_FOUND 8
49 #define MSG_DELETED 9
50 #define MSG_DEL_NONE 10
51 #define MSG_DELETED_ALL 11
52 #define MSG_MAX 11
53
54 struct newsmsgs {
55 int16 type;
56 const char *name;
57 int msgs[MSG_MAX+1];
58 };
59 static struct newsmsgs msgarray[] = {
60 { NEWS_LOGON, "LOGON",
61 { NEWS_LOGON_SYNTAX,
62 NEWS_LOGON_LIST_HEADER,
63 NEWS_LOGON_LIST_ENTRY,
64 NEWS_LOGON_LIST_NONE,
65 NEWS_LOGON_ADD_SYNTAX,
66 NEWS_LOGON_ADD_FULL,
67 NEWS_LOGON_ADDED,
68 NEWS_LOGON_DEL_SYNTAX,
69 NEWS_LOGON_DEL_NOT_FOUND,
70 NEWS_LOGON_DELETED,
71 NEWS_LOGON_DEL_NONE,
72 NEWS_LOGON_DELETED_ALL
73 }
74 },
75 { NEWS_OPER, "OPER",
76 { NEWS_OPER_SYNTAX,
77 NEWS_OPER_LIST_HEADER,
78 NEWS_OPER_LIST_ENTRY,
79 NEWS_OPER_LIST_NONE,
80 NEWS_OPER_ADD_SYNTAX,
81 NEWS_OPER_ADD_FULL,
82 NEWS_OPER_ADDED,
83 NEWS_OPER_DEL_SYNTAX,
84 NEWS_OPER_DEL_NOT_FOUND,
85 NEWS_OPER_DELETED,
86 NEWS_OPER_DEL_NONE,
87 NEWS_OPER_DELETED_ALL
88 }
89 }
90 };
91
92 static int *findmsgs(int16 type, char **typename) {
93 int i;
94 for (i = 0; i < lenof(msgarray); i++) {
95 if (msgarray[i].type == type) {
96 if (typename)
97 *typename = (char *)msgarray[i].name;
98 return msgarray[i].msgs;
99 }
100 }
101 return NULL;
102 }
103
104 /*************************************************************************/
105
106 /* Main handler for NEWS commands. */
107 static void do_news(User *u, int16 type);
108
109 /* Lists all a certain type of news. */
110 static void do_news_list(User *u, int16 type, int *msgs);
111
112 /* Add news items. */
113 static void do_news_add(User *u, int16 type, int *msgs, const char *typename);
114 static int add_newsitem(User *u, const char *text, int16 type);
115
116 /* Delete news items. */
117 static void do_news_del(User *u, int16 type, int *msgs, const char *typename);
118 static int del_newsitem(int num, int16 type);
119
120 /*************************************************************************/
121 /**************************** Database stuff *****************************/
122 /*************************************************************************/
123
124 static NewsItem *newslist = NULL;
125 static int32 newslist_count = 0;
126 static int newslist_iterator;
127
128 /*************************************************************************/
129
130 static void *new_news(void)
131 {
132 return scalloc(1, sizeof(NewsItem));
133 }
134
135 /*************************************************************************/
136
137 static void free_news(void *record)
138 {
139 NewsItem *news = record;
140 if (news) {
141 free(news->text);
142 free(news);
143 }
144 }
145
146 /*************************************************************************/
147
148 NewsItem *add_news(NewsItem *newsitem)
149 {
150 if (newslist_count >= MAX_NEWS)
151 fatal("add_news(): too many news items!");
152 ARRAY_EXTEND(newslist);
153 memcpy(&newslist[newslist_count-1], newsitem, sizeof(NewsItem));
154 newslist[newslist_count-1].next =
155 (NewsItem *)(long)(newslist_count-1); /*index*/
156 free(newsitem);
157 return &newslist[newslist_count-1];
158 }
159
160 /*************************************************************************/
161
162 void del_news(NewsItem *newsitem)
163 {
164 int num = (int)(long)(newsitem->next);
165 if (num < 0 || num >= newslist_count) {
166 module_log("del_news(): invalid index %d in news item at %p",
167 num, newsitem);
168 return;
169 }
170 free(newsitem->text);
171 ARRAY_REMOVE(newslist, num);
172 if (num < newslist_iterator)
173 newslist_iterator--;
174 while (num < newslist_count) {
175 newslist[num].next = (NewsItem *)(long)num;
176 num++;
177 }
178 }
179
180 /*************************************************************************/
181
182 NewsItem *get_news(int16 type, int32 num)
183 {
184 int i;
185
186 ARRAY_FOREACH (i, newslist) {
187 if (newslist[i].type == type && newslist[i].num == num)
188 break;
189 }
190 return i<newslist_count ? &newslist[i] : NULL;
191 }
192
193 /*************************************************************************/
194
195 NewsItem *put_news(NewsItem *news)
196 {
197 return news;
198 }
199
200 /*************************************************************************/
201
202 NewsItem *first_news(void)
203 {
204 newslist_iterator = 0;
205 return next_news();
206 }
207
208 NewsItem *next_news(void)
209 {
210 if (newslist_iterator >= newslist_count)
211 return NULL;
212 return &newslist[newslist_iterator++];
213 }
214
215 /*************************************************************************/
216
217 int news_count(void)
218 {
219 return newslist_count;
220 }
221
222 /*************************************************************************/
223
224 /* Free all memory used by database tables. */
225
226 static void clean_dbtables(void)
227 {
228 int i;
229
230 ARRAY_FOREACH (i, newslist)
231 free(newslist[i].text);
232 free(newslist);
233 newslist = NULL;
234 newslist_count = 0;
235 }
236
237 /*************************************************************************/
238
239 /* News database table info */
240 static DBField news_dbfields[] = {
241 { "type", DBTYPE_INT16, offsetof(NewsItem,type) },
242 { "num", DBTYPE_INT32, offsetof(NewsItem,num) },
243 { "text", DBTYPE_STRING, offsetof(NewsItem,text) },
244 { "who", DBTYPE_BUFFER, offsetof(NewsItem,who), NICKMAX },
245 { "time", DBTYPE_TIME, offsetof(NewsItem,time) },
246 { NULL }
247 };
248 static DBTable news_dbtable = {
249 .name = "news",
250 .newrec = new_news,
251 .freerec = free_news,
252 .insert = (void *)add_news,
253 .first = (void *)first_news,
254 .next = (void *)next_news,
255 .fields = news_dbfields,
256 };
257
258 /*************************************************************************/
259 /***************************** News display ******************************/
260 /*************************************************************************/
261
262 static void display_news(User *u, int16 type, time_t min_time)
263 {
264 NewsItem *news, *disp[NEWS_DISPCOUNT];
265 int count = 0; /* Number we're going to show--not more than 3 */
266 int msg;
267
268 if (type == NEWS_LOGON) {
269 msg = NEWS_LOGON_TEXT;
270 } else if (type == NEWS_OPER) {
271 msg = NEWS_OPER_TEXT;
272 } else {
273 module_log("Invalid type (%d) to display_news()", type);
274 return;
275 }
276
277 for (news = first_news(); news; news = next_news()) {
278 if (count >= NEWS_DISPCOUNT)
279 break;
280 if (news->type == type && (!min_time || news->time > min_time)) {
281 disp[count] = news;
282 count++;
283 }
284 }
285 while (--count >= 0) {
286 char timebuf[BUFSIZE];
287
288 strftime_lang(timebuf, sizeof(timebuf), u->ngi,
289 STRFTIME_SHORT_DATE_FORMAT, disp[count]->time);
290 notice_lang(s_GlobalNoticer, u, msg, timebuf, disp[count]->text);
291 }
292 }
293
294 /*************************************************************************/
295 /***************************** News editing ******************************/
296 /*************************************************************************/
297
298 static void do_logonnews(User *u)
299 {
300 do_news(u, NEWS_LOGON);
301 }
302
303 static void do_opernews(User *u)
304 {
305 do_news(u, NEWS_OPER);
306 }
307
308 /*************************************************************************/
309
310 /* Main news command handling routine. */
311 static void do_news(User *u, int16 type)
312 {
313 const char *cmd = strtok(NULL, " ");
314 char *typename;
315 int *msgs;
316
317 msgs = findmsgs(type, &typename);
318 if (!msgs) {
319 module_log("Invalid type to do_news()");
320 return;
321 }
322
323 if (!cmd)
324 cmd = "";
325
326 if (stricmp(cmd, "LIST") == 0) {
327 do_news_list(u, type, msgs);
328
329 } else if (stricmp(cmd, "ADD") == 0) {
330 if (is_services_oper(u))
331 do_news_add(u, type, msgs, typename);
332 else
333 notice_lang(s_OperServ, u, PERMISSION_DENIED);
334
335 } else if (stricmp(cmd, "DEL") == 0) {
336 if (is_services_oper(u))
337 do_news_del(u, type, msgs, typename);
338 else
339 notice_lang(s_OperServ, u, PERMISSION_DENIED);
340
341 } else {
342 char buf[32];
343 snprintf(buf, sizeof(buf), "%sNEWS", typename);
344 syntax_error(s_OperServ, u, buf, msgs[MSG_SYNTAX]);
345 }
346 }
347
348 /*************************************************************************/
349
350 /* Handle a {LOGON,OPER}NEWS LIST command. */
351
352 static void do_news_list(User *u, int16 type, int *msgs)
353 {
354 NewsItem *news;
355 int count = 0;
356 char timebuf[64];
357
358 for (news = first_news(); news; news = next_news()) {
359 if (news->type == type) {
360 if (count == 0)
361 notice_lang(s_OperServ, u, msgs[MSG_LIST_HEADER]);
362 strftime_lang(timebuf, sizeof(timebuf), u->ngi,
363 STRFTIME_DATE_TIME_FORMAT, news->time);
364 notice_lang(s_OperServ, u, msgs[MSG_LIST_ENTRY],
365 news->num, timebuf,
366 *news->who ? news->who : "<unknown>",
367 news->text);
368 count++;
369 }
370 }
371 if (count == 0)
372 notice_lang(s_OperServ, u, msgs[MSG_LIST_NONE]);
373 }
374
375 /*************************************************************************/
376
377 /* Handle a {LOGON,OPER}NEWS ADD command. */
378
379 static void do_news_add(User *u, int16 type, int *msgs, const char *typename)
380 {
381 char *text = strtok_remaining();
382
383 if (!text) {
384 char buf[32];
385 snprintf(buf, sizeof(buf), "%sNEWS", typename);
386 syntax_error(s_OperServ, u, buf, msgs[MSG_ADD_SYNTAX]);
387 } else {
388 int n = add_newsitem(u, text, type);
389 if (n < 0)
390 notice_lang(s_OperServ, u, msgs[MSG_ADD_FULL]);
391 else
392 notice_lang(s_OperServ, u, msgs[MSG_ADDED], n);
393 if (readonly)
394 notice_lang(s_OperServ, u, READ_ONLY_MODE);
395 }
396 }
397
398
399 /* Actually add a news item. Return the number assigned to the item, or -1
400 * if the news list is full (MAX_NEWS items).
401 */
402
403 static int add_newsitem(User *u, const char *text, int16 type)
404 {
405 NewsItem *news;
406 int num;
407
408 if (news_count() >= MAX_NEWS)
409 return -1;
410
411 num = 0;
412 for (news = first_news(); news; news = next_news()) {
413 if (news->type == type && num < news->num)
414 num = news->num;
415 }
416 if (num+1 < num) {
417 module_log("BUG: add_newsitem(): news number overflow (MAX_NEWS"
418 " too small?)");
419 return -1;
420 }
421 news = scalloc(1, sizeof(*news));
422 news->type = type;
423 news->num = num+1;
424 news->text = sstrdup(text);
425 news->time = time(NULL);
426 strbcpy(news->who, u->nick);
427 add_news(news);
428 return num+1;
429 }
430
431 /*************************************************************************/
432
433 /* Handle a {LOGON,OPER}NEWS DEL command. */
434
435 static void do_news_del(User *u, int16 type, int *msgs, const char *typename)
436 {
437 char *text = strtok(NULL, " ");
438
439 if (!text) {
440 char buf[32];
441 snprintf(buf, sizeof(buf), "%sNEWS", typename);
442 syntax_error(s_OperServ, u, buf, msgs[MSG_DEL_SYNTAX]);
443 } else {
444 if (stricmp(text, "ALL") != 0) {
445 int num = atoi(text);
446 if (num > 0 && del_newsitem(num, type))
447 notice_lang(s_OperServ, u, msgs[MSG_DELETED], num);
448 else
449 notice_lang(s_OperServ, u, msgs[MSG_DEL_NOT_FOUND], num);
450 } else {
451 if (del_newsitem(0, type))
452 notice_lang(s_OperServ, u, msgs[MSG_DELETED_ALL]);
453 else
454 notice_lang(s_OperServ, u, msgs[MSG_DEL_NONE]);
455 }
456 if (readonly)
457 notice_lang(s_OperServ, u, READ_ONLY_MODE);
458 }
459 }
460
461
462 /* Actually delete a news item. If `num' is 0, delete all news items of
463 * the given type. Returns the number of items deleted.
464 */
465
466 static int del_newsitem(int num, int16 type)
467 {
468 NewsItem *news;
469 int count = 0;
470
471 for (news = first_news(); news; news = next_news()) {
472 if (news->type == type && (num == 0 || news->num == num)) {
473 del_news(news);
474 count++;
475 }
476 }
477 return count;
478 }
479
480 /*************************************************************************/
481 /*************************** Callback routines ***************************/
482 /*************************************************************************/
483
484 /* Callback for users logging on. */
485
486 static int new_user_callback(User *u, int ac, char **av, int reconnect)
487 {
488 display_news(u, NEWS_LOGON, reconnect ? u->signon : 0);
489 return 0;
490 }
491
492 /*************************************************************************/
493
494 /* Callback to watch for mode +o to send oper news. */
495
496 static int user_mode_callback(User *u, int modechar, int add)
497 {
498 if (modechar == 'o' && add)
499 display_news(u, NEWS_OPER, 0);
500 return 0;
501 }
502
503 /*************************************************************************/
504
505 /* OperServ STATS ALL callback. */
506
507 static int do_stats_all(User *user, const char *s_OperServ)
508 {
509 int32 count, mem;
510 NewsItem *news;
511
512 count = mem = 0;
513 for (news = first_news(); news; news = next_news()) {
514 count++;
515 mem += sizeof(*news);
516 if (news->text)
517 mem += strlen(news->text)+1;
518 }
519 notice_lang(s_OperServ, user, OPER_STATS_ALL_NEWS_MEM,
520 count, (mem+512) / 1024);
521
522 return 0;
523 }
524
525 /*************************************************************************/
526 /***************************** Module stuff ******************************/
527 /*************************************************************************/
528
529 ConfigDirective module_config[] = {
530 { NULL }
531 };
532
533 /*************************************************************************/
534
535 int init_module()
536 {
537 module_operserv = find_module("operserv/main");
538 if (!module_operserv) {
539 module_log("Main OperServ module not loaded");
540 return 0;
541 }
542 use_module(module_operserv);
543
544 if (!register_commands(module_operserv, cmds)) {
545 module_log("Unable to register commands");
546 exit_module(0);
547 return 0;
548 }
549
550 if (!add_callback(NULL, "user create", new_user_callback)
551 || !add_callback(NULL, "user MODE", user_mode_callback)
552 || !add_callback(module_operserv, "STATS ALL", do_stats_all)
553 ) {
554 module_log("Unable to add callbacks");
555 exit_module(0);
556 return 0;
557 }
558
559 if (!register_dbtable(&news_dbtable)) {
560 module_log("Unable to register database table");
561 exit_module(0);
562 return 0;
563 }
564
565 return 1;
566 }
567
568 /*************************************************************************/
569
570 int exit_module(int shutdown_unused)
571 {
572 unregister_dbtable(&news_dbtable);
573 clean_dbtables();
574
575 remove_callback(NULL, "user create", new_user_callback);
576 remove_callback(NULL, "user MODE", user_mode_callback);
577
578 if (module_operserv) {
579 remove_callback(module_operserv, "STATS ALL", do_stats_all);
580 unregister_commands(module_operserv, cmds);
581 unuse_module(module_operserv);
582 module_operserv = NULL;
583 }
584
585 return 1;
586 }
587
588 /*************************************************************************/
589
590 /*
591 * Local variables:
592 * c-file-style: "stroustrup"
593 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
594 * indent-tabs-mode: nil
595 * End:
596 *
597 * vim: expandtab shiftwidth=4:
598 */