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