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