/* * IRC - Internet Relay Chat, ircd/m_join.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * 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_join.c 1336 2005-10-03 07:40:02Z sirvulcan $ */ /* * 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. */ #include "config.h" #include "channel.h" #include "class.h" #include "client.h" #include "gline.h" #include "hash.h" #include "ircd.h" #include "ircd_chattr.h" #include "ircd_features.h" #include "ircd_reply.h" #include "ircd_snprintf.h" #include "ircd_string.h" #include "msg.h" #include "numeric.h" #include "numnicks.h" #include "s_debug.h" #include "s_user.h" #include "send.h" #include #include #include /* * Helper function to find last 0 in a comma-separated list of * channel names. */ static char * last0(char *chanlist) { char *p; for (p = chanlist; p[0]; p++) /* find last "JOIN 0" */ if (p[0] == '0' && (p[1] == ',' || p[1] == '\0' || !IsChannelChar(p[1]))) { chanlist = p; /* we'll start parsing here */ if (!p[1]) /* hit the end */ break; p++; } else { while (p[0] != ',' && p[0] != '\0') /* skip past channel name */ p++; if (!p[0]) /* hit the end */ break; } return chanlist; } /* * Helper function to perform a JOIN 0 if needed; returns 0 if channel * name is not 0, else removes user from all channels and returns 1. */ static int join0(struct JoinBuf *join, struct Client *cptr, struct Client *sptr, char *chan) { struct Membership *member; struct JoinBuf part; /* is it a JOIN 0? */ if (chan[0] != '0' || chan[1] != '\0') return 0; joinbuf_join(join, 0, 0); /* join special channel 0 */ /* leave all channels */ joinbuf_init(&part, sptr, cptr, JOINBUF_TYPE_PARTALL, "Left all channels", 0); while ((member = cli_user(sptr)->channel)) joinbuf_join(&part, member->channel, IsZombie(member) ? CHFL_ZOMBIE : 0); joinbuf_flush(&part); return 1; } /* * m_join - generic message handler */ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel *chptr; struct JoinBuf join; struct JoinBuf create; struct Gline *gline; unsigned int flags = 0; int i, flex = 0; char *p = 0; char *chanlist; char *name; char *keys; char format_reply[BUFSIZE + 1]; if (parc < 2 || *parv[1] == '\0') return need_more_params(sptr, "JOIN"); joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime()); chanlist = last0(parv[1]); /* find last "JOIN 0" */ keys = parv[2]; /* remember where keys are */ for (name = ircd_strtok(&p, chanlist, ","); name; name = ircd_strtok(&p, 0, ",")) { clean_channelname(name); if (join0(&join, cptr, sptr, name)) /* did client do a JOIN 0? */ continue; /* bad channel name */ if ((!IsChannelName(name)) || (HasCntrl(name))) { send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } /* BADCHANed channel */ if ((gline = gline_find(name, GLINE_BADCHAN)) && GlineIsActive(gline) && !IsAnOper(sptr)) { send_reply(sptr, ERR_BADCHANNAME, name, gline->gl_reason); continue; } if ((chptr = FindChannel(name))) { if (find_member_link(chptr, sptr)) continue; /* already on channel */ flags = CHFL_DEOPPED; } else flags = CHFL_CHANOP; /* disallow creating local channels */ if (IsLocalChannel(name) && !chptr && !feature_bool(FEAT_LOCAL_CHANNELS) && (feature_bool(FEAT_OPER_LOCAL_CHANNELS) && !IsAnOper(sptr))) { send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } if (cli_user(sptr)->joined >= feature_int(FEAT_MAXCHANNELSPERUSER) && !HasPriv(sptr, PRIV_CHAN_LIMIT) && !IsBot(sptr)) { send_reply(sptr, ERR_TOOMANYCHANNELS, chptr ? chptr->chname : name); break; /* no point processing the other channels */ } if (!chptr && feature_bool(FEAT_CREATE_CHAN_OPER_ONLY) && !IsAnOper(sptr)) { send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } if (chptr) { if (check_target_limit(sptr, chptr, chptr->chname, 0)) continue; /* exceeded target limit */ else if ((i = can_join(sptr, chptr, keys))) { if (i == -1) flex = 1; else if (i > MAGIC_OPER_OVERRIDE) { /* oper overrode mode */ switch (i - MAGIC_OPER_OVERRIDE) { case ERR_CHANNELISFULL: /* figure out which mode */ i = 'l'; break; case ERR_INVITEONLYCHAN: i = 'i'; break; case ERR_BANNEDFROMCHAN: i = 'b'; break; case ERR_BADCHANNELKEY: i = 'k'; break; case ERR_NEEDREGGEDNICK: i = 'r'; break; case ERR_ADMINONLYCHAN: i = 'a'; break; case ERR_OPERONLYCHAN: i = 'O'; break; case ERR_SSLONLYCHAN: i = 'Z'; break; default: i = '?'; break; } /* send accountability notice */ if (IsGlobalChannel(chptr->chname)) sendwallto_group_butone(&me, WALL_DESYNCH, NULL, "OPER JOIN: %s JOIN %s (overriding +%c)", sptr->cli_name, chptr->chname, i); else sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " "(overriding +%c)", sptr, chptr, i); } else { if (i == ERR_OPERONLYCHAN) { ircd_snprintf(0, format_reply, sizeof(format_reply), "%s", format_message(cli_name(sptr), cli_username(sptr), (char*)ircd_ntoa((const char*) &(cli_ip(sptr))), cli_user(sptr)->host, chptr->chname,(char*)feature_str(FEAT_ERR_OPERONLYCHAN))); send_reply(sptr, i, chptr->chname, format_reply); } else send_reply(sptr, i, chptr->chname); continue; } } /* else if ((i = can_join(sptr, chptr, keys))) { */ joinbuf_join(&join, chptr, flags); } else if (!(chptr = get_channel(sptr, name, CGT_CREATE))) continue; /* couldn't get channel */ else if (check_target_limit(sptr, chptr, chptr->chname, 1)) { /* Note: check_target_limit will only ever return 0 here */ sub1_from_channel(chptr); /* created it... */ continue; } else { joinbuf_join(&create, chptr, flags); if (feature_bool(FEAT_AUTOCHANMODES) && feature_str(FEAT_AUTOCHANMODES_LIST) && strlen(feature_str(FEAT_AUTOCHANMODES_LIST)) > 0) SetAutoChanModes(chptr); } del_invite(sptr, chptr); if (chptr->topic[0]) { send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, chptr->topic_time); } do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */ /* * if (((chptr->mode.mode & MODE_ACCONLY) && !IsAccount(sptr)) || * ((chptr->mode.mode & MODE_MODERATED) && * (flex == 1) && feature_bool(FEAT_FLEXABLEKEYS))) { * sendcmdto_one(&me, CMD_MODE, sptr, "%H +v %C", chptr, sptr); * sendcmdto_serv_butone(&me, CMD_MODE, sptr, "%H +v %C", chptr, sptr); * sendcmdto_channel_butserv_butone(&me, CMD_MODE, chptr, cptr, 0, * "%H +v %C", chptr, sptr); * } */ } joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */ joinbuf_flush(&create); return 0; } /* * ms_join - server message handler */ int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Membership *member; struct Channel *chptr; struct JoinBuf join; unsigned int flags = 0; time_t creation = 0; char *p = 0; char *chanlist; char *name; if (IsServer(sptr)) { return protocol_violation(cptr, "%s tried to JOIN %s, duh!", cli_name(sptr), (parc < 2 || *parv[1] == '\0') ? "a channel":parv[1] ); } if (parc < 2 || *parv[1] == '\0') return need_more_params(sptr, "JOIN"); if (parc > 2 && parv[2]) creation = atoi(parv[2]); joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); chanlist = last0(parv[1]); /* find last "JOIN 0" */ for (name = ircd_strtok(&p, chanlist, ","); name; name = ircd_strtok(&p, 0, ",")) { if (join0(&join, cptr, sptr, name)) /* did client do a JOIN 0? */ continue; if (IsLocalChannel(name) || !IsChannelName(name)) { protocol_violation(cptr,"%s tried to join %s",cli_name(sptr),name); continue; } if (!(chptr = FindChannel(name))) { /* No channel exists, so create one */ if (!(chptr = get_channel(sptr, name, CGT_CREATE))) { protocol_violation(sptr,"couldn't get channel %s for %s", name,cli_name(sptr)); continue; } flags = CHFL_DEOPPED | (HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0); /* when the network is 2.10.11+ then remove MAGIC_REMOTE_JOIN_TS */ chptr->creationtime = creation ? creation : MAGIC_REMOTE_JOIN_TS; } else { /* We have a valid channel? */ if ((member = find_member_link(chptr, sptr))) { if (!IsZombie(member)) /* already on channel */ continue; flags = member->status & (CHFL_DEOPPED | CHFL_SERVOPOK); remove_user_from_channel(sptr, chptr); chptr = FindChannel(name); } else flags = CHFL_DEOPPED | (HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0); } joinbuf_join(&join, chptr, flags); } joinbuf_flush(&join); /* flush joins... */ return 0; }