]> jfr.im git - irc/evilnet/x3.git/blob - src/mod-memoserv.c
cleanup
[irc/evilnet/x3.git] / src / mod-memoserv.c
1 /* mod-memoserv.c - MemoServ module for srvx
2 * Copyright 2003-2004 Martijn Smit and srvx Development Team
3 * Copyright 2005-2006 X3 Development Team
4 *
5 * This file is part of x3.
6 *
7 * x3 is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with srvx; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20 */
21
22 /*
23 * /msg opserv bind nickserv * *memoserv.*
24 *
25 * If you want a dedicated MemoServ bot, make sure the service control
26 * commands are bound to OpServ:
27 * /msg opserv bind opserv service *modcmd.joiner
28 * /msg opserv bind opserv service\ add *modcmd.service\ add
29 * /msg opserv bind opserv service\ rename *modcmd.service\ rename
30 * /msg opserv bind opserv service\ trigger *modcmd.service\ trigger
31 * /msg opserv bind opserv service\ remove *modcmd.service\ remove
32 * Add the bot:
33 * /msg opserv service add MemoServ User-to-user Memorandum Service
34 * /msg opserv bind memoserv help *modcmd.help
35 * Restart srvx with the updated conf file (as above, butwith "bot"
36 * "MemoServ"), and bind the commands to it:
37 * /msg opserv bind memoserv * *memoserv.*
38 * /msg opserv bind memoserv set *memoserv.set
39 */
40
41 #include "chanserv.h"
42 #include "conf.h"
43 #include "modcmd.h"
44 #include "nickserv.h"
45 #include "opserv.h"
46 #include "saxdb.h"
47 #include "timeq.h"
48
49 #define KEY_MAIN_ACCOUNTS "accounts"
50 #define KEY_FLAGS "flags"
51 #define KEY_LIMIT "limit"
52
53 #define KEY_MAIN_HISTORY "history"
54 #define KEY_MAIN_MEMOS "memos"
55 #define KEY_SENT "sent"
56 #define KEY_RECIPIENT "to"
57 #define KEY_FROM "from"
58 #define KEY_MESSAGE "msg"
59 #define KEY_READ "read"
60 #define KEY_RECIEPT "reciept"
61 #define KEY_ID "id"
62
63
64 static const struct message_entry msgtab[] = {
65 { "MSMSG_CANNOT_SEND", "You cannot send to account $b%s$b." },
66 { "MSMSG_UNKNOWN_SEND_FLAG", "Unreccognised send flag '%c', message not sent." },
67 { "MSMSG_MEMO_SENT", "Message sent to $b%s$b (ID# %d)." },
68 { "MSMSG_NO_MESSAGES", "You have no messages." },
69 { "MSMSG_MEMOS_FOUND", "Found $b%d$b matches." },
70 { "MSMSG_HOWTO_READ", "Use READ <ID> to read a message." },
71 { "MSMSG_CLEAN_INBOX", "You have $b%d$b or more messages, please clean out your inbox.\nUse READ <ID> to read a message." },
72 { "MSMSG_LIST_HEAD", "$bID$b $bFrom$b $bTime Sent$b" },
73 { "MSMSG_LIST_FORMAT", "%-2u %s $b%s$b %s" },
74 { "MSMSG_HISTORY_HEADER", "$bID$b $bTo$b $bTime Sent$b" },
75 { "MSMSG_HISTORY_FORMAT", "%-2u %s %s" },
76 { "MSMSG_MEMO_HEAD", "Memo %u From $b%s$b, received on %s:" },
77 { "MSMSG_MEMO_RECIEPT", "$bRead Reciept$b requested, %s." },
78 { "MSMSG_BAD_MESSAGE_ID", "$b%s$b is not a valid message ID (it should be a number between 0 and %u)." },
79 { "MSMSG_NO_SUCH_MEMO", "You have no memo with that ID." },
80 { "MSMSG_MEMO_DELETED", "Memo $b%d$b deleted." },
81 { "MSMSG_MEMO_CANCEL_NUMBER", "You must specify a number id" },
82 { "MSMSG_MEMO_DONT_OWN", "You did not send memo# %d" },
83 { "MSMSG_MEMO_READ", "Memo# %d has already been read, you cannot cancel it." },
84 { "MSMSG_MEMO_CANT_LOCATE", "Could not locate memo# %d" },
85 { "MSMSG_EXPIRY_OFF", "I am currently not expiring messages. (turned off)" },
86 { "MSMSG_EXPIRY", "Messages will be expired when they are %s old (%d seconds)." },
87 { "MSMSG_MESSAGES_EXPIRED", "$b%lu$b message(s) expired." },
88 { "MSMSG_MEMOS_INBOX", "You have $b%d$b new message(s) in your inbox and %d old messages. Use LIST to list them." },
89 { "MSMSG_NEW_MESSAGE", "You have a new message from $b%s$b. Use LIST to see your messages." },
90 { "MSMSG_FULL_INBOX", "$b%s$b cannot recieve anymore memos as their inbox is full" },
91 { "MSMSG_DELETED_ALL", "Deleted all of your messages." },
92 { "MSMSG_USE_CONFIRM", "Please use DELETE * $bCONFIRM$b to delete $uall$u of your messages." },
93
94 { "MSMSG_STATUS_HIST_TOTAL", "I have $b%u$b history entries in my database." },
95 { "MSMSG_STATUS_TOTAL", "I have $b%u$b memos in my database." },
96 { "MSMSG_STATUS_EXPIRED", "$b%ld$b memos expired during the time I am awake." },
97 { "MSMSG_STATUS_SENT", "$b%ld$b memos have been sent." },
98
99 { "MSMSG_INVALID_OPTION", "$b%s$b is not a valid option." },
100 { "MSMSG_INVALID_BINARY", "$b%s$b is an invalid binary value." },
101 { "MSMSG_SET_AUTHNOTIFY", "$bAuthNotify$b: %s" },
102 { "MSMSG_SET_NEWNOTIFY", "$bNewNotify$b: %s" },
103 { "MSMSG_SET_PRIVMSG", "$bPrivmsg$b: %s" },
104 { "MSMSG_SET_PRIVATE", "$bPrivate$b: %s" },
105 { "MSMSG_SET_IGNORERECIEPTS", "$bIgnoreReciepts$b: %s" },
106 { "MSMSG_SET_SENDRECIEPTS", "$bSendReciepts$b: %s" },
107 { "MSMSG_SET_LIMIT", "$bLimit$b: %d" },
108 { "MSMSG_SET_OPTIONS", "$bMessaging Options$b" },
109 { "MSMSG_SET_OPTIONS_END", "-------------End of Options-------------" },
110
111 { "MSMSG_LIST_END", "--------------End of Memos--------------" },
112 { "MSMSG_BAR", "----------------------------------------"},
113
114 { "MSMSG_DEFCON_NO_NEW_MEMOS", "You cannot send new memos at this time, please try again soon." },
115
116 { NULL, NULL }
117 };
118
119 struct memo {
120 struct memo_account *recipient;
121 struct memo_account *sender;
122 char *message;
123 time_t sent;
124 unsigned long id;
125 unsigned int is_read : 1;
126 unsigned int reciept : 1;
127 };
128
129 struct history {
130 struct memo_account *recipient;
131 struct memo_account *sender;
132 time_t sent;
133 unsigned long id;
134 };
135
136 struct userNode *memoserv;
137
138 #define MEMOSERV_FUNC(NAME) MODCMD_FUNC(NAME)
139 #define MEMOSERV_SYNTAX() svccmd_send_help_brief(user, memoserv, cmd)
140 #define MEMOSERV_MIN_PARAMS(N) if(argc < (N)) { \
141 reply("MSG_MISSING_PARAMS", argv[0]); \
142 MEMOSERV_SYNTAX(); \
143 return 0; }
144
145 DECLARE_LIST(memoList, struct memo*);
146 DEFINE_LIST(memoList, struct memo*);
147 DECLARE_LIST(historyList, struct history*);
148 DEFINE_LIST(historyList, struct history*);
149
150 /* memo_account.flags fields */
151 #define MEMO_NOTIFY_NEW 0x00000001
152 #define MEMO_NOTIFY_LOGIN 0x00000002
153 #define MEMO_DENY_NONCHANNEL 0x00000004
154 #define MEMO_IGNORE_RECIEPTS 0x00000008
155 #define MEMO_ALWAYS_RECIEPTS 0x00000010
156 #define MEMO_USE_PRIVMSG 0x00000020
157
158 struct memo_account {
159 struct handle_info *handle;
160 unsigned int flags : 6;
161 unsigned int limit;
162 struct memoList sent;
163 struct memoList recvd;
164 struct historyList hsent;
165 struct historyList hrecvd;
166 };
167
168 static struct {
169 struct userNode *bot;
170 int message_expiry;
171 unsigned int limit;
172 } memoserv_conf;
173
174 #define MEMOSERV_FUNC(NAME) MODCMD_FUNC(NAME)
175 #define OPTION_FUNC(NAME) int NAME(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
176 typedef OPTION_FUNC(option_func_t);
177
178 unsigned long memo_id;
179
180 extern struct string_list *autojoin_channels;
181 const char *memoserv_module_deps[] = { NULL };
182 static struct module *memoserv_module;
183 static struct log_type *MS_LOG;
184 static unsigned long memosSent, memosExpired;
185 static struct dict *memos; /* memo_account->handle->handle -> memo_account */
186 static struct dict *historys;
187 static dict_t memoserv_opt_dict; /* contains option_func_t* */
188
189 static struct memo_account *
190 memoserv_get_account(struct handle_info *hi)
191 {
192 struct memo_account *ma;
193 if (!hi)
194 return NULL;
195 ma = dict_find(memos, hi->handle, NULL);
196 if (ma)
197 return ma;
198 ma = calloc(1, sizeof(*ma));
199 if (!ma)
200 return ma;
201 ma->handle = hi;
202 ma->flags = MEMO_NOTIFY_NEW | MEMO_NOTIFY_LOGIN | MEMO_USE_PRIVMSG;
203 ma->limit = memoserv_conf.limit;
204 dict_insert(memos, ma->handle->handle, ma);
205 dict_insert(historys, ma->handle->handle, ma);
206 return ma;
207 }
208
209 static void
210 delete_memo(struct memo *memo)
211 {
212 memoList_remove(&memo->recipient->recvd, memo);
213 memoList_remove(&memo->sender->sent, memo);
214 free(memo->message);
215 free(memo);
216 }
217
218 static void
219 delete_history(struct history *history)
220 {
221 historyList_remove(&history->recipient->hrecvd, history);
222 historyList_remove(&history->sender->hsent, history);
223 free(history);
224 }
225
226 static void
227 delete_memo_account(void *data)
228 {
229 struct memo_account *ma = data;
230
231 while (ma->recvd.used)
232 delete_memo(ma->recvd.list[0]);
233 while (ma->sent.used)
234 delete_memo(ma->sent.list[0]);
235 memoList_clean(&ma->recvd);
236 memoList_clean(&ma->sent);
237
238 while (ma->hrecvd.used)
239 delete_history(ma->hrecvd.list[0]);
240 while (ma->hsent.used)
241 delete_history(ma->hsent.list[0]);
242 historyList_clean(&ma->hrecvd);
243 historyList_clean(&ma->hsent);
244 free(ma);
245 }
246
247 void
248 do_expire(void)
249 {
250 dict_iterator_t it;
251 for (it = dict_first(memos); it; it = iter_next(it)) {
252 struct memo_account *acct = iter_data(it);
253 unsigned int ii;
254 for (ii = 0; ii < acct->sent.used; ++ii) {
255 struct memo *memo = acct->sent.list[ii];
256 if ((now - memo->sent) > memoserv_conf.message_expiry) {
257 delete_memo(memo);
258 memosExpired++;
259 ii--;
260 }
261 }
262 }
263
264 for (it = dict_first(historys); it; it = iter_next(it)) {
265 struct memo_account *acct = iter_data(it);
266 unsigned int ii;
267 for (ii = 0; ii < acct->hsent.used; ++ii) {
268 struct history *history = acct->hsent.list[ii];
269 if ((now - history->sent) > memoserv_conf.message_expiry) {
270 delete_history(history);
271 memosExpired++;
272 ii--;
273 }
274 }
275 }
276 }
277
278 static void
279 expire_memos(UNUSED_ARG(void *data))
280 {
281 if (memoserv_conf.message_expiry) {
282 do_expire();
283 timeq_add(now + memoserv_conf.message_expiry, expire_memos, NULL);
284 }
285 }
286
287 static struct history*
288 add_history(time_t sent, struct memo_account *recipient, struct memo_account *sender, unsigned long id)
289 {
290 struct history *history;
291
292 history = calloc(1, sizeof(*history));
293 if (!history)
294 return NULL;
295
296 history->id = id;
297 history->recipient = recipient;
298 historyList_append(&recipient->hrecvd, history);
299 history->sender = sender;
300 historyList_append(&sender->hsent, history);
301 history->sent = sent;
302
303 return history;
304 }
305
306
307 static struct memo*
308 add_memo(time_t sent, struct memo_account *recipient, struct memo_account *sender, char *message, int nfrom_read)
309 {
310 struct memo *memo;
311 struct history *history;
312
313 memo = calloc(1, sizeof(*memo));
314 if (!memo)
315 return NULL;
316
317 if (nfrom_read) {
318 memo_id++;
319 memo->id = memo_id;
320 }
321
322 memo->recipient = recipient;
323 memoList_append(&recipient->recvd, memo);
324 memo->sender = sender;
325 memoList_append(&sender->sent, memo);
326 memo->sent = sent;
327 memo->message = strdup(message);
328 memosSent++;
329
330 if (nfrom_read)
331 history = add_history(sent, recipient, sender, memo->id);
332
333 return memo;
334 }
335
336 static int
337 memoserv_can_send(struct userNode *bot, struct userNode *user, struct memo_account *acct)
338 {
339 extern struct userData *_GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended);
340 struct userData *dest;
341 unsigned int i = 0, match = 0;
342
343 if (!user->handle_info)
344 return 0;
345
346 /* Sanity checks here because if the user doesnt have a limit set
347 the limit comes out at like 21233242 if you try and use it. */
348 if (acct->limit > memoserv_conf.limit)
349 acct->limit = memoserv_conf.limit;
350
351 if (acct->recvd.used > acct->limit) {
352 send_message(user, bot, "MSMSG_FULL_INBOX", acct->handle->handle);
353 send_message(user, bot, "MSMSG_CANNOT_SEND", acct->handle->handle);
354 return 0;
355 }
356
357 if (acct->handle->ignores->used) {
358 for (i=0; i < acct->handle->ignores->used; i++) {
359 if (user_matches_glob(user, acct->handle->ignores->list[i], MATCH_USENICK)) {
360 match = 1;
361 break;
362 }
363 }
364
365 if (match) {
366 send_message(user, bot, "MSMSG_CANNOT_SEND", acct->handle->handle);
367 return 0;
368 }
369 }
370
371 if (!(acct->flags & MEMO_DENY_NONCHANNEL))
372 return 1;
373
374 for (dest = acct->handle->channels; dest; dest = dest->u_next)
375 if (_GetChannelUser(dest->channel, user->handle_info, 1, 0))
376 return 1;
377
378 send_message(user, bot, "MSMSG_CANNOT_SEND", acct->handle->handle);
379 return 0;
380 }
381
382 static struct memo *find_memo(struct userNode *user, struct svccmd *cmd, struct memo_account *ma, const char *msgid, unsigned int *id)
383 {
384 unsigned int memoid;
385 if (!isdigit(msgid[0])) {
386 if (ma->recvd.used)
387 reply("MSMSG_BAD_MESSAGE_ID", msgid, ma->recvd.used - 1);
388 else
389 reply("MSMSG_NO_MESSAGES");
390 return NULL;
391 }
392 memoid = atoi(msgid);
393 if (memoid >= ma->recvd.used) {
394 reply("MSMSG_NO_SUCH_MEMO");
395 return NULL;
396 }
397 return ma->recvd.list[*id = memoid];
398 }
399
400 static MODCMD_FUNC(cmd_send)
401 {
402 char *message;
403 int reciept = 0, inc = 2;
404 struct handle_info *hi;
405 struct memo_account *ma, *sender;
406 struct memo *memo;
407
408 MEMOSERV_MIN_PARAMS(3);
409
410 if (checkDefCon(DEFCON_NO_NEW_MEMOS) && !IsOper(user)) {
411 reply("MSMSG_DEFCON_NO_NEW_MEMOS");
412 return 0;
413 }
414
415 if (!(hi = modcmd_get_handle_info(user, argv[1])))
416 return 0;
417
418 if (!(sender = memoserv_get_account(user->handle_info))
419 || !(ma = memoserv_get_account(hi))) {
420 reply("MSG_INTERNAL_FAILURE");
421 return 0;
422 }
423
424 if (!(memoserv_can_send(cmd->parent->bot, user, ma)))
425 return 0;
426
427 inc = 2; /* Start of message on 3rd ([2]) word */
428 if(argv[2][0] == '-' && argv[2][1] != '-') { /* first word is flags ('-r')*/
429 char *flags = argv[2];
430 inc++; /* Start of message is now 1 word later */
431 for(flags++;*flags;flags++) {
432 switch (*flags) {
433 case 'r':
434 reciept = 1;
435 break;
436
437 default:
438 /* Unknown mode. Give an error */
439 reply("MSMSG_UNKNOWN_SEND_FLAG", *flags);
440 return 0;
441 }
442 }
443 }
444 else
445 inc = 2; /* Start of message is word 2 */
446
447 message = unsplit_string(argv + inc, argc - inc, NULL);
448 memo = add_memo(now, ma, sender, message, 1);
449 if ((reciept == 1) || (ma->flags & MEMO_ALWAYS_RECIEPTS))
450 memo->reciept = 1;
451
452 if (ma->flags & MEMO_NOTIFY_NEW) {
453 struct userNode *other;
454
455 for (other = ma->handle->users; other; other = other->next_authed)
456 send_message_type((ma->flags & MEMO_USE_PRIVMSG)? MSG_TYPE_PRIVMSG : MSG_TYPE_NOTICE, other, memoserv ? memoserv : cmd->parent->bot, "MSMSG_NEW_MESSAGE", user->nick);
457 }
458
459 reply("MSMSG_MEMO_SENT", ma->handle->handle, memo_id);
460 return 1;
461 }
462
463 static MODCMD_FUNC(cmd_list)
464 {
465 struct memo_account *ma;
466 struct memo *memo;
467 unsigned int ii;
468 char posted[24];
469 struct tm tm;
470
471 if (!(ma = memoserv_get_account(user->handle_info)))
472 return 0;
473
474 reply("MSMSG_LIST_HEAD");
475
476 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
477 reply("MSMSG_BAR");
478
479 for (ii = 0; (ii < ma->recvd.used) && (ii < 15); ++ii) {
480 memo = ma->recvd.list[ii];
481 localtime_r(&memo->sent, &tm);
482 strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", &tm);
483 reply("MSMSG_LIST_FORMAT", ii, memo->sender->handle->handle, memo->reciept ? "(r)" : "", posted);
484 }
485 if (ii == 0)
486 reply("MSG_NONE");
487 else if (ii == 15)
488 reply("MSMSG_CLEAN_INBOX", ii);
489 else {
490 reply("MSMSG_MEMOS_FOUND", ii);
491 reply("MSMSG_HOWTO_READ");
492 }
493
494 reply("MSMSG_LIST_END");
495
496 return 1;
497 }
498
499 static MODCMD_FUNC(cmd_history)
500 {
501 struct memo_account *ma;
502 struct history *history;
503 dict_iterator_t it;
504 unsigned int ii = 0;
505 unsigned int cc = 0;
506 char posted[24];
507 struct tm tm;
508
509 if (!(ma = memoserv_get_account(user->handle_info)))
510 return 0;
511
512 reply("MSMSG_HISTORY_HEADER");
513
514 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
515 reply("MSMSG_BAR");
516
517 for (it = dict_first(historys); it; it = iter_next(it)) {
518 ma = iter_data(it);
519 for (ii = 0; ii < ma->hrecvd.used; ++ii) {
520 history = ma->hrecvd.list[ii];
521 if (!strcasecmp(history->sender->handle->handle, user->handle_info->handle)) {
522 cc++;
523 localtime_r(&history->sent, &tm);
524 strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", &tm);
525 reply("MSMSG_HISTORY_FORMAT", history->id, history->recipient->handle->handle, posted);
526 }
527 }
528 }
529
530 if (cc == 0)
531 reply("MSG_NONE");
532 else
533 reply("MSMSG_MEMOS_FOUND", cc);
534
535 reply("MSMSG_LIST_END");
536
537 return 1;
538 }
539
540 static MODCMD_FUNC(cmd_read)
541 {
542 struct memo_account *ma;
543 unsigned int memoid;
544 int rignore = 0, brk = 0, s = 0;
545 struct memo *memo;
546 struct memo *memob;
547 char posted[24];
548 struct tm tm;
549
550 if (!(ma = memoserv_get_account(user->handle_info)))
551 return 0;
552 if (!(memo = find_memo(user, cmd, ma, argv[1], &memoid)))
553 return 0;
554
555 if (argc > 2) {
556 char *argtwo = argv[2];
557 while (*argtwo) {
558 switch (*argtwo) {
559 case '-':
560 if (s != 0)
561 brk = 1;
562 break;
563
564 case 'i':
565 if (s > 0)
566 rignore = 1;
567 break;
568
569 default: break;
570 }
571
572 if (brk == 1)
573 break;
574 else {
575 s++;
576 argtwo++;
577 }
578 }
579 }
580
581 localtime_r(&memo->sent, &tm);
582 strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", &tm);
583
584 reply("MSMSG_MEMO_HEAD", memoid, memo->sender->handle->handle, posted);
585 send_message_type(4, user, cmd->parent->bot, "%s", memo->message);
586 memo->is_read = 1;
587 memob = memo;
588
589 if (ma->flags & MEMO_IGNORE_RECIEPTS)
590 rignore = 1;
591
592 if (memo->reciept == 1) {
593 memo->reciept = 0;
594 reply("MSMSG_MEMO_RECIEPT", rignore ? "ignoring" : "sending");
595 if (rignore == 0) {
596 struct memo_account *ma;
597 struct memo_account *sender;
598 char content[MAXLEN];
599
600 ma = memoserv_get_account(user->handle_info);
601 sender = memoserv_get_account(memo->sender->handle);
602
603 sprintf(content, "%s has read your memo dated %s.", ma->handle->handle, posted);
604
605 memo = add_memo(now, sender, ma, content, 1);
606 reply("MSMSG_MEMO_SENT", memob->sender->handle->handle, memo_id);
607
608 if (sender->flags & MEMO_NOTIFY_NEW) {
609 struct userNode *other;
610
611 for (other = sender->handle->users; other; other = other->next_authed)
612 send_message_type((ma->flags & MEMO_USE_PRIVMSG)? MSG_TYPE_PRIVMSG : MSG_TYPE_NOTICE, other, cmd->parent->bot, "MSMSG_NEW_MESSAGE", ma->handle->handle);
613 }
614
615
616 }
617 }
618 return 1;
619 }
620
621 static MODCMD_FUNC(cmd_delete)
622 {
623 struct memo_account *ma;
624 struct memo *memo;
625 unsigned int memoid;
626
627 MEMOSERV_MIN_PARAMS(2);
628
629 if (!(ma = memoserv_get_account(user->handle_info)))
630 return 0;
631 if (!irccasecmp(argv[1], "*") || !irccasecmp(argv[1], "all")) {
632 if ((argc < 3) || irccasecmp(argv[2], "confirm")) {
633 reply("MSMSG_USE_CONFIRM");
634 return 0;
635 }
636 while (ma->recvd.used)
637 delete_memo(ma->recvd.list[0]);
638 reply("MSMSG_DELETED_ALL");
639 return 1;
640 }
641
642 if (!(memo = find_memo(user, cmd, ma, argv[1], &memoid)))
643 return 0;
644 delete_memo(memo);
645 reply("MSMSG_MEMO_DELETED", memoid);
646 return 1;
647 }
648
649 static MODCMD_FUNC(cmd_cancel)
650 {
651 unsigned long id;
652 unsigned int ii;
653 dict_iterator_t it;
654 struct memo *memo;
655 struct memo_account *ma;
656
657 MEMOSERV_MIN_PARAMS(2);
658
659 if (isdigit(argv[1][0])) {
660 id = strtoul(argv[1], NULL, 0);
661 } else {
662 reply("MSMSG_MEMO_CANCEL_NUMBER");
663 return 0;
664 }
665
666 for (it = dict_first(memos); it; it = iter_next(it)) {
667 ma = iter_data(it);
668 for (ii = 0; ii < ma->recvd.used; ++ii) {
669 memo = ma->recvd.list[ii];
670
671 if (id == memo->id) {
672 if (!strcasecmp(memo->sender->handle->handle, user->handle_info->handle)) {
673 if (memo->is_read) {
674 reply("MSMSG_MEMO_READ", id);
675 return 0;
676 } else {
677 delete_memo(memo);
678 reply("MSMSG_MEMO_DELETED", id);
679 return 1;
680 }
681 } else {
682 reply("MSMSG_MEMO_DONT_OWN", id);
683 return 0;
684 }
685 }
686 }
687 }
688
689 reply("MSMSG_MEMO_CANT_LOCATE", id);
690 return 0;
691 }
692
693 static MODCMD_FUNC(cmd_expire)
694 {
695 unsigned long old_expired = memosExpired;
696 do_expire();
697 reply("MSMSG_MESSAGES_EXPIRED", memosExpired - old_expired);
698 return 1;
699 }
700
701 static MODCMD_FUNC(cmd_expiry)
702 {
703 char interval[INTERVALLEN];
704
705 if (!memoserv_conf.message_expiry) {
706 reply("MSMSG_EXPIRY_OFF");
707 return 1;
708 }
709
710 intervalString(interval, memoserv_conf.message_expiry, user->handle_info);
711 reply("MSMSG_EXPIRY", interval, memoserv_conf.message_expiry);
712 return 1;
713 }
714
715
716 static void
717 set_list(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, int override)
718 {
719 option_func_t *opt;
720 unsigned int i;
721 char *set_display[] = {"AUTHNOTIFY", "NEWNOTIFY", "PRIVMSG", "PRIVATE", "LIMIT",
722 "IGNORERECIEPTS", "SENDRECIEPTS"};
723
724 reply("MSMSG_SET_OPTIONS");
725 reply("MSMSG_BAR");
726
727 /* Do this so options are presented in a consistent order. */
728 for (i = 0; i < ArrayLength(set_display); ++i)
729 if ((opt = dict_find(memoserv_opt_dict, set_display[i], NULL)))
730 opt(cmd, user, hi, override, 0, NULL);
731 reply("MSMSG_SET_OPTIONS_END");
732 }
733
734 static MODCMD_FUNC(cmd_set)
735 {
736 struct handle_info *hi;
737 option_func_t *opt;
738
739 hi = user->handle_info;
740 if (argc < 2) {
741 set_list(cmd, user, hi, 0);
742 return 1;
743 }
744
745 if (!(opt = dict_find(memoserv_opt_dict, argv[1], NULL))) {
746 reply("MSMSG_INVALID_OPTION", argv[1]);
747 return 0;
748 }
749
750 return opt(cmd, user, hi, 0, argc-1, argv+1);
751 }
752
753 static MODCMD_FUNC(cmd_oset)
754 {
755 struct handle_info *hi;
756 option_func_t *opt;
757
758 MEMOSERV_MIN_PARAMS(2);
759
760 if (!(hi = get_victim_oper(cmd, user, argv[1])))
761 return 0;
762
763 if (argc < 3) {
764 set_list(cmd, user, hi, 0);
765 return 1;
766 }
767
768 if (!(opt = dict_find(memoserv_opt_dict, argv[2], NULL))) {
769 reply("MSMSG_INVALID_OPTION", argv[2]);
770 return 0;
771 }
772
773 return opt(cmd, user, hi, 1, argc-2, argv+2);
774 }
775
776 static OPTION_FUNC(opt_newnotify)
777 {
778 struct memo_account *ma;
779 char *choice;
780
781 if (!(ma = memoserv_get_account(hi)))
782 return 0;
783 if (argc > 1) {
784 choice = argv[1];
785 if (enabled_string(choice)) {
786 ma->flags |= MEMO_NOTIFY_NEW;
787 } else if (disabled_string(choice)) {
788 ma->flags &= ~MEMO_NOTIFY_NEW;
789 } else {
790 reply("MSMSG_INVALID_BINARY", choice);
791 return 0;
792 }
793 }
794
795 choice = (ma->flags & MEMO_NOTIFY_NEW) ? "on" : "off";
796 reply("MSMSG_SET_NEWNOTIFY", choice);
797 return 1;
798 }
799
800 static OPTION_FUNC(opt_privmsg)
801 {
802 struct memo_account *ma;
803 char *choice;
804
805 if (!(ma = memoserv_get_account(hi)))
806 return 0;
807 if (argc > 1) {
808 choice = argv[1];
809 if (enabled_string(choice)) {
810 ma->flags |= MEMO_USE_PRIVMSG;
811 } else if (disabled_string(choice)) {
812 ma->flags &= ~MEMO_USE_PRIVMSG;
813 } else {
814 reply("MSMSG_INVALID_BINARY", choice);
815 return 0;
816 }
817 }
818 choice = (ma->flags & MEMO_USE_PRIVMSG) ? "on" : "off";
819 reply("MSMSG_SET_PRIVMSG", choice);
820 return 1;
821 }
822
823 static OPTION_FUNC(opt_authnotify)
824 {
825 struct memo_account *ma;
826 char *choice;
827
828 if (!(ma = memoserv_get_account(hi)))
829 return 0;
830 if (argc > 1) {
831 choice = argv[1];
832 if (enabled_string(choice)) {
833 ma->flags |= MEMO_NOTIFY_LOGIN;
834 } else if (disabled_string(choice)) {
835 ma->flags &= ~MEMO_NOTIFY_LOGIN;
836 } else {
837 reply("MSMSG_INVALID_BINARY", choice);
838 return 0;
839 }
840 }
841
842 choice = (ma->flags & MEMO_NOTIFY_LOGIN) ? "on" : "off";
843 reply("MSMSG_SET_AUTHNOTIFY", choice);
844 return 1;
845 }
846
847 static OPTION_FUNC(opt_ignorereciepts)
848 {
849 struct memo_account *ma;
850 char *choice;
851
852 if (!(ma = memoserv_get_account(hi)))
853 return 0;
854 if (argc > 1) {
855 choice = argv[1];
856 if (enabled_string(choice)) {
857 ma->flags |= MEMO_IGNORE_RECIEPTS;
858 } else if (disabled_string(choice)) {
859 ma->flags &= ~MEMO_IGNORE_RECIEPTS;
860 } else {
861 reply("MSMSG_INVALID_BINARY", choice);
862 return 0;
863 }
864 }
865
866 choice = (ma->flags & MEMO_IGNORE_RECIEPTS) ? "on" : "off";
867 reply("MSMSG_SET_IGNORERECIEPTS", choice);
868 return 1;
869 }
870
871 static OPTION_FUNC(opt_sendreciepts)
872 {
873 struct memo_account *ma;
874 char *choice;
875
876 if (!(ma = memoserv_get_account(hi)))
877 return 0;
878 if (argc > 1) {
879 choice = argv[1];
880 if (enabled_string(choice)) {
881 ma->flags |= MEMO_ALWAYS_RECIEPTS;
882 } else if (disabled_string(choice)) {
883 ma->flags &= ~MEMO_ALWAYS_RECIEPTS;
884 } else {
885 reply("MSMSG_INVALID_BINARY", choice);
886 return 0;
887 }
888 }
889
890 choice = (ma->flags & MEMO_ALWAYS_RECIEPTS) ? "on" : "off";
891 reply("MSMSG_SET_SENDRECIEPTS", choice);
892 return 1;
893 }
894
895 static OPTION_FUNC(opt_private)
896 {
897 struct memo_account *ma;
898 char *choice;
899
900 if (!(ma = memoserv_get_account(hi)))
901 return 0;
902 if (argc > 1) {
903 choice = argv[1];
904 if (enabled_string(choice)) {
905 ma->flags |= MEMO_DENY_NONCHANNEL;
906 } else if (disabled_string(choice)) {
907 ma->flags &= ~MEMO_DENY_NONCHANNEL;
908 } else {
909 reply("MSMSG_INVALID_BINARY", choice);
910 return 0;
911 }
912 }
913
914 choice = (ma->flags & MEMO_DENY_NONCHANNEL) ? "on" : "off";
915 reply("MSMSG_SET_PRIVATE", choice);
916 return 1;
917 }
918
919 static OPTION_FUNC(opt_limit)
920 {
921 struct memo_account *ma;
922 unsigned int choice;
923
924 if (!(ma = memoserv_get_account(hi)))
925 return 0;
926 if (argc > 1) {
927 choice = atoi(argv[1]);
928 if (choice > memoserv_conf.limit)
929 choice = memoserv_conf.limit;
930
931 ma->limit = choice;
932 }
933
934 reply("MSMSG_SET_LIMIT", ma->limit);
935 return 1;
936 }
937
938 static MODCMD_FUNC(cmd_status)
939 {
940 struct memo_account *ma;
941 dict_iterator_t it;
942 int mc = 0, hc = 0;
943 unsigned int ii;
944
945 for (it = dict_first(memos); it; it = iter_next(it)) {
946 ma = iter_data(it);
947 for (ii = 0; ii < ma->recvd.used; ++ii)
948 mc++;
949 }
950
951 for (it = dict_first(historys); it; it = iter_next(it)) {
952 ma = iter_data(it);
953 for (ii = 0; ii < ma->hrecvd.used; ++ii)
954 hc++;
955 }
956
957 reply("MSMSG_STATUS_HIST_TOTAL", hc);
958 reply("MSMSG_STATUS_TOTAL", mc);
959 reply("MSMSG_STATUS_EXPIRED", memosExpired);
960 reply("MSMSG_STATUS_SENT", memosSent);
961 return 1;
962 }
963
964 static void
965 memoserv_conf_read(void)
966 {
967 dict_t conf_node;
968 const char *str;
969
970 str = "modules/memoserv";
971 if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
972 log_module(MS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
973 return;
974 }
975
976 str = database_get_data(conf_node, "limit", RECDB_QSTRING);
977 memoserv_conf.limit = str ? atoi(str) : 50;
978
979 str = database_get_data(conf_node, "message_expiry", RECDB_QSTRING);
980 memoserv_conf.message_expiry = str ? ParseInterval(str) : 60*24*30;
981 }
982
983 static int
984 memoserv_user_read(const char *key, struct record_data *hir)
985 {
986 char *str;
987 struct memo_account *ma;
988 struct handle_info *hi;
989
990 if (!(hi = get_handle_info(key)))
991 return 0;
992
993 ma = dict_find(memos, hi->handle, NULL);
994 if (ma)
995 return 0;
996
997
998 ma = calloc(1, sizeof(*ma));
999 if (!ma)
1000 return 0;
1001
1002 ma->handle = hi;
1003
1004 str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
1005 if (!str) {
1006 log_module(MS_LOG, LOG_ERROR, "Flags not present in memo %s; skipping", key);
1007 return 0;
1008 }
1009 ma->flags = strtoul(str, NULL, 0);
1010
1011 str = database_get_data(hir->d.object, KEY_LIMIT, RECDB_QSTRING);
1012 if (!str) {
1013 log_module(MS_LOG, LOG_ERROR, "Limit not present in memo %s; skipping", key);
1014 return 0;
1015 }
1016 ma->limit = strtoul(str, NULL, 0);
1017
1018 dict_insert(memos, ma->handle->handle, ma);
1019 dict_insert(historys, ma->handle->handle, ma);
1020
1021 return 0;
1022 }
1023
1024 static int
1025 memoserv_memo_read(const char *key, struct record_data *hir)
1026 {
1027 char *str;
1028 struct handle_info *sender, *recipient;
1029 struct memo *memo;
1030 unsigned long id;
1031 time_t sent;
1032
1033 if (hir->type != RECDB_OBJECT) {
1034 log_module(MS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, key);
1035 return 0;
1036 }
1037
1038 if (!(str = database_get_data(hir->d.object, KEY_SENT, RECDB_QSTRING))) {
1039 log_module(MS_LOG, LOG_ERROR, "Date sent not present in memo %s; skipping", key);
1040 return 0;
1041 }
1042
1043 sent = atoi(str);
1044
1045 if (!(str = database_get_data(hir->d.object, KEY_ID, RECDB_QSTRING))) {
1046 log_module(MS_LOG, LOG_ERROR, "ID sent not present in memo %s; skipping", key);
1047 return 0;
1048 }
1049 id = strtoul(str, NULL, 0);
1050 if (id > memo_id)
1051 memo_id = id;
1052
1053 if (!(str = database_get_data(hir->d.object, KEY_RECIPIENT, RECDB_QSTRING))) {
1054 log_module(MS_LOG, LOG_ERROR, "Recipient not present in memo %s; skipping", key);
1055 return 0;
1056 } else if (!(recipient = get_handle_info(str))) {
1057 log_module(MS_LOG, LOG_ERROR, "Invalid recipient %s in memo %s; skipping", str, key);
1058 return 0;
1059 }
1060
1061 if (!(str = database_get_data(hir->d.object, KEY_FROM, RECDB_QSTRING))) {
1062 log_module(MS_LOG, LOG_ERROR, "Sender not present in memo %s; skipping", key);
1063 return 0;
1064 } else if (!(sender = get_handle_info(str))) {
1065 log_module(MS_LOG, LOG_ERROR, "Invalid sender %s in memo %s; skipping", str, key);
1066 return 0;
1067 }
1068
1069 if (!(str = database_get_data(hir->d.object, KEY_MESSAGE, RECDB_QSTRING))) {
1070 log_module(MS_LOG, LOG_ERROR, "Message not present in memo %s; skipping", key);
1071 return 0;
1072 }
1073
1074 memo = add_memo(sent, memoserv_get_account(recipient), memoserv_get_account(sender), str, 0);
1075 if ((str = database_get_data(hir->d.object, KEY_READ, RECDB_QSTRING)))
1076 memo->is_read = 1;
1077
1078 if ((str = database_get_data(hir->d.object, KEY_RECIEPT, RECDB_QSTRING)))
1079 memo->reciept = 1;
1080
1081 memo->id = id;
1082
1083 return 0;
1084 }
1085
1086 static int
1087 memoserv_history_read(const char *key, struct record_data *hir)
1088 {
1089 char *str;
1090 struct handle_info *sender, *recipient;
1091 struct history *history;
1092 unsigned long id;
1093 time_t sent;
1094
1095 if (hir->type != RECDB_OBJECT) {
1096 log_module(MS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, key);
1097 return 0;
1098 }
1099
1100 if (!(str = database_get_data(hir->d.object, KEY_SENT, RECDB_QSTRING))) {
1101 log_module(MS_LOG, LOG_ERROR, "Date sent not present in history %s; skipping", key);
1102 return 0;
1103 }
1104
1105 sent = atoi(str);
1106
1107 if (!(str = database_get_data(hir->d.object, KEY_ID, RECDB_QSTRING))) {
1108 log_module(MS_LOG, LOG_ERROR, "ID sent not present in history %s; skipping", key);
1109 return 0;
1110 }
1111 id = strtoul(str, NULL, 0);
1112
1113 if (!(str = database_get_data(hir->d.object, KEY_RECIPIENT, RECDB_QSTRING))) {
1114 log_module(MS_LOG, LOG_ERROR, "Recipient not present in history %s; skipping", key);
1115 return 0;
1116 } else if (!(recipient = get_handle_info(str))) {
1117 log_module(MS_LOG, LOG_ERROR, "Invalid recipient %s in history %s; skipping", str, key);
1118 return 0;
1119 }
1120
1121 if (!(str = database_get_data(hir->d.object, KEY_FROM, RECDB_QSTRING))) {
1122 log_module(MS_LOG, LOG_ERROR, "Sender not present in history %s; skipping", key);
1123 return 0;
1124 } else if (!(sender = get_handle_info(str))) {
1125 log_module(MS_LOG, LOG_ERROR, "Invalid sender %s in history %s; skipping", str, key);
1126 return 0;
1127 }
1128
1129 history = add_history(sent, memoserv_get_account(recipient), memoserv_get_account(sender), id);
1130
1131 return 0;
1132 }
1133
1134 static int
1135 memoserv_saxdb_read(struct dict *database)
1136 {
1137 struct dict *section;
1138 dict_iterator_t it;
1139
1140 if((section = database_get_data(database, KEY_MAIN_ACCOUNTS, RECDB_OBJECT)))
1141 for(it = dict_first(section); it; it = iter_next(it))
1142 memoserv_user_read(iter_key(it), iter_data(it));
1143
1144 if((section = database_get_data(database, KEY_MAIN_MEMOS, RECDB_OBJECT)))
1145 for(it = dict_first(section); it; it = iter_next(it))
1146 memoserv_memo_read(iter_key(it), iter_data(it));
1147
1148 if((section = database_get_data(database, KEY_MAIN_HISTORY, RECDB_OBJECT)))
1149 for(it = dict_first(section); it; it = iter_next(it))
1150 memoserv_history_read(iter_key(it), iter_data(it));
1151
1152 return 0;
1153 }
1154
1155 static int
1156 memoserv_write_users(struct saxdb_context *ctx, struct memo_account *ma)
1157 {
1158 saxdb_start_record(ctx, ma->handle->handle, 0);
1159
1160 saxdb_write_int(ctx, KEY_FLAGS, ma->flags);
1161 saxdb_write_int(ctx, KEY_LIMIT, ma->limit);
1162
1163 saxdb_end_record(ctx);
1164 return 0;
1165 }
1166
1167 static int
1168 memoserv_write_memos(struct saxdb_context *ctx, struct memo *memo)
1169 {
1170 char str[20];
1171
1172 memset(str, '\0', sizeof(str));
1173 saxdb_start_record(ctx, inttobase64(str, memo->id, sizeof(str)-1), 0);
1174
1175 saxdb_write_int(ctx, KEY_SENT, memo->sent);
1176 saxdb_write_int(ctx, KEY_ID, memo->id);
1177 saxdb_write_string(ctx, KEY_RECIPIENT, memo->recipient->handle->handle);
1178 saxdb_write_string(ctx, KEY_FROM, memo->sender->handle->handle);
1179 saxdb_write_string(ctx, KEY_MESSAGE, memo->message);
1180
1181 if (memo->is_read)
1182 saxdb_write_int(ctx, KEY_READ, 1);
1183
1184 if (memo->reciept)
1185 saxdb_write_int(ctx, KEY_RECIEPT, 1);
1186
1187 saxdb_end_record(ctx);
1188 return 0;
1189 }
1190
1191 static int
1192 memoserv_write_history(struct saxdb_context *ctx, struct history *history)
1193 {
1194 char str[20];
1195
1196 memset(str, '\0', sizeof(str));
1197 saxdb_start_record(ctx, inttobase64(str, history->id, sizeof(str)-1), 0);
1198
1199 saxdb_write_int(ctx, KEY_SENT, history->sent);
1200 saxdb_write_int(ctx, KEY_ID, history->id);
1201 saxdb_write_string(ctx, KEY_RECIPIENT, history->recipient->handle->handle);
1202 saxdb_write_string(ctx, KEY_FROM, history->sender->handle->handle);
1203
1204 saxdb_end_record(ctx);
1205 return 0;
1206 }
1207
1208 static int
1209 memoserv_saxdb_write(struct saxdb_context *ctx)
1210 {
1211 dict_iterator_t it;
1212 struct memo_account *ma;
1213 struct memo *memo;
1214 struct history *history;
1215 unsigned int ii;
1216
1217 /* Accounts */
1218 saxdb_start_record(ctx, KEY_MAIN_ACCOUNTS, 1);
1219 for (it = dict_first(memos); it; it = iter_next(it)) {
1220 ma = iter_data(it);
1221 memoserv_write_users(ctx, ma);
1222 }
1223 saxdb_end_record(ctx);
1224
1225 /* Memos */
1226 saxdb_start_record(ctx, KEY_MAIN_MEMOS, 1);
1227 for (it = dict_first(memos); it; it = iter_next(it)) {
1228 ma = iter_data(it);
1229 for (ii = 0; ii < ma->recvd.used; ++ii) {
1230 memo = ma->recvd.list[ii];
1231 memoserv_write_memos(ctx, memo);
1232 }
1233 }
1234 saxdb_end_record(ctx);
1235
1236 /* History */
1237 saxdb_start_record(ctx, KEY_MAIN_HISTORY, 1);
1238 for (it = dict_first(historys); it; it = iter_next(it)) {
1239 ma = iter_data(it);
1240 for (ii = 0; ii < ma->hrecvd.used; ++ii) {
1241 history = ma->hrecvd.list[ii];
1242 memoserv_write_history(ctx, history);
1243 }
1244 }
1245 saxdb_end_record(ctx);
1246
1247 return 0;
1248 }
1249
1250 static void
1251 memoserv_cleanup(void)
1252 {
1253 dict_delete(memos);
1254 dict_delete(historys);
1255 }
1256
1257 static void
1258 memoserv_check_messages(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
1259 {
1260 unsigned int ii, unseen;
1261 struct memo_account *ma;
1262 struct memo *memo;
1263
1264 if (!user->uplink->burst) {
1265 if (!(ma = memoserv_get_account(user->handle_info))
1266 || !(ma->flags & MEMO_NOTIFY_LOGIN))
1267 return;
1268 for (ii = unseen = 0; ii < ma->recvd.used; ++ii) {
1269 memo = ma->recvd.list[ii];
1270 if (!memo->is_read)
1271 unseen++;
1272 }
1273 if (ma->recvd.used && memoserv)
1274 if(unseen) send_message_type((ma->flags & MEMO_USE_PRIVMSG)? 1 : 0, user, memoserv, "MSMSG_MEMOS_INBOX", unseen, ma->recvd.used - unseen);
1275 }
1276 }
1277
1278 static void
1279 memoserv_rename_account(struct handle_info *hi, const char *old_handle)
1280 {
1281 struct memo_account *ma;
1282 if (!(ma = dict_find(memos, old_handle, NULL)))
1283 return;
1284 dict_remove2(memos, old_handle, 1);
1285 dict_insert(memos, hi->handle, ma);
1286
1287 dict_remove2(historys, old_handle, 1);
1288 dict_insert(historys, hi->handle, ma);
1289 }
1290
1291 static void
1292 memoserv_unreg_account(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
1293 {
1294 dict_remove(memos, handle->handle);
1295 dict_remove(historys, handle->handle);
1296 }
1297
1298 int
1299 memoserv_init(void)
1300 {
1301 MS_LOG = log_register_type("MemoServ", "file:memoserv.log");
1302 memos = dict_new();
1303 historys = dict_new();
1304 dict_set_free_data(memos, delete_memo_account);
1305 reg_auth_func(memoserv_check_messages);
1306 reg_handle_rename_func(memoserv_rename_account);
1307 reg_unreg_func(memoserv_unreg_account);
1308 conf_register_reload(memoserv_conf_read);
1309 reg_exit_func(memoserv_cleanup);
1310 saxdb_register("MemoServ", memoserv_saxdb_read, memoserv_saxdb_write);
1311
1312 memoserv_module = module_register("MemoServ", MS_LOG, "mod-memoserv.help", NULL);
1313 modcmd_register(memoserv_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
1314 modcmd_register(memoserv_module, "list", cmd_list, 1, MODCMD_REQUIRE_AUTHED, NULL);
1315 modcmd_register(memoserv_module, "read", cmd_read, 2, MODCMD_REQUIRE_AUTHED, NULL);
1316 modcmd_register(memoserv_module, "delete", cmd_delete, 2, MODCMD_REQUIRE_AUTHED, NULL);
1317 modcmd_register(memoserv_module, "cancel", cmd_cancel, 2, MODCMD_REQUIRE_AUTHED, NULL);
1318 modcmd_register(memoserv_module, "history", cmd_history, 1, MODCMD_REQUIRE_AUTHED, NULL);
1319 modcmd_register(memoserv_module, "expire", cmd_expire, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
1320 modcmd_register(memoserv_module, "expiry", cmd_expiry, 1, 0, NULL);
1321 modcmd_register(memoserv_module, "status", cmd_status, 1, 0, NULL);
1322 modcmd_register(memoserv_module, "set", cmd_set, 1, MODCMD_REQUIRE_AUTHED, NULL);
1323 modcmd_register(memoserv_module, "oset", cmd_oset, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
1324
1325 memoserv_opt_dict = dict_new();
1326 dict_insert(memoserv_opt_dict, "AUTHNOTIFY", opt_authnotify);
1327 dict_insert(memoserv_opt_dict, "NEWNOTIFY", opt_newnotify);
1328 dict_insert(memoserv_opt_dict, "PRIVMSG", opt_privmsg);
1329 dict_insert(memoserv_opt_dict, "PRIVATE", opt_private);
1330 dict_insert(memoserv_opt_dict, "IGNORERECIEPTS", opt_ignorereciepts);
1331 dict_insert(memoserv_opt_dict, "SENDRECIEPTS", opt_sendreciepts);
1332 dict_insert(memoserv_opt_dict, "LIMIT", opt_limit);
1333
1334 message_register_table(msgtab);
1335
1336 if (memoserv_conf.message_expiry)
1337 timeq_add(now + memoserv_conf.message_expiry, expire_memos, NULL);
1338
1339 return 1;
1340 }
1341
1342 int
1343 memoserv_finalize(void) {
1344 struct chanNode *chan;
1345 unsigned int i;
1346 dict_t conf_node;
1347 const char *str;
1348
1349 str = "modules/memoserv";
1350 if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
1351 log_module(MS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
1352 return 0;
1353 }
1354
1355 str = database_get_data(conf_node, "bot", RECDB_QSTRING);
1356 if (str) {
1357 memoserv = memoserv_conf.bot;
1358 const char *modes = conf_get_data("modules/memoserv/modes", RECDB_QSTRING);
1359 memoserv = AddService(str, modes ? modes : NULL, "User-User Memorandum Services", NULL);
1360
1361 } else {
1362 log_module(MS_LOG, LOG_ERROR, "database_get_data for memoserv_conf.bot failed!");
1363 exit(1);
1364 }
1365
1366 if (autojoin_channels && memoserv) {
1367 for (i = 0; i < autojoin_channels->used; i++) {
1368 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
1369 AddChannelUser(memoserv, chan)->modes |= MODE_CHANOP;
1370 }
1371 }
1372
1373 return 1;
1374 }