+ /* Send reply to client and inform other servers */
+ ok = IRC_WriteStrClientPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
+ IRC_WriteStrServersPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
+ }
+ LogDebug("%s \"%s\": Mode change, now \"%s\".",
+ Client_TypeText(Target), Client_Mask(Target),
+ Client_Modes(Target));
+ }
+
+ IRC_SetPenalty( Client, 1 );
+ return ok;
+} /* Client_Mode */
+
+
+static bool
+Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
+{
+ char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], argadd[CLIENT_PASS_LEN];
+ const char *mode_ptr;
+
+ /* Member or not? -- That's the question! */
+ if (!Channel_IsMemberOf(Channel, Origin))
+ return IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
+ Client_ID(Origin), Channel_Name(Channel), Channel_Modes(Channel));
+
+ /* The sender is a member: generate extended reply */
+ strlcpy(the_modes, Channel_Modes(Channel), sizeof(the_modes));
+ mode_ptr = the_modes;
+ the_args[0] = '\0';
+
+ while(*mode_ptr) {
+ switch(*mode_ptr) {
+ case 'l':
+ snprintf(argadd, sizeof(argadd), " %lu", Channel_MaxUsers(Channel));
+ strlcat(the_args, argadd, sizeof(the_args));
+ break;
+ case 'k':
+ strlcat(the_args, " ", sizeof(the_args));
+ strlcat(the_args, Channel_Key(Channel), sizeof(the_args));
+ break;
+ }
+ mode_ptr++;
+ }
+ if (the_args[0])
+ strlcat(the_modes, the_args, sizeof(the_modes));
+
+ if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
+ Client_ID(Origin), Channel_Name(Channel),
+ the_modes))
+ return DISCONNECTED;
+#ifndef STRICT_RFC
+ if (!IRC_WriteStrClient(Origin, RPL_CREATIONTIME_MSG,
+ Client_ID(Origin), Channel_Name(Channel),
+ Channel_CreationTime(Channel)))
+ return DISCONNECTED;
+#endif
+ return CONNECTED;
+}
+
+
+/**
+ * Handle channel mode and channel-user mode changes
+ */
+static bool
+Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
+{
+ char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
+ argadd[CLIENT_PASS_LEN], *mode_ptr;
+ bool connected, set, skiponce, retval, onchannel, modeok, use_servermode;
+ int mode_arg, arg_arg;
+ CLIENT *client;
+ long l;
+ size_t len;
+
+ if (Channel_IsModeless(Channel))
+ return IRC_WriteStrClient(Client, ERR_NOCHANMODES_MSG,
+ Client_ID(Client), Channel_Name(Channel));
+
+ /* Mode request: let's answer it :-) */
+ if (Req->argc <= 1)
+ return Channel_Mode_Answer_Request(Origin, Channel);
+
+ Channel_CheckAdminRights(Channel, Client, Origin,
+ &onchannel, &modeok, &use_servermode);
+
+ if (!onchannel && !modeok)
+ return IRC_WriteStrClient(Origin, ERR_NOTONCHANNEL_MSG,
+ Client_ID(Origin),
+ Channel_Name(Channel));
+
+ mode_arg = 1;
+ mode_ptr = Req->argv[mode_arg];
+ if (Req->argc > mode_arg + 1)
+ arg_arg = mode_arg + 1;
+ else
+ arg_arg = -1;
+
+ /* Initial state: set or unset modes? */
+ skiponce = false;
+ switch (*mode_ptr) {
+ case '-':
+ set = false;
+ break;
+ case '+':
+ set = true;
+ break;
+ default:
+ set = true;
+ skiponce = true;
+ }
+
+ /* Prepare reply string */
+ strcpy(the_modes, set ? "+" : "-");
+ the_args[0] = '\0';
+
+ x[1] = '\0';
+ connected = CONNECTED;
+ while (mode_ptr) {
+ if (!skiponce)
+ mode_ptr++;
+ if (!*mode_ptr) {
+ /* Try next argument if there's any */
+ if (arg_arg > mode_arg)
+ mode_arg = arg_arg;
+ else
+ mode_arg++;
+
+ if (mode_arg >= Req->argc)
+ break;
+ mode_ptr = Req->argv[mode_arg];
+
+ if (Req->argc > mode_arg + 1)
+ arg_arg = mode_arg + 1;
+ else
+ arg_arg = -1;
+ }
+ skiponce = false;
+
+ switch (*mode_ptr) {
+ case '+':
+ case '-':
+ if (((*mode_ptr == '+') && !set)
+ || ((*mode_ptr == '-') && set)) {
+ /* Action modifier ("+"/"-") must be changed ... */
+ len = strlen(the_modes) - 1;
+ if (the_modes[len] == '+' || the_modes[len] == '-') {
+ /* Adjust last action modifier in result */
+ the_modes[len] = *mode_ptr;
+ } else {
+ /* Append modifier character to result string */
+ x[0] = *mode_ptr;
+ strlcat(the_modes, x, sizeof(the_modes));