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