+ /* Check for valid channel name. This is cached, the original is saved
+ in the channel context. */
+ channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
+ SILC_STRING_UTF8, 256, NULL);
+ if (!channel_namec) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_BAD_CHANNEL, 0);
+ goto out;
+ }
+
+ /* Get Client ID of the client who is joining to the channel */
+ if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NO_CLIENT_ID,
+ 0);
+ goto out;
+ }
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+
+ /* Get cipher, hmac name and auth payload */
+ cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
+ hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len);
+ cauth = silc_argument_get_arg_type(cmd->args, 7, &cauth_len);
+
+ /* See if the channel exists */
+ channel = silc_idlist_find_channel_by_name(server->local_list,
+ channel_namec, NULL);
+
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ SilcClientEntry entry = (SilcClientEntry)idata;
+ if (!entry) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
+
+#ifndef SILC_DIST_INPLACE
+ /* Limit how many channels client can join */
+ if (!cmd->pending && entry->channels &&
+ silc_hash_table_count(entry->channels) >=
+ server->config->param.chlimit) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ goto out;
+ }
+#endif /* SILC_DIST_INPLACE */
+
+ if (!channel ||
+ (channel->disabled && server->server_type != SILC_ROUTER)) {
+ /* Channel not found or not valid */
+
+ /* If we are standalone server we don't have a router, we just create
+ the channel by ourselves (unless it existed). */
+ if (server->standalone) {
+ if (!channel) {
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ if (cipher) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, cipher, strlen(cipher));
+ } else if (hmac) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, hmac, strlen(hmac));
+ } else {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ }
+ goto out;
+ }
+
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+ }
+ } else {
+
+ /* The channel does not exist on our server. If we are normal server
+ we will send JOIN command to our router which will handle the
+ joining procedure (either creates the channel if it doesn't exist
+ or joins the client to it). */
+ if (server->server_type != SILC_ROUTER) {
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
+
+ /* If this is pending command callback then we've resolved
+ it and it didn't work, return since we've notified the
+ client already in the command reply callback. */
+ if (cmd->pending)
+ goto out;
+
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ /* Send JOIN command to our router */
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_JOIN,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_join,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
+
+ /* We are router and the channel does not seem exist so we will check
+ our global list as well for the channel. */
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_namec, NULL);
+ if (!channel) {
+ /* Channel really does not exist, create it */
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ if (cipher) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, cipher, strlen(cipher));
+ } else if (hmac) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, hmac, strlen(hmac));
+ } else {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ }
+ goto out;
+ }
+
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+ }
+ }
+ }
+ } else {
+ if (!channel) {
+ /* Channel not found */
+
+ /* If the command came from router and we are normal server then
+ something went wrong with the joining as the channel was not found.
+ We can't do anything else but ignore this. */
+ if (idata->conn_type == SILC_CONN_ROUTER ||
+ server->server_type != SILC_ROUTER)
+ goto out;
+
+ /* We are router and the channel does not seem exist so we will check
+ our global list as well for the channel. */
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_namec, NULL);
+ if (!channel) {
+ /* Channel really does not exist, create it */
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ if (cipher) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, cipher, strlen(cipher));
+ } else if (hmac) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, hmac, strlen(hmac));
+ } else {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ }
+ goto out;
+ }
+
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+ }
+ }
+ }
+
+ /* Check whether the channel was created by our router */
+ if (cmd->pending && context2) {
+ SilcServerCommandReplyContext reply = context2;
+
+ if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
+ tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
+ SILC_GET32_MSB(created, tmp);
+ if (silc_argument_get_arg_type(reply->args, 7, NULL))
+ create_key = FALSE; /* Router returned the key already */
+
+ if (silc_command_get_status(reply->payload, NULL, NULL) &&
+ channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ /* Save channel passphrase, if user provided it successfully */
+ unsigned char *pa;
+ SilcUInt32 pa_len;
+ pa = silc_argument_get_arg_type(cmd->args, 3, &pa_len);
+ if (pa) {
+ silc_free(channel->passphrase);
+ channel->passphrase = silc_memdup(pa, pa_len);
+ }
+ }
+ }
+
+ if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
+ !channel->disabled && !silc_hash_table_count(channel->user_list))
+ created = TRUE;
+ }
+
+ /* If the channel does not have global users and is also empty the client
+ will be the channel founder and operator. */
+ if (!channel->disabled &&
+ !channel->global_users && !silc_hash_table_count(channel->user_list))
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+
+ /* Join to the channel */
+ silc_server_command_join_channel(server, cmd, channel, SILC_ID_GET_ID(id),
+ created, create_key, umode,
+ auth, auth_len, cauth, cauth_len);
+