Author: Kev <klmitch@mit.edu>
authorBleep <twhelvey1@home.com>
Tue, 21 Mar 2000 01:08:51 +0000 (01:08 +0000)
committerBleep <twhelvey1@home.com>
Tue, 21 Mar 2000 01:08:51 +0000 (01:08 +0000)
Log message:
Add missing jupe files doh!!

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@42 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

doc/readme.jupe [new file with mode: 0644]
include/jupe.h [new file with mode: 0644]
ircd/jupe.c [new file with mode: 0644]
ircd/m_jupe.c [new file with mode: 0644]

diff --git a/doc/readme.jupe b/doc/readme.jupe
new file mode 100644 (file)
index 0000000..da36f9a
--- /dev/null
@@ -0,0 +1,55 @@
+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.
diff --git a/include/jupe.h b/include/jupe.h
new file mode 100644 (file)
index 0000000..5c0cf7a
--- /dev/null
@@ -0,0 +1,71 @@
+#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 */
diff --git a/ircd/jupe.c b/ircd/jupe.c
new file mode 100644 (file)
index 0000000..bde16cb
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * 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;
+}
diff --git a/ircd/m_jupe.c b/ircd/m_jupe.c
new file mode 100644 (file)
index 0000000..ccb7737
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * 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]);
+}