/** 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). */ static void do_settopic(struct Client *sptr, struct Client *cptr, struct Channel *chptr, char *topic, time_t ts) { 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(sptr, CMD_TOPIC, cptr, "%H %Tu %Tu :%s", chptr, chptr->creationtime, chptr->topic_time, chptr->topic); if (newtopic) sendcmdto_channel(from, CMD_TOPIC, chptr, NULL, SKIP_SERVERS, "%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); }
/** Handle a RESTART message from a server connection. * * \a parv has the following elements: * \li \a parv[1] is the target server, or "*" for all. * \li \a parv[2] is either "cancel" or a time interval in seconds * \li \a parv[\a parc - 1] is the reason * * All fields must be present. Additionally, the time interval should * not be 0 for messages sent to "*", as that may not function * reliably due to buffering in the server. * * 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_restart(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { const char *target, *when, *reason; if (parc < 4) return need_more_params(sptr, "RESTART"); target = parv[1]; when = parv[2]; reason = parv[parc - 1]; /* is it a message we should pay attention to? */ if (target[0] != '*' || target[1] != '\0') { if (hunt_server_cmd(sptr, CMD_RESTART, cptr, 0, "%C %s :%s", 1, parc, parv) != HUNTED_ISME) return 0; } else /* must forward the message */ sendcmdto_serv(sptr, CMD_RESTART, cptr, "* %s :%s", when, reason); /* OK, the message has been forwarded, but before we can act... */ if (!feature_bool(FEAT_NETWORK_RESTART)) return 0; /* is it a cancellation? */ if (!ircd_strcmp(when, "cancel")) exit_cancel(sptr); /* cancel a pending exit */ else /* schedule an exit */ exit_schedule(1, atoi(when), sptr, reason); return 0; }
/** Handle an AWAY message from a server. * * \a parv has the following elements: * \li \a parv[1] (optional) is the new away message. * * 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_away(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* away_message = parv[1]; assert(0 != cptr); assert(0 != sptr); /* * servers can't set away */ if (IsServer(sptr)) return protocol_violation(sptr,"Server trying to set itself away"); if (user_set_away(cli_user(sptr), away_message)) sendcmdto_serv(sptr, CMD_AWAY, cptr, ":%s", away_message); else sendcmdto_serv(sptr, CMD_AWAY, cptr, ""); return 0; }
/** Handle an AWAY message from a local user. * * \a parv has the following elements: * \li \a parv[1] (optional) is the new away message. * * 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_away(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* away_message = parv[1]; int was_away = cli_user(sptr)->away != 0; assert(0 != cptr); assert(cptr == sptr); if (user_set_away(cli_user(sptr), away_message)) { if (!was_away) sendcmdto_serv(sptr, CMD_AWAY, cptr, ":%s", away_message); send_reply(sptr, RPL_NOWAWAY); } else { sendcmdto_serv(sptr, CMD_AWAY, cptr, ""); send_reply(sptr, RPL_UNAWAY); } return 0; }
/** Handle an ACCOUNT message from a server connection. * * \a parv has the following elements: * \li \a parv[1] is the numnick of the client to act on * \li \a parv[2] is the account name * \li \a parv[3] (optional) is the account timestamp * * 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_account(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; if (parc < 3) return need_more_params(sptr, "ACCOUNT"); if (!IsServer(sptr)) return protocol_violation(cptr, "ACCOUNT from non-server %s", cli_name(sptr)); if (!(acptr = findNUser(parv[1]))) return 0; /* Ignore ACCOUNT for a user that QUIT; probably crossed */ if (IsAccount(acptr)) return protocol_violation(cptr, "ACCOUNT for already registered user %s " "(%s -> %s)", cli_name(acptr), cli_user(acptr)->account, parv[2]); assert(0 == cli_user(acptr)->account[0]); if (strlen(parv[2]) > ACCOUNTLEN) return protocol_violation(cptr, "Received account (%s) longer than %d for %s; " "ignoring.", parv[2], ACCOUNTLEN, cli_name(acptr)); if (parc > 3) { cli_user(acptr)->acc_create = atoi(parv[3]); Debug((DEBUG_DEBUG, "Received timestamped account: account \"%s\", " "timestamp %Tu", parv[2], cli_user(acptr)->acc_create)); } ircd_strncpy(cli_user(acptr)->account, parv[2], ACCOUNTLEN); hide_hostmask(acptr, FLAG_ACCOUNT); sendcmdto_serv(sptr, CMD_ACCOUNT, cptr, cli_user(acptr)->acc_create ? "%C %s %Tu" : "%C %s", acptr, cli_user(acptr)->account, cli_user(acptr)->acc_create); return 0; }
/** Check whether the introduction of a new server would cause a loop * or be disallowed by leaf and hub configuration directives. * @param[in] cptr Neighbor who sent the message. * @param[in] sptr Client that originated the message. * @param[out] ghost If non-NULL, receives ghost timestamp for new server. * @param[in] host Name of new server. * @param[in] numnick Numnick mask of new server. * @param[in] timestamp Claimed link timestamp of new server. * @param[in] hop Number of hops to the new server. * @param[in] junction Non-zero if the new server is still bursting. * @return CPTR_KILLED if \a cptr was SQUIT. 0 if some other server * was SQUIT. 1 if the new server is allowed. */ static int check_loop_and_lh(struct Client* cptr, struct Client *sptr, time_t *ghost, const char *host, const char *numnick, time_t timestamp, unsigned int hop, int junction) { struct Client* acptr; struct Client* LHcptr = NULL; struct ConfItem* lhconf; enum lh_type active_lh_line = ALLOWED; int ii; if (ghost) *ghost = 0; /* * Calculate type of connect limit and applicable config item. */ lhconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER); assert(lhconf != NULL); if (ghost) { if (!feature_bool(FEAT_HUB)) for (ii = 0; ii <= HighestFd; ii++) if (LocalClientArray[ii] && IsServer(LocalClientArray[ii])) { active_lh_line = I_AM_NOT_HUB; break; } } else if (hop > lhconf->maximum) { /* Because "maximum" should be 0 for non-hub links, check whether * there is a hub mask -- if not, complain that the server isn't * allowed to hub. */ active_lh_line = lhconf->hub_limit ? MAX_HOPS_EXCEEDED : NOT_ALLOWED_TO_HUB; } else if (lhconf->hub_limit && match(lhconf->hub_limit, host)) { struct Client *ac3ptr; active_lh_line = NOT_ALLOWED_TO_HUB; if (junction) for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (IsJunction(ac3ptr)) { LHcptr = ac3ptr; break; } } /* * We want to find IsConnecting() and IsHandshake() too, * use FindClient(). * The second finds collisions with numeric representation of existing * servers - these shouldn't happen anymore when all upgraded to 2.10. * -- Run */ while ((acptr = FindClient(host)) || (numnick && (acptr = FindNServer(numnick)))) { /* * This link is trying feed me a server that I already have * access through another path * * Do not allow Uworld to do this. * Do not allow servers that are juped. * Do not allow servers that have older link timestamps * then this try. * Do not allow servers that use the same numeric as an existing * server, but have a different name. * * If my ircd.conf sucks, I can try to connect to myself: */ if (acptr == &me) return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s), check server number in M:?", host); /* * Detect wrong numeric. */ if (0 != ircd_strcmp(cli_name(acptr), host)) { sendcmdto_serv(&me, CMD_WALLOPS, cptr, ":SERVER Numeric Collision: %s != %s", cli_name(acptr), host); return exit_client_msg(cptr, cptr, &me, "NUMERIC collision between %s and %s." " Is your server numeric correct ?", host, cli_name(acptr)); } /* * Kill our try, if we had one. */ if (IsConnecting(acptr)) { if (active_lh_line == ALLOWED && exit_client(cptr, acptr, &me, "Just connected via another link") == CPTR_KILLED) return CPTR_KILLED; /* * We can have only ONE 'IsConnecting', 'IsHandshake' or * 'IsServer', because new 'IsConnecting's are refused to * the same server if we already had it. */ break; } /* * Avoid other nick collisions... * This is a doubtful test though, what else would it be * when it has a server.name ? */ else if (!IsServer(acptr) && !IsHandshake(acptr)) return exit_client_msg(cptr, cptr, &me, "Nickname %s already exists!", host); /* * Our new server might be a juped server: */ else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4))) { if (!IsServer(sptr)) return exit_client(cptr, sptr, &me, cli_info(acptr)); sendcmdto_serv(&me, CMD_WALLOPS, cptr, ":Received :%s SERVER %s from %s !?!", NumServ(cptr), host, cli_name(cptr)); return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr)); } /* * Of course we find the handshake this link was before :) */ else if (IsHandshake(acptr) && acptr == cptr) break; /* * Here we have a server nick collision... * We don't want to kill the link that was last /connected, * but we neither want to kill a good (old) link. * Therefor we kill the second youngest link. */ if (1) { struct Client* c2ptr = 0; struct Client* c3ptr = acptr; struct Client* ac2ptr; struct Client* ac3ptr; /* Search youngest link: */ for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp) c3ptr = ac3ptr; if (IsServer(sptr)) { for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp) c3ptr = ac3ptr; } if (timestamp > cli_serv(c3ptr)->timestamp) { c3ptr = 0; c2ptr = acptr; /* Make sure they differ */ } /* Search second youngest link: */ for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up) if (ac2ptr != c3ptr && cli_serv(ac2ptr)->timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = ac2ptr; if (IsServer(sptr)) { for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up) if (ac2ptr != c3ptr && cli_serv(ac2ptr)->timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = ac2ptr; } if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = 0; /* If timestamps are equal, decide which link to break * by name. */ if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) == (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp)) { const char *n2, *n2up, *n3, *n3up; if (c2ptr) { n2 = cli_name(c2ptr); n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up); } else { n2 = host; n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me); } if (c3ptr) { n3 = cli_name(c3ptr); n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up); } else { n3 = host; n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me); } if (strcmp(n2, n2up) > 0) n2 = n2up; if (strcmp(n3, n3up) > 0) n3 = n3up; if (strcmp(n3, n2) > 0) { ac2ptr = c2ptr; c2ptr = c3ptr; c3ptr = ac2ptr; } } /* Now squit the second youngest link: */ if (!c2ptr) return exit_new_server(cptr, sptr, host, timestamp, "server %s already exists and is %ld seconds younger.", host, (long)cli_serv(acptr)->timestamp - (long)timestamp); else if (cli_from(c2ptr) == cptr || IsServer(sptr)) { struct Client *killedptrfrom = cli_from(c2ptr); if (active_lh_line != ALLOWED) { /* * If the L: or H: line also gets rid of this link, * we sent just one squit. */ if (LHcptr && a_kills_b_too(LHcptr, c2ptr)) break; /* * If breaking the loop here solves the L: or H: * line problem, we don't squit that. */ if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr))) active_lh_line = ALLOWED; else { /* * If we still have a L: or H: line problem, * we prefer to squit the new server, solving * loop and L:/H: line problem with only one squit. */ LHcptr = 0; break; } } /* * If the new server was introduced by a server that caused a * Ghost less then 20 seconds ago, this is probably also * a Ghost... (20 seconds is more then enough because all * SERVER messages are at the beginning of a net.burst). --Run */ if (CurrentTime - cli_serv(cptr)->ghost < 20) { killedptrfrom = cli_from(acptr); if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED) return CPTR_KILLED; } else if (exit_client_msg(cptr, c2ptr, &me, "Loop <-- %s (new link is %ld seconds younger)", host, (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) - (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED) return CPTR_KILLED; /* * Did we kill the incoming server off already ? */ if (killedptrfrom == cptr) return 0; } else { if (active_lh_line != ALLOWED) { if (LHcptr && a_kills_b_too(LHcptr, acptr)) break; if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr))) active_lh_line = ALLOWED; else { LHcptr = 0; break; } } /* * We can't believe it is a lagged server message * when it directly connects to us... * kill the older link at the ghost, rather then * at the second youngest link, assuming it isn't * a REAL loop. */ if (ghost) *ghost = CurrentTime; /* Mark that it caused a ghost */ if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED) return CPTR_KILLED; break; } } } if (active_lh_line != ALLOWED) { if (!LHcptr) LHcptr = sptr; if (active_lh_line == MAX_HOPS_EXCEEDED) { return exit_client_msg(cptr, LHcptr, &me, "Maximum hops exceeded for %s at %s", cli_name(cptr), host); } else if (active_lh_line == NOT_ALLOWED_TO_HUB) { return exit_client_msg(cptr, LHcptr, &me, "%s is not allowed to hub for %s", cli_name(cptr), host); } else /* I_AM_NOT_HUB */ { ServerStats->is_ref++; return exit_client(cptr, LHcptr, &me, "I'm a leaf, define the HUB feature"); } } return 1; }
/** Handle a GLINE message from a server. * * \a parv has the following elements: * \li \a parv[1] is the target server numnick or "*" for all servers * \li \a parv[2] is the G-line mask (preceded by modifier flags) * \li \a parv[3] is the G-line lifetime in seconds * \li \a parv[4] (optional) is the G-line's last modification time * \li \a parv[\a parc - 1] is the G-line comment * * If the issuer is a server or there is no timestamp, the issuer must * be flagged as a UWorld server. In this case, if the '-' modifier * flag is used, the G-line lifetime and all following arguments may * be omitted. * * Three modifier flags are recognized, and must be present in this * order: * \li '!' Indicates an G-line that an oper forcibly applied. * \li '-' Indicates that the following G-line should be removed. * \li '+' (exclusive of '-') indicates that the G-line should be * activated. * * 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_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *acptr = 0; struct Gline *agline = 0; unsigned int flags = 0; enum GlineAction action = GLINE_MODIFY; time_t expire = 0, lastmod = 0, lifetime = 0; char *mask = parv[2], *target = parv[1], *reason = "No reason", *tmp = 0; if (parc < 3) return need_more_params(sptr, "GLINE"); if (IsServer(sptr)) flags |= GLINE_FORCE; if (*mask == '!') { mask++; flags |= GLINE_OPERFORCE; /* assume oper had WIDE_GLINE */ } switch (*mask) { /* handle +, -, <, and > */ case '+': /* activate the G-line */ action = GLINE_ACTIVATE; mask++; break; case '-': /* deactivate the G-line */ action = GLINE_DEACTIVATE; mask++; break; case '>': /* locally activate the G-line */ action = GLINE_LOCAL_ACTIVATE; mask++; break; case '<': /* locally deactivate the G-line */ action = GLINE_LOCAL_DEACTIVATE; mask++; break; } /* Is there no mask left? */ if (mask[0] == '\0') return need_more_params(sptr, "GLINE"); /* Now, let's figure out if it's a local or global G-line */ if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE || (target[0] == '*' && target[1] == '\0')) flags |= GLINE_GLOBAL; else flags |= GLINE_LOCAL; /* now figure out if we need to resolve a server */ if ((action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE || (flags & GLINE_LOCAL)) && !(acptr = FindNServer(target))) return 0; /* no such server, jump out */ /* If it's a local activate/deactivate and server isn't me, propagate it */ if ((action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE) && !IsMe(acptr)) { Debug((DEBUG_DEBUG, "I am forwarding a local change to a global gline " "to a remote server; target %s, mask %s, operforce %s, action %c", target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO", action == GLINE_LOCAL_ACTIVATE ? '>' : '<')); sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s", acptr, flags & GLINE_OPERFORCE ? "!" : "", action == GLINE_LOCAL_ACTIVATE ? '>' : '<', mask); return 0; /* all done */ } /* Next, try to find the G-line... */ if ((flags & GLINE_GLOBAL) || IsMe(acptr)) /* don't bother if it's not me! */ agline = gline_find(mask, flags | GLINE_ANY | GLINE_EXACT); /* We now have all the pieces to tell us what we've got; let's put * it all together and convert the rest of the arguments. */ /* Handle the local G-lines first... */ if (flags & GLINE_LOCAL) { assert(acptr); /* normalize the action, first */ if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_MODIFY) action = GLINE_ACTIVATE; else if (action == GLINE_LOCAL_DEACTIVATE) action = GLINE_DEACTIVATE; if (action == GLINE_ACTIVATE) { /* get expiration and reason */ if (parc < 5) /* check parameter count... */ return need_more_params(sptr, "GLINE"); expire = atoi(parv[3]); /* get expiration... */ expire = abs_expire(expire); /* convert to absolute... */ reason = parv[parc - 1]; /* and reason */ if (IsMe(acptr)) { if (agline) /* G-line already exists, so let's ignore it... */ return 0; /* OK, create the local G-line */ Debug((DEBUG_DEBUG, "I am creating a local G-line here; target %s, " "mask %s, operforce %s, action %s, expire %Tu, reason: %s", target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO", action == GLINE_ACTIVATE ? "+" : "-", expire, reason)); return gline_add(cptr, sptr, mask, reason, expire, lastmod, lifetime, flags | GLINE_ACTIVE); } } else if (IsMe(acptr)) { /* destroying a local G-line */ if (!agline) /* G-line doesn't exist, so let's complain... */ return send_reply(sptr, ERR_NOSUCHGLINE, mask); /* Let's now destroy the G-line */; Debug((DEBUG_DEBUG, "I am destroying a local G-line here; target %s, " "mask %s, operforce %s, action %s", target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO", action == GLINE_ACTIVATE ? "+" : "-")); return gline_destroy(cptr, sptr, agline); } /* OK, we've converted arguments; if it's not for us, forward */ /* UPDATE NOTE: Once all servers are updated to u2.10.12.11, the * format string in this sendcmdto_one() may be updated to omit * <lastmod> for GLINE_ACTIVATE and to omit <expire>, <lastmod>, * and <reason> for GLINE_DEACTIVATE. */ assert(!IsMe(acptr)); Debug((DEBUG_DEBUG, "I am forwarding a local G-line to a remote server; " "target %s, mask %s, operforce %s, action %c, expire %Tu, " "lastmod %Tu, reason: %s", target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO", action == GLINE_ACTIVATE ? '+' : '-', expire, TStime(), reason)); sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %Tu %Tu :%s", acptr, flags & GLINE_OPERFORCE ? "!" : "", action == GLINE_ACTIVATE ? '+' : '-', mask, expire - TStime(), TStime(), reason); return 0; /* all done */ } /* can't modify a G-line that doesn't exist, so remap to activate */ if (!agline && action == GLINE_MODIFY) action = GLINE_ACTIVATE; /* OK, let's figure out what other parameters we may have... */ switch (action) { case GLINE_LOCAL_ACTIVATE: /* locally activating a G-line */ case GLINE_LOCAL_DEACTIVATE: /* locally deactivating a G-line */ if (!agline) /* no G-line to locally activate or deactivate? */ return send_reply(sptr, ERR_NOSUCHGLINE, mask); lastmod = agline->gl_lastmod; break; /* no additional parameters to manipulate */ case GLINE_ACTIVATE: /* activating a G-line */ case GLINE_DEACTIVATE: /* deactivating a G-line */ /* in either of these cases, we have at least a lastmod parameter */ if (parc < 4) return need_more_params(sptr, "GLINE"); else if (parc == 4) /* lastmod only form... */ lastmod = atoi(parv[3]); /*FALLTHROUGH*/ case GLINE_MODIFY: /* modifying a G-line */ /* convert expire and lastmod, look for lifetime and reason */ if (parc > 4) { /* protect against fall-through from 4-param form */ expire = atoi(parv[3]); /* convert expiration and lastmod */ expire = abs_expire(expire); lastmod = atoi(parv[4]); flags |= GLINE_EXPIRE; /* we have an expiration time update */ if (parc > 6) { /* no question, have a lifetime and reason */ lifetime = atoi(parv[5]); reason = parv[parc - 1]; flags |= GLINE_LIFETIME | GLINE_REASON; } else if (parc == 6) { /* either a lifetime or a reason */ if (!agline || /* gline creation, has to be the reason */ /* trial-convert as lifetime, and if it doesn't fully convert, * it must be the reason */ (!(lifetime = strtoul(parv[5], &tmp, 10)) && !*tmp)) { lifetime = 0; reason = parv[5]; flags |= GLINE_REASON; /* have a reason update */ } else if (lifetime) flags |= GLINE_LIFETIME; /* have a lifetime update */ } } } if (!lastmod) /* must have a lastmod parameter by now */ return need_more_params(sptr, "GLINE"); Debug((DEBUG_DEBUG, "I have a global G-line I am acting upon now; " "target %s, mask %s, operforce %s, action %s, expire %Tu, " "lastmod %Tu, lifetime %Tu, reason: %s; gline %s! (fields " "present: %s %s %s)", target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO", action == GLINE_ACTIVATE ? "+" : (action == GLINE_DEACTIVATE ? "-" : (action == GLINE_LOCAL_ACTIVATE ? ">" : (action == GLINE_LOCAL_DEACTIVATE ? "<" : "(MODIFY)"))), expire, lastmod, lifetime, reason, agline ? "EXISTS" : "does not exist", flags & GLINE_EXPIRE ? "expire" : "", flags & GLINE_LIFETIME ? "lifetime" : "", flags & GLINE_REASON ? "reason" : "")); /* OK, at this point, we have converted all available parameters. * Let's actually do the action! */ if (agline) return gline_modify(cptr, sptr, agline, action, reason, expire, lastmod, lifetime, flags); assert(action != GLINE_LOCAL_ACTIVATE); assert(action != GLINE_LOCAL_DEACTIVATE); assert(action != GLINE_MODIFY); if (!expire) { /* Cannot *add* a G-line we don't have, but try hard */ Debug((DEBUG_DEBUG, "Propagating G-line %s for G-line we don't have", action == GLINE_ACTIVATE ? "activation" : "deactivation")); /* propagate the G-line, even though we don't have it */ sendcmdto_serv(sptr, CMD_GLINE, cptr, "* %c%s %Tu", action == GLINE_ACTIVATE ? '+' : '-', mask, lastmod); return 0; } return gline_add(cptr, sptr, mask, reason, expire, lastmod, lifetime, flags | ((action == GLINE_ACTIVATE) ? GLINE_ACTIVE : 0)); }
/** Handle a KICK message from a local connection. * * \a parv has the following elements: * \li \a parv[1] is the channel name to kick someone from * \li \a parv[2] is the nickname of the client to kick * \li \a parv[\a parc - 1] (optional) is the kick comment * * 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_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *who; struct Channel *chptr; struct Membership *member = 0; struct Membership* member2; char *name, *comment; if (parc < 3 || *parv[1] == '\0') return need_more_params(sptr, "KICK"); name = parv[1]; /* simple checks */ if (!(chptr = get_channel(sptr, name, CGT_NO_CREATE))) return send_reply(sptr, ERR_NOSUCHCHANNEL, name); #if defined(DDB) || defined(SERVICES) if (!(member2 = find_member_link(chptr, sptr)) || IsZombie(member2) || !(IsChanOwner(member2) || IsChanOp(member2))) #else if (!(member2 = find_member_link(chptr, sptr)) || IsZombie(member2) || !IsChanOp(member2)) #endif return send_reply(sptr, ERR_CHANOPRIVSNEEDED, name); if (!(who = find_chasing(sptr, parv[2], 0))) return 0; /* find_chasing sends the reply for us */ /* Don't allow the channel service to be kicked */ if (IsChannelService(who)) return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname); /* Prevent kicking opers from local channels -DM- */ if (IsLocalChannel(chptr->chname) && HasPriv(who, PRIV_DEOP_LCHAN)) return send_reply(sptr, ERR_ISOPERLCHAN, cli_name(who), chptr->chname); /* check if kicked user is actually on the channel */ if (!(member = find_member_link(chptr, who)) || IsZombie(member)) return send_reply(sptr, ERR_USERNOTINCHANNEL, cli_name(who), chptr->chname); #if defined(UNDERNET) /* Don't allow to kick member with a higher op-level, * or members with the same op-level unless both are MAXOPLEVEL. */ if (OpLevel(member) < OpLevel(member2) || (OpLevel(member) == OpLevel(member2) && OpLevel(member) < MAXOPLEVEL)) return send_reply(sptr, ERR_NOTLOWEROPLEVEL, cli_name(who), chptr->chname, OpLevel(member2), OpLevel(member), "kick", OpLevel(member) == OpLevel(member2) ? "the same" : "a higher"); #elif defined(DDB) || defined(SERVICES) /* Don't allow to kick channel owner */ if (IsChanOwner(member) && !IsAnOper(sptr)) return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname); #endif /* We rely on ircd_snprintf to truncate the comment */ comment = EmptyString(parv[parc - 1]) ? parv[0] : parv[parc - 1]; if (!IsLocalChannel(name)) sendcmdto_serv(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who, comment); if (IsDelayedJoin(member)) { /* If it's a delayed join, only send the KICK to the person doing * the kicking and the victim */ if (MyUser(who)) sendcmdto_one(sptr, CMD_KICK, who, "%H %C :%s", chptr, who, comment); sendcmdto_one(who, CMD_JOIN, sptr, "%H", chptr); sendcmdto_one(sptr, CMD_KICK, sptr, "%H %C :%s", chptr, who, comment); } else sendcmdto_channel(sptr, CMD_KICK, chptr, NULL, SKIP_SERVERS, "%H %C :%s", chptr, who, comment); make_zombie(member, who, cptr, sptr, chptr); return 0; }
/** Handle a KICK message from a server connection. * * \a parv has the following elements: * \li \a parv[1] is the channel name to kick someone from * \li \a parv[2] is the numnick of the client to kick * \li \a parv[\a parc - 1] is the kick comment * * 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_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *who; struct Channel *chptr; struct Membership *member = 0, *sptr_link = 0; char *name, *comment; if (parc < 3 || *parv[1] == '\0') return need_more_params(sptr, "KICK"); name = parv[1]; comment = parv[parc - 1]; /* figure out who gets kicked from what */ if (IsLocalChannel(name) || !(chptr = get_channel(sptr, name, CGT_NO_CREATE)) || !(who = findNUser(parv[2]))) return 0; /* We go ahead and pass on the KICK for users not on the channel */ member = find_member_link(chptr, who); if (member && IsZombie(member)) { /* We might get a KICK from a zombie's own server because the user * net-rode during a burst (which always generates a KICK) *and* * was kicked via another server. In that case, we must remove * the user from the channel. */ if (sptr == cli_user(who)->server) { remove_user_from_channel(who, chptr); } /* Otherwise, we treat zombies like they are not channel members. */ member = 0; } /* Send HACK notice, but not for servers in BURST */ /* 2002-10-17: Don't send HACK if the users local server is kicking them */ if (IsServer(sptr) && !IsBurstOrBurstAck(sptr) && sptr!=cli_user(who)->server) sendto_opmask(0, SNO_HACK4, "HACK: %C KICK %H %C %s", sptr, chptr, who, comment); /* Unless someone accepted it downstream (or the user isn't on the channel * here), if kicker is not on channel, or if kicker is not a channel * operator, bounce the kick */ #if defined(DDB) || defined(SERVICES) if (!IsServer(sptr) && member && cli_from(who) != cptr && !IsService(cli_user(sptr)->server) && (!(sptr_link = find_member_link(chptr, sptr)) || !(IsChanOwner(sptr_link) || IsChanOp(sptr_link)))) { #else if (!IsServer(sptr) && member && cli_from(who) != cptr && (!(sptr_link = find_member_link(chptr, sptr)) || !IsChanOp(sptr_link))) { #endif sendto_opmask(0, SNO_HACK2, "HACK: %C KICK %H %C %s", sptr, chptr, who, comment); sendcmdto_one(who, CMD_JOIN, cptr, "%H", chptr); /* Reop/revoice member */ if (IsChanOp(member) || HasVoice(member)) { struct ModeBuf mbuf; modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_SERVER | /* Send mode to a server */ MODEBUF_DEST_DEOP | /* Deop the source */ MODEBUF_DEST_BOUNCE)); /* And bounce the MODE */ #if defined(UNDERNET) if (IsChanOp(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, who, OpLevel(member)); if (HasVoice(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, who, MAXOPLEVEL + 1); #else if (IsChanOp(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, who, 0); if (HasVoice(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, who, 0); #endif modebuf_flush(&mbuf); } } else { /* Propagate kick... */ sendcmdto_serv(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who, comment); if (member) { /* and tell the channel about it */ if (IsDelayedJoin(member)) { if (MyUser(who)) sendcmdto_one(IsServer(sptr) ? &his : sptr, CMD_KICK, who, "%H %C :%s", chptr, who, comment); } else { sendcmdto_channel(IsServer(sptr) ? &his : sptr, CMD_KICK, chptr, NULL, SKIP_SERVERS, "%H %C :%s", chptr, who, comment); } make_zombie(member, who, cptr, sptr, chptr); } } return 0; }