/* 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; } /*************************************************************************/