/** * Generate predefined persistent channels and &SERVER */ GLOBAL void Channel_InitPredefined( void ) { CHANNEL *new_chan; const struct Conf_Channel *conf_chan; const char *c; size_t i, channel_count = array_length(&Conf_Channels, sizeof(*conf_chan)); conf_chan = array_start(&Conf_Channels); assert(channel_count == 0 || conf_chan != NULL); for (i = 0; i < channel_count; i++, conf_chan++) { if (!conf_chan->name[0]) continue; if (!Channel_IsValidName(conf_chan->name)) { Log(LOG_ERR, "Can't create pre-defined channel: invalid name: \"%s\"", conf_chan->name); continue; } new_chan = Channel_Search(conf_chan->name); if (new_chan) { Log(LOG_INFO, "Can't create pre-defined channel \"%s\": name already in use.", conf_chan->name); Set_KeyFile(new_chan, conf_chan->keyfile); continue; } new_chan = Channel_Create(conf_chan->name); if (!new_chan) { Log(LOG_ERR, "Can't create pre-defined channel \"%s\"!", conf_chan->name); continue; } Log(LOG_INFO, "Created pre-defined channel \"%s\".", conf_chan->name); Channel_ModeAdd(new_chan, 'P'); if (conf_chan->topic[0]) Channel_SetTopic(new_chan, NULL, conf_chan->topic); c = conf_chan->modes; while (*c) Channel_ModeAdd(new_chan, *c++); Channel_SetKey(new_chan, conf_chan->key); Channel_SetMaxUsers(new_chan, conf_chan->maxusers); Set_KeyFile(new_chan, conf_chan->keyfile); } if (channel_count) array_free(&Conf_Channels); /* Make sure the local &SERVER channel exists */ if (!Channel_Search("&SERVER")) { new_chan = Channel_Create("&SERVER"); if (new_chan) { Channel_SetModes(new_chan, "mnPt"); Channel_SetTopic(new_chan, Client_ThisServer(), "Server Messages"); } else Log(LOG_ERR, "Failed to create \"&SERVER\" channel!"); } else LogDebug("Required channel \"&SERVER\" already exists, ok."); } /* Channel_InitPredefined */
/** * Handle channel mode and channel-user mode changes * * @param Client The client from which this command has been received. * @param Req Request structure with prefix and all parameters. * @param Origin The originator of the MODE command (prefix). * @param Channel The target channel of this MODE command. * @return CONNECTED or DISCONNECTED. */ 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, use_servermode, is_halfop, is_op, is_admin, is_owner, is_machine, is_oper; int mode_arg, arg_arg, mode_arg_count = 0; CLIENT *client; long l; size_t len; is_halfop = is_op = is_admin = is_owner = is_machine = is_oper = false; if (Channel_IsModeless(Channel)) return IRC_WriteErrClient(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); /* Check if origin is oper and opers can use mode */ use_servermode = Conf_OperServerMode; if(Client_HasMode(Client, 'o') && Conf_OperCanMode) { is_oper = true; } /* Check if client is a server/service */ if(Client_Type(Client) == CLIENT_SERVER || Client_Type(Client) == CLIENT_SERVICE) { is_machine = true; } /* Check if client is member of channel or an oper or an server/service */ if(!Channel_IsMemberOf(Channel, Client) && !is_oper && !is_machine) return IRC_WriteErrClient(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 < 0) break; 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)); } set = *mode_ptr == '+'; } continue; } /* Are there arguments left? */ if (arg_arg >= Req->argc) arg_arg = -1; if(!is_machine && !is_oper) { if (Channel_UserHasMode(Channel, Client, 'q')) is_owner = true; if (Channel_UserHasMode(Channel, Client, 'a')) is_admin = true; if (Channel_UserHasMode(Channel, Client, 'o')) is_op = true; if (Channel_UserHasMode(Channel, Client, 'h')) is_halfop = true; } /* Validate modes */ x[0] = '\0'; argadd[0] = '\0'; client = NULL; switch (*mode_ptr) { /* --- Channel modes --- */ case 'R': /* Registered users only */ case 's': /* Secret channel */ case 'z': /* Secure connections only */ if(!is_oper && !is_machine && !is_owner && !is_admin && !is_op) { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); goto chan_exit; } case 'i': /* Invite only */ case 'V': /* Invite disallow */ case 'M': /* Only identified nicks can write */ case 'm': /* Moderated */ case 'n': /* Only members can write */ case 'N': /* Can't change nick while on this channel */ case 'Q': /* No kicks */ case 't': /* Topic locked */ if(is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) x[0] = *mode_ptr; else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; case 'k': /* Channel key */ if (Mode_Limit_Reached(Client, mode_arg_count++)) goto chan_exit; if (!set) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { x[0] = *mode_ptr; if (Channel_HasMode(Channel, 'k')) strlcpy(argadd, "*", sizeof(argadd)); if (arg_arg > mode_arg) arg_arg++; } else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; } if (arg_arg > mode_arg) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { Channel_ModeDel(Channel, 'k'); Channel_SetKey(Channel, Req->argv[arg_arg]); strlcpy(argadd, Channel_Key(Channel), sizeof(argadd)); x[0] = *mode_ptr; } else { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); } Req->argv[arg_arg][0] = '\0'; arg_arg++; } else { #ifdef STRICT_RFC /* Only send error message in "strict" mode, * this is how ircd2.11 and others behave ... */ connected = IRC_WriteErrClient(Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID(Origin), Req->command); #endif goto chan_exit; } break; case 'l': /* Member limit */ if (Mode_Limit_Reached(Client, mode_arg_count++)) goto chan_exit; if (!set) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) x[0] = *mode_ptr; else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; } if (arg_arg > mode_arg) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { l = atol(Req->argv[arg_arg]); if (l > 0 && l < 0xFFFF) { Channel_ModeDel(Channel, 'l'); Channel_SetMaxUsers(Channel, l); snprintf(argadd, sizeof(argadd), "%ld", l); x[0] = *mode_ptr; } } else { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); } Req->argv[arg_arg][0] = '\0'; arg_arg++; } else { #ifdef STRICT_RFC /* Only send error message in "strict" mode, * this is how ircd2.11 and others behave ... */ connected = IRC_WriteErrClient(Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID(Origin), Req->command); #endif goto chan_exit; } break; case 'O': /* IRC operators only */ if (set) { /* Only IRC operators are allowed to * set the 'O' channel mode! */ if(is_oper || is_machine) x[0] = 'O'; else connected = IRC_WriteErrClient(Origin, ERR_NOPRIVILEGES_MSG, Client_ID(Origin)); } else if(is_oper || is_machine || is_owner || is_admin || is_op) x[0] = 'O'; else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; case 'P': /* Persistent channel */ if (set) { /* Only IRC operators are allowed to * set the 'P' channel mode! */ if(is_oper || is_machine) x[0] = 'P'; else connected = IRC_WriteErrClient(Origin, ERR_NOPRIVILEGES_MSG, Client_ID(Origin)); } else if(is_oper || is_machine || is_owner || is_admin || is_op) x[0] = 'P'; else connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; /* --- Channel user modes --- */ case 'q': /* Owner */ case 'a': /* Channel admin */ if(!is_oper && !is_machine && !is_owner && !is_admin) { connected = IRC_WriteErrClient(Origin, ERR_CHANOPPRIVTOOLOW_MSG, Client_ID(Origin), Channel_Name(Channel)); goto chan_exit; } case 'o': /* Channel operator */ if(!is_oper && !is_machine && !is_owner && !is_admin && !is_op) { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); goto chan_exit; } case 'h': /* Half Op */ if(!is_oper && !is_machine && !is_owner && !is_admin && !is_op) { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); goto chan_exit; } case 'v': /* Voice */ if (arg_arg > mode_arg) { if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { client = Client_Search(Req->argv[arg_arg]); if (client) x[0] = *mode_ptr; else connected = IRC_WriteErrClient(Origin, ERR_NOSUCHNICK_MSG, Client_ID(Origin), Req->argv[arg_arg]); } else { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); } Req->argv[arg_arg][0] = '\0'; arg_arg++; } else { #ifdef STRICT_RFC /* Report an error to the client, when a user * mode should be changed but no nickname is * given. But don't do it when not in "strict" * mode, because most other servers don't do * it as well and some clients send "wired" * MODE commands like "MODE #chan -ooo nick". */ connected = IRC_WriteErrClient(Origin, ERR_NEEDMOREPARAMS_MSG, Client_ID(Origin), Req->command); #endif goto chan_exit; } break; /* --- Channel lists --- */ case 'I': /* Invite lists */ case 'b': /* Ban lists */ case 'e': /* Channel exception lists */ if (Mode_Limit_Reached(Client, mode_arg_count++)) goto chan_exit; if (arg_arg > mode_arg) { /* modify list */ if (is_oper || is_machine || is_owner || is_admin || is_op || is_halfop) { connected = set ? Add_To_List(*mode_ptr, Origin, Client, Channel, Req->argv[arg_arg]) : Del_From_List(*mode_ptr, Origin, Client, Channel, Req->argv[arg_arg]); } else { connected = IRC_WriteErrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); } Req->argv[arg_arg][0] = '\0'; arg_arg++; } else { switch (*mode_ptr) { case 'I': Channel_ShowInvites(Origin, Channel); break; case 'b': Channel_ShowBans(Origin, Channel); break; case 'e': Channel_ShowExcepts(Origin, Channel); break; } } break; default: if (Client_Type(Client) != CLIENT_SERVER) { Log(LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\" on %s!?", set ? '+' : '-', *mode_ptr, Client_ID(Origin), Channel_Name(Channel)); connected = IRC_WriteErrClient(Origin, ERR_UNKNOWNMODE_MSG, Client_ID(Origin), *mode_ptr, Channel_Name(Channel)); x[0] = '\0'; } else { Log(LOG_DEBUG, "Handling unknown mode \"%c%c\" from \"%s\" on %s ...", set ? '+' : '-', *mode_ptr, Client_ID(Origin), Channel_Name(Channel)); x[0] = *mode_ptr; } } if (!connected) break; /* Is there a valid mode change? */ if (!x[0]) continue; /* Validate target client */ if (client && (!Channel_IsMemberOf(Channel, client))) { if (!IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG, Client_ID(Origin), Client_ID(client), Channel_Name(Channel))) break; continue; } if (client) { /* Channel-User-Mode */ retval = set ? Channel_UserModeAdd(Channel, client, x[0]) : Channel_UserModeDel(Channel, client, x[0]); if (retval) { strlcat(the_args, " ", sizeof(the_args)); strlcat(the_args, Client_ID(client), sizeof(the_args)); strlcat(the_modes, x, sizeof(the_modes)); LogDebug ("User \"%s\": Mode change on %s, now \"%s\"", Client_Mask(client), Channel_Name(Channel), Channel_UserModes(Channel, client)); } } else { /* Channel-Mode */ retval = set ? Channel_ModeAdd(Channel, x[0]) : Channel_ModeDel(Channel, x[0]); if (retval) { strlcat(the_modes, x, sizeof(the_modes)); LogDebug("Channel %s: Mode change, now \"%s\".", Channel_Name(Channel), Channel_Modes(Channel)); } } /* Are there additional arguments to add? */ if (argadd[0]) { strlcat(the_args, " ", sizeof(the_args)); strlcat(the_args, argadd, sizeof(the_args)); } } chan_exit: /* Are there changed modes? */ if (the_modes[1]) { /* Clean up mode string */ len = strlen(the_modes) - 1; if ((the_modes[len] == '+') || (the_modes[len] == '-')) the_modes[len] = '\0'; if (Client_Type(Client) == CLIENT_SERVER) { /* MODE requests for local channels from other servers * are definitely invalid! */ if (Channel_IsLocal(Channel)) { Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored."); return CONNECTED; } /* Forward mode changes to channel users and all the * other remote servers: */ IRC_WriteStrServersPrefix(Client, Origin, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); IRC_WriteStrChannelPrefix(Client, Channel, Origin, false, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); } else { if (use_servermode) Origin = Client_ThisServer(); /* Send reply to client and inform other servers and channel users */ connected = IRC_WriteStrClientPrefix(Client, Origin, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); /* Only forward requests for non-local channels */ if (!Channel_IsLocal(Channel)) IRC_WriteStrServersPrefix(Client, Origin, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); IRC_WriteStrChannelPrefix(Client, Channel, Origin, false, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); } } return connected; } /* Channel_Mode */
/** * Handler for the IRC "JOIN" command. * * @param Client The client from which this command has been received. * @param Req Request structure with prefix and all parameters. * @return CONNECTED or DISCONNECTED. */ GLOBAL bool IRC_JOIN( CLIENT *Client, REQUEST *Req ) { char *channame, *key = NULL, *flags, *lastkey = NULL, *lastchan = NULL; CLIENT *target; CHANNEL *chan; assert (Client != NULL); assert (Req != NULL); _IRC_GET_SENDER_OR_RETURN_(target, Req, Client) /* 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_WriteErrClient(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); /* Local client? */ if (Client_Type(Client) == CLIENT_USER) { if (chan) { /* Already existing channel: already member? */ if (Channel_IsMemberOf(chan, Client)) goto join_next; } else { /* Channel must be created */ if (!strchr(Conf_AllowedChannelTypes, channame[0])) { /* ... but channel type is not allowed! */ IRC_WriteErrClient(Client, ERR_NOSUCHCHANNEL_MSG, Client_ID(Client), channame); goto join_next; } } /* Test if the user has reached the channel limit */ if ((Conf_MaxJoins > 0) && (Channel_CountForUser(Client) >= Conf_MaxJoins)) { if (!IRC_WriteErrClient(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 */ } } 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 */