]> jfr.im git - irc/evilnet/x3.git/blob - src/global.c
Merged in srvx 1.4-RC1 changes. DNSBL parts are missing as it hasnt even been impleme...
[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 3 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_NICK "nick"
37
38 /* Message data */
39 #define KEY_FLAGS "flags"
40 #define KEY_POSTED "posted"
41 #define KEY_DURATION "duration"
42 #define KEY_FROM "from"
43 #define KEY_MESSAGE "message"
44
45 /* Clarification: Notices are immediate, they are sent to matching users
46 _once_, then forgotten. Messages are stored in Global's database and
47 continually sent to users as they match the target specification until
48 they are deleted. */
49 static const struct message_entry msgtab[] = {
50 { "GMSG_INVALID_TARGET", "$b%s$b is an invalid message target." },
51 { "GMSG_MESSAGE_REQUIRED", "You $bmust$b provide a message to send." },
52 { "GMSG_MESSAGE_SENT", "Message to $b%s$b sent." },
53 { "GMSG_MESSAGE_ADDED", "Message to $b%s$b with ID %ld added." },
54 { "GMSG_MESSAGE_DELETED", "Message $b%s$b deleted." },
55 { "GMSG_ID_INVALID", "$b%s$b is an invalid message ID." },
56 { "GMSG_MESSAGE_COUNT", "$b%d$b messages found." },
57 { "GMSG_NO_MESSAGES", "There are no messages for you." },
58 { "GMSG_NOTICE_SOURCE", "Notice to [$b%s$b] from %s:" },
59 { "GMSG_MESSAGE_SOURCE", "Notice to [$b%s$b] from %s, posted %s:" },
60 //{ "GMSG_MOTD_HEADER", "$b------------- MESSAGE(S) OF THE DAY --------------$b" },
61 { "GMSG_MOTD_HEADER", "$bNetwork Announcements$b" },
62 { "GMSG_MOTD_BAR", "---------------------------------------" },
63 { "GMSG_MOTD_FOOTER", "--------------- Thank You--------------" },
64
65 /* These definitions are for other files that make use of global
66 * notices. Make sure you grep for them if you ever add args
67 * to the notice.
68 */
69 /* chanserv.c */
70 { "CSMSG_REGISTERED_TO", "%s registered to %s by %s." },
71 { "CSMSG_CHANNEL_MOVED", "%s moved to %s by %s." },
72 { "CSMSG_SUSPENSION_MODIFIED", "%s suspension modified by %s." },
73 { "CSMSG_SUSPENDED_BY", "%s suspended by %s." },
74 { "CSMSG_UNSUSPENDED_BY", "%s unsuspended by %s." },
75 { "CSMSG_OWNERSHIP_TRANSFERRED", "%s ownership transferred to %s by %s." },
76
77 /* mod-helpserv.c */
78 { "HSMSG_BOT_RENAMED", "HelpServ bot %s (in %s) renamed to %s by %s." },
79 { "HSMSG_BOT_MOVED", "HelpServ %s (%s) moved to %s by %s." },
80 { "HSMSG_BOT_REGISTERED", "HelpServ %s (%s) registered to %s by %s." },
81 { "HSMSG_BOT_EXPIRED", "HelpServ %s (%s) expired at request of %s." },
82 { "HSMSG_BOT_UNREGISTERED", "HelpServ %s (%s) unregistered by %s." },
83 { "HSMSG_SUSPENDED_BY", "%s suspended by %s. (HelpServ)" },
84 { "HSMSG_UNSUSPENDED_BY", "%s unsuspended by %s. (HelpServ)" },
85
86 /* nickserv.c */
87 { "NSMSG_ACCOUNT_RENAMED", "%s renamed account %s to %s." },
88 { "NSMSG_ACCOUNT_MERGED", "%s (%s) merged account %s into %s." },
89
90 /* opserv.c */
91 { "DEFCON_NETWORK_CHANGED", "Network DefCon level has changed to level %d" },
92 { "DEFCON_OPER_LEVEL_CHANGE", "%s is changing the DefCon level to %d" },
93 { "DEFCON_TIMEOUT_LEVEL_CHANGE", "The DefCon has changed back to level %d (timeout)" },
94 { "OSMSG_CHANNEL_ACTIVITY_WARN", "Channel activity warning for channel %s: %s" },
95
96 /* spamserv.c */
97 { "SSMSG_CHANNEL_MERGED", "$X (channel %s) merged into %s by %s." },
98 { "SSMSG_CHANNEL_MOVED", "$X (channel %s) moved into %s by %s." },
99 { "SSMSG_UNREG_MANUAL", "$X (channel %s) %s by %s." },
100 { "SSMSG_REG_EXPIRED", "$X (channel %s) registration expired." },
101 { "SSMSG_LOST_ALL_USERS", "$X (channel %s) lost all users." },
102 { "SSMSG_REGISTERED_BY", "$X (channel %s) registered by %s." },
103 { "SSMSG_UNREGISTERED_BY", "$X (channel %s) unregistered by %s." },
104
105 { NULL, NULL }
106 };
107
108 #define GLOBAL_SYNTAX() svccmd_send_help_brief(user, global, cmd)
109 #define GLOBAL_FUNC(NAME) MODCMD_FUNC(NAME)
110
111 struct globalMessage
112 {
113 unsigned long id;
114 long flags;
115
116 time_t posted;
117 char posted_s[24];
118 unsigned long duration;
119
120 char *from;
121 char *message;
122
123 struct globalMessage *prev;
124 struct globalMessage *next;
125 };
126
127 struct userNode *global;
128
129 static struct module *global_module;
130 static struct service *global_service;
131 static struct globalMessage *messageList;
132 extern struct string_list *autojoin_channels;
133 static long messageCount;
134 static time_t last_max_alert;
135 static struct log_type *G_LOG;
136
137 static struct
138 {
139 unsigned long db_backup_frequency;
140 } global_conf;
141
142 #define global_notice(target, format...) send_message(target , global , ## format)
143
144 void message_expire(void *data);
145
146 static struct globalMessage*
147 message_add(long flags, time_t posted, unsigned long duration, char *from, const char *msg)
148 {
149 struct globalMessage *message;
150 struct tm tm;
151
152 message = malloc(sizeof(struct globalMessage));
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
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
171 if(messageList)
172 {
173 messageList->prev = message;
174 }
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
188 static void
189 message_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
206 void message_expire(void *data)
207 {
208 struct globalMessage *message = data;
209
210 message->duration = 0;
211 message_del(message);
212 }
213
214 static struct globalMessage*
215 message_create(struct userNode *user, unsigned int argc, char *argv[])
216 {
217 unsigned long duration = 0;
218 char *text = NULL;
219 char *sender;
220 long flags = 0;
221 unsigned int i;
222
223 sender = user->handle_info->handle;
224
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 {
257 global_notice(user, "GMSG_INVALID_TARGET", argv[i]);
258 return NULL;
259 }
260 } else if (irccasecmp(argv[i], "duration") == 0) {
261 duration = ParseInterval(argv[++i]);
262 } else if (irccasecmp(argv[i], "from") == 0) {
263 sender = argv[++i];
264 } else {
265 global_notice(user, "MSG_INVALID_CRITERIA", argv[i]);
266 return NULL;
267 }
268 }
269
270 if(!flags)
271 {
272 flags = MESSAGE_RECIPIENT_LUSERS;
273 }
274
275 if(!text) {
276 global_notice(user, "GMSG_MESSAGE_REQUIRED");
277 return NULL;
278 }
279
280 return message_add(flags, now, duration, sender, text);
281 }
282
283 static const char *
284 messageType(const struct globalMessage *message)
285 {
286 if((message->flags & MESSAGE_RECIPIENT_ALL) == MESSAGE_RECIPIENT_ALL)
287 {
288 return "all";
289 }
290 if((message->flags & MESSAGE_RECIPIENT_STAFF) == MESSAGE_RECIPIENT_STAFF)
291 {
292 return "staff";
293 }
294 else if(message->flags & MESSAGE_RECIPIENT_OPERS)
295 {
296 return "opers";
297 }
298 else if(message->flags & MESSAGE_RECIPIENT_HELPERS)
299 {
300 return "helpers";
301 }
302 else if(message->flags & MESSAGE_RECIPIENT_LUSERS)
303 {
304 return "users";
305 }
306 else
307 {
308 return "channels";
309 }
310 }
311
312 static void
313 notice_target(const char *target, struct globalMessage *message)
314 {
315 if(!(message->flags & MESSAGE_OPTION_SOURCELESS))
316 {
317 if(message->flags & MESSAGE_OPTION_IMMEDIATE)
318 {
319 send_target_message(0, target, global, "GMSG_NOTICE_SOURCE", messageType(message), message->from);
320 }
321 else
322 {
323 send_target_message(0, target, global, "GMSG_MESSAGE_SOURCE", messageType(message), message->from, message->posted_s);
324 }
325 }
326
327 send_target_message(4, target, global, "%s", message->message);
328 }
329
330 static int
331 notice_channel(const char *key, void *data, void *extra)
332 {
333 struct chanNode *channel = data;
334 /* It should be safe to assume channel is not NULL. */
335 if(channel->channel_info)
336 notice_target(key, extra);
337 return 0;
338 }
339
340 static void
341 message_send(struct globalMessage *message)
342 {
343 struct userNode *user;
344 unsigned long n;
345
346 if(message->flags & MESSAGE_RECIPIENT_CHANNELS)
347 {
348 dict_foreach(channels, notice_channel, message);
349 }
350
351 if(message->flags & MESSAGE_RECIPIENT_LUSERS)
352 {
353 notice_target("$*", message);
354 return;
355 }
356
357 if(message->flags & MESSAGE_RECIPIENT_OPERS)
358 {
359 for(n = 0; n < curr_opers.used; n++)
360 {
361 user = curr_opers.list[n];
362
363 if(user->uplink != self)
364 {
365 notice_target(user->nick, message);
366 }
367 }
368 }
369
370 if(message->flags & MESSAGE_RECIPIENT_HELPERS)
371 {
372 for(n = 0; n < curr_helpers.used; n++)
373 {
374 user = curr_helpers.list[n];
375 if (IsOper(user))
376 continue;
377 notice_target(user->nick, message);
378 }
379 }
380 }
381
382 void
383 global_message_args(long targets, const char *language_entry, ...)
384 {
385 struct globalMessage *message = NULL;
386 va_list arg_list;
387 dict_iterator_t it;
388 char response[MAXLEN];
389 const char *fmt;
390
391 if(!targets || !global)
392 return;
393
394 fmt = strdup(language_entry);
395
396 /* Notice users/opers/helpers */
397 for (it = dict_first(clients); it; it = iter_next(it)) {
398 struct userNode *luser = iter_data(it);
399
400 language_entry = user_find_message(luser, fmt);
401
402 va_start(arg_list, language_entry);
403 vsnprintf(response, MAXLEN-2, language_entry, arg_list);
404 response[MAXLEN-1] = 0;
405
406 if (message)
407 message_del(message);
408
409 message = message_add(targets | MESSAGE_OPTION_SOURCELESS, now, 0, "", response);
410 if (!message)
411 continue;
412
413 /* opers */
414 if(message->flags & MESSAGE_RECIPIENT_OPERS && IsOper(luser)) {
415 if(luser->uplink != self)
416 notice_target(luser->nick, message);
417
418 if ((message->flags & MESSAGE_RECIPIENT_LUSERS) || (message->flags & MESSAGE_RECIPIENT_HELPERS))
419 continue;
420 }
421
422 /* helpers */
423 if (message->flags & MESSAGE_RECIPIENT_HELPERS && IsHelper(luser)) {
424 notice_target(luser->nick, message);
425
426 if (message->flags & MESSAGE_RECIPIENT_LUSERS)
427 continue;
428 }
429
430 /* users */
431 if (message->flags & MESSAGE_RECIPIENT_LUSERS)
432 notice_target(luser->nick, message);
433 }
434
435 message_del(message);
436 }
437
438 void
439 global_message(long targets, char *text)
440 {
441 struct globalMessage *message;
442
443 if(!targets || !global)
444 return;
445
446 message = message_add(targets | MESSAGE_OPTION_SOURCELESS, now, 0, "", text);
447 if(!message)
448 return;
449
450 message_send(message);
451 message_del(message);
452 }
453
454 static GLOBAL_FUNC(cmd_notice)
455 {
456 struct globalMessage *message = NULL;
457 const char *recipient = NULL, *text;
458 char *sender;
459 long target = 0;
460
461 assert(argc >= 3);
462 sender = user->handle_info->handle;
463 if(!irccasecmp(argv[1], "all")) {
464 target = MESSAGE_RECIPIENT_ALL;
465 } else if(!irccasecmp(argv[1], "users")) {
466 target = MESSAGE_RECIPIENT_LUSERS;
467 } else if(!irccasecmp(argv[1], "helpers")) {
468 target = MESSAGE_RECIPIENT_HELPERS;
469 } else if(!irccasecmp(argv[1], "opers")) {
470 target = MESSAGE_RECIPIENT_OPERS;
471 } else if(!irccasecmp(argv[1], "staff") || !irccasecmp(argv[1], "privileged")) {
472 target |= MESSAGE_RECIPIENT_HELPERS | MESSAGE_RECIPIENT_OPERS;
473 } else if(!irccasecmp(argv[1], "channels")) {
474 target = MESSAGE_RECIPIENT_CHANNELS;
475 } else {
476 global_notice(user, "GMSG_INVALID_TARGET", argv[1]);
477 return 0;
478 }
479 if(!irccasecmp(argv[2], "from")) {
480 if (argc < 5) {
481 reply("MSG_MISSING_PARAMS", argv[0]);
482 GLOBAL_SYNTAX();
483 return 0;
484 }
485 sender = argv[3];
486 text = unsplit_string(argv + 4, argc - 4, NULL);
487 } else {
488 text = unsplit_string(argv + 2, argc - 2, NULL);
489 }
490
491 message = message_add(target | MESSAGE_OPTION_IMMEDIATE, now, 0, sender, text);
492 if(!message)
493 return 0;
494
495 recipient = messageType(message);
496 message_send(message);
497 message_del(message);
498
499 global_notice(user, "GMSG_MESSAGE_SENT", recipient);
500 return 1;
501 }
502
503 static GLOBAL_FUNC(cmd_message)
504 {
505 struct globalMessage *message = NULL;
506 const char *recipient = NULL;
507
508 assert(argc >= 3);
509 message = message_create(user, argc - 1, argv + 1);
510 if(!message)
511 return 0;
512 recipient = messageType(message);
513 global_notice(user, "GMSG_MESSAGE_ADDED", recipient, message->id);
514 return 1;
515 }
516
517 static GLOBAL_FUNC(cmd_list)
518 {
519 struct globalMessage *message;
520 struct helpfile_table table;
521 unsigned int length, nn;
522
523 if(!messageList)
524 {
525 global_notice(user, "GMSG_NO_MESSAGES");
526 return 1;
527 }
528
529 for(nn=0, message = messageList; message; nn++, message=message->next) ;
530 table.length = nn+1;
531 table.width = 5;
532 table.flags = TABLE_NO_FREE;
533 table.contents = calloc(table.length, sizeof(char**));
534 table.contents[0] = calloc(table.width, sizeof(char*));
535 table.contents[0][0] = "ID";
536 table.contents[0][1] = "Target";
537 table.contents[0][2] = "Expires";
538 table.contents[0][3] = "From";
539 table.contents[0][4] = "Message";
540
541 for(nn=1, message = messageList; message; nn++, message = message->next)
542 {
543 char buffer[64];
544
545 table.contents[nn] = calloc(table.width, sizeof(char*));
546 snprintf(buffer, sizeof(buffer), "%lu", message->id);
547 table.contents[nn][0] = strdup(buffer);
548 table.contents[nn][1] = messageType(message);
549 if(message->duration)
550 intervalString(buffer, message->posted + message->duration - now, user->handle_info);
551 else
552 strcpy(buffer, "Never.");
553 table.contents[nn][2] = strdup(buffer);
554 table.contents[nn][3] = message->from;
555 length = strlen(message->message);
556 safestrncpy(buffer, message->message, sizeof(buffer));
557 if(length > (sizeof(buffer) - 4))
558 {
559 buffer[sizeof(buffer) - 1] = 0;
560 buffer[sizeof(buffer) - 2] = buffer[sizeof(buffer) - 3] = buffer[sizeof(buffer) - 4] = '.';
561 }
562 table.contents[nn][4] = strdup(buffer);
563 }
564 table_send(global, user->nick, 0, NULL, table);
565 for (nn=1; nn<table.length; nn++)
566 {
567 free((char*)table.contents[nn][0]);
568 free((char*)table.contents[nn][2]);
569 free((char*)table.contents[nn][4]);
570 free(table.contents[nn]);
571 }
572 free(table.contents[0]);
573 free(table.contents);
574
575 return 1;
576 }
577
578 static GLOBAL_FUNC(cmd_remove)
579 {
580 struct globalMessage *message = NULL;
581 unsigned long id;
582
583 assert(argc >= 2);
584 id = strtoul(argv[1], NULL, 0);
585
586 for(message = messageList; message; message = message->next)
587 {
588 if(message->id == id)
589 {
590 message_del(message);
591 global_notice(user, "GMSG_MESSAGE_DELETED", argv[1]);
592 return 1;
593 }
594 }
595
596 global_notice(user, "GMSG_ID_INVALID", argv[1]);
597 return 0;
598 }
599
600 static unsigned int
601 send_messages(struct userNode *user, long mask, int obstreperize)
602 {
603 struct globalMessage *message = messageList;
604 unsigned int count = 0;
605
606 while(message)
607 {
608 if(message->flags & mask)
609 {
610 if (obstreperize && !count)
611 {
612 send_target_message(0, user->nick, global, "GMSG_MOTD_HEADER");
613 send_target_message(0, user->nick, global, "GMSG_MOTD_BAR");
614 }
615 notice_target(user->nick, message);
616 count++;
617 }
618
619 message = message->next;
620 }
621 if (obstreperize && count)
622 send_target_message(0, user->nick, global, "GMSG_MOTD_FOOTER");
623 return count;
624 }
625
626 static GLOBAL_FUNC(cmd_messages)
627 {
628 long mask = MESSAGE_RECIPIENT_LUSERS | MESSAGE_RECIPIENT_CHANNELS;
629 unsigned int count;
630
631 if(IsOper(user))
632 mask |= MESSAGE_RECIPIENT_OPERS;
633
634 if(IsHelper(user))
635 mask |= MESSAGE_RECIPIENT_HELPERS;
636
637 count = send_messages(user, mask, 0);
638 if(count)
639 global_notice(user, "GMSG_MESSAGE_COUNT", count);
640 else
641 global_notice(user, "GMSG_NO_MESSAGES");
642
643 return 1;
644 }
645
646 static int
647 global_process_user(struct userNode *user)
648 {
649 if(IsLocal(user) || self->uplink->burst || user->uplink->burst)
650 return 0;
651 send_messages(user, MESSAGE_RECIPIENT_LUSERS, 1);
652
653 /* only alert on new usercount if the record was broken in the last
654 * 30 seconds, and no alert had been sent in that time.
655 */
656 if((now - max_clients_time) <= 30 && (now - last_max_alert) > 30)
657 {
658 char *message;
659 message = alloca(36);
660 sprintf(message, "New user count record: %d", max_clients);
661 global_message(MESSAGE_RECIPIENT_OPERS, message);
662 last_max_alert = now;
663 }
664
665 return 0;
666 }
667
668 static void
669 global_process_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
670 {
671 if(IsHelper(user))
672 send_messages(user, MESSAGE_RECIPIENT_HELPERS, 0);
673 }
674
675 static void
676 global_process_oper(struct userNode *user)
677 {
678 if(user->uplink->burst)
679 return;
680 send_messages(user, MESSAGE_RECIPIENT_OPERS, 0);
681 }
682
683 static void
684 global_conf_read(void)
685 {
686 dict_t conf_node;
687 const char *str;
688
689 if (!(conf_node = conf_get_data(GLOBAL_CONF_NAME, RECDB_OBJECT))) {
690 log_module(G_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", GLOBAL_CONF_NAME);
691 return;
692 }
693
694 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
695 global_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
696
697 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
698 if(global && str)
699 NickChange(global, str, 0);
700 }
701
702 static int
703 global_saxdb_read(struct dict *db)
704 {
705 struct record_data *hir;
706 time_t posted;
707 long flags;
708 unsigned long duration;
709 char *str, *from, *message;
710 dict_iterator_t it;
711
712 for(it=dict_first(db); it; it=iter_next(it))
713 {
714 hir = iter_data(it);
715 if(hir->type != RECDB_OBJECT)
716 {
717 log_module(G_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
718 continue;
719 }
720
721 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
722 flags = str ? strtoul(str, NULL, 0) : 0;
723
724 str = database_get_data(hir->d.object, KEY_POSTED, RECDB_QSTRING);
725 posted = str ? strtoul(str, NULL, 0) : 0;
726
727 str = database_get_data(hir->d.object, KEY_DURATION, RECDB_QSTRING);
728 duration = str ? strtoul(str, NULL, 0) : 0;
729
730 from = database_get_data(hir->d.object, KEY_FROM, RECDB_QSTRING);
731 message = database_get_data(hir->d.object, KEY_MESSAGE, RECDB_QSTRING);
732
733 message_add(flags, posted, duration, from, message);
734 }
735 return 0;
736 }
737
738 static int
739 global_saxdb_write(struct saxdb_context *ctx)
740 {
741 struct globalMessage *message;
742 char str[16];
743
744 for(message = messageList; message; message = message->next) {
745 snprintf(str, sizeof(str), "%li", message->id);
746 saxdb_start_record(ctx, str, 0);
747 saxdb_write_int(ctx, KEY_FLAGS, message->flags);
748 saxdb_write_int(ctx, KEY_POSTED, message->posted);
749 saxdb_write_int(ctx, KEY_DURATION, message->duration);
750 saxdb_write_string(ctx, KEY_FROM, message->from);
751 saxdb_write_string(ctx, KEY_MESSAGE, message->message);
752 saxdb_end_record(ctx);
753 }
754 return 0;
755 }
756
757 static void
758 global_db_cleanup(void)
759 {
760 while(messageList)
761 message_del(messageList);
762 }
763
764 void
765 init_global(const char *nick)
766 {
767 struct chanNode *chan;
768 unsigned int i;
769 G_LOG = log_register_type("Global", "file:global.log");
770 reg_new_user_func(global_process_user);
771 reg_auth_func(global_process_auth);
772 reg_oper_func(global_process_oper);
773
774 conf_register_reload(global_conf_read);
775
776 global_module = module_register("Global", G_LOG, "global.help", NULL);
777 modcmd_register(global_module, "LIST", cmd_list, 1, 0, "flags", "+oper", NULL);
778 modcmd_register(global_module, "MESSAGE", cmd_message, 3, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
779 modcmd_register(global_module, "MESSAGES", cmd_messages, 1, 0, NULL);
780 modcmd_register(global_module, "NOTICE", cmd_notice, 3, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
781 modcmd_register(global_module, "REMOVE", cmd_remove, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
782
783 if(nick)
784 {
785 const char *modes = conf_get_data("services/global/modes", RECDB_QSTRING);
786 global = AddLocalUser(nick, nick, NULL, "Global Services", modes);
787 global_service = service_register(global);
788 }
789
790 if(autojoin_channels && global) {
791 for (i = 0; i < autojoin_channels->used; i++) {
792 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
793 AddChannelUser(global, chan)->modes |= MODE_CHANOP;
794 }
795 }
796
797 saxdb_register("Global", global_saxdb_read, global_saxdb_write);
798 reg_exit_func(global_db_cleanup);
799 message_register_table(msgtab);
800 }