+ /* Local client? */
+ if (Client_Type(Client) == CLIENT_USER) {
+ /* Test if the user has reached the channel limit */
+ if ((Conf_MaxJoins > 0) &&
+ (Channel_CountForUser(Client) >= Conf_MaxJoins))
+ return IRC_WriteStrClient(Client,
+ ERR_TOOMANYCHANNELS_MSG,
+ Client_ID(Client), channame);
+ if (chan) {
+ /* Already existing channel: check if the
+ * client is allowed to join */
+ if (!join_allowed(Client, chan, channame, key))
+ break;
+ } else {
+ /* New channel: first user will become channel
+ * operator unless this is a modeless channel */
+ if (*channame != '+')
+ flags = "o";
+ }
+
+ /* Local client: update idle time */
+ Conn_UpdateIdle(Client_Conn(Client));
+ } else {
+ /* Remote server: we don't need to know whether the
+ * client is invited or not, but we have to make sure
+ * that the "one shot" entries (generated by INVITE
+ * commands) in this list become deleted when a user
+ * joins a channel this way. */
+ if (chan)
+ (void)Lists_Check(Channel_GetListInvites(chan),
+ target);
+ }
+
+ /* Join channel (and create channel if it doesn't exist) */
+ if (!Channel_Join(target, channame))
+ goto join_next;
+
+ if (!chan) { /* channel is new; it has been created above */
+ chan = Channel_Search(channame);
+ assert(chan != NULL);
+ if (Channel_IsModeless(chan)) {
+ Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */
+ Channel_ModeAdd(chan, 'n'); /* no external msgs */
+ }
+ }
+ assert(chan != NULL);
+
+ join_set_channelmodes(chan, target, flags);
+
+ join_forward(Client, target, chan, channame);
+
+ if (!join_send_topic(Client, target, chan, channame))
+ break; /* write error */
+
+ join_next:
+ /* next channel? */
+ channame = strtok_r(NULL, ",", &lastchan);
+ if (channame && key)
+ key = strtok_r(NULL, ",", &lastkey);
+ }
+ return CONNECTED;
+} /* IRC_JOIN */
+
+
+/**
+ * Handler for the IRC "PART" command.
+ *
+ * See RFC 2812, 3.2.2: "Part message".
+ *
+ * @param Client The client from which this command has been received
+ * @param Req Request structure with prefix and all parameters
+ * @returns CONNECTED or DISCONNECTED
+ */
+GLOBAL bool
+IRC_PART(CLIENT * Client, REQUEST * Req)
+{
+ CLIENT *target;
+ char *chan;
+
+ assert(Client != NULL);
+ assert(Req != NULL);
+
+ if (Req->argc < 1 || Req->argc > 2)
+ return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+ Client_ID(Client), Req->command);
+
+ /* Get the sender */
+ if (Client_Type(Client) == CLIENT_SERVER)
+ target = Client_Search(Req->prefix);
+ else
+ target = Client;
+ if (!target)
+ return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+ Client_ID(Client), Req->prefix);
+
+ /* Loop over all the given channel names */
+ chan = strtok(Req->argv[0], ",");
+
+ /* Make sure that "chan" is not the empty string ("PART :") */
+ if (! chan)
+ return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+ Client_ID(Client), Req->command);
+
+ while (chan) {
+ Channel_Part(target, Client, chan,
+ Req->argc > 1 ? Req->argv[1] : Client_ID(target));
+ chan = strtok(NULL, ",");