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