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