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