]> jfr.im git - irc/evilnet/x3.git/blame - src/global.c
Added RCHANNELS which will send notices to all registered channels. Also changed...
[irc/evilnet/x3.git] / src / global.c
CommitLineData
d76ed9a9 1/* global.c - Global notice service
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9 9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21#include "conf.h"
22#include "global.h"
985d4109 23#include "hash.h"
d76ed9a9 24#include "modcmd.h"
25#include "nickserv.h"
26#include "saxdb.h"
27#include "timeq.h"
28
29#define GLOBAL_CONF_NAME "services/global"
30
31#define GLOBAL_DB "global.db"
32#define GLOBAL_TEMP_DB "global.db.new"
33
34/* Global options */
35#define KEY_DB_BACKUP_FREQ "db_backup_freq"
0f6fe38c 36#define KEY_ANNOUNCEMENTS_DEFAULT "announcements_default"
d76ed9a9 37#define KEY_NICK "nick"
38
39/* Message data */
40#define KEY_FLAGS "flags"
41#define KEY_POSTED "posted"
42#define KEY_DURATION "duration"
43#define KEY_FROM "from"
44#define KEY_MESSAGE "message"
45
46/* Clarification: Notices are immediate, they are sent to matching users
47 _once_, then forgotten. Messages are stored in Global's database and
48 continually sent to users as they match the target specification until
49 they are deleted. */
50static const struct message_entry msgtab[] = {
51 { "GMSG_INVALID_TARGET", "$b%s$b is an invalid message target." },
52 { "GMSG_MESSAGE_REQUIRED", "You $bmust$b provide a message to send." },
53 { "GMSG_MESSAGE_SENT", "Message to $b%s$b sent." },
54 { "GMSG_MESSAGE_ADDED", "Message to $b%s$b with ID %ld added." },
55 { "GMSG_MESSAGE_DELETED", "Message $b%s$b deleted." },
56 { "GMSG_ID_INVALID", "$b%s$b is an invalid message ID." },
57 { "GMSG_MESSAGE_COUNT", "$b%d$b messages found." },
58 { "GMSG_NO_MESSAGES", "There are no messages for you." },
1478fd13 59 { "GMSG_NOTICE_SOURCE", "Notice to [$b%s$b] from %s:" },
60 { "GMSG_MESSAGE_SOURCE", "Notice to [$b%s$b] from %s, posted %s:" },
61 //{ "GMSG_MOTD_HEADER", "$b------------- MESSAGE(S) OF THE DAY --------------$b" },
62 { "GMSG_MOTD_HEADER", "$bNetwork Announcements$b" },
63 { "GMSG_MOTD_BAR", "---------------------------------------" },
64 { "GMSG_MOTD_FOOTER", "--------------- Thank You--------------" },
985d4109 65
66 /* These definitions are for other files that make use of global
67 * notices. Make sure you grep for them if you ever add args
68 * to the notice.
69 */
09a3057c 70 /* chanserv.c */
71 { "CSMSG_REGISTERED_TO", "%s registered to %s by %s." },
72 { "CSMSG_CHANNEL_MOVED", "%s moved to %s by %s." },
73 { "CSMSG_SUSPENSION_MODIFIED", "%s suspension modified by %s." },
74 { "CSMSG_SUSPENDED_BY", "%s suspended by %s." },
75 { "CSMSG_UNSUSPENDED_BY", "%s unsuspended by %s." },
76 { "CSMSG_OWNERSHIP_TRANSFERRED", "%s ownership transferred to %s by %s." },
77
57692f5e 78 /* mod-helpserv.c */
79 { "HSMSG_BOT_RENAMED", "HelpServ bot %s (in %s) renamed to %s by %s." },
80 { "HSMSG_BOT_MOVED", "HelpServ %s (%s) moved to %s by %s." },
81 { "HSMSG_BOT_REGISTERED", "HelpServ %s (%s) registered to %s by %s." },
82 { "HSMSG_BOT_EXPIRED", "HelpServ %s (%s) expired at request of %s." },
83 { "HSMSG_BOT_UNREGISTERED", "HelpServ %s (%s) unregistered by %s." },
3da28d8e 84 { "HSMSG_SUSPENDED_BY", "%s suspended by %s. (HelpServ)" },
85 { "HSMSG_UNSUSPENDED_BY", "%s unsuspended by %s. (HelpServ)" },
57692f5e 86
87 /* nickserv.c */
88 { "NSMSG_ACCOUNT_RENAMED", "%s renamed account %s to %s." },
89 { "NSMSG_ACCOUNT_MERGED", "%s (%s) merged account %s into %s." },
90
09a3057c 91 /* opserv.c */
92 { "DEFCON_NETWORK_CHANGED", "Network DefCon level has changed to level %d" },
93 { "DEFCON_OPER_LEVEL_CHANGE", "%s is changing the DefCon level to %d" },
94 { "DEFCON_TIMEOUT_LEVEL_CHANGE", "The DefCon has changed back to level %d (timeout)" },
57692f5e 95 { "OSMSG_CHANNEL_ACTIVITY_WARN", "Channel activity warning for channel %s: %s" },
96
97 /* spamserv.c */
98 { "SSMSG_CHANNEL_MERGED", "$X (channel %s) merged into %s by %s." },
99 { "SSMSG_CHANNEL_MOVED", "$X (channel %s) moved into %s by %s." },
100 { "SSMSG_UNREG_MANUAL", "$X (channel %s) %s by %s." },
101 { "SSMSG_REG_EXPIRED", "$X (channel %s) registration expired." },
102 { "SSMSG_LOST_ALL_USERS", "$X (channel %s) lost all users." },
103 { "SSMSG_REGISTERED_BY", "$X (channel %s) registered by %s." },
104 { "SSMSG_UNREGISTERED_BY", "$X (channel %s) unregistered by %s." },
985d4109 105
d76ed9a9 106 { NULL, NULL }
107};
108
b1bf690d 109#define GLOBAL_SYNTAX() svccmd_send_help_brief(user, global, cmd)
d76ed9a9 110#define GLOBAL_FUNC(NAME) MODCMD_FUNC(NAME)
111
dee9951d 112struct globalMessage
113{
114 unsigned long id;
115 long flags;
116
117 time_t posted;
118 char posted_s[24];
119 unsigned long duration;
120
121 char *from;
122 char *message;
123
124 struct globalMessage *prev;
125 struct globalMessage *next;
126};
127
d76ed9a9 128struct userNode *global;
129
130static struct module *global_module;
131static struct service *global_service;
132static struct globalMessage *messageList;
7637f48f 133extern struct string_list *autojoin_channels;
d76ed9a9 134static long messageCount;
135static time_t last_max_alert;
136static struct log_type *G_LOG;
137
138static struct
139{
140 unsigned long db_backup_frequency;
0f6fe38c 141 unsigned int announcements_default : 1;
d76ed9a9 142} global_conf;
143
144#define global_notice(target, format...) send_message(target , global , ## format)
145
146void message_expire(void *data);
147
148static struct globalMessage*
149message_add(long flags, time_t posted, unsigned long duration, char *from, const char *msg)
150{
151 struct globalMessage *message;
dee9951d 152 struct tm tm;
d76ed9a9 153
154 message = malloc(sizeof(struct globalMessage));
d76ed9a9 155 if(!message)
156 {
157 return NULL;
158 }
159
160 message->id = messageCount++;
161 message->flags = flags;
162 message->posted = posted;
163 message->duration = duration;
164 message->from = strdup(from);
165 message->message = strdup(msg);
166
dee9951d 167 if ((flags & MESSAGE_OPTION_IMMEDIATE) == 0) {
168 localtime_r(&message->posted, &tm);
169 strftime(message->posted_s, sizeof(message->posted_s),
170 "%I:%M %p, %m/%d/%Y", &tm);
171 }
172
d76ed9a9 173 if(messageList)
174 {
175 messageList->prev = message;
176 }
d76ed9a9 177 message->prev = NULL;
178 message->next = messageList;
179
180 messageList = message;
181
182 if(duration)
183 {
184 timeq_add(now + duration, message_expire, message);
185 }
186
187 return message;
188}
189
190static void
191message_del(struct globalMessage *message)
192{
193 if(message->duration)
194 {
195 timeq_del(0, NULL, message, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
196 }
197
198 if(message->prev) message->prev->next = message->next;
199 else messageList = message->next;
200
201 if(message->next) message->next->prev = message->prev;
202
203 free(message->from);
204 free(message->message);
205 free(message);
206}
207
208void message_expire(void *data)
209{
210 struct globalMessage *message = data;
211
212 message->duration = 0;
213 message_del(message);
214}
215
216static struct globalMessage*
217message_create(struct userNode *user, unsigned int argc, char *argv[])
218{
219 unsigned long duration = 0;
220 char *text = NULL;
dee9951d 221 char *sender;
d76ed9a9 222 long flags = 0;
223 unsigned int i;
224
dee9951d 225 sender = user->handle_info->handle;
226
d76ed9a9 227 for(i = 0; i < argc; i++)
228 {
229 if((i + 1) > argc)
230 {
231 global_notice(user, "MSG_MISSING_PARAMS", argv[argc]);
232 return NULL;
233 }
234
235 if(!irccasecmp(argv[i], "text"))
236 {
237 i++;
238 text = unsplit_string(argv + i, argc - i, NULL);
239 break;
240 } else if (!irccasecmp(argv[i], "sourceless")) {
241 i++;
242 flags |= MESSAGE_OPTION_SOURCELESS;
243 } else if (!irccasecmp(argv[i], "target")) {
244 i++;
245
246 if(!irccasecmp(argv[i], "all")) {
247 flags |= MESSAGE_RECIPIENT_ALL;
42c38d42 248 } else if(!irccasecmp(argv[i], "authed")) {
249 flags |= MESSAGE_RECIPIENT_AUTHED;
d76ed9a9 250 } else if(!irccasecmp(argv[i], "users")) {
251 flags |= MESSAGE_RECIPIENT_LUSERS;
252 } else if(!irccasecmp(argv[i], "helpers")) {
253 flags |= MESSAGE_RECIPIENT_HELPERS;
254 } else if(!irccasecmp(argv[i], "opers")) {
255 flags |= MESSAGE_RECIPIENT_OPERS;
256 } else if(!irccasecmp(argv[i], "staff") || !irccasecmp(argv[i], "privileged")) {
257 flags |= MESSAGE_RECIPIENT_STAFF;
258 } else if(!irccasecmp(argv[i], "channels")) {
259 flags |= MESSAGE_RECIPIENT_CHANNELS;
40c18c28 260 } else if(!irccasecmp(argv[i], "rchannels")) {
261 flags |= MESSAGE_RECIPIENT_RCHANNELS;
0f6fe38c 262 } else if(!irccasecmp(argv[i], "announcement") || !irccasecmp(argv[i], "announce")) {
263 flags |= MESSAGE_RECIPIENT_ANNOUNCE;
d76ed9a9 264 } else {
265 global_notice(user, "GMSG_INVALID_TARGET", argv[i]);
266 return NULL;
267 }
268 } else if (irccasecmp(argv[i], "duration") == 0) {
269 duration = ParseInterval(argv[++i]);
dee9951d 270 } else if (irccasecmp(argv[i], "from") == 0) {
271 sender = argv[++i];
d76ed9a9 272 } else {
273 global_notice(user, "MSG_INVALID_CRITERIA", argv[i]);
274 return NULL;
275 }
276 }
277
278 if(!flags)
279 {
280 flags = MESSAGE_RECIPIENT_LUSERS;
281 }
282
283 if(!text) {
284 global_notice(user, "GMSG_MESSAGE_REQUIRED");
285 return NULL;
286 }
287
dee9951d 288 return message_add(flags, now, duration, sender, text);
d76ed9a9 289}
290
291static const char *
292messageType(const struct globalMessage *message)
293{
294 if((message->flags & MESSAGE_RECIPIENT_ALL) == MESSAGE_RECIPIENT_ALL)
295 {
296 return "all";
297 }
298 if((message->flags & MESSAGE_RECIPIENT_STAFF) == MESSAGE_RECIPIENT_STAFF)
299 {
300 return "staff";
301 }
0f6fe38c 302 else if(message->flags & MESSAGE_RECIPIENT_ANNOUNCE)
303 {
304 return "announcement";
305 }
d76ed9a9 306 else if(message->flags & MESSAGE_RECIPIENT_OPERS)
307 {
308 return "opers";
309 }
310 else if(message->flags & MESSAGE_RECIPIENT_HELPERS)
311 {
312 return "helpers";
313 }
314 else if(message->flags & MESSAGE_RECIPIENT_LUSERS)
315 {
316 return "users";
317 }
42c38d42 318 else if(message->flags & MESSAGE_RECIPIENT_AUTHED)
319 {
320 return "authed";
321 }
40c18c28 322 else if(message->flags & MESSAGE_RECIPIENT_RCHANNELS)
323 {
324 return "rchannels";
325 }
d76ed9a9 326 else
327 {
328 return "channels";
329 }
330}
331
332static void
333notice_target(const char *target, struct globalMessage *message)
334{
335 if(!(message->flags & MESSAGE_OPTION_SOURCELESS))
336 {
337 if(message->flags & MESSAGE_OPTION_IMMEDIATE)
338 {
339 send_target_message(0, target, global, "GMSG_NOTICE_SOURCE", messageType(message), message->from);
340 }
341 else
342 {
dee9951d 343 send_target_message(0, target, global, "GMSG_MESSAGE_SOURCE", messageType(message), message->from, message->posted_s);
d76ed9a9 344 }
345 }
346
347 send_target_message(4, target, global, "%s", message->message);
348}
349
350static int
351notice_channel(const char *key, void *data, void *extra)
352{
353 struct chanNode *channel = data;
354 /* It should be safe to assume channel is not NULL. */
355 if(channel->channel_info)
356 notice_target(key, extra);
357 return 0;
358}
359
360static void
361message_send(struct globalMessage *message)
362{
363 struct userNode *user;
364 unsigned long n;
0f6fe38c 365 dict_iterator_t it;
d76ed9a9 366
367 if(message->flags & MESSAGE_RECIPIENT_CHANNELS)
368 {
40c18c28 369 dict_iterator_t it;
370
371 for (it = dict_first(channels); it; it = iter_next(it)) {
372 struct chanNode *chan = iter_data(it);
373
374 notice_target(chan->name, message);
375 }
376 }
377
378 if(message->flags & MESSAGE_RECIPIENT_RCHANNELS)
379 {
380 dict_iterator_t it;
381
382 for (it = dict_first(channels); it; it = iter_next(it)) {
383 struct chanNode *chan = iter_data(it);
384
385 if (chan->channel_info)
386 notice_target(chan->name, message);
387 }
d76ed9a9 388 }
389
390 if(message->flags & MESSAGE_RECIPIENT_LUSERS)
391 {
392 notice_target("$*", message);
393 return;
394 }
395
0f6fe38c 396 if(message->flags & MESSAGE_RECIPIENT_ANNOUNCE)
397 {
398 char announce;
399
400 for (it = dict_first(clients); it; it = iter_next(it)) {
401 user = iter_data(it);
402 if (user->uplink == self) continue;
403 announce = user->handle_info ? user->handle_info->announcements : '?';
404 if (announce == 'n') continue;
405 if ((announce == '?') && !global_conf.announcements_default) continue;
406 notice_target(user->nick, message);
407 }
408 }
409
d76ed9a9 410 if(message->flags & MESSAGE_RECIPIENT_OPERS)
411 {
412 for(n = 0; n < curr_opers.used; n++)
413 {
414 user = curr_opers.list[n];
415
416 if(user->uplink != self)
417 {
418 notice_target(user->nick, message);
419 }
420 }
421 }
422
423 if(message->flags & MESSAGE_RECIPIENT_HELPERS)
424 {
425 for(n = 0; n < curr_helpers.used; n++)
426 {
427 user = curr_helpers.list[n];
428 if (IsOper(user))
429 continue;
430 notice_target(user->nick, message);
431 }
432 }
42c38d42 433
434 if(message->flags & MESSAGE_RECIPIENT_AUTHED)
435 {
436 dict_iterator_t it;
437 for (it = dict_first(clients); it; it = iter_next(it)) {
438 struct userNode *luser = iter_data(it);
439 if (luser->handle_info)
440 notice_target(luser->nick, message);
441 }
442 }
d76ed9a9 443}
444
985d4109 445void
446global_message_args(long targets, const char *language_entry, ...)
447{
7827220c 448 struct globalMessage *message = NULL;
985d4109 449 va_list arg_list;
450 dict_iterator_t it;
451 char response[MAXLEN];
452 const char *fmt;
453
454 if(!targets || !global)
455 return;
456
457 fmt = strdup(language_entry);
458
459 /* Notice users/opers/helpers */
460 for (it = dict_first(clients); it; it = iter_next(it)) {
461 struct userNode *luser = iter_data(it);
462
463 language_entry = user_find_message(luser, fmt);
464
465 va_start(arg_list, language_entry);
466 vsnprintf(response, MAXLEN-2, language_entry, arg_list);
467 response[MAXLEN-1] = 0;
468
a2dbccef 469 if (message)
470 message_del(message);
471
985d4109 472 message = message_add(targets | MESSAGE_OPTION_SOURCELESS, now, 0, "", response);
473 if (!message)
474 continue;
475
476 /* opers */
477 if(message->flags & MESSAGE_RECIPIENT_OPERS && IsOper(luser)) {
478 if(luser->uplink != self)
479 notice_target(luser->nick, message);
4d99495c 480
42c38d42 481 if ((message->flags & MESSAGE_RECIPIENT_LUSERS) || (message->flags & MESSAGE_RECIPIENT_HELPERS) ||
482 (message->flags & MESSAGE_RECIPIENT_AUTHED))
4d99495c 483 continue;
985d4109 484 }
485
486 /* helpers */
487 if (message->flags & MESSAGE_RECIPIENT_HELPERS && IsHelper(luser)) {
985d4109 488 notice_target(luser->nick, message);
4d99495c 489
42c38d42 490 if ((message->flags & MESSAGE_RECIPIENT_LUSERS) || (message->flags & MESSAGE_RECIPIENT_AUTHED))
4d99495c 491 continue;
985d4109 492 }
493
42c38d42 494 /* authed */
495 if ((message->flags & MESSAGE_RECIPIENT_AUTHED) && luser->handle_info) {
496 notice_target(luser->nick, message);
497
498 if (message->flags & MESSAGE_RECIPIENT_LUSERS)
499 continue;
500 }
501
985d4109 502 /* users */
42c38d42 503 if (message->flags & MESSAGE_RECIPIENT_LUSERS) {
985d4109 504 notice_target(luser->nick, message);
42c38d42 505
506 }
985d4109 507 }
508
509 message_del(message);
510}
511
d76ed9a9 512void
513global_message(long targets, char *text)
514{
515 struct globalMessage *message;
516
517 if(!targets || !global)
518 return;
519
520 message = message_add(targets | MESSAGE_OPTION_SOURCELESS, now, 0, "", text);
521 if(!message)
522 return;
523
524 message_send(message);
525 message_del(message);
526}
527
528static GLOBAL_FUNC(cmd_notice)
529{
530 struct globalMessage *message = NULL;
531 const char *recipient = NULL, *text;
dee9951d 532 char *sender;
d76ed9a9 533 long target = 0;
534
535 assert(argc >= 3);
dee9951d 536 sender = user->handle_info->handle;
d76ed9a9 537 if(!irccasecmp(argv[1], "all")) {
538 target = MESSAGE_RECIPIENT_ALL;
539 } else if(!irccasecmp(argv[1], "users")) {
540 target = MESSAGE_RECIPIENT_LUSERS;
42c38d42 541 } else if(!irccasecmp(argv[1], "authed")) {
542 target = MESSAGE_RECIPIENT_AUTHED;
d76ed9a9 543 } else if(!irccasecmp(argv[1], "helpers")) {
544 target = MESSAGE_RECIPIENT_HELPERS;
545 } else if(!irccasecmp(argv[1], "opers")) {
546 target = MESSAGE_RECIPIENT_OPERS;
547 } else if(!irccasecmp(argv[1], "staff") || !irccasecmp(argv[1], "privileged")) {
548 target |= MESSAGE_RECIPIENT_HELPERS | MESSAGE_RECIPIENT_OPERS;
0f6fe38c 549 } else if(!irccasecmp(argv[1], "announcement") || !irccasecmp(argv[1], "announce")) {
550 target |= MESSAGE_RECIPIENT_ANNOUNCE;
d76ed9a9 551 } else if(!irccasecmp(argv[1], "channels")) {
552 target = MESSAGE_RECIPIENT_CHANNELS;
40c18c28 553 } else if(!irccasecmp(argv[1], "rchannels")) {
554 target = MESSAGE_RECIPIENT_RCHANNELS;
d76ed9a9 555 } else {
556 global_notice(user, "GMSG_INVALID_TARGET", argv[1]);
557 return 0;
558 }
dee9951d 559 if(!irccasecmp(argv[2], "from")) {
560 if (argc < 5) {
561 reply("MSG_MISSING_PARAMS", argv[0]);
562 GLOBAL_SYNTAX();
563 return 0;
564 }
565 sender = argv[3];
566 text = unsplit_string(argv + 4, argc - 4, NULL);
567 } else {
568 text = unsplit_string(argv + 2, argc - 2, NULL);
569 }
d76ed9a9 570
dee9951d 571 message = message_add(target | MESSAGE_OPTION_IMMEDIATE, now, 0, sender, text);
d76ed9a9 572 if(!message)
d76ed9a9 573 return 0;
d76ed9a9 574
575 recipient = messageType(message);
d76ed9a9 576 message_send(message);
577 message_del(message);
578
579 global_notice(user, "GMSG_MESSAGE_SENT", recipient);
580 return 1;
581}
582
583static GLOBAL_FUNC(cmd_message)
584{
585 struct globalMessage *message = NULL;
586 const char *recipient = NULL;
587
588 assert(argc >= 3);
589 message = message_create(user, argc - 1, argv + 1);
590 if(!message)
591 return 0;
592 recipient = messageType(message);
593 global_notice(user, "GMSG_MESSAGE_ADDED", recipient, message->id);
594 return 1;
595}
596
597static GLOBAL_FUNC(cmd_list)
598{
599 struct globalMessage *message;
600 struct helpfile_table table;
601 unsigned int length, nn;
602
603 if(!messageList)
604 {
605 global_notice(user, "GMSG_NO_MESSAGES");
606 return 1;
607 }
608
609 for(nn=0, message = messageList; message; nn++, message=message->next) ;
610 table.length = nn+1;
611 table.width = 5;
612 table.flags = TABLE_NO_FREE;
613 table.contents = calloc(table.length, sizeof(char**));
614 table.contents[0] = calloc(table.width, sizeof(char*));
615 table.contents[0][0] = "ID";
616 table.contents[0][1] = "Target";
617 table.contents[0][2] = "Expires";
618 table.contents[0][3] = "From";
619 table.contents[0][4] = "Message";
620
621 for(nn=1, message = messageList; message; nn++, message = message->next)
622 {
623 char buffer[64];
624
625 table.contents[nn] = calloc(table.width, sizeof(char*));
626 snprintf(buffer, sizeof(buffer), "%lu", message->id);
627 table.contents[nn][0] = strdup(buffer);
628 table.contents[nn][1] = messageType(message);
629 if(message->duration)
630 intervalString(buffer, message->posted + message->duration - now, user->handle_info);
631 else
632 strcpy(buffer, "Never.");
633 table.contents[nn][2] = strdup(buffer);
634 table.contents[nn][3] = message->from;
635 length = strlen(message->message);
636 safestrncpy(buffer, message->message, sizeof(buffer));
637 if(length > (sizeof(buffer) - 4))
638 {
639 buffer[sizeof(buffer) - 1] = 0;
640 buffer[sizeof(buffer) - 2] = buffer[sizeof(buffer) - 3] = buffer[sizeof(buffer) - 4] = '.';
641 }
642 table.contents[nn][4] = strdup(buffer);
643 }
644 table_send(global, user->nick, 0, NULL, table);
645 for (nn=1; nn<table.length; nn++)
646 {
647 free((char*)table.contents[nn][0]);
648 free((char*)table.contents[nn][2]);
649 free((char*)table.contents[nn][4]);
650 free(table.contents[nn]);
651 }
652 free(table.contents[0]);
653 free(table.contents);
654
655 return 1;
656}
657
658static GLOBAL_FUNC(cmd_remove)
659{
660 struct globalMessage *message = NULL;
661 unsigned long id;
662
663 assert(argc >= 2);
664 id = strtoul(argv[1], NULL, 0);
665
666 for(message = messageList; message; message = message->next)
667 {
668 if(message->id == id)
669 {
670 message_del(message);
671 global_notice(user, "GMSG_MESSAGE_DELETED", argv[1]);
672 return 1;
673 }
674 }
675
676 global_notice(user, "GMSG_ID_INVALID", argv[1]);
677 return 0;
678}
679
680static unsigned int
681send_messages(struct userNode *user, long mask, int obstreperize)
682{
683 struct globalMessage *message = messageList;
684 unsigned int count = 0;
685
686 while(message)
687 {
688 if(message->flags & mask)
689 {
690 if (obstreperize && !count)
1478fd13 691 {
d76ed9a9 692 send_target_message(0, user->nick, global, "GMSG_MOTD_HEADER");
1478fd13 693 send_target_message(0, user->nick, global, "GMSG_MOTD_BAR");
694 }
d76ed9a9 695 notice_target(user->nick, message);
696 count++;
697 }
698
699 message = message->next;
700 }
701 if (obstreperize && count)
702 send_target_message(0, user->nick, global, "GMSG_MOTD_FOOTER");
703 return count;
704}
705
706static GLOBAL_FUNC(cmd_messages)
707{
40c18c28 708 long mask = MESSAGE_RECIPIENT_AUTHED | MESSAGE_RECIPIENT_LUSERS | MESSAGE_RECIPIENT_CHANNELS | MESSAGE_RECIPIENT_RCHANNELS;
d76ed9a9 709 unsigned int count;
710
711 if(IsOper(user))
712 mask |= MESSAGE_RECIPIENT_OPERS;
713
714 if(IsHelper(user))
715 mask |= MESSAGE_RECIPIENT_HELPERS;
716
717 count = send_messages(user, mask, 0);
718 if(count)
719 global_notice(user, "GMSG_MESSAGE_COUNT", count);
720 else
721 global_notice(user, "GMSG_NO_MESSAGES");
722
723 return 1;
724}
725
726static int
727global_process_user(struct userNode *user)
728{
729 if(IsLocal(user) || self->uplink->burst || user->uplink->burst)
730 return 0;
731 send_messages(user, MESSAGE_RECIPIENT_LUSERS, 1);
732
733 /* only alert on new usercount if the record was broken in the last
734 * 30 seconds, and no alert had been sent in that time.
735 */
736 if((now - max_clients_time) <= 30 && (now - last_max_alert) > 30)
737 {
738 char *message;
739 message = alloca(36);
740 sprintf(message, "New user count record: %d", max_clients);
741 global_message(MESSAGE_RECIPIENT_OPERS, message);
742 last_max_alert = now;
743 }
744
745 return 0;
746}
747
748static void
749global_process_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
750{
751 if(IsHelper(user))
752 send_messages(user, MESSAGE_RECIPIENT_HELPERS, 0);
753}
754
755static void
756global_process_oper(struct userNode *user)
757{
758 if(user->uplink->burst)
759 return;
760 send_messages(user, MESSAGE_RECIPIENT_OPERS, 0);
761}
762
763static void
764global_conf_read(void)
765{
766 dict_t conf_node;
767 const char *str;
768
769 if (!(conf_node = conf_get_data(GLOBAL_CONF_NAME, RECDB_OBJECT))) {
770 log_module(G_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", GLOBAL_CONF_NAME);
771 return;
772 }
773
774 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
775 global_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
0f6fe38c 776 str = database_get_data(conf_node, KEY_ANNOUNCEMENTS_DEFAULT, RECDB_QSTRING);
777 global_conf.announcements_default = str ? enabled_string(str) : 1;
d76ed9a9 778
779 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
780 if(global && str)
781 NickChange(global, str, 0);
782}
783
784static int
785global_saxdb_read(struct dict *db)
786{
787 struct record_data *hir;
788 time_t posted;
789 long flags;
790 unsigned long duration;
791 char *str, *from, *message;
792 dict_iterator_t it;
793
794 for(it=dict_first(db); it; it=iter_next(it))
795 {
796 hir = iter_data(it);
797 if(hir->type != RECDB_OBJECT)
798 {
799 log_module(G_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
800 continue;
801 }
802
803 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
804 flags = str ? strtoul(str, NULL, 0) : 0;
805
806 str = database_get_data(hir->d.object, KEY_POSTED, RECDB_QSTRING);
807 posted = str ? strtoul(str, NULL, 0) : 0;
808
809 str = database_get_data(hir->d.object, KEY_DURATION, RECDB_QSTRING);
810 duration = str ? strtoul(str, NULL, 0) : 0;
811
812 from = database_get_data(hir->d.object, KEY_FROM, RECDB_QSTRING);
813 message = database_get_data(hir->d.object, KEY_MESSAGE, RECDB_QSTRING);
814
815 message_add(flags, posted, duration, from, message);
816 }
817 return 0;
818}
819
820static int
821global_saxdb_write(struct saxdb_context *ctx)
822{
823 struct globalMessage *message;
824 char str[16];
825
826 for(message = messageList; message; message = message->next) {
827 snprintf(str, sizeof(str), "%li", message->id);
828 saxdb_start_record(ctx, str, 0);
829 saxdb_write_int(ctx, KEY_FLAGS, message->flags);
830 saxdb_write_int(ctx, KEY_POSTED, message->posted);
831 saxdb_write_int(ctx, KEY_DURATION, message->duration);
832 saxdb_write_string(ctx, KEY_FROM, message->from);
833 saxdb_write_string(ctx, KEY_MESSAGE, message->message);
834 saxdb_end_record(ctx);
835 }
836 return 0;
837}
838
839static void
840global_db_cleanup(void)
841{
842 while(messageList)
843 message_del(messageList);
844}
845
846void
847init_global(const char *nick)
848{
7637f48f 849 struct chanNode *chan;
850 unsigned int i;
d76ed9a9 851 G_LOG = log_register_type("Global", "file:global.log");
852 reg_new_user_func(global_process_user);
853 reg_auth_func(global_process_auth);
854 reg_oper_func(global_process_oper);
855
856 conf_register_reload(global_conf_read);
857
858 global_module = module_register("Global", G_LOG, "global.help", NULL);
859 modcmd_register(global_module, "LIST", cmd_list, 1, 0, "flags", "+oper", NULL);
860 modcmd_register(global_module, "MESSAGE", cmd_message, 3, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
861 modcmd_register(global_module, "MESSAGES", cmd_messages, 1, 0, NULL);
862 modcmd_register(global_module, "NOTICE", cmd_notice, 3, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
863 modcmd_register(global_module, "REMOVE", cmd_remove, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
864
865 if(nick)
866 {
a32da4c7 867 const char *modes = conf_get_data("services/global/modes", RECDB_QSTRING);
0f6fe38c 868 global = AddService(nick, modes ? modes : NULL, "Global Services", NULL);
d76ed9a9 869 global_service = service_register(global);
870 }
7637f48f 871
872 if(autojoin_channels && global) {
873 for (i = 0; i < autojoin_channels->used; i++) {
874 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
875 AddChannelUser(global, chan)->modes |= MODE_CHANOP;
876 }
877 }
878
d76ed9a9 879 saxdb_register("Global", global_saxdb_read, global_saxdb_write);
880 reg_exit_func(global_db_cleanup);
881 message_register_table(msgtab);
882}