]> jfr.im git - irc/evilnet/x3.git/blame - src/global.c
add automode 6 - voice those with access
[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
8 * the Free Software Foundation; either version 2 of the License, or
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"
36#define KEY_ANNOUNCEMENTS_DEFAULT "announcements_default"
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." },
84
85 /* nickserv.c */
86 { "NSMSG_ACCOUNT_RENAMED", "%s renamed account %s to %s." },
87 { "NSMSG_ACCOUNT_MERGED", "%s (%s) merged account %s into %s." },
88
09a3057c 89 /* opserv.c */
90 { "DEFCON_NETWORK_CHANGED", "Network DefCon level has changed to level %d" },
91 { "DEFCON_OPER_LEVEL_CHANGE", "%s is changing the DefCon level to %d" },
92 { "DEFCON_TIMEOUT_LEVEL_CHANGE", "The DefCon has changed back to level %d (timeout)" },
57692f5e 93 { "OSMSG_CHANNEL_ACTIVITY_WARN", "Channel activity warning for channel %s: %s" },
94
95 /* spamserv.c */
96 { "SSMSG_CHANNEL_MERGED", "$X (channel %s) merged into %s by %s." },
97 { "SSMSG_CHANNEL_MOVED", "$X (channel %s) moved into %s by %s." },
98 { "SSMSG_UNREG_MANUAL", "$X (channel %s) %s by %s." },
99 { "SSMSG_REG_EXPIRED", "$X (channel %s) registration expired." },
100 { "SSMSG_LOST_ALL_USERS", "$X (channel %s) lost all users." },
101 { "SSMSG_REGISTERED_BY", "$X (channel %s) registered by %s." },
102 { "SSMSG_UNREGISTERED_BY", "$X (channel %s) unregistered by %s." },
985d4109 103
d76ed9a9 104 { NULL, NULL }
105};
106
b1bf690d 107#define GLOBAL_SYNTAX() svccmd_send_help_brief(user, global, cmd)
d76ed9a9 108#define GLOBAL_FUNC(NAME) MODCMD_FUNC(NAME)
109
dee9951d 110struct globalMessage
111{
112 unsigned long id;
113 long flags;
114
115 time_t posted;
116 char posted_s[24];
117 unsigned long duration;
118
119 char *from;
120 char *message;
121
122 struct globalMessage *prev;
123 struct globalMessage *next;
124};
125
d76ed9a9 126struct userNode *global;
127
128static struct module *global_module;
129static struct service *global_service;
130static struct globalMessage *messageList;
7637f48f 131extern struct string_list *autojoin_channels;
d76ed9a9 132static long messageCount;
133static time_t last_max_alert;
134static struct log_type *G_LOG;
135
136static struct
137{
138 unsigned long db_backup_frequency;
139 unsigned int announcements_default : 1;
140} global_conf;
141
142#define global_notice(target, format...) send_message(target , global , ## format)
143
144void message_expire(void *data);
145
146static struct globalMessage*
147message_add(long flags, time_t posted, unsigned long duration, char *from, const char *msg)
148{
149 struct globalMessage *message;
dee9951d 150 struct tm tm;
d76ed9a9 151
152 message = malloc(sizeof(struct globalMessage));
d76ed9a9 153 if(!message)
154 {
155 return NULL;
156 }
157
158 message->id = messageCount++;
159 message->flags = flags;
160 message->posted = posted;
161 message->duration = duration;
162 message->from = strdup(from);
163 message->message = strdup(msg);
164
dee9951d 165 if ((flags & MESSAGE_OPTION_IMMEDIATE) == 0) {
166 localtime_r(&message->posted, &tm);
167 strftime(message->posted_s, sizeof(message->posted_s),
168 "%I:%M %p, %m/%d/%Y", &tm);
169 }
170
d76ed9a9 171 if(messageList)
172 {
173 messageList->prev = message;
174 }
d76ed9a9 175 message->prev = NULL;
176 message->next = messageList;
177
178 messageList = message;
179
180 if(duration)
181 {
182 timeq_add(now + duration, message_expire, message);
183 }
184
185 return message;
186}
187
188static void
189message_del(struct globalMessage *message)
190{
191 if(message->duration)
192 {
193 timeq_del(0, NULL, message, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
194 }
195
196 if(message->prev) message->prev->next = message->next;
197 else messageList = message->next;
198
199 if(message->next) message->next->prev = message->prev;
200
201 free(message->from);
202 free(message->message);
203 free(message);
204}
205
206void message_expire(void *data)
207{
208 struct globalMessage *message = data;
209
210 message->duration = 0;
211 message_del(message);
212}
213
214static struct globalMessage*
215message_create(struct userNode *user, unsigned int argc, char *argv[])
216{
217 unsigned long duration = 0;
218 char *text = NULL;
dee9951d 219 char *sender;
d76ed9a9 220 long flags = 0;
221 unsigned int i;
222
dee9951d 223 sender = user->handle_info->handle;
224
d76ed9a9 225 for(i = 0; i < argc; i++)
226 {
227 if((i + 1) > argc)
228 {
229 global_notice(user, "MSG_MISSING_PARAMS", argv[argc]);
230 return NULL;
231 }
232
233 if(!irccasecmp(argv[i], "text"))
234 {
235 i++;
236 text = unsplit_string(argv + i, argc - i, NULL);
237 break;
238 } else if (!irccasecmp(argv[i], "sourceless")) {
239 i++;
240 flags |= MESSAGE_OPTION_SOURCELESS;
241 } else if (!irccasecmp(argv[i], "target")) {
242 i++;
243
244 if(!irccasecmp(argv[i], "all")) {
245 flags |= MESSAGE_RECIPIENT_ALL;
246 } else if(!irccasecmp(argv[i], "users")) {
247 flags |= MESSAGE_RECIPIENT_LUSERS;
248 } else if(!irccasecmp(argv[i], "helpers")) {
249 flags |= MESSAGE_RECIPIENT_HELPERS;
250 } else if(!irccasecmp(argv[i], "opers")) {
251 flags |= MESSAGE_RECIPIENT_OPERS;
252 } else if(!irccasecmp(argv[i], "staff") || !irccasecmp(argv[i], "privileged")) {
253 flags |= MESSAGE_RECIPIENT_STAFF;
254 } else if(!irccasecmp(argv[i], "channels")) {
255 flags |= MESSAGE_RECIPIENT_CHANNELS;
256 } else if(!irccasecmp(argv[i], "announcement") || !irccasecmp(argv[i], "announce")) {
257 flags |= MESSAGE_RECIPIENT_ANNOUNCE;
258 } else {
259 global_notice(user, "GMSG_INVALID_TARGET", argv[i]);
260 return NULL;
261 }
262 } else if (irccasecmp(argv[i], "duration") == 0) {
263 duration = ParseInterval(argv[++i]);
dee9951d 264 } else if (irccasecmp(argv[i], "from") == 0) {
265 sender = argv[++i];
d76ed9a9 266 } else {
267 global_notice(user, "MSG_INVALID_CRITERIA", argv[i]);
268 return NULL;
269 }
270 }
271
272 if(!flags)
273 {
274 flags = MESSAGE_RECIPIENT_LUSERS;
275 }
276
277 if(!text) {
278 global_notice(user, "GMSG_MESSAGE_REQUIRED");
279 return NULL;
280 }
281
dee9951d 282 return message_add(flags, now, duration, sender, text);
d76ed9a9 283}
284
285static const char *
286messageType(const struct globalMessage *message)
287{
288 if((message->flags & MESSAGE_RECIPIENT_ALL) == MESSAGE_RECIPIENT_ALL)
289 {
290 return "all";
291 }
292 if((message->flags & MESSAGE_RECIPIENT_STAFF) == MESSAGE_RECIPIENT_STAFF)
293 {
294 return "staff";
295 }
296 else if(message->flags & MESSAGE_RECIPIENT_ANNOUNCE)
297 {
298 return "announcement";
299 }
300 else if(message->flags & MESSAGE_RECIPIENT_OPERS)
301 {
302 return "opers";
303 }
304 else if(message->flags & MESSAGE_RECIPIENT_HELPERS)
305 {
306 return "helpers";
307 }
308 else if(message->flags & MESSAGE_RECIPIENT_LUSERS)
309 {
310 return "users";
311 }
312 else
313 {
314 return "channels";
315 }
316}
317
318static void
319notice_target(const char *target, struct globalMessage *message)
320{
321 if(!(message->flags & MESSAGE_OPTION_SOURCELESS))
322 {
323 if(message->flags & MESSAGE_OPTION_IMMEDIATE)
324 {
325 send_target_message(0, target, global, "GMSG_NOTICE_SOURCE", messageType(message), message->from);
326 }
327 else
328 {
dee9951d 329 send_target_message(0, target, global, "GMSG_MESSAGE_SOURCE", messageType(message), message->from, message->posted_s);
d76ed9a9 330 }
331 }
332
333 send_target_message(4, target, global, "%s", message->message);
334}
335
336static int
337notice_channel(const char *key, void *data, void *extra)
338{
339 struct chanNode *channel = data;
340 /* It should be safe to assume channel is not NULL. */
341 if(channel->channel_info)
342 notice_target(key, extra);
343 return 0;
344}
345
346static void
347message_send(struct globalMessage *message)
348{
349 struct userNode *user;
350 unsigned long n;
351 dict_iterator_t it;
352
353 if(message->flags & MESSAGE_RECIPIENT_CHANNELS)
354 {
355 dict_foreach(channels, notice_channel, message);
356 }
357
358 if(message->flags & MESSAGE_RECIPIENT_LUSERS)
359 {
360 notice_target("$*", message);
361 return;
362 }
363
364 if(message->flags & MESSAGE_RECIPIENT_ANNOUNCE)
365 {
366 char announce;
367
368 for (it = dict_first(clients); it; it = iter_next(it)) {
369 user = iter_data(it);
370 if (user->uplink == self) continue;
371 announce = user->handle_info ? user->handle_info->announcements : '?';
372 if (announce == 'n') continue;
373 if ((announce == '?') && !global_conf.announcements_default) continue;
374 notice_target(user->nick, message);
375 }
376 }
377
378 if(message->flags & MESSAGE_RECIPIENT_OPERS)
379 {
380 for(n = 0; n < curr_opers.used; n++)
381 {
382 user = curr_opers.list[n];
383
384 if(user->uplink != self)
385 {
386 notice_target(user->nick, message);
387 }
388 }
389 }
390
391 if(message->flags & MESSAGE_RECIPIENT_HELPERS)
392 {
393 for(n = 0; n < curr_helpers.used; n++)
394 {
395 user = curr_helpers.list[n];
396 if (IsOper(user))
397 continue;
398 notice_target(user->nick, message);
399 }
400 }
401}
402
985d4109 403void
404global_message_args(long targets, const char *language_entry, ...)
405{
7827220c 406 struct globalMessage *message = NULL;
985d4109 407 va_list arg_list;
408 dict_iterator_t it;
409 char response[MAXLEN];
410 const char *fmt;
411
412 if(!targets || !global)
413 return;
414
415 fmt = strdup(language_entry);
416
417 /* Notice users/opers/helpers */
418 for (it = dict_first(clients); it; it = iter_next(it)) {
419 struct userNode *luser = iter_data(it);
420
421 language_entry = user_find_message(luser, fmt);
422
423 va_start(arg_list, language_entry);
424 vsnprintf(response, MAXLEN-2, language_entry, arg_list);
425 response[MAXLEN-1] = 0;
426
427 message = message_add(targets | MESSAGE_OPTION_SOURCELESS, now, 0, "", response);
428 if (!message)
429 continue;
430
431 /* opers */
432 if(message->flags & MESSAGE_RECIPIENT_OPERS && IsOper(luser)) {
433 if(luser->uplink != self)
434 notice_target(luser->nick, message);
4d99495c 435
436 if ((message->flags & MESSAGE_RECIPIENT_LUSERS) || (message->flags & MESSAGE_RECIPIENT_HELPERS))
437 continue;
985d4109 438 }
439
440 /* helpers */
441 if (message->flags & MESSAGE_RECIPIENT_HELPERS && IsHelper(luser)) {
985d4109 442 notice_target(luser->nick, message);
4d99495c 443
444 if (message->flags & MESSAGE_RECIPIENT_LUSERS)
445 continue;
985d4109 446 }
447
448 /* users */
449 if (message->flags & MESSAGE_RECIPIENT_LUSERS)
450 notice_target(luser->nick, message);
451 }
452
453 message_del(message);
454}
455
d76ed9a9 456void
457global_message(long targets, char *text)
458{
459 struct globalMessage *message;
460
461 if(!targets || !global)
462 return;
463
464 message = message_add(targets | MESSAGE_OPTION_SOURCELESS, now, 0, "", text);
465 if(!message)
466 return;
467
468 message_send(message);
469 message_del(message);
470}
471
472static GLOBAL_FUNC(cmd_notice)
473{
474 struct globalMessage *message = NULL;
475 const char *recipient = NULL, *text;
dee9951d 476 char *sender;
d76ed9a9 477 long target = 0;
478
479 assert(argc >= 3);
dee9951d 480 sender = user->handle_info->handle;
d76ed9a9 481 if(!irccasecmp(argv[1], "all")) {
482 target = MESSAGE_RECIPIENT_ALL;
483 } else if(!irccasecmp(argv[1], "users")) {
484 target = MESSAGE_RECIPIENT_LUSERS;
485 } else if(!irccasecmp(argv[1], "helpers")) {
486 target = MESSAGE_RECIPIENT_HELPERS;
487 } else if(!irccasecmp(argv[1], "opers")) {
488 target = MESSAGE_RECIPIENT_OPERS;
489 } else if(!irccasecmp(argv[1], "staff") || !irccasecmp(argv[1], "privileged")) {
490 target |= MESSAGE_RECIPIENT_HELPERS | MESSAGE_RECIPIENT_OPERS;
491 } else if(!irccasecmp(argv[1], "announcement") || !irccasecmp(argv[1], "announce")) {
492 target |= MESSAGE_RECIPIENT_ANNOUNCE;
493 } else if(!irccasecmp(argv[1], "channels")) {
494 target = MESSAGE_RECIPIENT_CHANNELS;
495 } else {
496 global_notice(user, "GMSG_INVALID_TARGET", argv[1]);
497 return 0;
498 }
dee9951d 499 if(!irccasecmp(argv[2], "from")) {
500 if (argc < 5) {
501 reply("MSG_MISSING_PARAMS", argv[0]);
502 GLOBAL_SYNTAX();
503 return 0;
504 }
505 sender = argv[3];
506 text = unsplit_string(argv + 4, argc - 4, NULL);
507 } else {
508 text = unsplit_string(argv + 2, argc - 2, NULL);
509 }
d76ed9a9 510
dee9951d 511 message = message_add(target | MESSAGE_OPTION_IMMEDIATE, now, 0, sender, text);
d76ed9a9 512 if(!message)
d76ed9a9 513 return 0;
d76ed9a9 514
515 recipient = messageType(message);
d76ed9a9 516 message_send(message);
517 message_del(message);
518
519 global_notice(user, "GMSG_MESSAGE_SENT", recipient);
520 return 1;
521}
522
523static GLOBAL_FUNC(cmd_message)
524{
525 struct globalMessage *message = NULL;
526 const char *recipient = NULL;
527
528 assert(argc >= 3);
529 message = message_create(user, argc - 1, argv + 1);
530 if(!message)
531 return 0;
532 recipient = messageType(message);
533 global_notice(user, "GMSG_MESSAGE_ADDED", recipient, message->id);
534 return 1;
535}
536
537static GLOBAL_FUNC(cmd_list)
538{
539 struct globalMessage *message;
540 struct helpfile_table table;
541 unsigned int length, nn;
542
543 if(!messageList)
544 {
545 global_notice(user, "GMSG_NO_MESSAGES");
546 return 1;
547 }
548
549 for(nn=0, message = messageList; message; nn++, message=message->next) ;
550 table.length = nn+1;
551 table.width = 5;
552 table.flags = TABLE_NO_FREE;
553 table.contents = calloc(table.length, sizeof(char**));
554 table.contents[0] = calloc(table.width, sizeof(char*));
555 table.contents[0][0] = "ID";
556 table.contents[0][1] = "Target";
557 table.contents[0][2] = "Expires";
558 table.contents[0][3] = "From";
559 table.contents[0][4] = "Message";
560
561 for(nn=1, message = messageList; message; nn++, message = message->next)
562 {
563 char buffer[64];
564
565 table.contents[nn] = calloc(table.width, sizeof(char*));
566 snprintf(buffer, sizeof(buffer), "%lu", message->id);
567 table.contents[nn][0] = strdup(buffer);
568 table.contents[nn][1] = messageType(message);
569 if(message->duration)
570 intervalString(buffer, message->posted + message->duration - now, user->handle_info);
571 else
572 strcpy(buffer, "Never.");
573 table.contents[nn][2] = strdup(buffer);
574 table.contents[nn][3] = message->from;
575 length = strlen(message->message);
576 safestrncpy(buffer, message->message, sizeof(buffer));
577 if(length > (sizeof(buffer) - 4))
578 {
579 buffer[sizeof(buffer) - 1] = 0;
580 buffer[sizeof(buffer) - 2] = buffer[sizeof(buffer) - 3] = buffer[sizeof(buffer) - 4] = '.';
581 }
582 table.contents[nn][4] = strdup(buffer);
583 }
584 table_send(global, user->nick, 0, NULL, table);
585 for (nn=1; nn<table.length; nn++)
586 {
587 free((char*)table.contents[nn][0]);
588 free((char*)table.contents[nn][2]);
589 free((char*)table.contents[nn][4]);
590 free(table.contents[nn]);
591 }
592 free(table.contents[0]);
593 free(table.contents);
594
595 return 1;
596}
597
598static GLOBAL_FUNC(cmd_remove)
599{
600 struct globalMessage *message = NULL;
601 unsigned long id;
602
603 assert(argc >= 2);
604 id = strtoul(argv[1], NULL, 0);
605
606 for(message = messageList; message; message = message->next)
607 {
608 if(message->id == id)
609 {
610 message_del(message);
611 global_notice(user, "GMSG_MESSAGE_DELETED", argv[1]);
612 return 1;
613 }
614 }
615
616 global_notice(user, "GMSG_ID_INVALID", argv[1]);
617 return 0;
618}
619
620static unsigned int
621send_messages(struct userNode *user, long mask, int obstreperize)
622{
623 struct globalMessage *message = messageList;
624 unsigned int count = 0;
625
626 while(message)
627 {
628 if(message->flags & mask)
629 {
630 if (obstreperize && !count)
1478fd13 631 {
d76ed9a9 632 send_target_message(0, user->nick, global, "GMSG_MOTD_HEADER");
1478fd13 633 send_target_message(0, user->nick, global, "GMSG_MOTD_BAR");
634 }
d76ed9a9 635 notice_target(user->nick, message);
636 count++;
637 }
638
639 message = message->next;
640 }
641 if (obstreperize && count)
642 send_target_message(0, user->nick, global, "GMSG_MOTD_FOOTER");
643 return count;
644}
645
646static GLOBAL_FUNC(cmd_messages)
647{
648 long mask = MESSAGE_RECIPIENT_LUSERS | MESSAGE_RECIPIENT_CHANNELS;
649 unsigned int count;
650
651 if(IsOper(user))
652 mask |= MESSAGE_RECIPIENT_OPERS;
653
654 if(IsHelper(user))
655 mask |= MESSAGE_RECIPIENT_HELPERS;
656
657 count = send_messages(user, mask, 0);
658 if(count)
659 global_notice(user, "GMSG_MESSAGE_COUNT", count);
660 else
661 global_notice(user, "GMSG_NO_MESSAGES");
662
663 return 1;
664}
665
666static int
667global_process_user(struct userNode *user)
668{
669 if(IsLocal(user) || self->uplink->burst || user->uplink->burst)
670 return 0;
671 send_messages(user, MESSAGE_RECIPIENT_LUSERS, 1);
672
673 /* only alert on new usercount if the record was broken in the last
674 * 30 seconds, and no alert had been sent in that time.
675 */
676 if((now - max_clients_time) <= 30 && (now - last_max_alert) > 30)
677 {
678 char *message;
679 message = alloca(36);
680 sprintf(message, "New user count record: %d", max_clients);
681 global_message(MESSAGE_RECIPIENT_OPERS, message);
682 last_max_alert = now;
683 }
684
685 return 0;
686}
687
688static void
689global_process_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
690{
691 if(IsHelper(user))
692 send_messages(user, MESSAGE_RECIPIENT_HELPERS, 0);
693}
694
695static void
696global_process_oper(struct userNode *user)
697{
698 if(user->uplink->burst)
699 return;
700 send_messages(user, MESSAGE_RECIPIENT_OPERS, 0);
701}
702
703static void
704global_conf_read(void)
705{
706 dict_t conf_node;
707 const char *str;
708
709 if (!(conf_node = conf_get_data(GLOBAL_CONF_NAME, RECDB_OBJECT))) {
710 log_module(G_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", GLOBAL_CONF_NAME);
711 return;
712 }
713
714 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
715 global_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
716 str = database_get_data(conf_node, KEY_ANNOUNCEMENTS_DEFAULT, RECDB_QSTRING);
717 global_conf.announcements_default = str ? enabled_string(str) : 1;
718
719 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
720 if(global && str)
721 NickChange(global, str, 0);
722}
723
724static int
725global_saxdb_read(struct dict *db)
726{
727 struct record_data *hir;
728 time_t posted;
729 long flags;
730 unsigned long duration;
731 char *str, *from, *message;
732 dict_iterator_t it;
733
734 for(it=dict_first(db); it; it=iter_next(it))
735 {
736 hir = iter_data(it);
737 if(hir->type != RECDB_OBJECT)
738 {
739 log_module(G_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
740 continue;
741 }
742
743 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
744 flags = str ? strtoul(str, NULL, 0) : 0;
745
746 str = database_get_data(hir->d.object, KEY_POSTED, RECDB_QSTRING);
747 posted = str ? strtoul(str, NULL, 0) : 0;
748
749 str = database_get_data(hir->d.object, KEY_DURATION, RECDB_QSTRING);
750 duration = str ? strtoul(str, NULL, 0) : 0;
751
752 from = database_get_data(hir->d.object, KEY_FROM, RECDB_QSTRING);
753 message = database_get_data(hir->d.object, KEY_MESSAGE, RECDB_QSTRING);
754
755 message_add(flags, posted, duration, from, message);
756 }
757 return 0;
758}
759
760static int
761global_saxdb_write(struct saxdb_context *ctx)
762{
763 struct globalMessage *message;
764 char str[16];
765
766 for(message = messageList; message; message = message->next) {
767 snprintf(str, sizeof(str), "%li", message->id);
768 saxdb_start_record(ctx, str, 0);
769 saxdb_write_int(ctx, KEY_FLAGS, message->flags);
770 saxdb_write_int(ctx, KEY_POSTED, message->posted);
771 saxdb_write_int(ctx, KEY_DURATION, message->duration);
772 saxdb_write_string(ctx, KEY_FROM, message->from);
773 saxdb_write_string(ctx, KEY_MESSAGE, message->message);
774 saxdb_end_record(ctx);
775 }
776 return 0;
777}
778
779static void
780global_db_cleanup(void)
781{
782 while(messageList)
783 message_del(messageList);
784}
785
786void
787init_global(const char *nick)
788{
7637f48f 789 struct chanNode *chan;
790 unsigned int i;
d76ed9a9 791 G_LOG = log_register_type("Global", "file:global.log");
792 reg_new_user_func(global_process_user);
793 reg_auth_func(global_process_auth);
794 reg_oper_func(global_process_oper);
795
796 conf_register_reload(global_conf_read);
797
798 global_module = module_register("Global", G_LOG, "global.help", NULL);
799 modcmd_register(global_module, "LIST", cmd_list, 1, 0, "flags", "+oper", NULL);
800 modcmd_register(global_module, "MESSAGE", cmd_message, 3, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
801 modcmd_register(global_module, "MESSAGES", cmd_messages, 1, 0, NULL);
802 modcmd_register(global_module, "NOTICE", cmd_notice, 3, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
803 modcmd_register(global_module, "REMOVE", cmd_remove, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
804
805 if(nick)
806 {
a32da4c7 807 const char *modes = conf_get_data("services/global/modes", RECDB_QSTRING);
808 global = AddService(nick, modes ? modes : NULL, "Global Services", NULL);
d76ed9a9 809 global_service = service_register(global);
810 }
7637f48f 811
812 if(autojoin_channels && global) {
813 for (i = 0; i < autojoin_channels->used; i++) {
814 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
815 AddChannelUser(global, chan)->modes |= MODE_CHANOP;
816 }
817 }
818
d76ed9a9 819 saxdb_register("Global", global_saxdb_read, global_saxdb_write);
820 reg_exit_func(global_db_cleanup);
821 message_register_table(msgtab);
822}