+/**
+ * Part from all channels.
+ *
+ * RFC 2812, (3.2.1 Join message Command):
+ * Note that this message accepts a special argument ("0"), which is a
+ * special request to leave all channels the user is currently a member of.
+ * The server will process this message as if the user had sent a PART
+ * command (See Section 3.2.2) for each channel he is a member of.
+ *
+ * @param client Client that initiated the part request
+ * @param target Client that should part all joined channels
+ * @returns CONNECTED or DISCONNECTED
+ */
+static bool
+part_from_all_channels(CLIENT* client, CLIENT *target)
+{
+ CL2CHAN *cl2chan;
+ CHANNEL *chan;
+
+ while ((cl2chan = Channel_FirstChannelOf(target))) {
+ chan = Channel_GetChannel(cl2chan);
+ assert( chan != NULL );
+ Channel_Part(target, client, Channel_Name(chan), Client_ID(target));
+ }
+ return CONNECTED;
+} /* part_from_all_channels */
+
+/**
+ * Check weather a local client is allowed to join an already existing
+ * channel or not.
+ *
+ * @param Client Client that sent the JOIN command
+ * @param chan Channel to check
+ * @param channame Name of the channel
+ * @param key Provided channel key (or NULL)
+ * @returns true if client is allowed to join, false otherwise
+ */
+static bool
+join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
+ const char *key)
+{
+ bool is_invited, is_banned, is_exception;
+
+ /* Allow IRC operators to overwrite channel limits */
+ if (Client_HasMode(Client, 'o'))
+ return true;
+
+ is_banned = Lists_Check(Channel_GetListBans(chan), Client);
+ is_exception = Lists_Check(Channel_GetListExcepts(chan), Client);
+ is_invited = Lists_Check(Channel_GetListInvites(chan), Client);
+
+ if (is_banned && !is_invited && !is_exception) {
+ /* Client is banned from channel (and not on invite list) */
+ IRC_WriteErrClient(Client, ERR_BANNEDFROMCHAN_MSG,
+ Client_ID(Client), channame);
+ return false;
+ }
+
+ if (Channel_HasMode(chan, 'i') && !is_invited) {
+ /* Channel is "invite-only" and client is not on invite list */
+ IRC_WriteErrClient(Client, ERR_INVITEONLYCHAN_MSG,
+ Client_ID(Client), channame);
+ return false;
+ }
+
+ if (!Channel_CheckKey(chan, Client, key ? key : "")) {
+ /* Channel is protected by a channel key and the client
+ * didn't specify the correct one */
+ IRC_WriteErrClient(Client, ERR_BADCHANNELKEY_MSG,
+ Client_ID(Client), channame);
+ return false;
+ }
+
+ if (Channel_HasMode(chan, 'l') &&
+ (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) {
+ /* There are more clints joined to this channel than allowed */
+ IRC_WriteErrClient(Client, ERR_CHANNELISFULL_MSG,
+ Client_ID(Client), channame);
+ return false;
+ }
+
+ if (Channel_HasMode(chan, 'z') && !Conn_UsesSSL(Client_Conn(Client))) {
+ /* Only "secure" clients are allowed, but clients doesn't
+ * use SSL encryption */
+ IRC_WriteErrClient(Client, ERR_SECURECHANNEL_MSG,
+ Client_ID(Client), channame);
+ return false;
+ }
+
+ if (Channel_HasMode(chan, 'O') && !Client_HasMode(Client, 'o')) {
+ /* Only IRC operators are allowed! */
+ IRC_WriteErrClient(Client, ERR_OPONLYCHANNEL_MSG,
+ Client_ID(Client), channame);
+ return false;
+ }
+
+ if (Channel_HasMode(chan, 'R') && !Client_HasMode(Client, 'R')) {
+ /* Only registered users are allowed! */
+ IRC_WriteErrClient(Client, ERR_REGONLYCHANNEL_MSG,
+ Client_ID(Client), channame);
+ return false;
+ }
+
+ return true;
+} /* join_allowed */
+
+/**
+ * Set user channel modes.
+ *
+ * @param chan Channel
+ * @param target User to set modes for
+ * @param flags Channel modes to add
+ */
+static void
+join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags)
+{
+ if (flags) {
+ while (*flags) {
+ Channel_UserModeAdd(chan, target, *flags);
+ flags++;
+ }
+ }
+
+ /* If the channel is persistent (+P) and client is an IRC op:
+ * make client chanop, if not disabled in configuration. */
+ if (Channel_HasMode(chan, 'P') && Conf_OperChanPAutoOp
+ && Client_HasMode(target, 'o'))
+ Channel_UserModeAdd(chan, target, 'o');
+} /* join_set_channelmodes */
+
+/**
+ * Forward JOIN command to a specific server
+ *
+ * This function differentiates between servers using RFC 2813 mode that
+ * support the JOIN command with appended ASCII 7 character and channel
+ * modes, and servers using RFC 1459 protocol which require separate JOIN
+ * and MODE commands.
+ *
+ * @param To Forward JOIN (and MODE) command to this peer server
+ * @param Prefix Client used to prefix the genrated commands
+ * @param Data Parameters of JOIN command to forward, probably
+ * containing channel modes separated by ASCII 7.
+ */
+static void
+cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data)
+{
+ CONN_ID conn;
+ char str[COMMAND_LEN], *ptr = NULL;
+
+ strlcpy(str, (char *)Data, sizeof(str));
+ conn = Client_Conn(To);
+
+ if (Conn_Options(conn) & CONN_RFC1459) {
+ /* RFC 1459 compatibility mode, appended modes are NOT
+ * supported, so strip them off! */
+ ptr = strchr(str, 0x7);
+ if (ptr)
+ *ptr++ = '\0';
+ }
+
+ IRC_WriteStrClientPrefix(To, Prefix, "JOIN %s", str);
+ if (ptr && *ptr)
+ IRC_WriteStrClientPrefix(To, Prefix, "MODE %s +%s %s", str, ptr,
+ Client_ID(Prefix));
+} /* cb_join_forward */