void silc_client_free(SilcClient client)
{
if (client) {
+ if (client->rng)
+ silc_rng_free(client->rng);
+
silc_free(client);
}
}
/* Initialize random number generator */
client->rng = silc_rng_alloc();
silc_rng_init(client->rng);
- silc_math_primegen_init(); /* XXX */
+ silc_rng_global_init(client->rng);
/* Register protocols */
silc_client_protocols_register();
be sent as argument. */
SilcClientConnection silc_client_add_connection(SilcClient client,
+ char *hostname,
+ int port,
void *context)
{
SilcClientConnection conn;
conn->channel_cache = silc_idcache_alloc(0);
conn->server_cache = silc_idcache_alloc(0);
conn->client = client;
+ conn->remote_host = strdup(hostname);
+ conn->remote_port = port;
conn->context = context;
+ conn->pending_commands = silc_dlist_init();
/* Add the connection to connections table */
for (i = 0; i < client->conns_count; i++)
for (i = 0; i < client->conns_count; i++)
if (client->conns[i] == conn) {
+ if (conn->pending_commands)
+ silc_dlist_uninit(conn->pending_commands);
silc_free(conn);
client->conns[i] = NULL;
}
SILC_LOG_DEBUG(("Connecting to port %d of server %s",
port, host));
- conn = silc_client_add_connection(client, context);
- conn->remote_host = strdup(host);
- conn->remote_port = port;
+ conn = silc_client_add_connection(client, host, port, context);
client->ops->say(client, conn,
"Connecting to port %d of server %s", port, host);
return sock;
}
+/* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
+ key material between client and server. This function can be called
+ directly if application is performing its own connecting and does not
+ use the connecting provided by this library. */
+
+int silc_client_start_key_exchange(SilcClient client,
+ SilcClientConnection conn,
+ int fd)
+{
+ SilcProtocol protocol;
+ SilcClientKEInternalContext *proto_ctx;
+ void *context;
+
+ /* Allocate new socket connection object */
+ silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
+ if (conn->sock == NULL) {
+ client->ops->say(client, conn,
+ "Error: Could not allocate connection socket");
+ return FALSE;
+ }
+
+ conn->nickname = strdup(client->username);
+ conn->sock->hostname = conn->remote_host;
+ conn->sock->ip = strdup(conn->remote_host);
+ conn->sock->port = conn->remote_port;
+
+ /* Allocate internal Key Exchange context. This is sent to the
+ protocol as context. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->client = (void *)client;
+ proto_ctx->sock = conn->sock;
+ proto_ctx->rng = client->rng;
+ proto_ctx->responder = FALSE;
+
+ /* Perform key exchange protocol. silc_client_connect_to_server_final
+ will be called after the protocol is finished. */
+ silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
+ &protocol, (void *)proto_ctx,
+ silc_client_connect_to_server_second);
+ if (!protocol) {
+ client->ops->say(client, conn,
+ "Error: Could not start authentication protocol");
+ return FALSE;
+ }
+ conn->sock->protocol = protocol;
+
+ /* Register the connection for network input and output. This sets
+ that scheduler will listen for incoming packets for this connection
+ and sets that outgoing packets may be sent to this connection as well.
+ However, this doesn't set the scheduler for outgoing traffic, it will
+ be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
+ later when outgoing data is available. */
+ context = (void *)client;
+ SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
+
+ /* Execute the protocol */
+ protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+ return TRUE;
+}
+
/* Start of the connection to the remote server. This is called after
succesful TCP/IP connection has been established to the remote host. */
(SilcClientInternalConnectContext *)context;
SilcClient client = ctx->client;
SilcClientConnection conn = ctx->conn;
- SilcProtocol protocol;
- SilcClientKEInternalContext *proto_ctx;
int opt, opt_len = sizeof(opt);
SILC_LOG_DEBUG(("Start"));
silc_task_unregister(client->io_queue, ctx->task);
silc_free(ctx);
- /* Allocate new socket connection object */
- silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
- if (conn->sock == NULL) {
- client->ops->say(client, conn,
- "Error: Could not allocate connection socket");
+ if (!silc_client_start_key_exchange(client, conn, fd)) {
silc_net_close_connection(fd);
client->ops->connect(client, conn, FALSE);
- return;
- }
-
- conn->nickname = strdup(client->username);
- conn->sock->hostname = conn->remote_host;
- conn->sock->port = conn->remote_port;
-
- /* Allocate internal Key Exchange context. This is sent to the
- protocol as context. */
- proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
- proto_ctx->client = (void *)client;
- proto_ctx->sock = conn->sock;
- proto_ctx->rng = client->rng;
- proto_ctx->responder = FALSE;
-
- /* Perform key exchange protocol. silc_client_connect_to_server_final
- will be called after the protocol is finished. */
- silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
- &protocol, (void *)proto_ctx,
- silc_client_connect_to_server_second);
- if (!protocol) {
- client->ops->say(client, conn,
- "Error: Could not start authentication protocol");
- client->ops->connect(client, conn, FALSE);
- return;
}
- conn->sock->protocol = protocol;
-
- /* Register the connection for network input and output. This sets
- that scheduler will listen for incoming packets for this connection
- and sets that outgoing packets may be sent to this connection as well.
- However, this doesn't set the scheduler for outgoing traffic, it will
- be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
- later when outgoing data is available. */
- context = (void *)client;
- SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
-
- /* Execute the protocol */
- protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
}
/* Second part of the connecting to the server. This executed
SILC_LOG_DEBUG(("Start"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
SILC_LOG_DEBUG(("Error during KE protocol"));
silc_protocol_free(protocol);
if (ctx->dest_id)
silc_free(ctx->dest_id);
ctx->sock->protocol = NULL;
- silc_free(ctx);
/* Notify application of failure */
client->ops->connect(client, ctx->sock->user_data, FALSE);
+ silc_free(ctx);
return;
}
&proto_ctx->auth_data_len))
{
/* XXX do AUTH_REQUEST resolcing with server */
- proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
+ proto_ctx->auth_meth = SILC_AUTH_NONE;
}
/* Free old protocol as it is finished now */
silc_protocol_free(protocol);
if (ctx->packet)
- silc_buffer_free(ctx->packet);
+ silc_packet_context_free(ctx->packet);
silc_free(ctx);
/* silc_free(ctx->keymat....); */
sock->protocol = NULL;
SILC_LOG_DEBUG(("Start"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
SILC_LOG_DEBUG(("Error during authentication protocol"));
silc_protocol_free(protocol);
silc_ske_free(ctx->ske);
if (ctx->dest_id)
silc_free(ctx->dest_id);
- silc_free(ctx);
conn->sock->protocol = NULL;
/* Notify application of failure */
client->ops->connect(client, ctx->sock->user_data, FALSE);
+ silc_free(ctx);
return;
}
conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
conn->remote_id_data_len = SILC_ID_SERVER_LEN;
- client->ops->say(client, conn, "Connected to port %d of host %s",
- conn->remote_port, conn->remote_host);
-
- /* Notify application of successful connection */
- client->ops->connect(client, conn, TRUE);
-
silc_protocol_free(protocol);
if (ctx->auth_data)
silc_free(ctx->auth_data);
if (ctx->ske)
silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
silc_free(ctx);
conn->sock->protocol = NULL;
}
return;
}
- client->ops->say(client, conn, "Connection closed: premature EOF");
- SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+ SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
client->ops->disconnect(client, conn);
silc_client_close_connection(client, sock);
return;
/* Process the packet. This will call the parser that will then
decrypt and parse the packet. */
- if (!silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
- silc_client_packet_parse, client)) {
- silc_buffer_clear(sock->inbuf);
- return;
- }
+ silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
+ silc_client_packet_parse, client);
}
}
silc_client_packet_parse_type(client, sock, packet);
out:
- silc_buffer_clear(buffer);
- silc_free(packet);
+ silc_buffer_clear(sock->inbuf);
+ silc_packet_context_free(packet);
silc_free(parse_ctx);
}
* protocol and it will call the final callback.
*/
if (sock->protocol) {
- sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
sock->protocol->execute(client->timeout_queue, 0,
sock->protocol, sock->sock, 0, 0);
+
+ /* XXX We have only two protocols currently thus we know what this
+ failure indication is. */
+ if (buffer->len >= 4) {
+ unsigned int failure;
+
+ SILC_GET32_MSB(failure, buffer->data);
+
+ /* Notify application */
+ client->ops->failure(client, sock->user_data, sock->protocol,
+ (void *)failure);
+ }
}
break;
case SILC_PACKET_REJECT:
/*
* Received notify message
*/
- silc_client_notify_by_server(client, sock, buffer);
+ silc_client_notify_by_server(client, sock, packet);
break;
case SILC_PACKET_ERROR:
SilcClientKEInternalContext *proto_ctx =
(SilcClientKEInternalContext *)sock->protocol->context;
- proto_ctx->packet = buffer;
+ proto_ctx->packet = silc_packet_context_dup(packet);
proto_ctx->dest_id_type = packet->src_id_type;
- proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+ proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!proto_ctx->dest_id)
+ break;
/* Let the protocol handle the packet */
sock->protocol->execute(client->timeout_queue, 0,
(SilcClientKEInternalContext *)sock->protocol->context;
if (proto_ctx->packet)
- silc_buffer_free(proto_ctx->packet);
+ silc_packet_context_free(proto_ctx->packet);
- proto_ctx->packet = buffer;
+ proto_ctx->packet = silc_packet_context_dup(packet);
proto_ctx->dest_id_type = packet->src_id_type;
- proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+ proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!proto_ctx->dest_id)
+ break;
/* Let the protocol handle the packet */
sock->protocol->execute(client->timeout_queue, 0,
* user changes nickname but in that case the new ID is received
* as command reply and not as this packet type.
*/
- unsigned char *id_string;
- unsigned short id_type;
-
- silc_buffer_unformat(buffer,
- SILC_STR_UI_SHORT(&id_type),
- SILC_STR_UI16_STRING_ALLOC(&id_string),
- SILC_STR_END);
-
- if ((SilcIdType)id_type != SILC_ID_CLIENT)
+ SilcIDPayload idp;
+
+ idp = silc_id_payload_parse(buffer);
+ if (!idp)
+ break;
+ if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT)
break;
- silc_client_receive_new_id(client, sock, id_string);
- silc_free(id_string);
+ silc_client_receive_new_id(client, sock, idp);
+ silc_id_payload_free(idp);
break;
}
+ case SILC_PACKET_HEARTBEAT:
+ /*
+ * Received heartbeat packet
+ */
+ SILC_LOG_DEBUG(("Heartbeat packet"));
+ break;
+
default:
SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
break;
/* Generate IV */
if (!channel->iv)
- for (i = 0; i < 16; i++)
- channel->iv[i] = silc_rng_get_byte(client->rng);
+ for (i = 0; i < 16; i++) channel->iv[i] = silc_rng_get_byte(client->rng);
else
silc_hash_make(client->md5hash, channel->iv, 16, channel->iv);
/* Encode the channel payload */
- payload = silc_channel_encode_payload(strlen(conn->nickname), conn->nickname,
- data_len, data, 16, channel->iv,
+ payload = silc_channel_payload_encode(data_len, data, 16, channel->iv,
client->rng);
if (!payload) {
client->ops->say(client, conn,
silc_net_close_connection(sock->sock);
client->ops->say(client, sock->user_data,
- "Closed connection to host %s", sock->hostname ?
- sock->hostname : sock->ip);
+ "Closed connection to host %s", sock->hostname);
/* Free everything */
if (sock->user_data) {
memset(conn->hmac_key, 0, conn->hmac_key_len);
silc_free(conn->hmac_key);
}
+ if (conn->pending_commands)
+ silc_dlist_uninit(conn->pending_commands);
conn->sock = NULL;
conn->remote_port = 0;
conn->local_id_data = NULL;
conn->remote_host = NULL;
conn->current_channel = NULL;
+ conn->pending_commands = NULL;
silc_client_del_connection(client, conn);
}
silc_free(msg);
}
+/* Called when notify is received and some async operation (such as command)
+ is required before processing the notify message. This calls again the
+ silc_client_notify_by_server and reprocesses the original notify packet. */
+
+static void silc_client_notify_by_server_pending(void *context)
+{
+ SilcPacketContext *p = (SilcPacketContext *)context;
+ silc_client_notify_by_server(p->context, p->sock, p);
+}
+
+/* Destructor for the pending command callback */
+
+static void silc_client_notify_by_server_destructor(void *context)
+{
+ silc_packet_context_free((SilcPacketContext *)context);
+}
+
+/* Resolve client information from server by Client ID. */
+
+static void silc_client_notify_by_server_resolve(SilcClient client,
+ SilcClientConnection conn,
+ SilcPacketContext *packet,
+ SilcClientID *client_id)
+{
+ SilcPacketContext *p = silc_packet_context_dup(packet);
+ SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+
+ p->context = (void *)client;
+ p->sock = conn->sock;
+
+ silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
+ 1, 3, idp->data, idp->len);
+ silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+ silc_client_notify_by_server_destructor,
+ silc_client_notify_by_server_pending, p);
+ silc_buffer_free(idp);
+}
+
/* Received notify message from server */
void silc_client_notify_by_server(SilcClient client,
SilcSocketConnection sock,
- SilcBuffer message)
+ SilcPacketContext *packet)
{
- char *msg;
+ SilcBuffer buffer = packet->buffer;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ SilcNotifyPayload payload;
+ SilcNotifyType type;
+ SilcArgumentPayload args;
+
+ SilcClientID *client_id = NULL;
+ SilcChannelID *channel_id = NULL;
+ SilcClientEntry client_entry;
+ SilcClientEntry client_entry2;
+ SilcChannelEntry channel;
+ SilcChannelUser chu;
+ SilcIDCacheEntry id_cache = NULL;
+ unsigned char *tmp;
+ unsigned int tmp_len, mode;
- msg = silc_calloc(message->len + 1, sizeof(char));
- memcpy(msg, message->data, message->len);
- client->ops->say(client, sock->user_data, msg);
- silc_free(msg);
+ payload = silc_notify_payload_parse(buffer);
+ if (!payload)
+ goto out;
+
+ type = silc_notify_get_type(payload);
+ args = silc_notify_get_args(payload);
+ if (!args)
+ goto out;
+
+ switch(type) {
+ case SILC_NOTIFY_TYPE_NONE:
+ /* Notify application */
+ client->ops->notify(client, conn, type,
+ silc_argument_get_arg_type(args, 1, NULL));
+ break;
+
+ case SILC_NOTIFY_TYPE_INVITE:
+ /*
+ * Someone invited me to a channel. Find Client and Channel entries
+ * for the application.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry and if not found query it */
+ client_entry = silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ goto out;
+ }
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* XXX Will ALWAYS fail because currently we don't have way to resolve
+ channel information for channel that we're not joined to. */
+ /* XXX ways to fix: use (extended) LIST command, or define the channel
+ name to the notfy type when name resolving is not mandatory. */
+ /* Find channel entry */
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ goto out;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, client_entry, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_JOIN:
+ /*
+ * Someone has joined to a channel. Get their ID and nickname and
+ * cache them for later use.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry and if not found query it */
+ client_entry = silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ goto out;
+ }
+
+ /* If nickname or username hasn't been resolved, do so */
+ if (!client_entry->nickname || !client_entry->username) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ goto out;
+ }
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* Get channel entry */
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Add client to channel */
+ chu = silc_calloc(1, sizeof(*chu));
+ chu->client = client_entry;
+ silc_list_add(channel->clients, chu);
+
+ /* XXX add support for multiple same nicks on same channel. Check
+ for them here */
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_LEAVE:
+ /*
+ * Someone has left a channel. We will remove it from the channel but
+ * we'll keep it in the cache in case we'll need it later.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Remove client from channel */
+ silc_list_start(channel->clients);
+ while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+ if (chu->client == client_entry) {
+ silc_list_del(channel->clients, chu);
+ silc_free(chu);
+ break;
+ }
+ }
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_SIGNOFF:
+ /*
+ * Someone left SILC. We'll remove it from all channels and from cache.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Remove from all channels */
+ silc_client_remove_from_channels(client, conn, client_entry);
+
+ /* Remove from cache */
+ silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
+ client_entry->id);
+
+ /* Get signoff message */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (tmp_len > 128)
+ tmp = NULL;
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, client_entry, tmp);
+
+ /* Free data */
+ if (client_entry->nickname)
+ silc_free(client_entry->nickname);
+ if (client_entry->server)
+ silc_free(client_entry->server);
+ if (client_entry->id)
+ silc_free(client_entry->id);
+ if (client_entry->send_key)
+ silc_cipher_free(client_entry->send_key);
+ if (client_entry->receive_key)
+ silc_cipher_free(client_entry->receive_key);
+ break;
+
+ case SILC_NOTIFY_TYPE_TOPIC_SET:
+ /*
+ * Someone set the topic on a channel.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get topic */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, tmp, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_NICK_CHANGE:
+ /*
+ * Someone changed their nickname. If we don't have entry for the new
+ * ID we will query it and return here after it's done. After we've
+ * returned we fetch the old entry and free it and notify the
+ * application.
+ */
+
+ /* Get new Client ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Ignore my ID */
+ if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
+ break;
+
+ /* Find Client entry and if not found query it */
+ client_entry2 =
+ silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry2) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ goto out;
+ }
+ silc_free(client_id);
+
+ /* Get old Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find old Client entry */
+ client_entry =
+ silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Remove the old from cache */
+ silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
+ client_entry->id);
+
+ /* Replace old ID entry with new one on all channels. */
+ silc_client_replace_from_channels(client, conn, client_entry,
+ client_entry2);
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, client_entry, client_entry2);
+
+ /* Free data */
+ if (client_entry->nickname)
+ silc_free(client_entry->nickname);
+ if (client_entry->server)
+ silc_free(client_entry->server);
+ if (client_entry->id)
+ silc_free(client_entry->id);
+ if (client_entry->send_key)
+ silc_cipher_free(client_entry->send_key);
+ if (client_entry->receive_key)
+ silc_cipher_free(client_entry->receive_key);
+ break;
+
+ case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+ /*
+ * Someone changed a channel mode
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get the mode */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ SILC_GET32_MSB(mode, tmp);
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Save the new mode */
+ channel->mode = mode;
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, mode, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+ /*
+ * Someone changed user's mode on a channel
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get the mode */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ SILC_GET32_MSB(mode, tmp);
+
+ /* Get target Client ID */
+ tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ silc_free(client_id);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find target Client entry */
+ client_entry2 =
+ silc_idlist_get_client_by_id(client, conn, client_id);
+ if (!client_entry2)
+ goto out;
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Save the mode */
+ silc_list_start(channel->clients);
+ while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+ if (chu->client == client_entry) {
+ chu->mode = mode;
+ break;
+ }
+ }
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, mode,
+ client_entry2, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_MOTD:
+ /*
+ * Received Message of the day
+ */
+
+ /* Get motd */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, tmp);
+ break;
+
+ case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+ /*
+ * Router has enforced a new ID to a channel. Let's change the old
+ * ID to the one provided here.
+ */
+
+ /* Get the old ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* Get the channel entry */
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Free the old ID */
+ silc_free(channel_id);
+ silc_free(channel->id);
+
+ /* Get the new ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+ channel->id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel->id)
+ goto out;
+
+ id_cache->id = (void *)channel->id;
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, channel, channel);
+ break;
+
+ default:
+ break;
+ }
+
+ out:
+ silc_notify_payload_free(payload);
+ if (client_id)
+ silc_free(client_id);
+ if (channel_id)
+ silc_free(channel_id);
}
/* Processes the received new Client ID from server. Old Client ID is
void silc_client_receive_new_id(SilcClient client,
SilcSocketConnection sock,
- unsigned char *id_string)
+ SilcIDPayload idp)
{
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ int connecting = FALSE;
+
+ if (!conn->local_entry)
+ connecting = TRUE;
/* Delete old ID from ID cache */
silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
/* Save the new ID */
if (conn->local_id)
silc_free(conn->local_id);
- conn->local_id = silc_id_str2id(id_string, SILC_ID_CLIENT);
if (conn->local_id_data)
silc_free(conn->local_id_data);
- conn->local_id_data =
- silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
- memcpy(conn->local_id_data, id_string, SILC_ID_CLIENT_LEN);
- conn->local_id_data_len = SILC_ID_CLIENT_LEN;
+
+ conn->local_id = silc_id_payload_get_id(idp);
+ conn->local_id_data = silc_id_payload_get_data(idp);
+ conn->local_id_data_len = silc_id_payload_get_len(idp);;
+
if (!conn->local_entry)
conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
+
conn->local_entry->nickname = conn->nickname;
+ if (!conn->local_entry->username) {
+ conn->local_entry->username =
+ silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
+ sizeof(conn->local_entry->username));
+ sprintf(conn->local_entry->username, "%s@%s", client->username,
+ client->hostname);
+ }
+ conn->local_entry->server = strdup(conn->remote_host);
conn->local_entry->id = conn->local_id;
/* Put it to the ID cache */
silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
conn->local_id, (void *)conn->local_entry, TRUE);
+
+ /* Notify application of successful connection. We do it here now that
+ we've received the Client ID and are allowed to send traffic. */
+ if (connecting)
+ client->ops->connect(client, conn, TRUE);
}
/* Processed received Channel ID for a channel. This is called when client
void silc_client_new_channel_id(SilcClient client,
SilcSocketConnection sock,
char *channel_name,
- unsigned int mode,
- unsigned char *id_string)
+ unsigned int mode, SilcIDPayload idp)
{
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcChannelID *id;
SilcChannelEntry channel;
SILC_LOG_DEBUG(("New channel ID"));
- id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
channel = silc_calloc(1, sizeof(*channel));
channel->channel_name = channel_name;
- channel->id = id;
+ channel->id = silc_id_payload_get_id(idp);
channel->mode = mode;
+ silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
+
conn->current_channel = channel;
-
+
/* Put it to the ID cache */
silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
- (void *)id, (void *)channel, TRUE);
+ (void *)channel->id, (void *)channel, TRUE);
}
-/* Processes received key for channel. The received key will be used
- to protect the traffic on the channel for now on. Client must receive
- the key to the channel before talking on the channel is possible.
- This is the key that server has generated, this is not the channel
- private key, it is entirely local setting. */
+/* Saves channel key from encoded `key_payload'. This is used when we
+ receive Channel Key Payload and when we are processing JOIN command
+ reply. */
-void silc_client_receive_channel_key(SilcClient client,
- SilcSocketConnection sock,
- SilcBuffer packet)
+void silc_client_save_channel_key(SilcClientConnection conn,
+ SilcBuffer key_payload,
+ SilcChannelEntry channel)
{
unsigned char *id_string, *key, *cipher;
- unsigned int key_len;
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ unsigned int tmp_len;
SilcChannelID *id;
SilcIDCacheEntry id_cache = NULL;
- SilcChannelEntry channel;
SilcChannelKeyPayload payload;
- SILC_LOG_DEBUG(("Received key for channel"));
-
- payload = silc_channel_key_parse_payload(packet);
+ payload = silc_channel_key_payload_parse(key_payload);
if (!payload)
return;
- id_string = silc_channel_key_get_id(payload, NULL);
+ id_string = silc_channel_key_get_id(payload, &tmp_len);
if (!id_string) {
- silc_channel_key_free_payload(payload);
+ silc_channel_key_payload_free(payload);
+ return;
+ }
+
+ id = silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL);
+ if (!id) {
+ silc_channel_key_payload_free(payload);
return;
}
- id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
/* Find channel. */
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
- SILC_ID_CHANNEL, &id_cache))
- goto out;
-
+ if (!channel) {
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
+ SILC_ID_CHANNEL, &id_cache))
+ goto out;
+
+ /* Get channel entry */
+ channel = (SilcChannelEntry)id_cache->context;
+ }
+
/* Save the key */
- key = silc_channel_key_get_key(payload, &key_len);
+ key = silc_channel_key_get_key(payload, &tmp_len);
cipher = silc_channel_key_get_cipher(payload, NULL);
+ channel->key_len = tmp_len;
+ channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
+ memcpy(channel->key, key, tmp_len);
- channel = (SilcChannelEntry)id_cache->context;
- channel->key_len = key_len;
- channel->key = silc_calloc(key_len, sizeof(*channel->key));
- memcpy(channel->key, key, key_len);
-
- silc_cipher_alloc(cipher, &channel->channel_key);
- if (!channel->channel_key) {
- client->ops->say(client, conn,
+ if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+ conn->client->ops->say(conn->client, conn,
"Cannot talk to channel: unsupported cipher %s", cipher);
goto out;
}
channel->channel_key->cipher->set_key(channel->channel_key->context,
- key, key_len);
+ key, tmp_len);
/* Client is now joined to the channel */
channel->on_channel = TRUE;
out:
silc_free(id);
- silc_channel_key_free_payload(payload);
+ silc_channel_key_payload_free(payload);
+}
+
+/* Processes received key for channel. The received key will be used
+ to protect the traffic on the channel for now on. Client must receive
+ the key to the channel before talking on the channel is possible.
+ This is the key that server has generated, this is not the channel
+ private key, it is entirely local setting. */
+
+void silc_client_receive_channel_key(SilcClient client,
+ SilcSocketConnection sock,
+ SilcBuffer packet)
+{
+ SILC_LOG_DEBUG(("Received key for channel"));
+
+ /* Save the key */
+ silc_client_save_channel_key(sock->user_data, packet, NULL);
}
/* Process received message to a channel (or from a channel, really). This
SilcChannelPayload payload = NULL;
SilcChannelID *id = NULL;
SilcChannelEntry channel;
+ SilcChannelUser chu;
SilcIDCacheEntry id_cache = NULL;
+ SilcClientID *client_id = NULL;
+ int found = FALSE;
/* Sanity checks */
if (packet->dst_id_type != SILC_ID_CHANNEL)
goto out;
- id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
+ client_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ SILC_ID_CLIENT);
+ if (!client_id)
+ goto out;
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+ if (!id)
+ goto out;
- /* Find the channel entry from channels on this window */
+ /* Find the channel entry from channels on this connection */
if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
SILC_ID_CHANNEL, &id_cache))
goto out;
silc_buffer_pull_tail(buffer, 16);
/* Parse the channel message payload */
- payload = silc_channel_parse_payload(buffer);
+ payload = silc_channel_payload_parse(buffer);
if (!payload)
goto out;
- /* Pass the message to application */
- if (packet->src_id_type == SILC_ID_CLIENT) {
- client->ops->channel_message(client, conn,
- silc_channel_get_nickname(payload, NULL),
- channel->channel_name,
- silc_channel_get_data(payload, NULL));
- } else {
- /* Message from server */
- /* XXX maybe this should be passed to app... */
- client->ops->say(client, conn, "%s", silc_channel_get_data(payload, NULL));
+ /* Find client entry */
+ silc_list_start(channel->clients);
+ while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+ if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
+ found = TRUE;
+ break;
+ }
}
+ /* Pass the message to application */
+ client->ops->channel_message(client, conn, found ? chu->client : NULL,
+ channel, silc_channel_get_data(payload, NULL));
+
out:
if (id)
silc_free(id);
+ if (client_id)
+ silc_free(client_id);
if (payload)
- silc_channel_free_payload(payload);
+ silc_channel_payload_free(payload);
}
/* Private message received. This processes the private message and
{
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
SilcBuffer buffer = packet->buffer;
+ SilcIDCacheEntry id_cache;
+ SilcClientID *remote_id = NULL;
+ SilcClientEntry remote_client;
unsigned short nick_len;
- unsigned char *nickname, *message;
+ unsigned char *nickname, *message = NULL;
+ int ret;
+
+ if (packet->src_id_type != SILC_ID_CLIENT)
+ goto out;
/* Get nickname */
- silc_buffer_unformat(buffer,
- SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
- SILC_STR_END);
+ ret = silc_buffer_unformat(buffer,
+ SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
+ SILC_STR_END);
+ if (ret == -1)
+ return;
+
silc_buffer_pull(buffer, 2 + nick_len);
-
+
message = silc_calloc(buffer->len + 1, sizeof(char));
memcpy(message, buffer->data, buffer->len);
+ remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ SILC_ID_CLIENT);
+ if (!remote_id)
+ goto out;
+
+ /* Check whether we know this client already */
+ if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
+ SILC_ID_CLIENT, &id_cache))
+ {
+ /* Allocate client entry */
+ remote_client = silc_calloc(1, sizeof(*remote_client));
+ remote_client->id = remote_id;
+ silc_parse_nickname(nickname, &remote_client->nickname,
+ &remote_client->server, &remote_client->num);
+
+ /* Save the client to cache */
+ silc_idcache_add(conn->client_cache, remote_client->nickname,
+ SILC_ID_CLIENT, remote_client->id, remote_client,
+ TRUE);
+ } else {
+ remote_client = (SilcClientEntry)id_cache->context;
+ }
+
/* Pass the private message to application */
- client->ops->private_message(client, conn, nickname, message);
+ client->ops->private_message(client, conn, remote_client, message);
/* See if we are away (gone). If we are away we will reply to the
sender with the set away message. */
if (conn->away && conn->away->away) {
- SilcClientID *remote_id;
- SilcClientEntry remote_client;
- SilcIDCacheEntry id_cache;
-
- if (packet->src_id_type != SILC_ID_CLIENT)
- goto out;
-
- remote_id = silc_id_str2id(packet->src_id, SILC_ID_CLIENT);
- if (!remote_id)
- goto out;
-
+ /* If it's me, ignore */
if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
goto out;
- /* Check whether we know this client already */
- if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
- SILC_ID_CLIENT, &id_cache))
- {
- /* Allocate client entry */
- remote_client = silc_calloc(1, sizeof(*remote_client));
- remote_client->id = remote_id;
- remote_client->nickname = strdup(nickname);
-
- /* Save the client to cache */
- silc_idcache_add(conn->client_cache, remote_client->nickname,
- SILC_ID_CLIENT, remote_client->id, remote_client,
- TRUE);
- } else {
- silc_free(remote_id);
- remote_client = (SilcClientEntry)id_cache->context;
- }
-
/* Send the away message */
silc_client_packet_send_private_message(client, sock, remote_client,
conn->away->away,
}
out:
- memset(message, 0, buffer->len);
- silc_free(message);
+ if (remote_id)
+ silc_free(remote_id);
+
+ if (message) {
+ memset(message, 0, buffer->len);
+ silc_free(message);
+ }
silc_free(nickname);
}
+
+/* Removes a client entry from all channel it has joined. This really is
+ a performance killer (client_entry should have pointers to channel
+ entry list). */
+
+void silc_client_remove_from_channels(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
+{
+ SilcIDCacheEntry id_cache;
+ SilcIDCacheList list;
+ SilcChannelEntry channel;
+ SilcChannelUser chu;
+
+ if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
+ SILC_ID_CHANNEL, &list))
+ return;
+
+ silc_idcache_list_first(list, &id_cache);
+ channel = (SilcChannelEntry)id_cache->context;
+
+ while (channel) {
+
+ /* Remove client from channel */
+ silc_list_start(channel->clients);
+ while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+ if (chu->client == client_entry) {
+ silc_list_del(channel->clients, chu);
+ silc_free(chu);
+ break;
+ }
+ }
+
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+ }
+
+ silc_idcache_list_free(list);
+}
+
+/* Replaces `old' client entries from all channels to `new' client entry.
+ This can be called for example when nickname changes and old ID entry
+ is replaced from ID cache with the new one. If the old ID entry is only
+ updated, then this fucntion needs not to be called. */
+
+void silc_client_replace_from_channels(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry old,
+ SilcClientEntry new)
+{
+ SilcIDCacheEntry id_cache;
+ SilcIDCacheList list;
+ SilcChannelEntry channel;
+ SilcChannelUser chu;
+
+ if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
+ SILC_ID_CHANNEL, &list))
+ return;
+
+ silc_idcache_list_first(list, &id_cache);
+ channel = (SilcChannelEntry)id_cache->context;
+
+ while (channel) {
+
+ /* Replace client entry */
+ silc_list_start(channel->clients);
+ while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+ if (chu->client == old) {
+ chu->client = new;
+ break;
+ }
+ }
+
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+ }
+
+ silc_idcache_list_free(list);
+}
+
+/* Parses mode mask and returns the mode as string. */
+
+char *silc_client_chmode(unsigned int mode)
+{
+ char string[20];
+
+ if (!mode)
+ return NULL;
+
+ memset(string, 0, sizeof(string));
+
+ if (mode & SILC_CHANNEL_MODE_PRIVATE)
+ strncat(string, "p", 1);
+
+ if (mode & SILC_CHANNEL_MODE_SECRET)
+ strncat(string, "s", 1);
+
+ if (mode & SILC_CHANNEL_MODE_PRIVKEY)
+ strncat(string, "k", 1);
+
+ if (mode & SILC_CHANNEL_MODE_INVITE)
+ strncat(string, "i", 1);
+
+ if (mode & SILC_CHANNEL_MODE_TOPIC)
+ strncat(string, "t", 1);
+
+ if (mode & SILC_CHANNEL_MODE_ULIMIT)
+ strncat(string, "l", 1);
+
+ if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
+ strncat(string, "a", 1);
+
+ /* Rest of mode is ignored */
+
+ return strdup(string);
+}
+
+/* Parses channel user mode mask and returns te mode as string */
+
+char *silc_client_chumode(unsigned int mode)
+{
+ char string[4];
+
+ if (!mode)
+ return NULL;
+
+ memset(string, 0, sizeof(string));
+
+ if (mode & SILC_CHANNEL_UMODE_CHANFO)
+ strncat(string, "f", 1);
+
+ if (mode & SILC_CHANNEL_UMODE_CHANOP)
+ strncat(string, "o", 1);
+
+ return strdup(string);
+}
+
+/* Parses channel user mode and returns it as special mode character. */
+
+char *silc_client_chumode_char(unsigned int mode)
+{
+ char string[4];
+
+ if (!mode)
+ return NULL;
+
+ memset(string, 0, sizeof(string));
+
+ if (mode & SILC_CHANNEL_UMODE_CHANFO)
+ strncat(string, "*", 1);
+
+ if (mode & SILC_CHANNEL_UMODE_CHANOP)
+ strncat(string, "@", 1);
+
+ return strdup(string);
+}