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