diff options
Diffstat (limited to 'memoserv.c')
-rw-r--r-- | memoserv.c | 1175 |
1 files changed, 1175 insertions, 0 deletions
diff --git a/memoserv.c b/memoserv.c new file mode 100644 index 000000000..94f096df5 --- /dev/null +++ b/memoserv.c @@ -0,0 +1,1175 @@ +/* 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,v 1.30 2004/03/22 18:52:47 dane Exp $ + * + */ + +#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; +} + +/*************************************************************************/ |