/* MemoServ functions.
*
* (C) 2003 Anope Team
* Contact us at info@anope.org
*
* Please read COPYING and README for furhter details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*
* $Id: memoserv.c 5 2004-03-29 01:29:50Z dane $
*
*/
#include "services.h"
#include "pseudo.h"
/*************************************************************************/
/* *INDENT-OFF* */
NickCore *nclists[1024];
static int delmemo(MemoInfo *mi, int num);
static int do_help(User *u);
static int do_send(User *u);
void memo_send(User *u, char *name, char *text, int z);
static int do_cancel(User *u);
static int do_list(User *u);
static int do_read(User *u);
static int do_del(User *u);
static int do_set(User *u);
static int do_set_notify(User *u, MemoInfo *mi, char *param);
static int do_set_limit(User *u, MemoInfo *mi, char *param);
static int do_info(User *u);
static int do_staff(User *u);
static int do_sendall(User *u);
void moduleAddMemoServCmds(void);
/*************************************************************************/
void moduleAddMemoServCmds(void) {
Command *c;
c = createCommand("HELP", do_help, NULL, -1, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("SEND", do_send, NULL, MEMO_HELP_SEND, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("CANCEL", do_cancel, NULL, MEMO_HELP_CANCEL, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("LIST", do_list, NULL, MEMO_HELP_LIST, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("READ", do_read, NULL, MEMO_HELP_READ, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("DEL", do_del, NULL, MEMO_HELP_DEL, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("STAFF", do_staff, is_services_oper, MEMO_HELP_STAFF, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("SET", do_set, NULL, MEMO_HELP_SET, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("SET NOTIFY", NULL, NULL, MEMO_HELP_SET_NOTIFY, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
c = createCommand("SET LIMIT", NULL, NULL, -1,MEMO_HELP_SET_LIMIT, MEMO_SERVADMIN_HELP_SET_LIMIT,MEMO_SERVADMIN_HELP_SET_LIMIT, MEMO_SERVADMIN_HELP_SET_LIMIT); addCoreCommand(MEMOSERV,c);
c = createCommand("INFO", do_info, NULL, -1,MEMO_HELP_INFO, MEMO_SERVADMIN_HELP_INFO,MEMO_SERVADMIN_HELP_INFO, MEMO_SERVADMIN_HELP_INFO); addCoreCommand(MEMOSERV,c);
c = createCommand("SENDALL", do_sendall, is_services_admin, MEMO_HELP_SENDALL, -1,-1,-1,-1); addCoreCommand(MEMOSERV,c);
}
/*************************************************************************/
/*************************************************************************/
/* *INDENT-ON* */
/* MemoServ initialization. */
void ms_init(void)
{
Command *cmd;
moduleAddMemoServCmds();
cmd = findCommand(MEMOSERV, "SET LIMIT");
if (cmd)
cmd->help_param1 = (char *) (long) MSMaxMemos;
}
/*************************************************************************/
/* memoserv: Main MemoServ routine.
* Note that the User structure passed to the do_* routines will
* always be valid (non-NULL) and will always have a valid
* NickInfo pointer in the `ni' field.
*/
void memoserv(User * u, char *buf)
{
char *cmd, *s;
cmd = strtok(buf, " ");
if (!cmd) {
return;
} else if (stricmp(cmd, "\1PING") == 0) {
if (!(s = strtok(NULL, "")))
s = "\1";
notice(s_MemoServ, u->nick, "\1PING %s", s);
} else if (skeleton) {
notice_lang(s_MemoServ, u, SERVICE_OFFLINE, s_MemoServ);
} else {
if (!u->na && stricmp(cmd, "HELP") != 0)
notice_lang(s_MemoServ, u, NICK_NOT_REGISTERED_HELP,
s_NickServ);
else
mod_run_cmd(s_MemoServ, u, MEMOSERV, cmd);
}
}
/*************************************************************************/
/* check_memos: See if the given user has any unread memos, and send a
* NOTICE to that user if so (and if the appropriate flag is
* set).
*/
void check_memos(User * u)
{
NickCore *nc;
int i, newcnt = 0;
if (!(nc = (u->na ? u->na->nc : NULL)) || !nick_recognized(u) ||
!(nc->flags & NI_MEMO_SIGNON))
return;
for (i = 0; i < nc->memos.memocount; i++) {
if (nc->memos.memos[i].flags & MF_UNREAD)
newcnt++;
}
if (newcnt > 0) {
notice_lang(s_MemoServ, u,
newcnt == 1 ? MEMO_HAVE_NEW_MEMO : MEMO_HAVE_NEW_MEMOS,
newcnt);
if (newcnt == 1 && (nc->memos.memos[i - 1].flags & MF_UNREAD)) {
notice_lang(s_MemoServ, u, MEMO_TYPE_READ_LAST, s_MemoServ);
} else if (newcnt == 1) {
for (i = 0; i < nc->memos.memocount; i++) {
if (nc->memos.memos[i].flags & MF_UNREAD)
break;
}
notice_lang(s_MemoServ, u, MEMO_TYPE_READ_NUM, s_MemoServ,
nc->memos.memos[i].number);
} else {
notice_lang(s_MemoServ, u, MEMO_TYPE_LIST_NEW, s_MemoServ);
}
}
if (nc->memos.memomax > 0 && nc->memos.memocount >= nc->memos.memomax) {
if (nc->memos.memocount > nc->memos.memomax)
notice_lang(s_MemoServ, u, MEMO_OVER_LIMIT, nc->memos.memomax);
else
notice_lang(s_MemoServ, u, MEMO_AT_LIMIT, nc->memos.memomax);
}
}
/*************************************************************************/
/*********************** MemoServ private routines ***********************/
/*************************************************************************/
/* Return the MemoInfo corresponding to the given nick or channel name.
* Return in `ischan' 1 if the name was a channel name, else 0.
*/
static MemoInfo *getmemoinfo(const char *name, int *ischan)
{
if (*name == '#') {
ChannelInfo *ci;
if (ischan)
*ischan = 1;
ci = cs_findchan(name);
if (ci && !(ci->flags & CI_VERBOTEN))
return &ci->memos;
else
return NULL;
} else {
NickAlias *na;
if (ischan)
*ischan = 0;
na = findnick(name);
if (na && !(na->status & NS_VERBOTEN))
return &na->nc->memos;
else
return NULL;
}
}
/*************************************************************************/
/* Delete a memo by number. Return 1 if the memo was found, else 0. */
static int delmemo(MemoInfo * mi, int num)
{
int i;
for (i = 0; i < mi->memocount; i++) {
if (mi->memos[i].number == num)
break;
}
if (i < mi->memocount) {
free(mi->memos[i].text); /* Deallocate memo text memory */
mi->memocount--; /* One less memo now */
if (i < mi->memocount) /* Move remaining memos down a slot */
memmove(mi->memos + i, mi->memos + i + 1,
sizeof(Memo) * (mi->memocount - i));
if (mi->memocount == 0) { /* If no more memos, free array */
free(mi->memos);
mi->memos = NULL;
}
return 1;
} else {
return 0;
}
}
/*************************************************************************/
/*********************** MemoServ command routines ***********************/
/*************************************************************************/
/* Return a help message. */
static int do_help(User * u)
{
char *cmd = strtok(NULL, "");
if (!cmd) {
notice_help(s_MemoServ, u, MEMO_HELP);
if (is_services_oper(u)) {
notice_help(s_MemoServ, u, MEMO_HELP_OPER);
}
if (is_services_admin(u)) {
notice_help(s_MemoServ, u, MEMO_HELP_ADMIN);
}
moduleDisplayHelp(3, u);
notice_help(s_MemoServ, u, MEMO_HELP_FOOTER, s_ChanServ);
} else {
mod_help_cmd(s_MemoServ, u, MEMOSERV, cmd);
}
return MOD_CONT;
}
/*************************************************************************/
/* Send a memo to a nick/channel. */
static int do_send(User * u)
{
char *name = strtok(NULL, " ");
char *text = strtok(NULL, "");
int z = 0;
memo_send(u, name, text, z);
return MOD_CONT;
}
/**
* Split from do_send, this way we can easily send a memo from any point :)
* u - sender User
* name - target name
* text - memo Text
* z - output level,
* 0 - reply to user
* 1 - silent
* 2 - silent with no delay timer
**/
void memo_send(User * u, char *name, char *text, int z)
{
int ischan;
Memo *m;
MemoInfo *mi;
time_t now = time(NULL);
char *source = u->na->nc->display;
int is_servadmin = is_services_admin(u);
if (readonly) {
notice_lang(s_MemoServ, u, MEMO_SEND_DISABLED);
} else if (checkDefCon(DEFCON_NO_NEW_MEMOS)) {
notice_lang(s_MemoServ, u, OPER_DEFCON_DENIED);
return;
} else if (!text) {
if (z == 0)
syntax_error(s_MemoServ, u, "SEND", MEMO_SEND_SYNTAX);
} else if (!nick_recognized(u)) {
if (z == 0)
notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
} else if (!(mi = getmemoinfo(name, &ischan))) {
if (z == 0)
notice_lang(s_MemoServ, u,
ischan ? CHAN_X_NOT_REGISTERED :
NICK_X_NOT_REGISTERED, name);
} else if (z != 2 && MSSendDelay > 0 &&
u && u->lastmemosend + MSSendDelay > now && !is_servadmin) {
u->lastmemosend = now;
if (z == 0)
notice_lang(s_MemoServ, u, MEMO_SEND_PLEASE_WAIT, MSSendDelay);
} else if (mi->memomax == 0 && !is_servadmin) {
if (z == 0)
notice_lang(s_MemoServ, u, MEMO_X_GETS_NO_MEMOS, name);
} else if (mi->memomax > 0 && mi->memocount >= mi->memomax
&& !is_servadmin) {
if (z == 0)
notice_lang(s_MemoServ, u, MEMO_X_HAS_TOO_MANY_MEMOS, name);
} else {
u->lastmemosend = now;
mi->memocount++;
mi->memos = srealloc(mi->memos, sizeof(Memo) * mi->memocount);
m = &mi->memos[mi->memocount - 1];
strscpy(m->sender, source, NICKMAX);
if (mi->memocount > 1) {
m->number = m[-1].number + 1;
if (m->number < 1) {
int i;
for (i = 0; i < mi->memocount; i++)
mi->memos[i].number = i + 1;
}
} else {
m->number = 1;
}
m->time = time(NULL);
m->text = sstrdup(text);
m->flags = MF_UNREAD;
if (z == 0)
notice_lang(s_MemoServ, u, MEMO_SENT, name);
if (!ischan) {
NickAlias *na;
NickCore *nc = (findnick(name))->nc;
if (MSNotifyAll) {
if ((nc->flags & NI_MEMO_RECEIVE)
&& get_ignore(name) == NULL) {
int i;
for (i = 0; i < nc->aliases.count; i++) {
na = nc->aliases.list[i];
if (na->u && nick_identified(na->u))
notice_lang(s_MemoServ, na->u,
MEMO_NEW_MEMO_ARRIVED, source,
s_MemoServ, m->number);
}
} else {
if ((u = finduser(name)) && nick_identified(u))
notice_lang(s_MemoServ, u, MEMO_NEW_MEMO_ARRIVED,
source, s_MemoServ, m->number);
} /* if (flags & MEMO_RECEIVE) */
} /* if (MSNotifyAll) */
} else {
struct c_userlist *cu, *next;
Channel *c;
if (MSNotifyAll && (c = findchan(name))) {
for (cu = c->users; cu; cu = next) {
next = cu->next;
if (check_access(cu->user, c->ci, CA_MEMO)) {
if (cu->user->na
&& (cu->user->na->nc->flags & NI_MEMO_RECEIVE)
&& get_ignore(cu->user->nick) == NULL) {
notice_lang(s_MemoServ, cu->user,
MEMO_NEW_X_MEMO_ARRIVED,
c->ci->name, s_MemoServ,
c->ci->name, m->number);
}
}
}
} /* MSNotifyAll */
} /* if (!ischan) */
} /* if command is valid */
}
/*************************************************************************/
static int do_cancel(User * u)
{
int ischan;
char *name = strtok(NULL, " ");
MemoInfo *mi;
if (!name) {
syntax_error(s_MemoServ, u, "CANCEL", MEMO_CANCEL_SYNTAX);
} else if (!nick_recognized(u)) {
notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
} else if (!(mi = getmemoinfo(name, &ischan))) {
notice_lang(s_MemoServ, u,
ischan ? CHAN_X_NOT_REGISTERED : NICK_X_NOT_REGISTERED,
name);
} else {
int i;
for (i = mi->memocount - 1; i >= 0; i--) {
if ((mi->memos[i].flags & MF_UNREAD)
&& !stricmp(mi->memos[i].sender, u->na->nc->display)) {
delmemo(mi, mi->memos[i].number);
notice_lang(s_MemoServ, u, MEMO_CANCELLED, name);
return MOD_CONT;
}
}
notice_lang(s_MemoServ, u, MEMO_CANCEL_NONE);
}
return MOD_CONT;
}
/*************************************************************************/
/* Display a single memo entry, possibly printing the header first. */
static int list_memo(User * u, int index, MemoInfo * mi, int *sent_header,
int new, const char *chan)
{
Memo *m;
char timebuf[64];
struct tm tm;
if (index < 0 || index >= mi->memocount)
return 0;
if (!*sent_header) {
if (chan) {
notice_lang(s_MemoServ, u,
new ? MEMO_LIST_CHAN_NEW_MEMOS :
MEMO_LIST_CHAN_MEMOS, chan, s_MemoServ, chan);
} else {
notice_lang(s_MemoServ, u,
new ? MEMO_LIST_NEW_MEMOS : MEMO_LIST_MEMOS,
u->nick, s_MemoServ);
}
notice_lang(s_MemoServ, u, MEMO_LIST_HEADER);
*sent_header = 1;
}
m = &mi->memos[index];
tm = *localtime(&m->time);
strftime_lang(timebuf, sizeof(timebuf),
u, STRFTIME_DATE_TIME_FORMAT, &tm);
timebuf[sizeof(timebuf) - 1] = 0; /* just in case */
notice_lang(s_MemoServ, u, MEMO_LIST_FORMAT,
(m->flags & MF_UNREAD) ? '*' : ' ',
m->number, m->sender, timebuf);
return 1;
}
static int list_memo_callback(User * u, int num, va_list args)
{
MemoInfo *mi = va_arg(args, MemoInfo *);
int *sent_header = va_arg(args, int *);
const char *chan = va_arg(args, const char *);
int i;
for (i = 0; i < mi->memocount; i++) {
if (mi->memos[i].number == num)
break;
}
/* Range checking done by list_memo() */
return list_memo(u, i, mi, sent_header, 0, chan);
}
/* List the memos (if any) for the source nick or given channel. */
static int do_list(User * u)
{
char *param = strtok(NULL, " "), *chan = NULL;
ChannelInfo *ci;
MemoInfo *mi;
Memo *m;
int i;
if (param && *param == '#') {
chan = param;
param = strtok(NULL, " ");
if (!(ci = cs_findchan(chan))) {
notice_lang(s_MemoServ, u, CHAN_X_NOT_REGISTERED, chan);
return MOD_CONT;
} else if (ci->flags & CI_VERBOTEN) {
notice_lang(s_MemoServ, u, CHAN_X_FORBIDDEN, chan);
return MOD_CONT;
} else if (!check_access(u, ci, CA_MEMO)) {
notice_lang(s_MemoServ, u, ACCESS_DENIED);
return MOD_CONT;
}
mi = &ci->memos;
} else {
if (!nick_identified(u)) {
notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
return MOD_CONT;
}
mi = &u->na->nc->memos;
}
if (param && !isdigit(*param) && stricmp(param, "NEW") != 0) {
syntax_error(s_MemoServ, u, "LIST", MEMO_LIST_SYNTAX);
} else if (mi->memocount == 0) {
if (chan)
notice_lang(s_MemoServ, u, MEMO_X_HAS_NO_MEMOS, chan);
else
notice_lang(s_MemoServ, u, MEMO_HAVE_NO_MEMOS);
} else {
int sent_header = 0;
if (param && isdigit(*param)) {
process_numlist(param, NULL, list_memo_callback, u,
mi, &sent_header, chan);
} else {
if (param) {
for (i = 0, m = mi->memos; i < mi->memocount; i++, m++) {
if (m->flags & MF_UNREAD)
break;
}
if (i == mi->memocount) {
if (chan)
notice_lang(s_MemoServ, u, MEMO_X_HAS_NO_NEW_MEMOS,
chan);
else
notice_lang(s_MemoServ, u, MEMO_HAVE_NO_NEW_MEMOS);
return MOD_CONT;
}
}
for (i = 0, m = mi->memos; i < mi->memocount; i++, m++) {
if (param && !(m->flags & MF_UNREAD))
continue;
list_memo(u, i, mi, &sent_header, param != NULL, chan);
}
}
}
return MOD_CONT;
}
/*************************************************************************/
/* Send a single memo to the given user. */
static int read_memo(User * u, int index, MemoInfo * mi, const char *chan)
{
Memo *m;
char timebuf[64];
struct tm tm;
if (index < 0 || index >= mi->memocount)
return 0;
m = &mi->memos[index];
tm = *localtime(&m->time);
strftime_lang(timebuf, sizeof(timebuf),
u, STRFTIME_DATE_TIME_FORMAT, &tm);
timebuf[sizeof(timebuf) - 1] = 0;
if (chan)
notice_lang(s_MemoServ, u, MEMO_CHAN_HEADER, m->number,
m->sender, timebuf, s_MemoServ, chan, m->number);
else
notice_lang(s_MemoServ, u, MEMO_HEADER, m->number,
m->sender, timebuf, s_MemoServ, m->number);
notice_lang(s_MemoServ, u, MEMO_TEXT, m->text);
m->flags &= ~MF_UNREAD;
return 1;
}
static int read_memo_callback(User * u, int num, va_list args)
{
MemoInfo *mi = va_arg(args, MemoInfo *);
const char *chan = va_arg(args, const char *);
int i;
for (i = 0; i < mi->memocount; i++) {
if (mi->memos[i].number == num)
break;
}
/* Range check done in read_memo */
return read_memo(u, i, mi, chan);
}
/* Read memos. */
static int do_read(User * u)
{
MemoInfo *mi;
ChannelInfo *ci;
char *numstr = strtok(NULL, " "), *chan = NULL;
int num, count;
if (numstr && *numstr == '#') {
chan = numstr;
numstr = strtok(NULL, " ");
if (!(ci = cs_findchan(chan))) {
notice_lang(s_MemoServ, u, CHAN_X_NOT_REGISTERED, chan);
return MOD_CONT;
} else if (ci->flags & CI_VERBOTEN) {
notice_lang(s_MemoServ, u, CHAN_X_FORBIDDEN, chan);
return MOD_CONT;
} else if (!check_access(u, ci, CA_MEMO)) {
notice_lang(s_MemoServ, u, ACCESS_DENIED);
return MOD_CONT;
}
mi = &ci->memos;
} else {
if (!nick_identified(u)) {
notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
return MOD_CONT;
}
mi = &u->na->nc->memos;
}
num = numstr ? atoi(numstr) : -1;
if (!numstr
|| (stricmp(numstr, "LAST") != 0 && stricmp(numstr, "NEW") != 0
&& num <= 0)) {
syntax_error(s_MemoServ, u, "READ", MEMO_READ_SYNTAX);
} else if (mi->memocount == 0) {
if (chan)
notice_lang(s_MemoServ, u, MEMO_X_HAS_NO_MEMOS, chan);
else
notice_lang(s_MemoServ, u, MEMO_HAVE_NO_MEMOS);
} else {
int i;
if (stricmp(numstr, "NEW") == 0) {
int readcount = 0;
for (i = 0; i < mi->memocount; i++) {
if (mi->memos[i].flags & MF_UNREAD) {
read_memo(u, i, mi, chan);
readcount++;
}
}
if (!readcount) {
if (chan)
notice_lang(s_MemoServ, u, MEMO_X_HAS_NO_NEW_MEMOS,
chan);
else
notice_lang(s_MemoServ, u, MEMO_HAVE_NO_NEW_MEMOS);
}
} else if (stricmp(numstr, "LAST") == 0) {
for (i = 0; i < mi->memocount - 1; i++);
read_memo(u, i, mi, chan);
} else { /* number[s] */
if (!process_numlist(numstr, &count, read_memo_callback, u,
mi, chan)) {
if (count == 1)
notice_lang(s_MemoServ, u, MEMO_DOES_NOT_EXIST, num);
else
notice_lang(s_MemoServ, u, MEMO_LIST_NOT_FOUND,
numstr);
}
}
}
return MOD_CONT;
}
/*************************************************************************/
/* Delete a single memo from a MemoInfo. */
static int del_memo_callback(User * u, int num, va_list args)
{
MemoInfo *mi = va_arg(args, MemoInfo *);
int *last = va_arg(args, int *);
int *last0 = va_arg(args, int *);
char **end = va_arg(args, char **);
int *left = va_arg(args, int *);
if (delmemo(mi, num)) {
if (num != (*last) + 1) {
if (*last != -1) {
int len;
if (*last0 != *last)
len = snprintf(*end, *left, ",%d-%d", *last0, *last);
else
len = snprintf(*end, *left, ",%d", *last);
*end += len;
*left -= len;
}
*last0 = num;
}
*last = num;
return 1;
} else {
return 0;
}
}
/* Delete memos. */
static int do_del(User * u)
{
MemoInfo *mi;
ChannelInfo *ci;
char *numstr = strtok(NULL, ""), *chan = NULL;
int last, last0, i;
char buf[BUFSIZE], *end;
int delcount, count, left;
if (numstr && *numstr == '#') {
chan = strtok(numstr, " ");
numstr = strtok(NULL, "");
if (!(ci = cs_findchan(chan))) {
notice_lang(s_MemoServ, u, CHAN_X_NOT_REGISTERED, chan);
return MOD_CONT;
} else if (ci->flags & CI_VERBOTEN) {
notice_lang(s_MemoServ, u, CHAN_X_FORBIDDEN, chan);
return MOD_CONT;
} else if (!check_access(u, ci, CA_MEMO)) {
notice_lang(s_MemoServ, u, ACCESS_DENIED);
return MOD_CONT;
}
mi = &ci->memos;
} else {
if (!nick_identified(u)) {
notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
return MOD_CONT;
}
mi = &u->na->nc->memos;
}
if (!numstr
|| (!isdigit(*numstr) && stricmp(numstr, "ALL") != 0
&& stricmp(numstr, "LAST") != 0)) {
syntax_error(s_MemoServ, u, "DEL", MEMO_DEL_SYNTAX);
} else if (mi->memocount == 0) {
if (chan)
notice_lang(s_MemoServ, u, MEMO_X_HAS_NO_MEMOS, chan);
else
notice_lang(s_MemoServ, u, MEMO_HAVE_NO_MEMOS);
} else {
if (isdigit(*numstr)) {
/* Delete a specific memo or memos. */
last = -1; /* Last memo deleted */
last0 = -1; /* Beginning of range of last memos deleted */
end = buf;
left = sizeof(buf);
delcount =
process_numlist(numstr, &count, del_memo_callback, u, mi,
&last, &last0, &end, &left);
if (last != -1) {
/* Some memos got deleted; tell them which ones. */
if (delcount > 1) {
if (last0 != last)
end += snprintf(end, sizeof(buf) - (end - buf),
",%d-%d", last0, last);
else
end += snprintf(end, sizeof(buf) - (end - buf),
",%d", last);
/* "buf+1" here because *buf == ',' */
notice_lang(s_MemoServ, u, MEMO_DELETED_SEVERAL,
buf + 1);
} else {
notice_lang(s_MemoServ, u, MEMO_DELETED_ONE, last);
}
} else {
/* No memos were deleted. Tell them so. */
if (count == 1)
notice_lang(s_MemoServ, u, MEMO_DOES_NOT_EXIST,
atoi(numstr));
else
notice_lang(s_MemoServ, u, MEMO_DELETED_NONE);
}
} else if (stricmp(numstr, "LAST") == 0) {
/* Delete last memo. */
for (i = 0; i < mi->memocount; i++)
last = mi->memos[i].number;
delmemo(mi, last);
notice_lang(s_MemoServ, u, MEMO_DELETED_ONE, last);
} else {
/* Delete all memos. */
for (i = 0; i < mi->memocount; i++)
free(mi->memos[i].text);
free(mi->memos);
mi->memos = NULL;
mi->memocount = 0;
if (chan)
notice_lang(s_MemoServ, u, MEMO_CHAN_DELETED_ALL, chan);
else
notice_lang(s_MemoServ, u, MEMO_DELETED_ALL);
}
/* Reset the order */
for (i = 0; i < mi->memocount; i++)
mi->memos[i].number = i + 1;
}
return MOD_CONT;
}
/*************************************************************************/
static int do_set(User * u)
{
char *cmd = strtok(NULL, " ");
char *param = strtok(NULL, "");
MemoInfo *mi = &u->na->nc->memos;
if (readonly) {
notice_lang(s_MemoServ, u, MEMO_SET_DISABLED);
return MOD_CONT;
}
if (!param) {
syntax_error(s_MemoServ, u, "SET", MEMO_SET_SYNTAX);
} else if (!nick_identified(u)) {
notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
return MOD_CONT;
} else if (stricmp(cmd, "NOTIFY") == 0) {
do_set_notify(u, mi, param);
} else if (stricmp(cmd, "LIMIT") == 0) {
do_set_limit(u, mi, param);
} else {
notice_lang(s_MemoServ, u, MEMO_SET_UNKNOWN_OPTION, cmd);
notice_lang(s_MemoServ, u, MORE_INFO, s_MemoServ, "SET");
}
return MOD_CONT;
}
/*************************************************************************/
static int do_set_notify(User * u, MemoInfo * mi, char *param)
{
if (stricmp(param, "ON") == 0) {
u->na->nc->flags |= NI_MEMO_SIGNON | NI_MEMO_RECEIVE;
notice_lang(s_MemoServ, u, MEMO_SET_NOTIFY_ON, s_MemoServ);
} else if (stricmp(param, "LOGON") == 0) {
u->na->nc->flags |= NI_MEMO_SIGNON;
u->na->nc->flags &= ~NI_MEMO_RECEIVE;
notice_lang(s_MemoServ, u, MEMO_SET_NOTIFY_LOGON, s_MemoServ);
} else if (stricmp(param, "NEW") == 0) {
u->na->nc->flags &= ~NI_MEMO_SIGNON;
u->na->nc->flags |= NI_MEMO_RECEIVE;
notice_lang(s_MemoServ, u, MEMO_SET_NOTIFY_NEW, s_MemoServ);
} else if (stricmp(param, "OFF") == 0) {
u->na->nc->flags &= ~(NI_MEMO_SIGNON | NI_MEMO_RECEIVE);
notice_lang(s_MemoServ, u, MEMO_SET_NOTIFY_OFF, s_MemoServ);
} else {
syntax_error(s_MemoServ, u, "SET NOTIFY", MEMO_SET_NOTIFY_SYNTAX);
}
return MOD_CONT;
}
/*************************************************************************/
static int do_set_limit(User * u, MemoInfo * mi, char *param)
{
char *p1 = strtok(param, " ");
char *p2 = strtok(NULL, " ");
char *p3 = strtok(NULL, " ");
char *user = NULL, *chan = NULL;
int32 limit;
NickAlias *na = u->na;
ChannelInfo *ci = NULL;
int is_servadmin = is_services_admin(u);
if (p1 && *p1 == '#') {
chan = p1;
p1 = p2;
p2 = p3;
p3 = strtok(NULL, " ");
if (!(ci = cs_findchan(chan))) {
notice_lang(s_MemoServ, u, CHAN_X_NOT_REGISTERED, chan);
return MOD_CONT;
} else if (ci->flags & CI_VERBOTEN) {
notice_lang(s_MemoServ, u, CHAN_X_FORBIDDEN, chan);
return MOD_CONT;
} else if (!is_servadmin && !check_access(u, ci, CA_MEMO)) {
notice_lang(s_MemoServ, u, ACCESS_DENIED);
return MOD_CONT;
}
mi = &ci->memos;
}
if (is_servadmin) {
if (p2 && stricmp(p2, "HARD") != 0 && !chan) {
if (!(na = findnick(p1))) {
notice_lang(s_MemoServ, u, NICK_X_NOT_REGISTERED, p1);
return MOD_CONT;
}
user = p1;
mi = &na->nc->memos;
p1 = p2;
p2 = p3;
} else if (!p1) {
syntax_error(s_MemoServ, u, "SET LIMIT",
MEMO_SET_LIMIT_SERVADMIN_SYNTAX);
return MOD_CONT;
}
if ((!isdigit(*p1) && stricmp(p1, "NONE") != 0) ||
(p2 && stricmp(p2, "HARD") != 0)) {
syntax_error(s_MemoServ, u, "SET LIMIT",
MEMO_SET_LIMIT_SERVADMIN_SYNTAX);
return MOD_CONT;
}
if (chan) {
if (p2)
ci->flags |= CI_MEMO_HARDMAX;
else
ci->flags &= ~CI_MEMO_HARDMAX;
} else {
if (p2)
na->nc->flags |= NI_MEMO_HARDMAX;
else
na->nc->flags &= ~NI_MEMO_HARDMAX;
}
limit = atoi(p1);
if (limit < 0 || limit > 32767) {
notice_lang(s_MemoServ, u, MEMO_SET_LIMIT_OVERFLOW, 32767);
limit = 32767;
}
if (stricmp(p1, "NONE") == 0)
limit = -1;
} else {
if (!p1 || p2 || !isdigit(*p1)) {
syntax_error(s_MemoServ, u, "SET LIMIT",
MEMO_SET_LIMIT_SYNTAX);
return MOD_CONT;
}
if (chan && (ci->flags & CI_MEMO_HARDMAX)) {
notice_lang(s_MemoServ, u, MEMO_SET_LIMIT_FORBIDDEN, chan);
return MOD_CONT;
} else if (!chan && (na->nc->flags & NI_MEMO_HARDMAX)) {
notice_lang(s_MemoServ, u, MEMO_SET_YOUR_LIMIT_FORBIDDEN);
return MOD_CONT;
}
limit = atoi(p1);
/* The first character is a digit, but we could still go negative
* from overflow... watch out! */
if (limit < 0 || (MSMaxMemos > 0 && limit > MSMaxMemos)) {
if (chan) {
notice_lang(s_MemoServ, u, MEMO_SET_LIMIT_TOO_HIGH,
chan, MSMaxMemos);
} else {
notice_lang(s_MemoServ, u, MEMO_SET_YOUR_LIMIT_TOO_HIGH,
MSMaxMemos);
}
return MOD_CONT;
} else if (limit > 32767) {
notice_lang(s_MemoServ, u, MEMO_SET_LIMIT_OVERFLOW, 32767);
limit = 32767;
}
}
mi->memomax = limit;
if (limit > 0) {
if (!chan && na->nc == u->na->nc)
notice_lang(s_MemoServ, u, MEMO_SET_YOUR_LIMIT, limit);
else
notice_lang(s_MemoServ, u, MEMO_SET_LIMIT,
chan ? chan : user, limit);
} else if (limit == 0) {
if (!chan && na->nc == u->na->nc)
notice_lang(s_MemoServ, u, MEMO_SET_YOUR_LIMIT_ZERO);
else
notice_lang(s_MemoServ, u, MEMO_SET_LIMIT_ZERO,
chan ? chan : user);
} else {
if (!chan && na->nc == u->na->nc)
notice_lang(s_MemoServ, u, MEMO_UNSET_YOUR_LIMIT);
else
notice_lang(s_MemoServ, u, MEMO_UNSET_LIMIT,
chan ? chan : user);
}
return MOD_CONT;
}
/*************************************************************************/
static int do_info(User * u)
{
MemoInfo *mi;
NickAlias *na = NULL;
ChannelInfo *ci = NULL;
char *name = strtok(NULL, " ");
int is_servadmin = is_services_admin(u);
int hardmax = 0;
if (is_servadmin && name && *name != '#') {
na = findnick(name);
if (!na) {
notice_lang(s_MemoServ, u, NICK_X_NOT_REGISTERED, name);
return MOD_CONT;
}
mi = &na->nc->memos;
hardmax = na->nc->flags & NI_MEMO_HARDMAX ? 1 : 0;
} else if (name && *name == '#') {
ci = cs_findchan(name);
if (!ci) {
notice_lang(s_MemoServ, u, CHAN_X_NOT_REGISTERED, name);
return MOD_CONT;
} else if (ci->flags & CI_VERBOTEN) {
notice_lang(s_MemoServ, u, CHAN_X_FORBIDDEN, name);
return MOD_CONT;
} else if (!check_access(u, ci, CA_MEMO)) {
notice_lang(s_MemoServ, u, ACCESS_DENIED);
return MOD_CONT;
}
mi = &ci->memos;
hardmax = ci->flags & CI_MEMO_HARDMAX ? 1 : 0;
} else if (name) { /* It's not a chan and we aren't services admin */
notice_lang(s_MemoServ, u, ACCESS_DENIED);
return MOD_CONT;
} else { /* !name */
if (!nick_identified(u)) {
notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
return MOD_CONT;
}
mi = &u->na->nc->memos;
hardmax = u->na->nc->flags & NI_MEMO_HARDMAX ? 1 : 0;
}
if (name && (ci || na->nc != u->na->nc)) {
if (!mi->memocount) {
notice_lang(s_MemoServ, u, MEMO_INFO_X_NO_MEMOS, name);
} else if (mi->memocount == 1) {
if (mi->memos[0].flags & MF_UNREAD)
notice_lang(s_MemoServ, u, MEMO_INFO_X_MEMO_UNREAD, name);
else
notice_lang(s_MemoServ, u, MEMO_INFO_X_MEMO, name);
} else {
int count = 0, i;
for (i = 0; i < mi->memocount; i++) {
if (mi->memos[i].flags & MF_UNREAD)
count++;
}
if (count == mi->memocount)
notice_lang(s_MemoServ, u, MEMO_INFO_X_MEMOS_ALL_UNREAD,
name, count);
else if (count == 0)
notice_lang(s_MemoServ, u, MEMO_INFO_X_MEMOS, name,
mi->memocount);
else if (count == 0)
notice_lang(s_MemoServ, u, MEMO_INFO_X_MEMOS_ONE_UNREAD,
name, mi->memocount);
else
notice_lang(s_MemoServ, u, MEMO_INFO_X_MEMOS_SOME_UNREAD,
name, mi->memocount, count);
}
if (mi->memomax >= 0) {
if (hardmax)
notice_lang(s_MemoServ, u, MEMO_INFO_X_HARD_LIMIT, name,
mi->memomax);
else
notice_lang(s_MemoServ, u, MEMO_INFO_X_LIMIT, name,
mi->memomax);
} else {
notice_lang(s_MemoServ, u, MEMO_INFO_X_NO_LIMIT, name);
}
/* I ripped this code out of ircservices 4.4.5, since I didn't want
to rewrite the whole thing (it pisses me off). */
if (na) {
if ((na->nc->flags & NI_MEMO_RECEIVE)
&& (na->nc->flags & NI_MEMO_SIGNON)) {
notice_lang(s_MemoServ, u, MEMO_INFO_X_NOTIFY_ON, name);
} else if (na->nc->flags & NI_MEMO_RECEIVE) {
notice_lang(s_MemoServ, u, MEMO_INFO_X_NOTIFY_RECEIVE,
name);
} else if (na->nc->flags & NI_MEMO_SIGNON) {
notice_lang(s_MemoServ, u, MEMO_INFO_X_NOTIFY_SIGNON,
name);
} else {
notice_lang(s_MemoServ, u, MEMO_INFO_X_NOTIFY_OFF, name);
}
}
} else { /* !name || (!ci || na->nc == u->na->nc) */
if (!mi->memocount) {
notice_lang(s_MemoServ, u, MEMO_INFO_NO_MEMOS);
} else if (mi->memocount == 1) {
if (mi->memos[0].flags & MF_UNREAD)
notice_lang(s_MemoServ, u, MEMO_INFO_MEMO_UNREAD);
else
notice_lang(s_MemoServ, u, MEMO_INFO_MEMO);
} else {
int count = 0, i;
for (i = 0; i < mi->memocount; i++) {
if (mi->memos[i].flags & MF_UNREAD)
count++;
}
if (count == mi->memocount)
notice_lang(s_MemoServ, u, MEMO_INFO_MEMOS_ALL_UNREAD,
count);
else if (count == 0)
notice_lang(s_MemoServ, u, MEMO_INFO_MEMOS, mi->memocount);
else if (count == 1)
notice_lang(s_MemoServ, u, MEMO_INFO_MEMOS_ONE_UNREAD,
mi->memocount);
else
notice_lang(s_MemoServ, u, MEMO_INFO_MEMOS_SOME_UNREAD,
mi->memocount, count);
}
if (mi->memomax == 0) {
if (!is_servadmin && hardmax)
notice_lang(s_MemoServ, u, MEMO_INFO_HARD_LIMIT_ZERO);
else
notice_lang(s_MemoServ, u, MEMO_INFO_LIMIT_ZERO);
} else if (mi->memomax > 0) {
if (!is_servadmin && hardmax)
notice_lang(s_MemoServ, u, MEMO_INFO_HARD_LIMIT,
mi->memomax);
else
notice_lang(s_MemoServ, u, MEMO_INFO_LIMIT, mi->memomax);
} else {
notice_lang(s_MemoServ, u, MEMO_INFO_NO_LIMIT);
}
/* Ripped too. But differently because of a seg fault (loughs) */
if ((u->na->nc->flags & NI_MEMO_RECEIVE)
&& (u->na->nc->flags & NI_MEMO_SIGNON)) {
notice_lang(s_MemoServ, u, MEMO_INFO_NOTIFY_ON);
} else if (u->na->nc->flags & NI_MEMO_RECEIVE) {
notice_lang(s_MemoServ, u, MEMO_INFO_NOTIFY_RECEIVE);
} else if (u->na->nc->flags & NI_MEMO_SIGNON) {
notice_lang(s_MemoServ, u, MEMO_INFO_NOTIFY_SIGNON);
} else {
notice_lang(s_MemoServ, u, MEMO_INFO_NOTIFY_OFF);
}
}
return MOD_CONT; /* if (name && (ci || na->nc != u->na->nc)) */
}
/*************************************************************************/
/**
* Allow the easy sending of memo's to all user's on the oper/admin/root lists
* - Rob
* Opers in several lists won't get the memo twice from now on
* - Certus
**/
static int do_staff(User * u)
{
NickCore *nc;
int i, z = 0;
char *text = strtok(NULL, "");
if (readonly) {
notice_lang(s_MemoServ, u, MEMO_SEND_DISABLED);
return MOD_CONT;
} else if (checkDefCon(DEFCON_NO_NEW_MEMOS)) {
notice_lang(s_MemoServ, u, OPER_DEFCON_DENIED);
return MOD_CONT;
} else if (text == NULL) {
syntax_error(s_MemoServ, u, "SEND", MEMO_SEND_SYNTAX);
return MOD_CONT;
}
for (i = 0; i < 1024; i++) {
for (nc = nclists[i]; nc; nc = nc->next) {
if (nick_is_services_oper(nc))
memo_send(u, nc->display, text, z);
}
}
return MOD_CONT;
}
/*************************************************************************/
/**
* Send a memo to all registered nicks
* - Certus - 06/06/2003
**/
static int do_sendall(User * u)
{
int i, z = 1;
NickCore *nc;
char *text = strtok(NULL, "");
if (readonly) {
notice_lang(s_MemoServ, u, MEMO_SEND_DISABLED);
return MOD_CONT;
} else if (checkDefCon(DEFCON_NO_NEW_MEMOS)) {
notice_lang(s_MemoServ, u, OPER_DEFCON_DENIED);
return MOD_CONT;
} else if (!text) {
syntax_error(s_MemoServ, u, "SENDALL", MEMO_SEND_SYNTAX);
return MOD_CONT;
}
for (i = 0; i < 1024; i++) {
for (nc = nclists[i]; nc; nc = nc->next) {
if (stricmp(u->nick, nc->display) != 0)
memo_send(u, nc->display, text, z);
} /* /nc */
} /* /i */
notice_lang(s_MemoServ, u, MEMO_MASS_SENT);
return MOD_CONT;
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1