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