--- /dev/null
+JUPE documentation, last updated on 18 Mar 2000
+
+For an ordinary user, the syntax is:
+
+ JUPE [<server>]
+
+If <server> is given, and if a jupe for that server exists, all the
+information about that jupe is displayed. If <server> is not given,
+all un-expired jupes are displayed.
+
+For an operator, the syntax is:
+
+ JUPE [[+|-]<server> [<target> <expiration> :<reason>]]
+
+If <server> is not given, or if it is not prefixed by "+" or "-", the
+operation is exactly the same as if it were issued by an ordinary
+user. If the "+" or "-" prefixes are used, the arguments <target>,
+<expiration>, and <reason> must be given, even if the jupe already
+exists. If <target> is "*" and the currently existing jupe is a local
+jupe, the local jupe will be erased and recreated with the parameters
+given, as described below. Otherwise, if the jupe currently exists, a
+prefix of "+" will cause an inactive jupe to be activated, whereas a
+prefix of "-" will cause an active jupe to be deactivated.
+
+If the jupe does not already exist, it is created; <target> is used to
+select whether the jupe is only to apply to a single server (which
+need not be the local server) or to the whole network. This could be
+useful if a single particular link is having problems, for instance.
+The <expiration> parameter is a number of seconds, not to exceed 7
+days, for the jupe to exist. The <reason> argument is mandatory and
+should describe why this particular jupe was placed.
+
+For a server, the syntax is:
+
+ <prefix> JU <target> (+|-)<server> <expiration> <lastmod> :<reason>
+
+The <target> may be a server numeric or the character "*", for a
+globally scoped jupe. The <server> argument is a server name, and
+must be prefixed by one of "+" (to indicate an active jupe) or "-" (to
+indicate an inactive jupe). The parameter <expiration> is a total
+number of seconds the jupe is to live for, and <lastmod> is used for
+versioning. Since JUPEs are propagated during netbursts, there must
+be some way of resolving conflicting states, which is the reason for
+this argument, and is also the reason jupes cannot be deleted, only
+deactivated. The <reason> parameter indicates the reason the jupe was
+placed.
+
+If a JUPE is received with a <target> of "*", any jupes with local
+scope are deleted, in preference for the globally scoped version. If
+the jupe already exists, the values of <lastmod> are compared; if the
+received <lastmod> is less than the stored <lastmod>, the existing
+jupe is resent to the server from which the JUPE message was received;
+otherwise, the jupe is activated or deactivated, depending on the
+<server> prefix. If the jupe does not currently exist, it is created
+with the parameters given.
--- /dev/null
+#ifndef INCLUDED_jupe_h
+#define INCLUDED_jupe_h
+/*
+ * IRC - Internet Relay Chat, include/jupe.h
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+#ifndef INCLUDED_config_h
+#include "config.h"
+#endif
+#ifndef INCLUDED_sys_types_h
+#include <sys/types.h>
+#define INCLUDED_sys_types_h
+#endif
+
+
+struct Client;
+
+#define JUPE_MAX_EXPIRE 604800 /* max expire: 7 days */
+
+struct Jupe {
+ struct Jupe* ju_next;
+ struct Jupe** ju_prev_p;
+ char* ju_server;
+ char* ju_reason;
+ time_t ju_expire;
+ time_t ju_lastmod;
+ unsigned int ju_flags;
+};
+
+#define JUPE_ACTIVE 1
+#define JUPE_LOCAL 2
+
+#define JupeIsActive(j) ((j)->ju_flags & JUPE_ACTIVE)
+#define JupeIsLocal(j) ((j)->ju_flags & JUPE_LOCAL)
+
+#define JupeServer(j) ((j)->ju_server)
+#define JupeReason(j) ((j)->ju_reason)
+#define JupeLastMod(j) ((j)->ju_lastmod)
+
+extern int jupe_add(struct Client *cptr, struct Client *sptr, char *server,
+ char *reason, time_t expire, time_t lastmod, int local,
+ int active);
+extern int jupe_activate(struct Client *cptr, struct Client *sptr,
+ struct Jupe *jupe, time_t lastmod);
+extern int jupe_deactivate(struct Client *cptr, struct Client *sptr,
+ struct Jupe *jupe, time_t lastmod);
+extern struct Jupe* jupe_find(char *server);
+extern void jupe_free(struct Jupe *jupe);
+extern void jupe_burst(struct Client *cptr);
+extern int jupe_resend(struct Client *cptr, struct Jupe *jupe);
+extern int jupe_list(struct Client *sptr, char *server);
+
+#endif /* INCLUDED_jupe_h */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/jupe.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Finland
+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+#include "jupe.h"
+#include "client.h"
+#include "hash.h"
+#include "ircd.h"
+#include "ircd_alloc.h"
+#include "ircd_string.h"
+#include "match.h"
+#include "msg.h"
+#include "numeric.h"
+#include "numnicks.h"
+#include "s_bsd.h"
+#include "s_misc.h"
+#include "send.h"
+#include "struct.h"
+#include "support.h"
+#include "sys.h" /* FALSE bleah */
+
+#include <assert.h>
+
+static struct Jupe* GlobalJupeList = 0;
+
+static struct Jupe *
+make_jupe(char *server, char *reason, time_t expire, time_t lastmod,
+ unsigned int flags)
+{
+ struct Jupe *ajupe;
+
+ ajupe = (struct Jupe*) MyMalloc(sizeof(struct Jupe)); /* alloc memory */
+ assert(0 != ajupe);
+
+ DupString(ajupe->ju_server, server); /* copy vital information */
+ DupString(ajupe->ju_reason, reason);
+ ajupe->ju_expire = expire;
+ ajupe->ju_lastmod = lastmod;
+ ajupe->ju_flags = flags; /* set jupe flags */
+
+ ajupe->ju_next = GlobalJupeList; /* link it into the list */
+ ajupe->ju_prev_p = &GlobalJupeList;
+ if (GlobalJupeList)
+ GlobalJupeList->ju_prev_p = &ajupe->ju_next;
+ GlobalJupeList = ajupe;
+
+ return ajupe;
+}
+
+static int
+do_jupe(struct Client *cptr, struct Client *sptr, struct Jupe *jupe)
+{
+ struct Client *acptr;
+
+ if (!JupeIsActive(jupe)) /* no action to be taken on inactive jupes */
+ return 0;
+
+ acptr = FindServer(jupe->ju_server);
+
+ /* server isn't online or isn't local or is me */
+ if (!acptr || !MyConnect(acptr) || IsMe(acptr))
+ return 0;
+
+ return exit_client_msg(cptr, acptr, &me, "Juped: %s", jupe->ju_reason);
+}
+
+static void
+propagate_jupe(struct Client *cptr, struct Client *sptr, struct Jupe *jupe)
+{
+ if (JupeIsLocal(jupe)) /* don't propagate local jupes */
+ return;
+
+ if (IsUser(sptr)) /* select correct prefix */
+ sendto_serv_butone(cptr, "%s%s " TOK_JUPE " * %c%s " TIME_T_FMT " "
+ TIME_T_FMT " :%s", NumNick(sptr),
+ JupeIsActive(jupe) ? '+' : '-', jupe->ju_server,
+ jupe->ju_expire - TStime(), jupe->ju_lastmod,
+ jupe->ju_reason);
+ else
+ sendto_serv_butone(cptr, "%s " TOK_JUPE " * %c%s " TIME_T_FMT " "
+ TIME_T_FMT " :%s", NumServ(sptr),
+ JupeIsActive(jupe) ? '+' : '-', jupe->ju_server,
+ jupe->ju_expire - TStime(), jupe->ju_lastmod,
+ jupe->ju_reason);
+}
+
+int
+jupe_add(struct Client *cptr, struct Client *sptr, char *server, char *reason,
+ time_t expire, time_t lastmod, int local, int active)
+{
+ struct Jupe *ajupe;
+ unsigned int flags = 0;
+
+ assert(0 != server);
+ assert(0 != reason);
+
+ /*
+ * You cannot set a negative (or zero) expire time, nor can you set an
+ * expiration time for greater than JUPE_MAX_EXPIRE.
+ */
+ if (expire <= 0 || expire > JUPE_MAX_EXPIRE) {
+ if (!IsServer(cptr) && MyConnect(cptr))
+ sendto_one(cptr, err_str(ERR_BADEXPIRE), me.name, cptr->name, expire);
+ return 0;
+ }
+
+ expire += TStime(); /* convert from lifetime to timestamp */
+
+ /* Inform ops and log it */
+ if (IsServer(sptr)) {
+ sendto_op_mask(SNO_NETWORK, "%s adding %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s", sptr->name, local ? "local " : "",
+ server, expire, reason);
+#ifdef JPATH
+ write_log(JPATH, TIME_T_FMT " %s adding %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s\n", TStime(), sptr->name,
+ local ? "local " : "", server, expire, reason);
+#endif /* JPATH */
+ } else {
+ sendto_op_mask(SNO_NETWORK, "%s adding %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s", sptr->user->server->name,
+ local ? "local " : "", server, expire,
+ reason);
+#ifdef JPATH
+ write_log(JPATH, TIME_T_FMT, " %s!%s@%s adding %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s\n", TStime(), sptr->name, sptr->user->username,
+ sptr->user->host, local ? "local " : "", server, expire, reason);
+#endif /* JPATH */
+ }
+
+ if (active) /* compute initial flags */
+ flags |= JUPE_ACTIVE;
+ if (local)
+ flags |= JUPE_LOCAL;
+
+ /* make the jupe */
+ ajupe = make_jupe(server, reason, expire, lastmod, flags);
+
+ propagate_jupe(cptr, sptr, ajupe);
+
+ return do_jupe(cptr, sptr, ajupe); /* remove server if necessary */
+}
+
+int
+jupe_activate(struct Client *cptr, struct Client *sptr, struct Jupe *jupe,
+ time_t lastmod)
+{
+ assert(0 != jupe);
+
+ jupe->ju_flags |= JUPE_ACTIVE;
+ jupe->ju_lastmod = lastmod;
+
+ /* Inform ops and log it */
+ if (IsServer(sptr)) {
+ sendto_op_mask(SNO_NETWORK, "%s activating %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s", sptr->name, JupeIsLocal(jupe) ?
+ "local " : "", jupe->ju_server, jupe->ju_expire,
+ jupe->ju_reason);
+#ifdef JPATH
+ write_log(JPATH, TIME_T_FMT " %s activating %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s\n", TStime(), sptr->name, JupeIsLocal(jupe) ?
+ "local " : "", jupe->ju_server, jupe->ju_expire,
+ jupe->ju_reason);
+#endif /* JPATH */
+ } else {
+ sendto_op_mask(SNO_NETWORK, "%s activating %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s", sptr->user->server->name,
+ JupeIsLocal(jupe) ? "local " : "", jupe->ju_server,
+ jupe->ju_expire, jupe->ju_reason);
+#ifdef JPATH
+ write_log(JPATH, TIME_T_FMT, " %s!%s@%s activating %sJUPE for %s, "
+ "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
+ sptr->user->username, sptr->user->host, JupeIsLocal(jupe) ?
+ "local " : "", jupe->ju_server, jupe->ju_expire,
+ jupe->ju_reason);
+#endif /* JPATH */
+ }
+
+ propagate_jupe(cptr, sptr, jupe);
+
+ return do_jupe(cptr, sptr, jupe);
+}
+
+int
+jupe_deactivate(struct Client *cptr, struct Client *sptr, struct Jupe *jupe,
+ time_t lastmod)
+{
+ assert(0 != jupe);
+
+ jupe->ju_flags &= ~JUPE_ACTIVE;
+ jupe->ju_lastmod = lastmod;
+
+ /* Inform ops and log it */
+ if (IsServer(sptr)) {
+ sendto_op_mask(SNO_NETWORK, "%s deactivating %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s", sptr->name, JupeIsLocal(jupe) ?
+ "local " : "", jupe->ju_server, jupe->ju_expire,
+ jupe->ju_reason);
+#ifdef JPATH
+ write_log(JPATH, TIME_T_FMT " %s deactivating %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s\n", TStime(), sptr->name, JupeIsLocal(jupe) ?
+ "local " : "", jupe->ju_server, jupe->ju_expire,
+ jupe->ju_reason);
+#endif /* JPATH */
+ } else {
+ sendto_op_mask(SNO_NETWORK, "%s deactivating %sJUPE for %s, expiring at "
+ TIME_T_FMT ": %s", sptr->user->server->name,
+ JupeIsLocal(jupe) ? "local " : "", jupe->ju_server,
+ jupe->ju_expire, jupe->ju_reason);
+#ifdef JPATH
+ write_log(JPATH, TIME_T_FMT, " %s!%s@%s deactivating %sJUPE for %s, "
+ "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
+ sptr->user->username, sptr->user->host, JupeIsLocal(jupe) ?
+ "local " : "", jupe->ju_server, jupe->ju_expire,
+ jupe->ju_reason);
+#endif /* JPATH */
+ }
+
+ propagate_jupe(cptr, sptr, jupe);
+
+ return 0;
+}
+
+struct Jupe *
+jupe_find(char *server)
+{
+ struct Jupe* jupe;
+ struct Jupe* sjupe;
+
+ for (jupe = GlobalJupeList; jupe; jupe = sjupe) { /* go through jupes */
+ sjupe = jupe->ju_next;
+
+ if (jupe->ju_expire <= TStime()) /* expire any that need expiring */
+ jupe_free(jupe);
+ else if (0 == ircd_strcmp(server, jupe->ju_server)) /* found it yet? */
+ return jupe;
+ }
+
+ return 0;
+}
+
+void
+jupe_free(struct Jupe* jupe)
+{
+ assert(0 != jupe);
+
+ *jupe->ju_prev_p = jupe->ju_next; /* squeeze this jupe out */
+ if (jupe->ju_next)
+ jupe->ju_next->ju_prev_p = jupe->ju_prev_p;
+
+ MyFree(jupe->ju_server); /* and free up the memory */
+ MyFree(jupe->ju_reason);
+ MyFree(jupe);
+}
+
+void
+jupe_burst(struct Client *cptr)
+{
+ struct Jupe *jupe;
+ struct Jupe *sjupe;
+
+ for (jupe = GlobalJupeList; jupe; jupe = sjupe) { /* go through jupes */
+ sjupe = jupe->ju_next;
+
+ if (jupe->ju_expire <= TStime()) /* expire any that need expiring */
+ jupe_free(jupe);
+ else if (!JupeIsLocal(jupe)) /* forward global jupes */
+ sendto_one(cptr, "%s " TOK_JUPE " * %c%s "TIME_T_FMT" "TIME_T_FMT" :%s",
+ NumServ(&me), JupeIsActive(jupe) ? '+' : '-',
+ jupe->ju_server, jupe->ju_expire - TStime(),
+ jupe->ju_lastmod, jupe->ju_reason);
+ }
+}
+
+int
+jupe_resend(struct Client *cptr, struct Jupe *jupe)
+{
+ if (JupeIsLocal(jupe)) /* don't propagate local jupes */
+ return 0;
+
+ sendto_one(cptr, "%s " TOK_JUPE " * %c%s " TIME_T_FMT " " TIME_T_FMT " :%s",
+ NumServ(&me), JupeIsActive(jupe) ? '+' : '-', jupe->ju_server,
+ jupe->ju_expire - TStime(), jupe->ju_lastmod, jupe->ju_reason);
+
+ return 0;
+}
+
+int
+jupe_list(struct Client *sptr, char *server)
+{
+ struct Jupe *jupe;
+ struct Jupe *sjupe;
+
+ if (server) {
+ if (!(jupe = jupe_find(server))) { /* no such jupe */
+ sendto_one(sptr, err_str(ERR_NOSUCHJUPE), me.name, sptr->name, server);
+ return 0;
+ }
+
+ /* send jupe information along */
+ sendto_one(sptr, rpl_str(RPL_JUPELIST), me.name, sptr->name,
+ jupe->ju_server, jupe->ju_expire, JupeIsLocal(jupe) ?
+ me.name : "*", JupeIsActive(jupe) ? '+' : '-', jupe->ju_reason);
+ } else {
+ for (jupe = GlobalJupeList; jupe; jupe = sjupe) { /* go through jupes */
+ sjupe = jupe->ju_next;
+
+ if (jupe->ju_expire <= TStime()) /* expire any that need expiring */
+ jupe_free(jupe);
+ else /* send jupe information along */
+ sendto_one(sptr, rpl_str(RPL_JUPELIST), me.name, sptr->name,
+ jupe->ju_server, jupe->ju_expire, JupeIsLocal(jupe) ?
+ me.name : "*", JupeIsActive(jupe) ? '+' : '-',
+ jupe->ju_reason);
+ }
+ }
+
+ /* end of jupe information */
+ sendto_one(sptr, rpl_str(RPL_ENDOFJUPELIST), me.name, sptr->name);
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/m_jupe.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/*
+ * m_functions execute protocol messages on this server:
+ *
+ * cptr is always NON-NULL, pointing to a *LOCAL* client
+ * structure (with an open socket connected!). This
+ * identifies the physical socket where the message
+ * originated (or which caused the m_function to be
+ * executed--some m_functions may call others...).
+ *
+ * sptr is the source of the message, defined by the
+ * prefix part of the message if present. If not
+ * or prefix not found, then sptr==cptr.
+ *
+ * (!IsServer(cptr)) => (cptr == sptr), because
+ * prefixes are taken *only* from servers...
+ *
+ * (IsServer(cptr))
+ * (sptr == cptr) => the message didn't
+ * have the prefix.
+ *
+ * (sptr != cptr && IsServer(sptr) means
+ * the prefix specified servername. (?)
+ *
+ * (sptr != cptr && !IsServer(sptr) means
+ * that message originated from a remote
+ * user (not local).
+ *
+ * combining
+ *
+ * (!IsServer(sptr)) means that, sptr can safely
+ * taken as defining the target structure of the
+ * message in this server.
+ *
+ * *Always* true (if 'parse' and others are working correct):
+ *
+ * 1) sptr->from == cptr (note: cptr->from == cptr)
+ *
+ * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
+ * *cannot* be a local connection, unless it's
+ * actually cptr!). [MyConnect(x) should probably
+ * be defined as (x == x->from) --msa ]
+ *
+ * parc number of variable parameter strings (if zero,
+ * parv is allowed to be NULL)
+ *
+ * parv a NULL terminated list of parameter pointers,
+ *
+ * parv[0], sender (prefix string), if not present
+ * this points to an empty string.
+ * parv[1]...parv[parc-1]
+ * pointers to additional parameters
+ * parv[parc] == NULL, *always*
+ *
+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
+ * non-NULL pointers.
+ */
+#if 0
+/*
+ * No need to include handlers.h here the signatures must match
+ * and we don't need to force a rebuild of all the handlers everytime
+ * we add a new one to the list. --Bleep
+ */
+#include "handlers.h"
+#endif /* 0 */
+#include "client.h"
+#include "jupe.h"
+#include "hash.h"
+#include "ircd.h"
+#include "ircd_reply.h"
+#include "ircd_string.h"
+#include "match.h"
+#include "msg.h"
+#include "numeric.h"
+#include "numnicks.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "send.h"
+#include "support.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * ms_jupe - server message handler
+ *
+ * parv[0] = Send prefix
+ *
+ * From server:
+ *
+ * parv[1] = Target: server numeric or *
+ * parv[2] = (+|-)<server name>
+ * parv[3] = Expiration offset
+ * parv[4] = Last modification time
+ * parv[5] = Comment
+ *
+ */
+int ms_jupe(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+{
+ struct Client *acptr = 0;
+ struct Jupe *ajupe;
+ int local = 0, active = 1;
+ time_t expire_off, lastmod;
+ char *server = parv[2], *target = parv[1], *reason = parv[5];
+
+ if (parc < 6)
+ return need_more_params(sptr, "JUPE");
+
+ if (!(target[0] == '*' && target[1] == '\0')) {
+ if (!(acptr = FindNServer(target)))
+ return 0; /* no such server */
+
+ if (!IsMe(acptr)) { /* manually propagate, since we don't set it */
+ sendto_one(acptr, "%s " TOK_JUPE " %s %s %s %s :%s", NumServ(sptr),
+ target, server, parv[3], parv[4], reason);
+
+ return 0;
+ }
+
+ local = 1;
+ }
+
+ if (*server == '-') {
+ active = 0;
+ server++;
+ } else if (*server == '+') {
+ active = 1;
+ server++;
+ }
+
+ expire_off = atoi(parv[3]);
+ lastmod = atoi(parv[4]);
+
+ ajupe = jupe_find(server);
+
+ if (ajupe) {
+ if (JupeIsLocal(ajupe) && !local) /* global jupes override local ones */
+ jupe_free(ajupe);
+ else if (JupeLastMod(ajupe) < lastmod) { /* new modification */
+ if (active)
+ return jupe_activate(cptr, sptr, ajupe, lastmod);
+ else
+ return jupe_deactivate(cptr, sptr, ajupe, lastmod);
+ } else if (JupeLastMod(ajupe) == lastmod) /* no changes */
+ return 0;
+ else
+ return jupe_resend(cptr, ajupe); /* other server desynched WRT jupes */
+ }
+
+ return jupe_add(cptr, sptr, server, reason, expire_off, lastmod, local,
+ active);
+}
+
+/*
+ * mo_jupe - oper message handler
+ *
+ * parv[0] = Send prefix
+ *
+ * From oper:
+ *
+ * parv[1] = [[+|-]<server name>]
+ * parv[2] = [target]
+ * parv[3] = [Expiration offset]
+ * parv[4] = [Comment]
+ *
+ */
+int mo_jupe(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+{
+ struct Client *acptr = 0;
+ struct Jupe *ajupe;
+ int local = 0, active = 1;
+ time_t expire_off;
+ char *server = parv[1], *target = parv[2], *reason = parv[4];
+
+ if (parc < 2)
+ return jupe_list(sptr, 0);
+
+ if (*server == '+') {
+ active = 1;
+ server++;
+ } else if (*server == '-') {
+ active = 0;
+ server++;
+ } else
+ return jupe_list(sptr, server);
+
+ if (parc < 5)
+ return need_more_params(sptr, "JUPE");
+
+ if (!(target[0] == '*' && target[1] == '\0')) {
+ if (!(acptr = find_match_server(target))) {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], target);
+ return 0;
+ }
+
+ if (!IsMe(acptr)) { /* manually propagate, since we don't set it */
+ if (!IsOper(sptr)) {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+
+ sendto_one(acptr, "%s%s " TOK_JUPE " %s %c%s %s " TIME_T_FMT " :%s",
+ NumNick(sptr), NumServ(acptr), server, active ? '+' : '-',
+ parv[3], TStime(), reason);
+ return 0;
+ }
+
+ local = 1;
+ }
+
+ expire_off = atoi(parv[3]);
+
+ ajupe = jupe_find(server);
+
+ if (ajupe) {
+ if (JupeIsLocal(ajupe) && !local) /* global jupes override local ones */
+ jupe_free(ajupe);
+ else {
+ if (active)
+ return jupe_activate(cptr, sptr, ajupe, TStime());
+ else
+ return jupe_deactivate(cptr, sptr, ajupe, TStime());
+ }
+ }
+
+ return jupe_add(cptr, sptr, server, reason, expire_off, TStime(), local,
+ active);
+}
+
+/*
+ * m_jupe - user message handler
+ *
+ * parv[0] = Send prefix
+ *
+ * From user:
+ *
+ * parv[1] = [<server name>]
+ *
+ */
+int m_jupe(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+{
+ if (parc < 2)
+ return jupe_list(sptr, 0);
+
+ return jupe_list(sptr, parv[1]);
+}