+ /* Is argument "0"? */
+ if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
+ return part_from_all_channels(Client, target);
+
+ /* Are channel keys given? */
+ if (Req->argc > 1)
+ key = strtok_r(Req->argv[1], ",", &lastkey);
+
+ channame = Req->argv[0];
+ channame = strtok_r(channame, ",", &lastchan);
+
+ /* Make sure that "channame" is not the empty string ("JOIN :") */
+ if (! channame)
+ return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+ Client_ID(Client), Req->command);
+
+ while (channame) {
+ flags = NULL;
+
+ /* Did the server include channel-user-modes? */
+ if (Client_Type(Client) == CLIENT_SERVER) {
+ flags = strchr(channame, 0x7);
+ if (flags) {
+ *flags = '\0';
+ flags++;
+ }
+ }
+
+ chan = Channel_Search(channame);
+ if (!chan && Conf_PredefChannelsOnly) {
+ /* channel must be created, but forbidden by config */
+ IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG,
+ Client_ID(Client), channame);
+ goto join_next;
+ }
+
+ /* Local client? */
+ if (Client_Type(Client) == CLIENT_USER) {
+ if (chan) {
+ /* Already existing channel: already member? */
+ if (Channel_IsMemberOf(chan, Client))
+ goto join_next;
+ }
+
+ /* Test if the user has reached the channel limit */
+ if ((Conf_MaxJoins > 0) &&
+ (Channel_CountForUser(Client) >= Conf_MaxJoins)) {
+ if (!IRC_WriteStrClient(Client,
+ ERR_TOOMANYCHANNELS_MSG,
+ Client_ID(Client), channame))
+ return DISCONNECTED;
+ goto join_next;
+ }
+
+ if (chan) {
+ /* Already existing channel: check if the
+ * client is allowed to join */
+ if (!join_allowed(Client, chan, channame, key))
+ goto join_next;
+ } 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 */
+ }