]>
jfr.im git - irc/evilnet/x3.git/blob - src/mod-memoserv.c
1 /* mod-memoserv.c - MemoServ module for srvx
2 * Copyright 2003-2004 Martijn Smit and srvx Development Team
3 * Copyright 2005-2006 X3 Development Team
5 * This file is part of x3.
7 * x3 is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
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.
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.
23 * /msg opserv bind nickserv * *memoserv.*
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
33 * /msg opserv service add MemoServ User-to-user Memorandum Service
34 * /msg opserv bind memoserv help *modcmd.help
35 * Restart srvx with the updated conf file (as above, butwith "bot"
36 * "MemoServ"), and bind the commands to it:
37 * /msg opserv bind memoserv * *memoserv.*
38 * /msg opserv bind memoserv set *memoserv.set
49 #define KEY_MAIN_ACCOUNTS "accounts"
50 #define KEY_FLAGS "flags"
51 #define KEY_LIMIT "limit"
53 #define KEY_MAIN_HISTORY "history"
54 #define KEY_MAIN_MEMOS "memos"
55 #define KEY_SENT "sent"
56 #define KEY_RECIPIENT "to"
57 #define KEY_FROM "from"
58 #define KEY_MESSAGE "msg"
59 #undef KEY_READ /* thanks microsoft! */
60 #define KEY_READ "read"
61 #define KEY_RECIEPT "reciept"
65 static const struct message_entry msgtab
[] = {
66 { "MSMSG_CANNOT_SEND", "You cannot send to account $b%s$b." },
67 { "MSMSG_UNKNOWN_SEND_FLAG", "Unreccognised send flag '%c', message not sent." },
68 { "MSMSG_MEMO_SENT", "Message sent to $b%s$b (ID# %d)." },
69 { "MSMSG_NO_MESSAGES", "You have no messages." },
70 { "MSMSG_MEMOS_FOUND", "Found $b%d$b matches." },
71 { "MSMSG_HOWTO_READ", "Use READ <ID> to read a message." },
72 { "MSMSG_CLEAN_INBOX", "You have $b%d$b or more messages, please clean out your inbox.\nUse READ <ID> to read a message." },
73 { "MSMSG_LIST_HEAD", "$bID$b $bFrom$b $bTime Sent$b" },
74 { "MSMSG_LIST_FORMAT", "%-2u %s $b%s$b %s" },
75 { "MSMSG_HISTORY_HEADER", "$bID$b $bTo$b $bTime Sent$b" },
76 { "MSMSG_HISTORY_FORMAT", "%-2u %s %s" },
77 { "MSMSG_MEMO_HEAD", "Memo %u From $b%s$b, received on %s:" },
78 { "MSMSG_MEMO_RECIEPT", "$bRead Reciept$b requested, %s." },
79 { "MSMSG_BAD_MESSAGE_ID", "$b%s$b is not a valid message ID (it should be a number between 0 and %u)." },
80 { "MSMSG_NO_SUCH_MEMO", "You have no memo with that ID." },
81 { "MSMSG_MEMO_DELETED", "Memo $b%d$b deleted." },
82 { "MSMSG_MEMO_CANCEL_NUMBER", "You must specify a number id" },
83 { "MSMSG_MEMO_DONT_OWN", "You did not send memo# %d" },
84 { "MSMSG_MEMO_READ", "Memo# %d has already been read, you cannot cancel it." },
85 { "MSMSG_MEMO_CANT_LOCATE", "Could not locate memo# %d" },
86 { "MSMSG_EXPIRY_OFF", "I am currently not expiring messages. (turned off)" },
87 { "MSMSG_EXPIRY", "Messages will be expired when they are %s old (%d seconds)." },
88 { "MSMSG_MESSAGES_EXPIRED", "$b%lu$b message(s) expired." },
89 { "MSMSG_MEMOS_INBOX", "You have $b%d$b new message(s) in your inbox and %d old messages. Use LIST to list them." },
90 { "MSMSG_NEW_MESSAGE", "You have a new message from $b%s$b. Use LIST to see your messages." },
91 { "MSMSG_FULL_INBOX", "$b%s$b cannot recieve anymore memos as their inbox is full" },
92 { "MSMSG_DELETED_ALL", "Deleted all of your messages." },
93 { "MSMSG_USE_CONFIRM", "Please use DELETE * $bCONFIRM$b to delete $uall$u of your messages." },
95 { "MSMSG_STATUS_HIST_TOTAL", "I have $b%u$b history entries in my database." },
96 { "MSMSG_STATUS_TOTAL", "I have $b%u$b memos in my database." },
97 { "MSMSG_STATUS_EXPIRED", "$b%ld$b memos expired during the time I am awake." },
98 { "MSMSG_STATUS_SENT", "$b%ld$b memos have been sent." },
100 { "MSMSG_INVALID_OPTION", "$b%s$b is not a valid option." },
101 { "MSMSG_INVALID_BINARY", "$b%s$b is an invalid binary value." },
102 { "MSMSG_SET_AUTHNOTIFY", "$bAuthNotify$b: %s" },
103 { "MSMSG_SET_NEWNOTIFY", "$bNewNotify$b: %s" },
104 { "MSMSG_SET_PRIVMSG", "$bPrivmsg$b: %s" },
105 { "MSMSG_SET_PRIVATE", "$bPrivate$b: %s" },
106 { "MSMSG_SET_IGNORERECIEPTS", "$bIgnoreReciepts$b: %s" },
107 { "MSMSG_SET_SENDRECIEPTS", "$bSendReciepts$b: %s" },
108 { "MSMSG_SET_LIMIT", "$bLimit$b: %d" },
109 { "MSMSG_SET_OPTIONS", "$bMessaging Options$b" },
110 { "MSMSG_SET_OPTIONS_END", "-------------End of Options-------------" },
112 { "MSMSG_LIST_END", "--------------End of Memos--------------" },
113 { "MSMSG_BAR", "----------------------------------------"},
115 { "MSMSG_DEFCON_NO_NEW_MEMOS", "You cannot send new memos at this time, please try again soon." },
121 struct memo_account
*recipient
;
122 struct memo_account
*sender
;
126 unsigned int is_read
: 1;
127 unsigned int reciept
: 1;
131 struct memo_account
*recipient
;
132 struct memo_account
*sender
;
137 struct userNode
*memoserv
;
139 #define MEMOSERV_FUNC(NAME) MODCMD_FUNC(NAME)
140 #define MEMOSERV_SYNTAX() svccmd_send_help_brief(user, memoserv, cmd)
141 #define MEMOSERV_MIN_PARAMS(N) if(argc < (N)) { \
142 reply("MSG_MISSING_PARAMS", argv[0]); \
146 DECLARE_LIST(memoList
, struct memo
*);
147 DEFINE_LIST(memoList
, struct memo
*);
148 DECLARE_LIST(historyList
, struct history
*);
149 DEFINE_LIST(historyList
, struct history
*);
151 /* memo_account.flags fields */
152 #define MEMO_NOTIFY_NEW 0x00000001
153 #define MEMO_NOTIFY_LOGIN 0x00000002
154 #define MEMO_DENY_NONCHANNEL 0x00000004
155 #define MEMO_IGNORE_RECIEPTS 0x00000008
156 #define MEMO_ALWAYS_RECIEPTS 0x00000010
157 #define MEMO_USE_PRIVMSG 0x00000020
159 struct memo_account
{
160 struct handle_info
*handle
;
161 unsigned int flags
: 6;
163 struct memoList sent
;
164 struct memoList recvd
;
165 struct historyList hsent
;
166 struct historyList hrecvd
;
170 struct userNode
*bot
;
175 #define MEMOSERV_FUNC(NAME) MODCMD_FUNC(NAME)
176 #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[])
177 typedef OPTION_FUNC(option_func_t
);
179 unsigned long memo_id
;
181 extern struct string_list
*autojoin_channels
;
182 const char *memoserv_module_deps
[] = { NULL
};
183 static struct module *memoserv_module
;
184 static struct log_type
*MS_LOG
;
185 static unsigned long memosSent
, memosExpired
;
186 static struct dict
*memos
; /* memo_account->handle->handle -> memo_account */
187 static struct dict
*historys
;
188 static dict_t memoserv_opt_dict
; /* contains option_func_t* */
190 static struct memo_account
*
191 memoserv_get_account(struct handle_info
*hi
)
193 struct memo_account
*ma
;
196 ma
= dict_find(memos
, hi
->handle
, NULL
);
199 ma
= calloc(1, sizeof(*ma
));
203 ma
->flags
= MEMO_NOTIFY_NEW
| MEMO_NOTIFY_LOGIN
| MEMO_USE_PRIVMSG
;
204 ma
->limit
= memoserv_conf
.limit
;
205 dict_insert(memos
, ma
->handle
->handle
, ma
);
206 dict_insert(historys
, ma
->handle
->handle
, ma
);
211 delete_memo(struct memo
*memo
)
213 memoList_remove(&memo
->recipient
->recvd
, memo
);
214 memoList_remove(&memo
->sender
->sent
, memo
);
220 delete_history(struct history
*history
)
222 historyList_remove(&history
->recipient
->hrecvd
, history
);
223 historyList_remove(&history
->sender
->hsent
, history
);
228 delete_memo_account(void *data
)
230 struct memo_account
*ma
= data
;
232 while (ma
->recvd
.used
)
233 delete_memo(ma
->recvd
.list
[0]);
234 while (ma
->sent
.used
)
235 delete_memo(ma
->sent
.list
[0]);
236 memoList_clean(&ma
->recvd
);
237 memoList_clean(&ma
->sent
);
239 while (ma
->hrecvd
.used
)
240 delete_history(ma
->hrecvd
.list
[0]);
241 while (ma
->hsent
.used
)
242 delete_history(ma
->hsent
.list
[0]);
243 historyList_clean(&ma
->hrecvd
);
244 historyList_clean(&ma
->hsent
);
252 for (it
= dict_first(memos
); it
; it
= iter_next(it
)) {
253 struct memo_account
*acct
= iter_data(it
);
255 for (ii
= 0; ii
< acct
->sent
.used
; ++ii
) {
256 struct memo
*memo
= acct
->sent
.list
[ii
];
257 if ((now
- memo
->sent
) > memoserv_conf
.message_expiry
) {
265 for (it
= dict_first(historys
); it
; it
= iter_next(it
)) {
266 struct memo_account
*acct
= iter_data(it
);
268 for (ii
= 0; ii
< acct
->hsent
.used
; ++ii
) {
269 struct history
*history
= acct
->hsent
.list
[ii
];
270 if ((now
- history
->sent
) > memoserv_conf
.message_expiry
) {
271 delete_history(history
);
280 expire_memos(UNUSED_ARG(void *data
))
282 if (memoserv_conf
.message_expiry
) {
284 timeq_add(now
+ memoserv_conf
.message_expiry
, expire_memos
, NULL
);
288 static struct history
*
289 add_history(time_t sent
, struct memo_account
*recipient
, struct memo_account
*sender
, unsigned long id
)
291 struct history
*history
;
293 history
= calloc(1, sizeof(*history
));
298 history
->recipient
= recipient
;
299 historyList_append(&recipient
->hrecvd
, history
);
300 history
->sender
= sender
;
301 historyList_append(&sender
->hsent
, history
);
302 history
->sent
= sent
;
309 add_memo(time_t sent
, struct memo_account
*recipient
, struct memo_account
*sender
, char *message
, int nfrom_read
)
312 struct history
*history
;
314 memo
= calloc(1, sizeof(*memo
));
323 memo
->recipient
= recipient
;
324 memoList_append(&recipient
->recvd
, memo
);
325 memo
->sender
= sender
;
326 memoList_append(&sender
->sent
, memo
);
328 memo
->message
= strdup(message
);
332 history
= add_history(sent
, recipient
, sender
, memo
->id
);
338 memoserv_can_send(struct userNode
*bot
, struct userNode
*user
, struct memo_account
*acct
)
340 extern struct userData
*_GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
);
341 struct userData
*dest
;
342 unsigned int i
= 0, match
= 0;
344 if (!user
->handle_info
)
347 /* Sanity checks here because if the user doesnt have a limit set
348 the limit comes out at like 21233242 if you try and use it. */
349 if (acct
->limit
> memoserv_conf
.limit
)
350 acct
->limit
= memoserv_conf
.limit
;
352 if (acct
->recvd
.used
> acct
->limit
) {
353 send_message(user
, bot
, "MSMSG_FULL_INBOX", acct
->handle
->handle
);
354 send_message(user
, bot
, "MSMSG_CANNOT_SEND", acct
->handle
->handle
);
358 if (acct
->handle
->ignores
->used
) {
359 for (i
=0; i
< acct
->handle
->ignores
->used
; i
++) {
360 if (user_matches_glob(user
, acct
->handle
->ignores
->list
[i
], MATCH_USENICK
)) {
367 send_message(user
, bot
, "MSMSG_CANNOT_SEND", acct
->handle
->handle
);
372 if (!(acct
->flags
& MEMO_DENY_NONCHANNEL
))
375 for (dest
= acct
->handle
->channels
; dest
; dest
= dest
->u_next
)
376 if (_GetChannelUser(dest
->channel
, user
->handle_info
, 1, 0))
379 send_message(user
, bot
, "MSMSG_CANNOT_SEND", acct
->handle
->handle
);
383 static struct memo
*find_memo(struct userNode
*user
, struct svccmd
*cmd
, struct memo_account
*ma
, const char *msgid
, unsigned int *id
)
386 if (!isdigit(msgid
[0])) {
388 reply("MSMSG_BAD_MESSAGE_ID", msgid
, ma
->recvd
.used
- 1);
390 reply("MSMSG_NO_MESSAGES");
393 memoid
= atoi(msgid
);
394 if (memoid
>= ma
->recvd
.used
) {
395 reply("MSMSG_NO_SUCH_MEMO");
398 return ma
->recvd
.list
[*id
= memoid
];
401 static MODCMD_FUNC(cmd_send
)
404 int reciept
= 0, inc
= 2;
405 struct handle_info
*hi
;
406 struct memo_account
*ma
, *sender
;
409 MEMOSERV_MIN_PARAMS(3);
411 if (checkDefCon(DEFCON_NO_NEW_MEMOS
) && !IsOper(user
)) {
412 reply("MSMSG_DEFCON_NO_NEW_MEMOS");
416 if (!(hi
= modcmd_get_handle_info(user
, argv
[1])))
419 if (!(sender
= memoserv_get_account(user
->handle_info
))
420 || !(ma
= memoserv_get_account(hi
))) {
421 reply("MSG_INTERNAL_FAILURE");
425 if (!(memoserv_can_send(cmd
->parent
->bot
, user
, ma
)))
428 inc
= 2; /* Start of message on 3rd ([2]) word */
429 if(argv
[2][0] == '-' && argv
[2][1] != '-') { /* first word is flags ('-r')*/
430 char *flags
= argv
[2];
431 inc
++; /* Start of message is now 1 word later */
432 for(flags
++;*flags
;flags
++) {
439 /* Unknown mode. Give an error */
440 reply("MSMSG_UNKNOWN_SEND_FLAG", *flags
);
446 inc
= 2; /* Start of message is word 2 */
448 message
= unsplit_string(argv
+ inc
, argc
- inc
, NULL
);
449 memo
= add_memo(now
, ma
, sender
, message
, 1);
450 if ((reciept
== 1) || (ma
->flags
& MEMO_ALWAYS_RECIEPTS
))
453 if (ma
->flags
& MEMO_NOTIFY_NEW
) {
454 struct userNode
*other
;
456 for (other
= ma
->handle
->users
; other
; other
= other
->next_authed
)
457 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
);
460 reply("MSMSG_MEMO_SENT", ma
->handle
->handle
, memo_id
);
464 static MODCMD_FUNC(cmd_list
)
466 struct memo_account
*ma
;
472 if (!(ma
= memoserv_get_account(user
->handle_info
)))
475 reply("MSMSG_LIST_HEAD");
477 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
480 for (ii
= 0; (ii
< ma
->recvd
.used
) && (ii
< 15); ++ii
) {
481 memo
= ma
->recvd
.list
[ii
];
482 localtime_r(&memo
->sent
, &tm
);
483 strftime(posted
, sizeof(posted
), "%I:%M %p, %m/%d/%Y", &tm
);
484 reply("MSMSG_LIST_FORMAT", ii
, memo
->sender
->handle
->handle
, memo
->reciept
? "(r)" : "", posted
);
489 reply("MSMSG_CLEAN_INBOX", ii
);
491 reply("MSMSG_MEMOS_FOUND", ii
);
492 reply("MSMSG_HOWTO_READ");
495 reply("MSMSG_LIST_END");
500 static MODCMD_FUNC(cmd_history
)
502 struct memo_account
*ma
;
503 struct history
*history
;
510 if (!(ma
= memoserv_get_account(user
->handle_info
)))
513 reply("MSMSG_HISTORY_HEADER");
515 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
518 for (it
= dict_first(historys
); it
; it
= iter_next(it
)) {
520 for (ii
= 0; ii
< ma
->hrecvd
.used
; ++ii
) {
521 history
= ma
->hrecvd
.list
[ii
];
522 if (!strcasecmp(history
->sender
->handle
->handle
, user
->handle_info
->handle
)) {
524 localtime_r(&history
->sent
, &tm
);
525 strftime(posted
, sizeof(posted
), "%I:%M %p, %m/%d/%Y", &tm
);
526 reply("MSMSG_HISTORY_FORMAT", history
->id
, history
->recipient
->handle
->handle
, posted
);
534 reply("MSMSG_MEMOS_FOUND", cc
);
536 reply("MSMSG_LIST_END");
541 static MODCMD_FUNC(cmd_read
)
543 struct memo_account
*ma
;
545 int rignore
= 0, brk
= 0, s
= 0;
551 if (!(ma
= memoserv_get_account(user
->handle_info
)))
553 if (!(memo
= find_memo(user
, cmd
, ma
, argv
[1], &memoid
)))
557 char *argtwo
= argv
[2];
582 localtime_r(&memo
->sent
, &tm
);
583 strftime(posted
, sizeof(posted
), "%I:%M %p, %m/%d/%Y", &tm
);
585 reply("MSMSG_MEMO_HEAD", memoid
, memo
->sender
->handle
->handle
, posted
);
586 send_message_type(4, user
, cmd
->parent
->bot
, "%s", memo
->message
);
590 if (ma
->flags
& MEMO_IGNORE_RECIEPTS
)
593 if (memo
->reciept
== 1) {
595 reply("MSMSG_MEMO_RECIEPT", rignore
? "ignoring" : "sending");
597 struct memo_account
*ma
;
598 struct memo_account
*sender
;
599 char content
[MAXLEN
];
601 ma
= memoserv_get_account(user
->handle_info
);
602 sender
= memoserv_get_account(memo
->sender
->handle
);
604 sprintf(content
, "%s has read your memo dated %s.", ma
->handle
->handle
, posted
);
606 memo
= add_memo(now
, sender
, ma
, content
, 1);
607 reply("MSMSG_MEMO_SENT", memob
->sender
->handle
->handle
, memo_id
);
609 if (sender
->flags
& MEMO_NOTIFY_NEW
) {
610 struct userNode
*other
;
612 for (other
= sender
->handle
->users
; other
; other
= other
->next_authed
)
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
);
622 static MODCMD_FUNC(cmd_delete
)
624 struct memo_account
*ma
;
628 MEMOSERV_MIN_PARAMS(2);
630 if (!(ma
= memoserv_get_account(user
->handle_info
)))
632 if (!irccasecmp(argv
[1], "*") || !irccasecmp(argv
[1], "all")) {
633 if ((argc
< 3) || irccasecmp(argv
[2], "confirm")) {
634 reply("MSMSG_USE_CONFIRM");
637 while (ma
->recvd
.used
)
638 delete_memo(ma
->recvd
.list
[0]);
639 reply("MSMSG_DELETED_ALL");
643 if (!(memo
= find_memo(user
, cmd
, ma
, argv
[1], &memoid
)))
646 reply("MSMSG_MEMO_DELETED", memoid
);
650 static MODCMD_FUNC(cmd_cancel
)
656 struct memo_account
*ma
;
658 MEMOSERV_MIN_PARAMS(2);
660 if (isdigit(argv
[1][0])) {
661 id
= strtoul(argv
[1], NULL
, 0);
663 reply("MSMSG_MEMO_CANCEL_NUMBER");
667 for (it
= dict_first(memos
); it
; it
= iter_next(it
)) {
669 for (ii
= 0; ii
< ma
->recvd
.used
; ++ii
) {
670 memo
= ma
->recvd
.list
[ii
];
672 if (id
== memo
->id
) {
673 if (!strcasecmp(memo
->sender
->handle
->handle
, user
->handle_info
->handle
)) {
675 reply("MSMSG_MEMO_READ", id
);
679 reply("MSMSG_MEMO_DELETED", id
);
683 reply("MSMSG_MEMO_DONT_OWN", id
);
690 reply("MSMSG_MEMO_CANT_LOCATE", id
);
694 static MODCMD_FUNC(cmd_expire
)
696 unsigned long old_expired
= memosExpired
;
698 reply("MSMSG_MESSAGES_EXPIRED", memosExpired
- old_expired
);
702 static MODCMD_FUNC(cmd_expiry
)
704 char interval
[INTERVALLEN
];
706 if (!memoserv_conf
.message_expiry
) {
707 reply("MSMSG_EXPIRY_OFF");
711 intervalString(interval
, memoserv_conf
.message_expiry
, user
->handle_info
);
712 reply("MSMSG_EXPIRY", interval
, memoserv_conf
.message_expiry
);
718 set_list(struct svccmd
*cmd
, struct userNode
*user
, struct handle_info
*hi
, int override
)
722 char *set_display
[] = {"AUTHNOTIFY", "NEWNOTIFY", "PRIVMSG", "PRIVATE", "LIMIT",
723 "IGNORERECIEPTS", "SENDRECIEPTS"};
725 reply("MSMSG_SET_OPTIONS");
728 /* Do this so options are presented in a consistent order. */
729 for (i
= 0; i
< ArrayLength(set_display
); ++i
)
730 if ((opt
= dict_find(memoserv_opt_dict
, set_display
[i
], NULL
)))
731 opt(cmd
, user
, hi
, override
, 0, NULL
);
732 reply("MSMSG_SET_OPTIONS_END");
735 static MODCMD_FUNC(cmd_set
)
737 struct handle_info
*hi
;
740 hi
= user
->handle_info
;
742 set_list(cmd
, user
, hi
, 0);
746 if (!(opt
= dict_find(memoserv_opt_dict
, argv
[1], NULL
))) {
747 reply("MSMSG_INVALID_OPTION", argv
[1]);
751 return opt(cmd
, user
, hi
, 0, argc
-1, argv
+1);
754 static MODCMD_FUNC(cmd_oset
)
756 struct handle_info
*hi
;
759 MEMOSERV_MIN_PARAMS(2);
761 if (!(hi
= get_victim_oper(cmd
, user
, argv
[1])))
765 set_list(cmd
, user
, hi
, 0);
769 if (!(opt
= dict_find(memoserv_opt_dict
, argv
[2], NULL
))) {
770 reply("MSMSG_INVALID_OPTION", argv
[2]);
774 return opt(cmd
, user
, hi
, 1, argc
-2, argv
+2);
777 static OPTION_FUNC(opt_newnotify
)
779 struct memo_account
*ma
;
782 if (!(ma
= memoserv_get_account(hi
)))
786 if (enabled_string(choice
)) {
787 ma
->flags
|= MEMO_NOTIFY_NEW
;
788 } else if (disabled_string(choice
)) {
789 ma
->flags
&= ~MEMO_NOTIFY_NEW
;
791 reply("MSMSG_INVALID_BINARY", choice
);
796 choice
= (ma
->flags
& MEMO_NOTIFY_NEW
) ? "on" : "off";
797 reply("MSMSG_SET_NEWNOTIFY", choice
);
801 static OPTION_FUNC(opt_privmsg
)
803 struct memo_account
*ma
;
806 if (!(ma
= memoserv_get_account(hi
)))
810 if (enabled_string(choice
)) {
811 ma
->flags
|= MEMO_USE_PRIVMSG
;
812 } else if (disabled_string(choice
)) {
813 ma
->flags
&= ~MEMO_USE_PRIVMSG
;
815 reply("MSMSG_INVALID_BINARY", choice
);
819 choice
= (ma
->flags
& MEMO_USE_PRIVMSG
) ? "on" : "off";
820 reply("MSMSG_SET_PRIVMSG", choice
);
824 static OPTION_FUNC(opt_authnotify
)
826 struct memo_account
*ma
;
829 if (!(ma
= memoserv_get_account(hi
)))
833 if (enabled_string(choice
)) {
834 ma
->flags
|= MEMO_NOTIFY_LOGIN
;
835 } else if (disabled_string(choice
)) {
836 ma
->flags
&= ~MEMO_NOTIFY_LOGIN
;
838 reply("MSMSG_INVALID_BINARY", choice
);
843 choice
= (ma
->flags
& MEMO_NOTIFY_LOGIN
) ? "on" : "off";
844 reply("MSMSG_SET_AUTHNOTIFY", choice
);
848 static OPTION_FUNC(opt_ignorereciepts
)
850 struct memo_account
*ma
;
853 if (!(ma
= memoserv_get_account(hi
)))
857 if (enabled_string(choice
)) {
858 ma
->flags
|= MEMO_IGNORE_RECIEPTS
;
859 } else if (disabled_string(choice
)) {
860 ma
->flags
&= ~MEMO_IGNORE_RECIEPTS
;
862 reply("MSMSG_INVALID_BINARY", choice
);
867 choice
= (ma
->flags
& MEMO_IGNORE_RECIEPTS
) ? "on" : "off";
868 reply("MSMSG_SET_IGNORERECIEPTS", choice
);
872 static OPTION_FUNC(opt_sendreciepts
)
874 struct memo_account
*ma
;
877 if (!(ma
= memoserv_get_account(hi
)))
881 if (enabled_string(choice
)) {
882 ma
->flags
|= MEMO_ALWAYS_RECIEPTS
;
883 } else if (disabled_string(choice
)) {
884 ma
->flags
&= ~MEMO_ALWAYS_RECIEPTS
;
886 reply("MSMSG_INVALID_BINARY", choice
);
891 choice
= (ma
->flags
& MEMO_ALWAYS_RECIEPTS
) ? "on" : "off";
892 reply("MSMSG_SET_SENDRECIEPTS", choice
);
896 static OPTION_FUNC(opt_private
)
898 struct memo_account
*ma
;
901 if (!(ma
= memoserv_get_account(hi
)))
905 if (enabled_string(choice
)) {
906 ma
->flags
|= MEMO_DENY_NONCHANNEL
;
907 } else if (disabled_string(choice
)) {
908 ma
->flags
&= ~MEMO_DENY_NONCHANNEL
;
910 reply("MSMSG_INVALID_BINARY", choice
);
915 choice
= (ma
->flags
& MEMO_DENY_NONCHANNEL
) ? "on" : "off";
916 reply("MSMSG_SET_PRIVATE", choice
);
920 static OPTION_FUNC(opt_limit
)
922 struct memo_account
*ma
;
925 if (!(ma
= memoserv_get_account(hi
)))
928 choice
= atoi(argv
[1]);
929 if (choice
> memoserv_conf
.limit
)
930 choice
= memoserv_conf
.limit
;
935 reply("MSMSG_SET_LIMIT", ma
->limit
);
939 static MODCMD_FUNC(cmd_status
)
941 struct memo_account
*ma
;
946 for (it
= dict_first(memos
); it
; it
= iter_next(it
)) {
948 for (ii
= 0; ii
< ma
->recvd
.used
; ++ii
)
952 for (it
= dict_first(historys
); it
; it
= iter_next(it
)) {
954 for (ii
= 0; ii
< ma
->hrecvd
.used
; ++ii
)
958 reply("MSMSG_STATUS_HIST_TOTAL", hc
);
959 reply("MSMSG_STATUS_TOTAL", mc
);
960 reply("MSMSG_STATUS_EXPIRED", memosExpired
);
961 reply("MSMSG_STATUS_SENT", memosSent
);
966 memoserv_conf_read(void)
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
);
977 str
= database_get_data(conf_node
, "limit", RECDB_QSTRING
);
978 memoserv_conf
.limit
= str
? atoi(str
) : 50;
980 str
= database_get_data(conf_node
, "message_expiry", RECDB_QSTRING
);
981 memoserv_conf
.message_expiry
= str
? ParseInterval(str
) : 60*24*30;
985 memoserv_user_read(const char *key
, struct record_data
*hir
)
988 struct memo_account
*ma
;
989 struct handle_info
*hi
;
991 if (!(hi
= get_handle_info(key
)))
994 ma
= dict_find(memos
, hi
->handle
, NULL
);
999 ma
= calloc(1, sizeof(*ma
));
1005 str
= database_get_data(hir
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
1007 log_module(MS_LOG
, LOG_ERROR
, "Flags not present in memo %s; skipping", key
);
1010 ma
->flags
= strtoul(str
, NULL
, 0);
1012 str
= database_get_data(hir
->d
.object
, KEY_LIMIT
, RECDB_QSTRING
);
1014 log_module(MS_LOG
, LOG_ERROR
, "Limit not present in memo %s; skipping", key
);
1017 ma
->limit
= strtoul(str
, NULL
, 0);
1019 dict_insert(memos
, ma
->handle
->handle
, ma
);
1020 dict_insert(historys
, ma
->handle
->handle
, ma
);
1026 memoserv_memo_read(const char *key
, struct record_data
*hir
)
1029 struct handle_info
*sender
, *recipient
;
1034 if (hir
->type
!= RECDB_OBJECT
) {
1035 log_module(MS_LOG
, LOG_WARNING
, "Unexpected rectype %d for %s.", hir
->type
, key
);
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
);
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
);
1050 id
= strtoul(str
, NULL
, 0);
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
);
1057 } else if (!(recipient
= get_handle_info(str
))) {
1058 log_module(MS_LOG
, LOG_ERROR
, "Invalid recipient %s in memo %s; skipping", str
, key
);
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
);
1065 } else if (!(sender
= get_handle_info(str
))) {
1066 log_module(MS_LOG
, LOG_ERROR
, "Invalid sender %s in memo %s; skipping", str
, key
);
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
);
1075 memo
= add_memo(sent
, memoserv_get_account(recipient
), memoserv_get_account(sender
), str
, 0);
1076 if ((str
= database_get_data(hir
->d
.object
, KEY_READ
, RECDB_QSTRING
)))
1079 if ((str
= database_get_data(hir
->d
.object
, KEY_RECIEPT
, RECDB_QSTRING
)))
1088 memoserv_history_read(const char *key
, struct record_data
*hir
)
1091 struct handle_info
*sender
, *recipient
;
1092 struct history
*history
;
1096 if (hir
->type
!= RECDB_OBJECT
) {
1097 log_module(MS_LOG
, LOG_WARNING
, "Unexpected rectype %d for %s.", hir
->type
, key
);
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
);
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
);
1112 id
= strtoul(str
, NULL
, 0);
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
);
1117 } else if (!(recipient
= get_handle_info(str
))) {
1118 log_module(MS_LOG
, LOG_ERROR
, "Invalid recipient %s in history %s; skipping", str
, key
);
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
);
1125 } else if (!(sender
= get_handle_info(str
))) {
1126 log_module(MS_LOG
, LOG_ERROR
, "Invalid sender %s in history %s; skipping", str
, key
);
1130 history
= add_history(sent
, memoserv_get_account(recipient
), memoserv_get_account(sender
), id
);
1136 memoserv_saxdb_read(struct dict
*database
)
1138 struct dict
*section
;
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
));
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
));
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
));
1157 memoserv_write_users(struct saxdb_context
*ctx
, struct memo_account
*ma
)
1159 saxdb_start_record(ctx
, ma
->handle
->handle
, 0);
1161 saxdb_write_int(ctx
, KEY_FLAGS
, ma
->flags
);
1162 saxdb_write_int(ctx
, KEY_LIMIT
, ma
->limit
);
1164 saxdb_end_record(ctx
);
1169 memoserv_write_memos(struct saxdb_context
*ctx
, struct memo
*memo
)
1173 memset(str
, '\0', sizeof(str
));
1174 saxdb_start_record(ctx
, inttobase64(str
, memo
->id
, sizeof(str
)-1), 0);
1176 saxdb_write_int(ctx
, KEY_SENT
, memo
->sent
);
1177 saxdb_write_int(ctx
, KEY_ID
, memo
->id
);
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
);
1183 saxdb_write_int(ctx
, KEY_READ
, 1);
1186 saxdb_write_int(ctx
, KEY_RECIEPT
, 1);
1188 saxdb_end_record(ctx
);
1193 memoserv_write_history(struct saxdb_context
*ctx
, struct history
*history
)
1197 memset(str
, '\0', sizeof(str
));
1198 saxdb_start_record(ctx
, inttobase64(str
, history
->id
, sizeof(str
)-1), 0);
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
);
1205 saxdb_end_record(ctx
);
1210 memoserv_saxdb_write(struct saxdb_context
*ctx
)
1213 struct memo_account
*ma
;
1215 struct history
*history
;
1219 saxdb_start_record(ctx
, KEY_MAIN_ACCOUNTS
, 1);
1220 for (it
= dict_first(memos
); it
; it
= iter_next(it
)) {
1222 memoserv_write_users(ctx
, ma
);
1224 saxdb_end_record(ctx
);
1227 saxdb_start_record(ctx
, KEY_MAIN_MEMOS
, 1);
1228 for (it
= dict_first(memos
); it
; it
= iter_next(it
)) {
1230 for (ii
= 0; ii
< ma
->recvd
.used
; ++ii
) {
1231 memo
= ma
->recvd
.list
[ii
];
1232 memoserv_write_memos(ctx
, memo
);
1235 saxdb_end_record(ctx
);
1238 saxdb_start_record(ctx
, KEY_MAIN_HISTORY
, 1);
1239 for (it
= dict_first(historys
); it
; it
= iter_next(it
)) {
1241 for (ii
= 0; ii
< ma
->hrecvd
.used
; ++ii
) {
1242 history
= ma
->hrecvd
.list
[ii
];
1243 memoserv_write_history(ctx
, history
);
1246 saxdb_end_record(ctx
);
1252 memoserv_cleanup(void)
1255 dict_delete(historys
);
1259 memoserv_check_messages(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
1261 unsigned int ii
, unseen
;
1262 struct memo_account
*ma
;
1265 if (!user
->uplink
->burst
) {
1266 if (!(ma
= memoserv_get_account(user
->handle_info
))
1267 || !(ma
->flags
& MEMO_NOTIFY_LOGIN
))
1269 for (ii
= unseen
= 0; ii
< ma
->recvd
.used
; ++ii
) {
1270 memo
= ma
->recvd
.list
[ii
];
1274 if (ma
->recvd
.used
&& memoserv
)
1275 if(unseen
) send_message_type((ma
->flags
& MEMO_USE_PRIVMSG
)? 1 : 0, user
, memoserv
, "MSMSG_MEMOS_INBOX", unseen
, ma
->recvd
.used
- unseen
);
1280 memoserv_rename_account(struct handle_info
*hi
, const char *old_handle
)
1282 struct memo_account
*ma
;
1283 if (!(ma
= dict_find(memos
, old_handle
, NULL
)))
1285 dict_remove2(memos
, old_handle
, 1);
1286 dict_insert(memos
, hi
->handle
, ma
);
1288 dict_remove2(historys
, old_handle
, 1);
1289 dict_insert(historys
, hi
->handle
, ma
);
1293 memoserv_unreg_account(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
1295 dict_remove(memos
, handle
->handle
);
1296 dict_remove(historys
, handle
->handle
);
1302 MS_LOG
= log_register_type("MemoServ", "file:memoserv.log");
1304 historys
= dict_new();
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
);
1313 memoserv_module
= module_register("MemoServ", MS_LOG
, "mod-memoserv.help", NULL
);
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
);
1326 memoserv_opt_dict
= dict_new();
1327 dict_insert(memoserv_opt_dict
, "AUTHNOTIFY", opt_authnotify
);
1328 dict_insert(memoserv_opt_dict
, "NEWNOTIFY", opt_newnotify
);
1329 dict_insert(memoserv_opt_dict
, "PRIVMSG", opt_privmsg
);
1330 dict_insert(memoserv_opt_dict
, "PRIVATE", opt_private
);
1331 dict_insert(memoserv_opt_dict
, "IGNORERECIEPTS", opt_ignorereciepts
);
1332 dict_insert(memoserv_opt_dict
, "SENDRECIEPTS", opt_sendreciepts
);
1333 dict_insert(memoserv_opt_dict
, "LIMIT", opt_limit
);
1335 message_register_table(msgtab
);
1337 if (memoserv_conf
.message_expiry
)
1338 timeq_add(now
+ memoserv_conf
.message_expiry
, expire_memos
, NULL
);
1344 memoserv_finalize(void) {
1345 struct chanNode
*chan
;
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
);
1356 str
= database_get_data(conf_node
, "bot", RECDB_QSTRING
);
1358 memoserv
= memoserv_conf
.bot
;
1359 const char *modes
= conf_get_data("modules/memoserv/modes", RECDB_QSTRING
);
1360 memoserv
= AddLocalUser(str
, str
, NULL
, "User-User Memorandum Services", modes
? modes
: NULL
);
1363 log_module(MS_LOG
, LOG_ERROR
, "database_get_data for memoserv_conf.bot failed!");
1367 if (autojoin_channels
&& memoserv
) {
1368 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
1369 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
1370 AddChannelUser(memoserv
, chan
)->modes
|= MODE_CHANOP
;