* $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 "config.h"
+
#include "channel.h"
#include "client.h"
#include "hash.h"
#include "ircd.h"
+#include "ircd_features.h"
+#include "ircd_log.h"
#include "ircd_reply.h"
#include "ircd_string.h"
#include "msg.h"
#include "numnicks.h"
#include "send.h"
-#include <assert.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
+#include <stdlib.h> /* for atoi() */
-/*
- * m_topic - generic message handler
- *
- * parv[0] = sender prefix
- * parv[1] = channel
- * parv[parc - 1] = topic (if parc > 2)
+/** Set a channel topic or report an error.
+ * @param[in] sptr Original topic setter.
+ * @param[in] cptr Neighbor that sent the topic message.
+ * @param[in] chptr Channel to set topic on.
+ * @param[in] topic New topic.
+ * @param[in] ts Timestamp that topic was set (0 for current time).
*/
-int m_topic(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+static void do_settopic(struct Client *sptr, struct Client *cptr,
+ struct Channel *chptr, char *topic, time_t ts)
{
- struct Channel *chptr;
- char *topic = 0, *name, *p = 0;
-
- if (parc < 2)
- return need_more_params(sptr, "TOPIC");
-
- if (parc > 2)
- topic = parv[parc - 1];
-
- for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0)
- {
- chptr = 0;
- if (!IsChannelName(name) || !(chptr = FindChannel(name)) ||
- ((topic || SecretChannel(chptr)) && !find_channel_member(sptr, chptr)))
- {
- sendto_one(sptr, err_str(chptr ? ERR_NOTONCHANNEL : ERR_NOSUCHCHANNEL),
- me.name, parv[0], chptr ? chptr->chname : name);
- continue;
- }
- if (IsModelessChannel(name))
- {
- sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
- chptr->chname);
- continue;
- }
- if (IsLocalChannel(name) && !MyUser(sptr))
- continue;
-
- if (!topic) /* only asking for topic */
- {
- if (chptr->topic[0] == '\0')
- sendto_one(sptr, rpl_str(RPL_NOTOPIC), me.name, parv[0], chptr->chname);
- else
- {
- sendto_one(sptr, rpl_str(RPL_TOPIC),
- me.name, parv[0], chptr->chname, chptr->topic);
- sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME),
- me.name, parv[0], chptr->chname,
- chptr->topic_nick, chptr->topic_time);
- }
- }
- else if (((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
- is_chan_op(sptr, chptr)) && topic)
- {
- /* setting a topic */
- ircd_strncpy(chptr->topic, topic, TOPICLEN);
- ircd_strncpy(chptr->topic_nick, sptr->name, NICKLEN);
- chptr->topic_time = CurrentTime;
- sendto_serv_butone(cptr, "%s%s " TOK_TOPIC " %s :%s",
- NumNick(sptr), chptr->chname, chptr->topic);
- sendto_channel_butserv(chptr, sptr, ":%s TOPIC %s :%s",
- parv[0], chptr->chname, chptr->topic);
- }
- else
- sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
- me.name, parv[0], chptr->chname);
- }
- return 0;
+ struct Client *from;
+ int newtopic;
+
+ if (feature_bool(FEAT_HIS_BANWHO) && IsServer(sptr))
+ from = &his;
+ else
+ from = sptr;
+ /* Note if this is just a refresh of an old topic, and don't
+ * send it to all the clients to save bandwidth. We still send
+ * it to other servers as they may have split and lost the topic.
+ */
+ newtopic=ircd_strncmp(chptr->topic,topic,TOPICLEN)!=0;
+ /* setting a topic */
+ ircd_strncpy(chptr->topic, topic, TOPICLEN);
+ ircd_strncpy(chptr->topic_nick, cli_name(from), NICKLEN);
+ chptr->topic_time = ts ? ts : TStime();
+ /* Fixed in 2.10.11: Don't propagate local topics */
+ if (!IsLocalChannel(chptr->chname))
+ sendcmdto_serv_butone(sptr, CMD_TOPIC, cptr, "%H %Tu %Tu :%s", chptr,
+ chptr->creationtime, chptr->topic_time, chptr->topic);
+ if (newtopic)
+ {
+ struct Membership *member;
+
+ /* If the member is delayed-join, show them. */
+ member = find_channel_member(sptr, chptr);
+ if (member && IsDelayedJoin(member))
+ RevealDelayedJoin(member);
+
+ sendcmdto_channel_butserv_butone(from, CMD_TOPIC, chptr, NULL, 0,
+ "%H :%s", chptr, chptr->topic);
+ }
+ /* if this is the same topic as before we send it to the person that
+ * set it (so they knew it went through ok), but don't bother sending
+ * it to everyone else on the channel to save bandwidth
+ */
+ else if (MyUser(sptr))
+ sendcmdto_one(sptr, CMD_TOPIC, sptr, "%H :%s", chptr, chptr->topic);
}
-/*
- * ms_topic - server message handler
+/** Handle a local user's attempt to get or set a channel topic.
+ *
+ * \a parv has the following elements:
+ * \li \a parv[1] is the channel name
+ * \li \a parv[\a parc - 1] is the topic (if \a parc > 2)
*
- * parv[0] = sender prefix
- * parv[1] = channel
- * parv[parc - 1] = topic (if parc > 2)
+ * See @ref m_functions for discussion of the arguments.
+ * @param[in] cptr Client that sent us the message.
+ * @param[in] sptr Original source of message.
+ * @param[in] parc Number of arguments.
+ * @param[in] parv Argument vector.
*/
-int ms_topic(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+int m_topic(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
struct Channel *chptr;
char *topic = 0, *name, *p = 0;
for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0)
{
chptr = 0;
- if (!IsChannelName(name) || !(chptr = FindChannel(name)) ||
- ((topic || SecretChannel(chptr)) && !find_channel_member(sptr, chptr)))
+ /* Does the channel exist */
+ if (!IsChannelName(name) || !(chptr = FindChannel(name)))
{
- sendto_one(sptr, err_str(chptr ? ERR_NOTONCHANNEL : ERR_NOSUCHCHANNEL),
- me.name, parv[0], chptr ? chptr->chname : name);
- continue;
+ send_reply(sptr,ERR_NOSUCHCHANNEL,name);
+ continue;
}
- if (IsModelessChannel(name))
+ /* Trying to check a topic outside a secret channel */
+ if ((topic || SecretChannel(chptr)) && !find_channel_member(sptr, chptr))
{
- sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
- chptr->chname);
+ send_reply(sptr, ERR_NOTONCHANNEL, chptr->chname);
continue;
}
- if (IsLocalChannel(name) && !MyUser(sptr))
- continue;
- if (!topic) /* only asking for topic */
+ /* only asking for topic */
+ if (!topic)
{
if (chptr->topic[0] == '\0')
- sendto_one(sptr, rpl_str(RPL_NOTOPIC), me.name, parv[0], chptr->chname);
+ send_reply(sptr, RPL_NOTOPIC, chptr->chname);
else
{
- sendto_one(sptr, rpl_str(RPL_TOPIC),
- me.name, parv[0], chptr->chname, chptr->topic);
- sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME),
- me.name, parv[0], chptr->chname,
- chptr->topic_nick, chptr->topic_time);
+ send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic);
+ send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick,
+ chptr->topic_time);
}
}
- else if (((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
- is_chan_op(sptr, chptr)) && topic)
- {
- /* setting a topic */
- ircd_strncpy(chptr->topic, topic, TOPICLEN);
- ircd_strncpy(chptr->topic_nick, sptr->name, NICKLEN);
- chptr->topic_time = CurrentTime;
- sendto_serv_butone(cptr, "%s%s " TOK_TOPIC " %s :%s",
- NumNick(sptr), chptr->chname, chptr->topic);
- sendto_channel_butserv(chptr, sptr, ":%s TOPIC %s :%s",
- parv[0], chptr->chname, chptr->topic);
- }
+ else if ((chptr->mode.mode & MODE_TOPICLIMIT) && !is_chan_op(sptr, chptr))
+ send_reply(sptr, ERR_CHANOPRIVSNEEDED, chptr->chname);
+ else if (!client_can_send_to_channel(sptr, chptr, 1))
+ send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
else
- sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
- me.name, parv[0], chptr->chname);
+ do_settopic(sptr,cptr,chptr,topic,0);
}
return 0;
}
-
-#if 0
-/*
- * m_topic
- *
- * parv[0] = sender prefix
- * parv[1] = channel
- * parv[parc - 1] = topic (if parc > 2)
+/** Handle a remote user's attempt to set a channel topic.
+ * \a parv has the following elements:
+ * \li \a parv[1] is the channel name
+ * \li \a parv[2] is the channel creation timestamp (optional)
+ * \li \a parv[2] is the topic's timestamp (optional)
+ * \li \a parv[\a parc - 1] is the topic
+ *
+ * See @ref m_functions for discussion of the arguments.
+ * @param[in] cptr Client that sent us the message.
+ * @param[in] sptr Original source of message.
+ * @param[in] parc Number of arguments.
+ * @param[in] parv Argument vector.
*/
-int m_topic(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
+int ms_topic(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
struct Channel *chptr;
char *topic = 0, *name, *p = 0;
+ time_t ts = 0;
- if (parc < 2)
+ if (parc < 3)
return need_more_params(sptr, "TOPIC");
- if (parc > 2)
- topic = parv[parc - 1];
+ topic = parv[parc - 1];
for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0)
{
chptr = 0;
- if (!IsChannelName(name) || !(chptr = FindChannel(name)) ||
- ((topic || SecretChannel(chptr)) && !find_channel_member(sptr, chptr)))
+ /* Does the channel exist */
+ if (!IsChannelName(name) || !(chptr = FindChannel(name)))
{
- sendto_one(sptr, err_str(chptr ? ERR_NOTONCHANNEL : ERR_NOSUCHCHANNEL),
- me.name, parv[0], chptr ? chptr->chname : name);
- continue;
+ send_reply(sptr,ERR_NOSUCHCHANNEL,name);
+ continue;
}
- if (IsModelessChannel(name))
+
+ /* Ignore requests for topics from remote servers */
+ if (IsLocalChannel(name) && !MyUser(sptr))
{
- sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
- chptr->chname);
+ protocol_violation(sptr,"Topic request");
continue;
}
- if (IsLocalChannel(name) && !MyUser(sptr))
+
+ /* If existing channel is older or has newer topic, ignore */
+ if (parc > 3 && (ts = atoi(parv[2])) && chptr->creationtime < ts)
continue;
- if (!topic) /* only asking for topic */
- {
- if (chptr->topic[0] == '\0')
- sendto_one(sptr, rpl_str(RPL_NOTOPIC), me.name, parv[0], chptr->chname);
- else
- {
- sendto_one(sptr, rpl_str(RPL_TOPIC),
- me.name, parv[0], chptr->chname, chptr->topic);
- sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME),
- me.name, parv[0], chptr->chname,
- chptr->topic_nick, chptr->topic_time);
- }
- }
- else if (((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
- is_chan_op(sptr, chptr)) && topic)
- {
- /* setting a topic */
- ircd_strncpy(chptr->topic, topic, TOPICLEN);
- ircd_strncpy(chptr->topic_nick, sptr->name, NICKLEN);
- chptr->topic_time = CurrentTime;
- sendto_serv_butone(cptr, "%s%s " TOK_TOPIC " %s :%s",
- NumNick(sptr), chptr->chname, chptr->topic);
- sendto_channel_butserv(chptr, sptr, ":%s TOPIC %s :%s",
- parv[0], chptr->chname, chptr->topic);
- }
- else
- sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
- me.name, parv[0], chptr->chname);
+ ts = 0; /* Default to the current time if no topic_time is passed. */
+ if (parc > 4 && (ts = atoi(parv[3])) && chptr->topic_time > ts)
+ continue;
+
+ do_settopic(sptr,cptr,chptr,topic, ts);
}
return 0;
}
-#endif /* 0 */
-