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