From 275e2f50c1cbe4a0eec582cf490ef485049541af Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Thu, 11 Apr 2002 13:36:05 +0000 Subject: [PATCH] updates. --- CHANGES | 29 ++ TODO | 2 - apps/irssi/src/silc/core/client_ops.c | 114 +++-- apps/irssi/src/silc/core/client_ops.h | 3 +- apps/irssi/src/silc/core/silc-servers.c | 28 +- apps/silcd/command.c | 192 ++++---- apps/silcd/command.h | 1 + apps/silcd/command_reply.c | 2 + apps/silcd/idlist.h | 1 + apps/silcd/packet_receive.c | 483 ++++++++++++++++---- apps/silcd/packet_receive.h | 3 + apps/silcd/packet_send.c | 11 +- apps/silcd/packet_send.h | 4 +- apps/silcd/server.c | 16 +- apps/silcd/server_util.c | 141 ++++++ apps/silcd/server_util.h | 15 + doc/draft-riikonen-silc-commands-03.nroff | 36 +- doc/draft-riikonen-silc-pp-05.nroff | 79 +++- doc/draft-riikonen-silc-spec-05.nroff | 92 ++++ lib/silcclient/Makefile.am | 1 + lib/silcclient/client.c | 222 +++++---- lib/silcclient/client_internal.h | 16 + lib/silcclient/client_notify.c | 50 ++- lib/silcclient/client_ops_example.c | 3 +- lib/silcclient/client_resume.c | 521 ++++++++++++++++++++++ lib/silcclient/command.c | 41 +- lib/silcclient/command.h | 8 +- lib/silcclient/command_reply.c | 219 +++++---- lib/silcclient/command_reply.h | 24 +- lib/silcclient/idlist.c | 15 +- lib/silcclient/silcclient.h | 28 +- lib/silccore/silccommand.h | 1 + lib/silccore/silcmode.h | 1 + lib/silccore/silcpacket.h | 1 + 34 files changed, 1975 insertions(+), 428 deletions(-) create mode 100644 lib/silcclient/client_resume.c diff --git a/CHANGES b/CHANGES index 65a89453..3a357a62 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,32 @@ +Thu Apr 11 16:32:08 EEST 2002 Pekka Riikonen + + * Changed the UMODE's mode mask argument to be optional. If + not provided then the command merely returns the current mode + mask to the client. Updated protocol specs and the server. + Affected file is silcd/command.c. + + * Added SILC session detachment/resuming support. It is possible + to detach by closing the network connection and then re-connect + and resume to the old client session. Added DETACHED user + mode that server will set for detached client. Added new + packet RESUME_CLIENT which is used to perform the resuming + process. Added DETACH command. Updated the protocol specs, + core library, client and server. Protocol TODO #22. Very + many affected files around the tree. + +Wed Apr 10 16:32:01 EEST 2002 Pekka Riikonen + + * Changed the CMODE's mode mask argument to be optional. If + not provided then the command merely returns the current mode + mask to the client. Updated protocol specs and the server. + Affected file is silcd/command.c. + + * Changed the Killer's Client ID in KILLED notify to be just + any ID payload since router server is allowed to kill as well. + Updated protocol specs, client libary and server. Affected + files are lib/silcclient/client_notify.c, silcd/packet_receive.c, + and irssi/src/silc/core/client_ops.c. + Tue Apr 9 17:15:42 EEST 2002 Pekka Riikonen * Added new user modes ANONYMOUS for special anonymous servers diff --git a/TODO b/TODO index 25f39441..afa8ad74 100644 --- a/TODO +++ b/TODO @@ -130,8 +130,6 @@ describe new stuff to be added to protocol versions 1.x. 21. Subscription/IRC's notify kind support? - 22. Session detachment/resume? - o Inviting and banning by public key should be made possible. To be included in protocol version 1.2. diff --git a/apps/irssi/src/silc/core/client_ops.c b/apps/irssi/src/silc/core/client_ops.c index 35c9a64e..b4f71907 100644 --- a/apps/irssi/src/silc/core/client_ops.c +++ b/apps/irssi/src/silc/core/client_ops.c @@ -319,7 +319,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn, signal_emit("message topic", 5, server, channel->channel_name, tmp, server_entry->server_name, server_entry->server_name); - } else { + } else if (idtype == SILC_ID_CHANNEL) { channel = (SilcChannelEntry)entry; signal_emit("message topic", 5, server, channel->channel_name, tmp, channel->channel_name, channel->channel_name); @@ -385,7 +385,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn, MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE, channel->channel_name, tmp ? tmp : "removed all", server_entry->server_name); - } else { + } else if (idtype == SILC_ID_CHANNEL) { channel2 = (SilcChannelEntry)entry; printformat_module("fe-common/silc", server, channel->channel_name, MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE, @@ -439,7 +439,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn, channel->channel_name, client_entry2->nickname, tmp ? tmp : "removed all", server_entry->server_name); - } else { + } else if (idtype == SILC_ID_CHANNEL) { channel2 = (SilcChannelEntry)entry; printformat_module("fe-common/silc", server, channel->channel_name, MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE, @@ -518,13 +518,27 @@ void silc_notify(SilcClient client, SilcClientConnection conn, client_entry = va_arg(va, SilcClientEntry); tmp = va_arg(va, char *); - client_entry2 = va_arg(va, SilcClientEntry); + idtype = va_arg(va, int); + entry = va_arg(va, SilcClientEntry); if (client_entry == conn->local_entry) { - printformat_module("fe-common/silc", server, NULL, - MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, - client_entry2 ? client_entry2->nickname : "", - tmp ? tmp : ""); + if (idtype == SILC_ID_CLIENT) { + client_entry2 = (SilcClientEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, + client_entry2 ? client_entry2->nickname : "", + tmp ? tmp : ""); + } else if (idtype == SILC_ID_SERVER) { + server_entry = (SilcServerEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, + server_entry->server_name, tmp ? tmp : ""); + } else if (idtype == SILC_ID_CHANNEL) { + channel = (SilcChannelEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, + channel->channel_name, tmp ? tmp : ""); + } } else { list1 = nicklist_get_same_unique(SERVER(server), client_entry); for (list_tmp = list1; list_tmp != NULL; list_tmp = @@ -534,11 +548,26 @@ void silc_notify(SilcClient client, SilcClientConnection conn, nicklist_remove(channel, nickrec); } - printformat_module("fe-common/silc", server, NULL, - MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, - client_entry->nickname, - client_entry2 ? client_entry2->nickname : "", - tmp ? tmp : ""); + if (idtype == SILC_ID_CLIENT) { + client_entry2 = (SilcClientEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, + client_entry->nickname, + client_entry2 ? client_entry2->nickname : "", + tmp ? tmp : ""); + } else if (idtype == SILC_ID_SERVER) { + server_entry = (SilcServerEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, + client_entry->nickname, + server_entry->server_name, tmp ? tmp : ""); + } else if (idtype == SILC_ID_CHANNEL) { + channel = (SilcChannelEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, + client_entry->nickname, + channel->channel_name, tmp ? tmp : ""); + } } break; @@ -596,23 +625,48 @@ void silc_notify(SilcClient client, SilcClientConnection conn, or connecting failed. This is also the first time application receives the SilcClientConnection object which it should save somewhere. */ -void silc_connect(SilcClient client, SilcClientConnection conn, int success) +void silc_connect(SilcClient client, SilcClientConnection conn, + SilcClientConnectionStatus status) { SILC_SERVER_REC *server = conn->context; - if (!server && !success) { + if (!server && status == SILC_CLIENT_CONN_ERROR) { silc_client_close_connection(client, conn); return; } - if (success) { + switch (status) { + case SILC_CLIENT_CONN_SUCCESS: + /* We have successfully connected to server */ server->connected = TRUE; signal_emit("event connected", 1, server); - } else { + break; + + case SILC_CLIENT_CONN_SUCCESS_RESUME: + /* We have successfully resumed old detached session */ + server->connected = TRUE; + signal_emit("event connected", 1, server); + + /* If we resumed old session check whether we need to update + our nickname */ + if (strcmp(server->nick, conn->local_entry->nickname)) { + char *old; + old = g_strdup(server->nick); + server_change_nick(SERVER(server), conn->local_entry->nickname); + nicklist_rename_unique(SERVER(server), + conn->local_entry, server->nick, + conn->local_entry, conn->local_entry->nickname); + signal_emit("message own_nick", 4, server, server->nick, old, ""); + g_free(old); + } + break; + + default: server->connection_lost = TRUE; if (server->conn) server->conn->context = NULL; server_disconnect(SERVER(server)); + break; } } @@ -712,6 +766,7 @@ static void silc_client_join_get_users(SilcClient client, ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry)); nicklist_set_own(CHANNEL(chanrec), ownnick); signal_emit("channel joined", 1, chanrec); + chanrec->entry = channel; if (chanrec->topic) printformat_module("fe-common/silc", server, channel->channel_name, @@ -889,21 +944,23 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, "SILC Operator" : "[Unknown mode]"); } if (mode & SILC_UMODE_GONE) - strcat(buf, " away"); + strcat(buf, " [away]"); if (mode & SILC_UMODE_INDISPOSED) - strcat(buf, " indisposed"); + strcat(buf, " [indisposed]"); if (mode & SILC_UMODE_BUSY) - strcat(buf, " busy"); + strcat(buf, " [busy]"); if (mode & SILC_UMODE_PAGE) - strcat(buf, " page to reach"); + strcat(buf, " [page to reach]"); if (mode & SILC_UMODE_HYPER) - strcat(buf, " hyper active"); + strcat(buf, " [hyper active]"); if (mode & SILC_UMODE_ROBOT) - strcat(buf, " robot"); + strcat(buf, " [robot]"); if (mode & SILC_UMODE_ANONYMOUS) - strcat(buf, " anonymous"); + strcat(buf, " [anonymous]"); if (mode & SILC_UMODE_BLOCK_PRIVMSG) - strcat(buf, " blocks private messages"); + strcat(buf, " [blocks private messages]"); + if (mode & SILC_UMODE_DETACHED) + strcat(buf, " [detached]"); printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_WHOIS_MODES, buf); @@ -1851,7 +1908,14 @@ void silc_detach(SilcClient client, SilcClientConnection conn, const unsigned char *detach_data, SilcUInt32 detach_data_len) { + char file[256]; + /* Save the detachment data to file. */ + + memset(file, 0, sizeof(file)); + snprintf(file, sizeof(file) - 1, "%s/session.%s.%d", get_irssi_dir(), + conn->remote_host, conn->remote_port); + silc_file_writefile(file, detach_data, detach_data_len); } diff --git a/apps/irssi/src/silc/core/client_ops.h b/apps/irssi/src/silc/core/client_ops.h index 505b6343..60fda7ae 100644 --- a/apps/irssi/src/silc/core/client_ops.h +++ b/apps/irssi/src/silc/core/client_ops.h @@ -43,7 +43,8 @@ void silc_command(SilcClient client, SilcClientConnection conn, void silc_command_reply(SilcClient client, SilcClientConnection conn, SilcCommandPayload cmd_payload, int success, SilcCommand command, SilcCommandStatus status, ...); -void silc_connect(SilcClient client, SilcClientConnection conn, int success); +void silc_connect(SilcClient client, SilcClientConnection conn, + SilcClientConnectionStatus status); void silc_disconnect(SilcClient client, SilcClientConnection conn); void silc_ask_passphrase(SilcClient client, SilcClientConnection conn, SilcAskPassphrase completion, void *context); diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index e92ae9ba..fdcbd431 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -54,9 +54,11 @@ static void silc_send_channel(SILC_SERVER_REC *server, SILC_CHANNEL_REC *rec; rec = silc_channel_find(server, channel); - if (rec == NULL || rec->entry == NULL) + if (rec == NULL || rec->entry == NULL) { + cmd_return_error(CMDERR_NOT_JOINED); return; - + } + silc_client_send_channel_message(silc_client, server->conn, rec->entry, NULL, 0, msg, strlen(msg), TRUE); } @@ -192,18 +194,33 @@ static void send_message(SILC_SERVER_REC *server, char *target, static void sig_connected(SILC_SERVER_REC *server) { SilcClientConnection conn; + SilcClientConnectionParams params; + char file[256]; int fd; if (!IS_SILC_SERVER(server)) return; - conn = silc_client_add_connection(silc_client, NULL, + /* Try to read detached session data and use it if found. */ + memset(¶ms, 0, sizeof(params)); + memset(file, 0, sizeof(file)); + snprintf(file, sizeof(file) - 1, "%s/session.%s.%d", get_irssi_dir(), + server->connrec->address, server->connrec->port); + params.detach_data = silc_file_readfile(file, ¶ms.detach_data_len); + + /* Add connection to the client library */ + conn = silc_client_add_connection(silc_client, ¶ms, server->connrec->address, server->connrec->port, server); server->conn = conn; - + + silc_free(params.detach_data); + unlink(file); + fd = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle)); + + /* Start key exchange with the server */ silc_client_start_key_exchange(silc_client, conn, fd); server->ftp_sessions = silc_dlist_init(); @@ -325,6 +342,7 @@ char *silc_server_get_channels(SILC_SERVER_REC *server) /* SYNTAX: FILE CLOSE [] */ /* SYNTAX: FILE */ /* SYNTAX: JOIN [] [-cipher ] [-hmac ] [-founder <-pubkey|passwd>] */ +/* SYNTAX: DETACH */ void silc_command_exec(SILC_SERVER_REC *server, const char *command, const char *args) @@ -874,6 +892,7 @@ void silc_server_init(void) command_bind_silc("getkey", MODULE_NAME, (SIGNAL_FUNC) command_self); command_bind_silc("sconnect", MODULE_NAME, (SIGNAL_FUNC) command_sconnect); command_bind_silc("file", MODULE_NAME, (SIGNAL_FUNC) command_file); + command_bind_silc("detach", MODULE_NAME, (SIGNAL_FUNC) command_self); command_set_options("connect", "+silcnet"); } @@ -907,6 +926,7 @@ void silc_server_deinit(void) command_unbind("getkey", (SIGNAL_FUNC) command_self); command_unbind("sconnect", (SIGNAL_FUNC) command_sconnect); command_unbind("file", (SIGNAL_FUNC) command_file); + command_unbind("detach", (SIGNAL_FUNC) command_self); } void silc_server_free_ftp(SILC_SERVER_REC *server, diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 34f8ee29..afb9e539 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -66,6 +66,7 @@ SilcServerCommand silc_command_list[] = SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG), SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG), SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG), + SILC_SERVER_CMD(detach, DETACH, SILC_CF_LAG_STRICT | SILC_CF_REG), SILC_SERVER_CMD(silcoper, SILCOPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER), SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG), @@ -91,7 +92,7 @@ SilcServerCommand silc_command_list[] = of arguments. */ #define SILC_SERVER_COMMAND_CHECK(command, context, min, max) \ do { \ - SilcUInt32 _argc; \ + SilcUInt32 _argc; \ \ SILC_LOG_DEBUG(("Start")); \ \ @@ -2013,6 +2014,11 @@ SILC_SERVER_CMD_FUNC(nick) cmd->server->md5hash, nick, &new_id)) { nickfail++; + if (nickfail > 9) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_BAD_NICKNAME); + goto out; + } snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail); } @@ -2639,7 +2645,7 @@ SILC_SERVER_CMD_FUNC(quit) if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT) goto out; - /* Get destination ID */ + /* Get message */ tmp = silc_argument_get_arg_type(cmd->args, 1, &len); if (len > 128) tmp = NULL; @@ -2651,8 +2657,8 @@ SILC_SERVER_CMD_FUNC(quit) /* We quit the connection with little timeout */ silc_schedule_task_add(server->schedule, sock->sock, - silc_server_command_quit_cb, (void *)q, - 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); + silc_server_command_quit_cb, (void *)q, + 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); out: silc_server_command_free(cmd); @@ -2670,7 +2676,6 @@ SILC_SERVER_CMD_FUNC(kill) SilcClientID *client_id; unsigned char *tmp, *comment; SilcUInt32 tmp_len, tmp_len2; - SilcBuffer killer; bool local; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2); @@ -2730,57 +2735,9 @@ SILC_SERVER_CMD_FUNC(kill) silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL, SILC_STATUS_OK); - /* Send the KILL notify packets. First send it to the channel, then - to our primary router and then directly to the client who is being - killed right now. */ - - /* Send KILLED notify to the channels. It is not sent to the client - as it will be sent differently destined directly to the client and not - to the channel. */ - killer = silc_id_payload_encode(client->id, SILC_ID_CLIENT); - silc_server_send_notify_on_channels(server, remote_client, - remote_client, SILC_NOTIFY_TYPE_KILLED, - 3, tmp, tmp_len, - comment, comment ? tmp_len2 : 0, - killer->data, killer->len); - silc_buffer_free(killer); - - /* Send KILLED notify to primary route */ - if (!server->standalone) - silc_server_send_notify_killed(server, server->router->connection, TRUE, - remote_client->id, comment, client->id); - - /* Send KILLED notify to the client directly */ - silc_server_send_notify_killed(server, remote_client->connection ? - remote_client->connection : - remote_client->router->connection, FALSE, - remote_client->id, comment, client->id); - - /* Remove the client from all channels. This generates new keys to the - channels as well. */ - silc_server_remove_from_channels(server, NULL, remote_client, FALSE, - NULL, TRUE); - - /* Remove the client entry, If it is locally connected then we will also - disconnect the client here */ - if (remote_client->connection) { - /* Remove locally conneted client */ - SilcSocketConnection sock = remote_client->connection; - silc_server_free_client_data(server, sock, remote_client, FALSE, NULL); - silc_server_close_connection(server, sock); - } else { - /* Update statistics */ - if (remote_client->connection) - server->stat.my_clients--; - if (server->stat.cell_clients) - server->stat.cell_clients--; - SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR); - SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR); - - /* Remove remote client */ - silc_idlist_del_client(local ? server->local_list : - server->global_list, remote_client); - } + /* Now do the killing */ + silc_server_kill_client(server, remote_client, comment, client->id, + SILC_ID_CLIENT); out: silc_server_command_free(cmd); @@ -3297,7 +3254,9 @@ static void silc_server_command_join_channel(SilcServer server, if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL); - keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, + keyp = silc_channel_key_payload_encode(silc_id_get_len(channel->id, + SILC_ID_CHANNEL), + tmp, strlen(channel->channel_key-> cipher->name), channel->channel_key->cipher->name, @@ -3737,58 +3696,60 @@ SILC_SERVER_CMD_FUNC(umode) SilcServer server = cmd->server; SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data; SilcBuffer packet; - unsigned char *tmp_mask; - SilcUInt32 mask; + unsigned char *tmp_mask, m[4]; + SilcUInt32 mask = 0; SilcUInt16 ident = silc_command_get_ident(cmd->payload); + bool set_mask = FALSE; if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT) goto out; - SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2); + SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 1, 2); /* Get the client's mode mask */ tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL); - if (!tmp_mask) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); - goto out; - } - SILC_GET32_MSB(mask, tmp_mask); - - /* Check that mode changing is allowed. */ - if (!silc_server_check_umode_rights(server, client, mask)) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, - SILC_STATUS_ERR_PERM_DENIED); - goto out; + if (tmp_mask) { + SILC_GET32_MSB(mask, tmp_mask); + set_mask = TRUE; } - /* Anonymous mode cannot be set by client */ - if (mask & SILC_UMODE_ANONYMOUS) { - if (!(client->mode & SILC_UMODE_ANONYMOUS)) { + if (set_mask) { + /* Check that mode changing is allowed. */ + if (!silc_server_check_umode_rights(server, client, mask)) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, SILC_STATUS_ERR_PERM_DENIED); goto out; } - } else { - if (client->mode & SILC_UMODE_ANONYMOUS) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, - SILC_STATUS_ERR_PERM_DENIED); - goto out; + + /* Anonymous mode cannot be set by client */ + if (mask & SILC_UMODE_ANONYMOUS) { + if (!(client->mode & SILC_UMODE_ANONYMOUS)) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, + SILC_STATUS_ERR_PERM_DENIED); + goto out; + } + } else { + if (client->mode & SILC_UMODE_ANONYMOUS) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, + SILC_STATUS_ERR_PERM_DENIED); + goto out; + } } - } - /* Change the mode */ - client->mode = mask; + /* Change the mode */ + client->mode = mask; - /* Send UMODE change to primary router */ - if (!server->standalone) - silc_server_send_notify_umode(server, server->router->connection, TRUE, - client->id, client->mode); + /* Send UMODE change to primary router */ + if (!server->standalone) + silc_server_send_notify_umode(server, server->router->connection, TRUE, + client->id, client->mode); + } /* Send command reply to sender */ + SILC_PUT32_MSB(client->mode, m); packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE, SILC_STATUS_OK, 0, ident, 1, - 2, tmp_mask, 4); + 2, m, sizeof(m)); silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); silc_buffer_free(packet); @@ -4694,6 +4655,61 @@ SILC_SERVER_CMD_FUNC(oper) silc_server_command_free(cmd); } +SILC_TASK_CALLBACK(silc_server_command_detach_cb) +{ + QuitInternal q = (QuitInternal)context; + SilcClientEntry client = (SilcClientEntry)q->sock->user_data; + + /* If there is pending outgoing data for the client then purge it + to the network before closing connection. */ + silc_server_packet_queue_purge(q->server, q->sock); + + /* Close the connection on our side */ + client->router = NULL; + client->connection = NULL; + q->sock->user_data = NULL; + silc_server_close_connection(q->server, q->sock); + + silc_free(q); +} + +/* Server side of DETACH command. Detached the client from the network + by closing the connection but preserving the session. */ + +SILC_SERVER_CMD_FUNC(detach) +{ + SilcServerCommandContext cmd = (SilcServerCommandContext)context; + SilcServer server = cmd->server; + SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data; + QuitInternal q; + + if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT) + goto out; + + SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_DETACH, cmd, 0, 0); + + /* Send the user mode notify to notify that client is detached */ + client->mode |= SILC_UMODE_DETACHED; + client->data.status &= ~SILC_IDLIST_STATUS_RESUMED; + if (!server->standalone) + silc_server_send_notify_umode(server, server->router->connection, + server->server_type == SILC_SERVER ? + FALSE : TRUE, client->id, client->mode); + + q = silc_calloc(1, sizeof(*q)); + q->server = server; + q->sock = cmd->sock; + silc_schedule_task_add(server->schedule, 0, silc_server_command_detach_cb, + q, 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); + + /* Send reply to the sender */ + silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH, + SILC_STATUS_OK); + + out: + silc_server_command_free(cmd); +} + /* Server side of SILCOPER command. Client uses this comand to obtain router operator privileges to this router. */ diff --git a/apps/silcd/command.h b/apps/silcd/command.h index b8441b7f..229a3759 100644 --- a/apps/silcd/command.h +++ b/apps/silcd/command.h @@ -140,6 +140,7 @@ SILC_SERVER_CMD_FUNC(cmode); SILC_SERVER_CMD_FUNC(cumode); SILC_SERVER_CMD_FUNC(kick); SILC_SERVER_CMD_FUNC(ban); +SILC_SERVER_CMD_FUNC(detach); SILC_SERVER_CMD_FUNC(silcoper); SILC_SERVER_CMD_FUNC(leave); SILC_SERVER_CMD_FUNC(users); diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index bd4052a4..ccb425f0 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -1137,6 +1137,7 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) } client->data.public_key = public_key; + public_key = NULL; } else if (id_type == SILC_ID_SERVER) { server_id = silc_id_payload_get_id(idp); @@ -1150,6 +1151,7 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) } server_entry->data.public_key = public_key; + public_key = NULL; } else { goto out; } diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index 30fbd3a1..888e509b 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -62,6 +62,7 @@ typedef SilcUInt8 SilcIDListStatus; #define SILC_IDLIST_STATUS_RESOLVING 0x04 /* Entry is being resolved with WHOIS or IDENTIFY */ #define SILC_IDLIST_STATUS_DISABLED 0x08 /* Entry is disabled */ +#define SILC_IDLIST_STATUS_RESUMED 0x10 /* Entry is resumed */ /* Generic ID list data structure. diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 1a1be10d..29752416 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -25,8 +25,6 @@ #include "serverincludes.h" #include "server_internal.h" -extern char *server_version; - /* Received notify packet. Server can receive notify packets from router. Server then relays the notify messages to clients if needed. */ @@ -1214,27 +1212,29 @@ void silc_server_notify(SilcServer server, /* From protocol version 1.1 we get the killer's ID as well. */ tmp = silc_argument_get_arg_type(args, 3, &tmp_len); if (tmp) { - client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type); if (!client_id) goto out; - /* If the the client is not in local list we check global list */ - client2 = silc_idlist_find_client_by_id(server->global_list, - client_id, TRUE, NULL); - if (!client2) { - client2 = silc_idlist_find_client_by_id(server->local_list, + if (id_type == SILC_ID_CLIENT) { + /* If the the client is not in local list we check global list */ + client2 = silc_idlist_find_client_by_id(server->global_list, client_id, TRUE, NULL); if (!client2) { - silc_free(client_id); - goto out; + client2 = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, NULL); + if (!client2) { + silc_free(client_id); + goto out; + } } - } - silc_free(client_id); + silc_free(client_id); - /* Killer must be router operator */ - if (!(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) { - SILC_LOG_DEBUG(("Killing is not allowed")); - goto out; + /* Killer must be router operator */ + if (!(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) { + SILC_LOG_DEBUG(("Killing is not allowed")); + goto out; + } } } @@ -1292,6 +1292,10 @@ void silc_server_notify(SilcServer server, goto out; } + /* Remove internal resumed flag if client is marked detached now */ + if (mode & SILC_UMODE_DETACHED) + client->data.status &= ~SILC_IDLIST_STATUS_RESUMED; + /* Change the mode */ client->mode = mode; @@ -1453,11 +1457,19 @@ void silc_server_private_message(SilcServer server, packet->dst_id_len, NULL, &idata, &client); if (!dst_sock) { + SilcBuffer idp; + + if (client && client->mode & SILC_UMODE_DETACHED) { + SILC_LOG_DEBUG(("Locally connected client is detached, " + "discarding packet")); + return; + } + /* Send IDENTIFY command reply with error status to indicate that such destination ID does not exist or is invalid */ - SilcBuffer idp = silc_id_payload_encode_data(packet->dst_id, - packet->dst_id_len, - packet->dst_id_type); + idp = silc_id_payload_encode_data(packet->dst_id, + packet->dst_id_len, + packet->dst_id_type); if (!idp) return; @@ -1885,6 +1897,11 @@ SilcClientEntry silc_server_new_client(SilcServer server, while (!silc_id_create_client_id(server, server->id, server->rng, server->md5hash, nickname, &client_id)) { nickfail++; + if (nickfail > 9) { + silc_server_disconnect_remote(server, sock, + "Server closed connection: Bad nickname"); + return NULL; + } snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail); } @@ -1909,8 +1926,7 @@ SilcClientEntry silc_server_new_client(SilcServer server, /* Send the new client ID to the client. */ id_string = silc_id_id2str(client->id, SILC_ID_CLIENT); - reply = silc_buffer_alloc(2 + 2 + id_len); - silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply)); + reply = silc_buffer_alloc_size(2 + 2 + id_len); silc_buffer_format(reply, SILC_STR_UI_SHORT(SILC_ID_CLIENT), SILC_STR_UI_SHORT(id_len), @@ -1922,61 +1938,7 @@ SilcClientEntry silc_server_new_client(SilcServer server, silc_buffer_free(reply); /* Send some nice info to the client */ - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("Welcome to the SILC Network %s", - username)); - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("Your host is %s, running version %s", - server->server_name, server_version)); - - if (server->stat.clients && server->stat.servers + 1) - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("There are %d clients on %d servers in SILC " - "Network", server->stat.clients, - server->stat.servers + 1)); - if (server->stat.cell_clients && server->stat.cell_servers + 1) - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("There are %d clients on %d server in our cell", - server->stat.cell_clients, - server->stat.cell_servers + 1)); - if (server->server_type == SILC_ROUTER) { - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("I have %d clients, %d channels, %d servers and " - "%d routers", - server->stat.my_clients, - server->stat.my_channels, - server->stat.my_servers, - server->stat.my_routers)); - } else { - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("I have %d clients and %d channels formed", - server->stat.my_clients, - server->stat.my_channels)); - } - - if (server->stat.server_ops || server->stat.router_ops) - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("There are %d server operators and %d router " - "operators online", - server->stat.server_ops, - server->stat.router_ops)); - if (server->stat.my_router_ops + server->stat.my_server_ops) - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("I have %d operators online", - server->stat.my_router_ops + - server->stat.my_server_ops)); - - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("Your connection is secured with %s cipher, " - "key length %d bits", - idata->send_key->cipher->name, - idata->send_key->cipher->key_len)); - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("Your current nickname is %s", - client->nickname)); - - /* Send motd */ - silc_server_send_motd(server, sock); + silc_server_send_connect_notifys(server, sock, client); return client; } @@ -2849,3 +2811,372 @@ void silc_server_ftp(SilcServer server, idata->hmac_send, idata->psn_send++, packet, FALSE); } + +typedef struct { + SilcServer server; + SilcSocketConnection sock; + SilcPacketContext *packet; +} *SilcServerResumeResolve; + +SILC_SERVER_CMD_FUNC(resume_resolve) +{ + SilcServerResumeResolve r = (SilcServerResumeResolve)context; + SilcServer server = r->server; + SilcSocketConnection sock = r->sock; + SilcServerCommandReplyContext reply = context2; + + if (!context2 || !silc_command_get_status(reply->payload, NULL, NULL)) { + SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, " + "closing connection", sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Incomplete resume information"); + goto out; + } + + /* Reprocess the packet */ + silc_server_resume_client(server, sock, r->packet); + + out: + silc_socket_free(r->sock); + silc_packet_context_free(r->packet); + silc_free(r); +} + +/* Received client resuming packet. This is used to resume detached + client session. It can be sent by the client who wishes to resume + but this is also sent by servers and routers to notify other routers + that the client is not detached anymore. */ + +void silc_server_resume_client(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer, buf; + SilcIDListData idata; + SilcClientEntry detached_client; + SilcClientID *client_id = NULL; + unsigned char *id_string, *auth = NULL; + SilcUInt16 id_len, auth_len = 0; + int ret, nickfail = 0; + bool resolved, local; + SilcServerResumeResolve r; + + ret = silc_buffer_unformat(buffer, + SILC_STR_UI16_NSTRING(&id_string, &id_len), + SILC_STR_END); + if (ret != -1) + client_id = silc_id_str2id(id_string, id_len, SILC_ID_CLIENT); + + if (sock->type == SILC_SOCKET_TYPE_CLIENT) { + /* Client send this and is attempting to resume to old client session */ + SilcClientEntry client; + SilcChannelEntry channel; + SilcHashTableList htl; + SilcChannelClientEntry chl; + SilcBuffer keyp; + + if (ret != -1) { + silc_buffer_pull(buffer, 2 + id_len); + auth = buffer->data; + auth_len = buffer->len; + silc_buffer_push(buffer, 2 + id_len); + } + + if (!client_id || auth_len < 128) { + SILC_LOG_ERROR(("Client %s (%s) sent incomplete resume information, " + "closing connection", sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Incomplete resume information"); + return; + } + + /* Take client entry of this connection */ + client = (SilcClientEntry)sock->user_data; + idata = (SilcIDListData)client; + + /* Get entry to the client, and resolve it if we don't have it. */ + detached_client = silc_server_get_client_resolve(server, client_id, + &resolved); + if (!detached_client) { + if (resolved) { + /* The client info is being resolved. Reprocess this packet after + receiving the reply to the query. */ + r = silc_calloc(1, sizeof(*r)); + if (!r) + return; + + r->server = server; + r->sock = silc_socket_dup(sock); + r->packet = silc_packet_context_dup(packet); + silc_server_command_pending(server, SILC_COMMAND_WHOIS, + server->cmd_ident, + silc_server_command_resume_resolve, r); + } else { + SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, " + "closing connection", sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Incomplete resume information"); + } + return; + } + + /* Check that the client is detached */ + if (!(detached_client->mode & SILC_UMODE_DETACHED)) { + SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, " + "closing connection", sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Incomplete resume information"); + return; + } + + /* Check that we have the public key of the client, if not then we must + resolve it first. */ + if (!detached_client->data.public_key) { + if (server->standalone) { + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Incomplete resume information"); + } else { + /* We must retrieve the detached client's public key by sending + GETKEY command. Reprocess this packet after receiving the key */ + SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT); + SilcSocketConnection dest_sock = + silc_server_get_client_route(server, NULL, 0, client_id, NULL, NULL); + + silc_server_send_command(server, dest_sock ? dest_sock : + server->router->connection, + SILC_COMMAND_GETKEY, ++server->cmd_ident, + 1, idp->data, idp->len); + + r = silc_calloc(1, sizeof(*r)); + if (!r) + return; + + r->server = server; + r->sock = silc_socket_dup(sock); + r->packet = silc_packet_context_dup(packet); + silc_server_command_pending(server, SILC_COMMAND_GETKEY, + server->cmd_ident, + silc_server_command_resume_resolve, r); + + silc_buffer_free(idp); + } + return; + } + + /* Verify the authentication payload. This has to be successful in + order to allow the resuming */ + if (!silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY, + detached_client->data.public_key, 0, + idata->hash, detached_client->id, + SILC_ID_CLIENT)) { + SILC_LOG_ERROR(("Client %s (%s) resume authentication failed, " + "closing connection", sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Incomplete resume information"); + return; + } + + /* Now resume the client to the network */ + + sock->user_data = detached_client; + detached_client->connection = sock; + + /* Take new keys and stuff into use in the old entry */ + silc_idlist_del_data(detached_client); + silc_idlist_add_data(detached_client, idata); + detached_client->data.status |= SILC_IDLIST_STATUS_REGISTERED; + detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED; + detached_client->mode &= ~SILC_UMODE_DETACHED; + + /* Send the RESUME_CLIENT packet to our primary router so that others + know this client isn't detached anymore. */ + if (!server->standalone) { + buf = silc_buffer_alloc_size(2 + id_len); + silc_buffer_format(buf, + SILC_STR_UI_SHORT(id_len), + SILC_STR_UI_XNSTRING(id_string, id_len), + SILC_STR_END); + silc_server_packet_send(server, server->router->connection, + SILC_PACKET_RESUME_CLIENT, 0, + buf->data, buf->len, TRUE); + silc_buffer_free(buf); + } + + /* Delete this client entry since we're resuming to old one. */ + server->stat.my_clients--; + server->stat.clients--; + if (server->stat.cell_clients) + server->stat.cell_clients--; + silc_idlist_del_client(server->local_list, client); + client = detached_client; + + /* If the ID is not based in our ID then change it */ + if (!SILC_ID_COMPARE(client->id, server->id, server->id->ip.data_len)) { + while (!silc_id_create_client_id(server, server->id, server->rng, + server->md5hash, client->nickname, + &client_id)) { + nickfail++; + if (nickfail > 9) { + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Bad nickname"); + return; + } + snprintf(&client->nickname[strlen(client->nickname) - 1], 1, + "%d", nickfail); + } + + /* Notify about Client ID change, nickname doesn't actually change. */ + if (!server->standalone) + silc_server_send_notify_nick_change(server, server->router->connection, + FALSE, client->id, client_id, + client->nickname); + + silc_free(client->id); + client->id = client_id; + } + + /* Add the client again to the ID cache to get it to correct list */ + if (!silc_idcache_del_by_context(server->local_list->clients, client)) + silc_idcache_del_by_context(server->global_list->clients, client); + silc_idcache_add(server->local_list->clients, client->nickname, + client->id, client, client->mode, NULL); + + /* Send the new client ID to the client. */ + id_string = silc_id_id2str(client->id, SILC_ID_CLIENT); + buf = silc_buffer_alloc_size(2 + 2 + id_len); + silc_buffer_format(buf, + SILC_STR_UI_SHORT(SILC_ID_CLIENT), + SILC_STR_UI_SHORT(id_len), + SILC_STR_UI_XNSTRING(id_string, id_len), + SILC_STR_END); + silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, + buf->data, buf->len, FALSE); + silc_free(id_string); + silc_buffer_free(buf); + + /* Send some nice info to the client */ + silc_server_send_connect_notifys(server, sock, client); + + /* Send all channel keys of channels the client has joined */ + silc_hash_table_list(client->channels, &htl); + while (silc_hash_table_get(&htl, NULL, (void **)&chl)) { + channel = chl->channel; + id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL); + keyp = + silc_channel_key_payload_encode(silc_id_get_len(channel->id, + SILC_ID_CHANNEL), + id_string, + strlen(channel->channel_key-> + cipher->name), + channel->channel_key->cipher->name, + channel->key_len / 8, channel->key); + silc_free(id_string); + + /* Send the key packet to client */ + silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, + keyp->data, keyp->len, FALSE); + } + silc_hash_table_list_reset(&htl); + + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Your session is successfully resumed")); + + } else if (sock->type != SILC_SOCKET_TYPE_CLIENT) { + /* Server or router sent this to us to notify that that a client has + been resumed. */ + SilcServerEntry server_entry; + SilcServerID *server_id; + + if (!client_id) + return; + + /* Get entry to the client, and resolve it if we don't have it. */ + detached_client = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, NULL); + if (!detached_client) { + detached_client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, NULL); + if (!detached_client) + return; + } + + /* Check that the client has not been resumed already because it is + protocol error to attempt to resume more than once. The client + will be killed if this protocol error occurs. */ + if (detached_client->data.status & SILC_IDLIST_STATUS_RESUMED && + !(detached_client->mode & SILC_UMODE_DETACHED)) { + /* The client is clearly attempting to resume more than once and + perhaps playing around by resuming from several different places + at the same time. */ + silc_server_kill_client(server, detached_client, NULL, + server->id, SILC_ID_SERVER); + return; + } + + /* Check whether client is detached at all */ + if (!(detached_client->mode & SILC_UMODE_DETACHED)) + return; + + /* Client is detached, and now it is resumed. Remove the detached + mode and mark that it is resumed. */ + detached_client->mode &= ~SILC_UMODE_DETACHED; + detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED; + + /* Get the new owner of the resumed client */ + server_id = silc_id_str2id(packet->src_id, packet->src_id_len, + packet->src_id_type); + if (!server_id) + return; + + /* Get server entry */ + server_entry = silc_idlist_find_server_by_id(server->global_list, + server_id, TRUE, NULL); + local = TRUE; + if (!server_entry) { + server_entry = silc_idlist_find_server_by_id(server->local_list, + server_id, TRUE, NULL); + local = FALSE; + if (!server_entry) { + silc_free(server_id); + return; + } + } + + /* Change the client to correct list. */ + if (!silc_idcache_del_by_context(server->local_list->clients, + detached_client)) + silc_idcache_del_by_context(server->global_list->clients, + detached_client); + silc_idcache_add(local ? server->local_list->clients : + server->global_list->clients, detached_client->nickname, + detached_client->id, detached_client, FALSE, NULL); + + /* Change the owner of the client if needed */ + if (detached_client->router != server_entry) + detached_client->router = server_entry; + + /* If the sender of this packet is server and we are router we need to + broadcast this packet to other routers in the network. */ + if (!server->standalone && server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_SERVER && + !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { + SILC_LOG_DEBUG(("Broadcasting received Resume Client packet")); + silc_server_packet_send(server, server->router->connection, + packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + buffer->data, buffer->len, FALSE); + silc_server_backup_send(server, (SilcServerEntry)sock->user_data, + packet->type, packet->flags, + packet->buffer->data, packet->buffer->len, + FALSE, TRUE); + } + + silc_free(server_id); + } + + silc_free(client_id); +} diff --git a/apps/silcd/packet_receive.h b/apps/silcd/packet_receive.h index 0aed4ca9..b28d3de4 100644 --- a/apps/silcd/packet_receive.h +++ b/apps/silcd/packet_receive.h @@ -78,5 +78,8 @@ void silc_server_rekey(SilcServer server, void silc_server_ftp(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); +void silc_server_resume_client(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); #endif diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c index ab5b6219..019df069 100644 --- a/apps/silcd/packet_send.c +++ b/apps/silcd/packet_send.c @@ -444,6 +444,9 @@ void silc_server_packet_send_clients(SilcServer server, /* Send to locally connected client */ sock = (SilcSocketConnection)client->connection; + if (!sock) + continue; + silc_server_packet_send_dest(server, sock, type, flags, client->id, SILC_ID_CLIENT, data, data_len, force_send); @@ -1287,14 +1290,14 @@ void silc_server_send_notify_killed(SilcServer server, SilcSocketConnection sock, bool broadcast, SilcClientID *client_id, - char *comment, - SilcClientID *killer) + const char *comment, + void *killer, SilcIdType killer_type) { SilcBuffer idp1; SilcBuffer idp2; - idp1 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT); - idp2 = silc_id_payload_encode((void *)killer, SILC_ID_CLIENT); + idp1 = silc_id_payload_encode(client_id, SILC_ID_CLIENT); + idp2 = silc_id_payload_encode(killer, killer_type); silc_server_send_notify_dest(server, sock, broadcast, (void *)client_id, SILC_ID_CLIENT, SILC_NOTIFY_TYPE_KILLED, 3, idp1->data, idp1->len, diff --git a/apps/silcd/packet_send.h b/apps/silcd/packet_send.h index 17dc349e..c40c888c 100644 --- a/apps/silcd/packet_send.h +++ b/apps/silcd/packet_send.h @@ -172,8 +172,8 @@ void silc_server_send_notify_killed(SilcServer server, SilcSocketConnection sock, bool broadcast, SilcClientID *client_id, - char *comment, - SilcClientID *killer); + const char *comment, + void *killer, SilcIdType killer_type); void silc_server_send_notify_umode(SilcServer server, SilcSocketConnection sock, bool broadcast, diff --git a/apps/silcd/server.c b/apps/silcd/server.c index eb2c0a95..bb633a65 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -2335,6 +2335,14 @@ void silc_server_packet_parse_type(SilcServer server, silc_server_ftp(server, sock, packet); break; + case SILC_PACKET_RESUME_CLIENT: + /* Resume client */ + SILC_LOG_DEBUG(("Resume Client packet")); + if (packet->flags & SILC_PACKET_FLAG_LIST) + break; + silc_server_resume_client(server, sock, packet); + break; + case SILC_PACKET_RESUME_ROUTER: /* Resume router packet received. This packet is received for backup router resuming protocol. */ @@ -2515,9 +2523,9 @@ void silc_server_free_client_data(SilcServer server, (void *)i, 300, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; + client->mode = 0; client->router = NULL; client->connection = NULL; - client->mode = 0; } /* Frees user_data pointer from socket connection object. This also sends @@ -3974,6 +3982,9 @@ silc_server_get_client_route(SilcServer server, SILC_LOG_DEBUG(("Start")); + if (client_entry) + *client_entry = NULL; + /* Decode destination Client ID */ if (!client_id) { id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT); @@ -3985,9 +3996,6 @@ silc_server_get_client_route(SilcServer server, id = silc_id_dup(client_id, SILC_ID_CLIENT); } - if (client_entry) - *client_entry = NULL; - /* If the destination belongs to our server we don't have to route the packet anywhere but to send it to the local destination. */ client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL); diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index 5e9bdbd7..2fe705f3 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -21,6 +21,8 @@ #include "serverincludes.h" #include "server_internal.h" +extern char *server_version; + /* Removes the client from channels and possibly removes the channels as well. After removing those channels that exist, their channel keys are regnerated. This is called only by the function @@ -1117,3 +1119,142 @@ bool silc_server_check_umode_rights(SilcServer server, return TRUE; } + +/* This function is used to send the notify packets and motd to the + incoming client connection. */ + +void silc_server_send_connect_notifys(SilcServer server, + SilcSocketConnection sock, + SilcClientEntry client) +{ + SilcIDListData idata = (SilcIDListData)client; + + /* Send some nice info to the client */ + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Welcome to the SILC Network %s", + client->username)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Your host is %s, running version %s", + server->server_name, server_version)); + + if (server->stat.clients && server->stat.servers + 1) + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("There are %d clients on %d servers in SILC " + "Network", server->stat.clients, + server->stat.servers + 1)); + if (server->stat.cell_clients && server->stat.cell_servers + 1) + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("There are %d clients on %d server in our cell", + server->stat.cell_clients, + server->stat.cell_servers + 1)); + if (server->server_type == SILC_ROUTER) { + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("I have %d clients, %d channels, %d servers and " + "%d routers", + server->stat.my_clients, + server->stat.my_channels, + server->stat.my_servers, + server->stat.my_routers)); + } else { + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("I have %d clients and %d channels formed", + server->stat.my_clients, + server->stat.my_channels)); + } + + if (server->stat.server_ops || server->stat.router_ops) + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("There are %d server operators and %d router " + "operators online", + server->stat.server_ops, + server->stat.router_ops)); + if (server->stat.my_router_ops + server->stat.my_server_ops) + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("I have %d operators online", + server->stat.my_router_ops + + server->stat.my_server_ops)); + + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Your connection is secured with %s cipher, " + "key length %d bits", + idata->send_key->cipher->name, + idata->send_key->cipher->key_len)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Your current nickname is %s", + client->nickname)); + + /* Send motd */ + silc_server_send_motd(server, sock); +} + +/* Kill the client indicated by `remote_client' sending KILLED notify + to the client, to all channels client has joined and to primary + router if needed. The killed client is also removed from all channels. */ + +void silc_server_kill_client(SilcServer server, + SilcClientEntry remote_client, + const char *comment, + void *killer_id, + SilcIdType killer_id_type) +{ + SilcBuffer killed, killer; + + /* Send the KILL notify packets. First send it to the channel, then + to our primary router and then directly to the client who is being + killed right now. */ + + killed = silc_id_payload_encode(remote_client->id, SILC_ID_CLIENT); + killer = silc_id_payload_encode(killer_id, killer_id_type); + + /* Send KILLED notify to the channels. It is not sent to the client + as it will be sent differently destined directly to the client and not + to the channel. */ + silc_server_send_notify_on_channels(server, remote_client, + remote_client, SILC_NOTIFY_TYPE_KILLED, + 3, killed->data, killed->len, + comment, comment ? strlen(comment) : 0, + killer->data, killer->len); + + /* Send KILLED notify to primary route */ + if (!server->standalone) + silc_server_send_notify_killed(server, server->router->connection, TRUE, + remote_client->id, comment, + killer_id, killer_id_type); + + /* Send KILLED notify to the client directly */ + if (remote_client->connection || remote_client->router) + silc_server_send_notify_killed(server, remote_client->connection ? + remote_client->connection : + remote_client->router->connection, FALSE, + remote_client->id, comment, + killer_id, killer_id_type); + + /* Remove the client from all channels. This generates new keys to the + channels as well. */ + silc_server_remove_from_channels(server, NULL, remote_client, FALSE, + NULL, TRUE); + + /* Remove the client entry, If it is locally connected then we will also + disconnect the client here */ + if (remote_client->connection) { + /* Remove locally conneted client */ + SilcSocketConnection sock = remote_client->connection; + silc_server_free_client_data(server, sock, remote_client, FALSE, NULL); + silc_server_close_connection(server, sock); + } else { + /* Update statistics */ + server->stat.clients--; + server->stat.my_clients--; + if (server->stat.cell_clients) + server->stat.cell_clients--; + SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR); + SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR); + + /* Remove remote client */ + if (!silc_idlist_del_client(server->global_list, remote_client)) + silc_idlist_del_client(server->local_list, remote_client); +} + + silc_buffer_free(killer); + silc_buffer_free(killed); +} diff --git a/apps/silcd/server_util.h b/apps/silcd/server_util.h index 1545c1fd..5a531037 100644 --- a/apps/silcd/server_util.h +++ b/apps/silcd/server_util.h @@ -134,4 +134,19 @@ bool silc_server_check_umode_rights(SilcServer server, SilcClientEntry client, SilcUInt32 mode); +/* This function is used to send the notify packets and motd to the + incoming client connection. */ +void silc_server_send_connect_notifys(SilcServer server, + SilcSocketConnection sock, + SilcClientEntry client); + +/* Kill the client indicated by `remote_client' sending KILLED notify + to the client, to all channels client has joined and to primary + router if needed. The killed client is also removed from all channels. */ +void silc_server_kill_client(SilcServer server, + SilcClientEntry remote_client, + const char *comment, + void *killer_id, + SilcIdType killer_id_type); + #endif /* SERVER_UTIL_H */ diff --git a/doc/draft-riikonen-silc-commands-03.nroff b/doc/draft-riikonen-silc-commands-03.nroff index edc4cdbb..c915a498 100644 --- a/doc/draft-riikonen-silc-commands-03.nroff +++ b/doc/draft-riikonen-silc-commands-03.nroff @@ -284,11 +284,12 @@ List of all defined commands in SILC follows. option were defined in the query there will be only many replies from the server. - The server may return the list of channel the client has joined. - In this case the list is list of Channel Payloads. The Mode Mask - in the Channel Payload (see [SILC2] and section 2.3.2.3 for the - Channel Payload) is the client's mode on the channel. The list - is encoded by adding the Channel Payloads one after the other. + The server may return the list of channels if the client has + joined channels. In this case the list is list of Channel + Payloads. The Mode Mask in the Channel Payload (see [SILC2] and + section 2.3.2.3 for the Channel Payload) is the client's mode + on the channel. The list is encoded by adding the Channel + Payloads one after the other. The server may also send client's user mode, idle time, and the fingerprint of the client's public key. The is the @@ -965,7 +966,7 @@ List of all defined commands in SILC follows. 16 SILC_COMMAND_UMODE Max Arguments: 2 - Arguments: (1) (2) + Arguments: (1) (2) [] This command is used by client to set/unset modes for itself. However, there are some modes that the client MUST NOT set itself, @@ -1075,6 +1076,29 @@ List of all defined commands in SILC follows. The client MAY set and unset this mode. + 0x00000400 SILC_UMODE_DETACHED + + Marks that the client is detached from the SILC network. + This means that the actual network connection to the + client is lost but the client entry is still valid. The + detached client can be resumed at a later time. This + mode MUST NOT be set by client. It can only be set when + client has issued command SILC_COMMAND_DETACH. The server + sets this mode. This mode cannot be unset with this + command. It is unset when the client is resuming back to + the network and SILC_PACKET_RESUME_CLIENT packet is + received. + + This flag MUST NOT be used to determine whether a packet + can be sent to the client or not. Only the server that + had the original client connection can make the decision + by noticising that the network connection is not active. + In this case the default case is to discard the packet. + + If the was not provided this command merely + returns the mode mask to the client. + + Reply messages to the command: Max Arguments: 2 diff --git a/doc/draft-riikonen-silc-pp-05.nroff b/doc/draft-riikonen-silc-pp-05.nroff index 4c35ed82..b991f5a2 100644 --- a/doc/draft-riikonen-silc-pp-05.nroff +++ b/doc/draft-riikonen-silc-pp-05.nroff @@ -102,6 +102,7 @@ Table of Contents 2.3.20 Key Agreement Payload .............................. 43 2.3.21 Resume Router Payload .............................. 44 2.3.22 File Transfer Payload .............................. 44 + 2.3.23 Resume Client Payload .............................. XXXXXX 2.4 SILC ID Types ............................................. 46 2.5 Packet Encryption And Decryption .......................... 46 2.5.1 Normal Packet Encryption And Decryption ............. 46 @@ -145,6 +146,7 @@ Figure 20: New Server Payload Figure 21: Key Agreement Payload Figure 22: Resume Router Payload Figure 23: File Transfer Payload +Figure 24: Resume Client Payload .ti 0 @@ -770,7 +772,22 @@ List of SILC Packet types are defined as follows. Payload of the packet: See section 2.3.22 File Transfer Payload - 28 - 199 + 28 SILC_PACKET_RESUME_CLIENT + + This packet is used to resume a client back to the network + after it has been detached. A client is able to detach from + the network but the client is still valid client in the network. + The client may then later resume its session back by sending + this packet to a server. Routers also use this packet to notify + other routers in the network that the detached client has resumed. + + This packet MUST NOT be sent as list and the List flag MUST + NOT be set. + + Payload of the packet: See section 2.3.23 Resume Client Payload + + + 29 - 199 Currently undefined commands. @@ -2349,7 +2366,7 @@ protected with the negotiated key material. The payload may only be sent with SILC_PACKET_FTP packet. It MUST NOT be sent in any other packet type. The following diagram represents the -File Transfer Payload +File Transfer Payload. .in 5 .nf @@ -2392,6 +2409,64 @@ o Data (variable length) - Arbitrary file transfer data. The .in 3 +.ti 0 +2.3.23 Resume Client Payload + +This payload is used by client to resume its detached session in the +SILC Network. A client is able to detach itself from the network by +sending SILC_COMMAND_DETACH command to its server. The network +connection to the client is lost but the client remains as valid +client in the network. The client is able to resume the session back +by sending this packet and including the old Client ID, and an +Authentication Payload [SILC1] which the server uses to verify with +the detached client's public key. This also implies that the +mandatory authentication method is public key authentication. + +Server or router that receives this from the client also sends this, +without the Authentication Payload, to routers in the network so that +they know the detached client has resumed. Refer to the [SILC1] for +detailed description how the detaching and resuming prodecure is +performed. + +The payload may only be sent with SILC_PACKET_RESUME CLIENT packet. It +MUST NOT be sent in any other packet type. The following diagram +represents the Resume Client Payload. + +.in 5 +.nf + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Client ID Length | | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +| | +~ Client ID ~ +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | +~ Authentication Payload ~ +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.in 3 + +.ce +Figure 24: Resume Client Payload + + +.in 6 +o Client ID Length (1 byte) - The length of the Client ID + field not including any other field. + +o Client ID (variable length) - The detached client's Client + ID. The client that sends this payload must know the Client + ID. + +o Authentication Payload (variable length) - The authentication + payload that the server will verify with the detached client's + public key. If the server doesn't know the public key, it must + retrieve it for example with SILC_COMMAND_GETKEY command. +.in 3 + .ti 0 diff --git a/doc/draft-riikonen-silc-spec-05.nroff b/doc/draft-riikonen-silc-spec-05.nroff index 72369b8d..e8ee4842 100644 --- a/doc/draft-riikonen-silc-spec-05.nroff +++ b/doc/draft-riikonen-silc-spec-05.nroff @@ -125,6 +125,7 @@ Table of Contents 4.8 Session Key Regeneration .................................. 39 4.9 Command Sending and Reception ............................. 40 4.10 Closing Connection ....................................... 41 + 4.11 Detaching and Resuming a Session ......................... XXXXX 5 Security Considerations ....................................... 41 6 References .................................................... 42 7 Author's Address .............................................. 44 @@ -2109,6 +2110,97 @@ local clients that are joined on the same channels with the remote server's or router's clients. +.ti 0 +4.11 Detaching and Resuming a Session + +SILC protocol provides a possibility for a client to detach itself from +the network without actually signing off from the network. The client +connection to the server is closed but the client remains as valid client +in the network. The client may then later resume its session back from +any server in the network. + +When client wishes to detach from the network it MUST send the +SILC_COMMAND_DETACH command to its server. The server then MUST set +SILC_UMODE_DETACHED mode to the client and send SILC_NOTIFY_UMODE_CHANGE +notify to its primary router, which will then MUST broadcast it further +to other routers in the network. This user mode indicates that the +client is detached from the network. Implementations MUST NOT use +the SILC_UMODE_DETACHED flag to determine whether a packet can be sent +to the client. All packets MUST still be sent to the client even if +client is detached from the network. Only the server that originally +had the active client connection is able to make the decision after it +notices that the network connection is not active. In this case the +default case is to discard the packet. + +The SILC_UMODE_DETACHED flag cannot be set by client itself directly +with SILC_COMMAND_UMODE command, but only implicitly by sending the +SILC_COMMAND_DETACH command. The flag also cannot be unset by the +client, server or router with SILC_COMMAND_UMODE command, but only +implicitly by sending and receiving the SILC_PACKET_RESUME_CLIENT +packet. + +When the client wishes to resume its session in the SILC Network it +connects to a server in the network, which MAY also be a different +from the original server, and performs normal procedures regarding +creating a connection as described in section 4.1. After the SKE +and the Connection Authentication protocols has been successfully +completed the client MUST NOT send SILC_PACKET_NEW_CLIENT packet, but +MUST send SILC_PACKET_RESUME_CLIENT packet. This packet is used to +perform the resuming procedure. The packet MUST include the detached +client's Client ID, which the client must know. It also includes +Authentication Payload which includes signature made with the client's +private key. The signature is computed as defined in the section +3.9.1. Thus, the authentication method MUST be based in public key +authentication. + +When server receives the SILC_PACKET_RESUME_CLIENT packet it MUST +verify that the Client ID is valid client and that it has the +SILC_UMODE_DETACHED mode set. It then MUST verify the Authentication +Payload with the detached client's public key. If it does not have +the public key it MUST retrieve it by sending SILC_COMMAND_GETKEY +command to the server that has the public key from the original +client connection. The server MUST NOT use the public key received +in the SKE protocol for this connection. If the signature is valid +the server MUST unset the SILC_UMODE_DETACHED flag, and send the +SILC_PACKET_RESUME_CLIENT packet to its primary router. The routers +MUST broadcast the packet and unset the SILC_UMODE_DETACHED flag +when the packet is received. + +The servers and routers that receives the SILC_PACKET_RESUME_CLIENT +packet MUST know whether the packet already has been received for +the client. It is protocol error to attempt to resume the client +session from more than one server. The implementations could set +internal flag that indicates that the client is resumed. If router +receive SILC_PACKET_RESUME_CLIENT packet for client that is already +resumed the client MUST be killed from the network. This would +indicate that the client is attempting to resume the session more +than once which is protocol error. In this case the router sends +SILC_NOTIFY_TYPE_KILLED to the client. All routers that detect +the same situation MUST also send the notify for the client. + +The servers and routers that receive the SILC_PACKET_RESUME_CLIENT +must also understand that the client may not be found behind the +same server that it originally came from. They must update their +caches according this. The server that now owns the client session +MUST check whether the Client ID of the resumed client is based +on the server's Server ID. If it is not it MUST create new Client +ID and send SILC_NOTIFY_TYPE_NICK_CHANGE to the network. It MUST +also send the channel keys of all channels that the client is +joined to the client since it does not have them. Whether the +Client ID was changed or not the server MUST send SILC_PACKET_NEW_ID +packet to the client. Only after this the client is resumed back +to the network and may start sending packets and messages. + +It is also possible that the server does not know about the channels +that the client has joined. In this case it MUST join client internally +to the channels, generate new channel keys and distribute the keys +to the channels as described in section 4.4. + +It is implementation issue for how long servers keep detached client +sessions. It is RECOMMENDED that the detached sessions would be +persistent as long as the server is running. + + .ti 0 5 Security Considerations diff --git a/lib/silcclient/Makefile.am b/lib/silcclient/Makefile.am index b4ec607b..68d9f9d7 100644 --- a/lib/silcclient/Makefile.am +++ b/lib/silcclient/Makefile.am @@ -27,6 +27,7 @@ libsilcclient_a_SOURCES = \ client_prvmsg.c \ client_channel.c \ client_ftp.c \ + client_resume.c \ command.c \ command_reply.c \ idlist.c \ diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index efcc29da..2619df26 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -410,7 +410,7 @@ static void silc_client_start_key_exchange_cb(SilcSocketConnection sock, client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, "Error: Could not start key exchange protocol"); silc_net_close_connection(conn->sock->sock); - client->internal->ops->connect(client, conn, FALSE); + client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_ERROR); return; } conn->sock->protocol = protocol; @@ -465,7 +465,8 @@ SILC_TASK_CALLBACK(silc_client_connect_failure) (SilcClientKEInternalContext *)context; SilcClient client = (SilcClient)ctx->client; - client->internal->ops->connect(client, ctx->sock->user_data, FALSE); + client->internal->ops->connect(client, ctx->sock->user_data, + SILC_CLIENT_CONN_ERROR); if (ctx->packet) silc_packet_context_free(ctx->packet); silc_free(ctx); @@ -515,7 +516,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start) silc_free(ctx); /* Notify application of failure */ - client->internal->ops->connect(client, conn, FALSE); + client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_ERROR); silc_client_del_connection(client, conn); } return; @@ -670,26 +671,71 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final) return; } - /* Send NEW_CLIENT packet to the server. We will become registered - to the SILC network after sending this packet and we will receive - client ID from the server. */ - packet = silc_buffer_alloc(2 + 2 + strlen(client->username) + - strlen(client->realname)); - silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); - silc_buffer_format(packet, - SILC_STR_UI_SHORT(strlen(client->username)), - SILC_STR_UI_XNSTRING(client->username, - strlen(client->username)), - SILC_STR_UI_SHORT(strlen(client->realname)), - SILC_STR_UI_XNSTRING(client->realname, - strlen(client->realname)), - SILC_STR_END); + if (conn->params.detach_data) { + /* Send RESUME_CLIENT packet to the server, which is used to resume + old detached session back. */ + SilcBuffer auth; + SilcClientID *old_client_id; + unsigned char *old_id; + SilcUInt16 old_id_len; - /* Send the packet */ - silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT, - NULL, 0, NULL, NULL, - packet->data, packet->len, TRUE); - silc_buffer_free(packet); + if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) + return; + + old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT); + if (!old_client_id) { + silc_free(old_id); + return; + } + + /* Generate authentication data that server will verify */ + auth = silc_auth_public_key_auth_generate(client->public_key, + client->private_key, + client->rng, conn->hash, + old_client_id, SILC_ID_CLIENT); + if (!auth) { + silc_free(old_client_id); + silc_free(old_id); + return; + } + + packet = silc_buffer_alloc_size(2 + old_id_len + auth->len); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(old_id_len), + SILC_STR_UI_XNSTRING(old_id, old_id_len), + SILC_STR_UI_XNSTRING(auth->data, auth->len), + SILC_STR_END); + + /* Send the packet */ + silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT, + NULL, 0, NULL, NULL, + packet->data, packet->len, TRUE); + silc_buffer_free(packet); + silc_buffer_free(auth); + silc_free(old_client_id); + silc_free(old_id); + } else { + /* Send NEW_CLIENT packet to the server. We will become registered + to the SILC network after sending this packet and we will receive + client ID from the server. */ + packet = silc_buffer_alloc(2 + 2 + strlen(client->username) + + strlen(client->realname)); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(strlen(client->username)), + SILC_STR_UI_XNSTRING(client->username, + strlen(client->username)), + SILC_STR_UI_SHORT(strlen(client->realname)), + SILC_STR_UI_XNSTRING(client->realname, + strlen(client->realname)), + SILC_STR_END); + + /* Send the packet */ + silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT, + NULL, 0, NULL, NULL, + packet->data, packet->len, TRUE); + silc_buffer_free(packet); + } /* Save remote ID. */ conn->remote_id = ctx->dest_id; @@ -1480,6 +1526,34 @@ SILC_TASK_CALLBACK(silc_client_send_auto_nick) client->nickname, strlen(client->nickname)); } +/* Client session resuming callback. If the session was resumed + this callback is called after the resuming is completed. This + will call the `connect' client operation to the application + since it has not been called yet. */ + +static void silc_client_resume_session_cb(SilcClient client, + SilcClientConnection conn, + bool success, + void *context) +{ + SilcBuffer sidp; + + /* Notify application that connection is created to server */ + client->internal->ops->connect(client, conn, success ? + SILC_CLIENT_CONN_SUCCESS_RESUME : + SILC_CLIENT_CONN_ERROR); + + /* Issue INFO command to fetch the real server name and server + information and other stuff. */ + silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL, + silc_client_command_reply_info_i, 0, + ++conn->cmd_ident); + sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); + silc_client_command_send(client, conn, SILC_COMMAND_INFO, + conn->cmd_ident, 1, 2, sidp->data, sidp->len); + silc_buffer_free(sidp); +} + /* Processes the received new Client ID from server. Old Client ID is deleted from cache and new one is added. */ @@ -1490,7 +1564,6 @@ void silc_client_receive_new_id(SilcClient client, SilcClientConnection conn = (SilcClientConnection)sock->user_data; int connecting = FALSE; SilcClientID *client_id = silc_id_payload_get_id(idp); - SilcBuffer sidp; if (!conn->local_entry) connecting = TRUE; @@ -1539,26 +1612,37 @@ void silc_client_receive_new_id(SilcClient client, (void *)conn->local_entry, 0, NULL); if (connecting) { - /* Send NICK command if the nickname was set by the application (and is - not same as the username). Send this with little timeout. */ - if (client->nickname && strcmp(client->nickname, client->username)) - silc_schedule_task_add(client->schedule, 0, - silc_client_send_auto_nick, conn, - 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); - - /* Issue INFO command to fetch the real server name and server information - and other stuff. */ - silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL, - silc_client_command_reply_info_i, 0, - ++conn->cmd_ident); - sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); - silc_client_command_send(client, conn, SILC_COMMAND_INFO, - conn->cmd_ident, 1, 2, sidp->data, sidp->len); - silc_buffer_free(sidp); - - /* Notify application of successful connection. We do it here now that - we've received the Client ID and are allowed to send traffic. */ - client->internal->ops->connect(client, conn, TRUE); + if (!conn->params.detach_data) { + SilcBuffer sidp; + + /* Send NICK command if the nickname was set by the application (and is + not same as the username). Send this with little timeout. */ + if (client->nickname && strcmp(client->nickname, client->username)) + silc_schedule_task_add(client->schedule, 0, + silc_client_send_auto_nick, conn, + 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + + /* Notify application of successful connection. We do it here now that + we've received the Client ID and are allowed to send traffic. */ + client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_SUCCESS); + + /* Issue INFO command to fetch the real server name and server + information and other stuff. */ + silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL, + silc_client_command_reply_info_i, 0, + ++conn->cmd_ident); + sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); + silc_client_command_send(client, conn, SILC_COMMAND_INFO, + conn->cmd_ident, 1, 2, sidp->data, sidp->len); + silc_buffer_free(sidp); + } else { + /* We are resuming session. Start resolving informations from the + server we need to set the client libary in the state before + detaching the session. The connect client operation is called + after this is successfully completed */ + silc_client_resume_session(client, conn, silc_client_resume_session_cb, + NULL); + } } } @@ -1810,55 +1894,3 @@ silc_client_request_authentication_method(SilcClient client, client->internal->params->connauth_request_secs, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); } - -SilcBuffer silc_client_get_detach_data(SilcClient client, - SilcClientConnection conn) -{ - SilcBuffer detach; - SilcHashTableList htl; - SilcChannelUser chu; - - SILC_LOG_DEBUG(("Creating detachment data")); - - /* Save the nickname, Client ID and user mode in SILC network */ - detach = silc_buffer_alloc_size(2 + strlen(conn->nickname) + - 2 + conn->local_id_data_len + 4); - silc_buffer_format(detach, - SILC_STR_UI_SHORT(strlen(conn->nickname)), - SILC_STR_UI_XNSTRING(conn->nickname, - strlen(conn->nickname)), - SILC_STR_UI_SHORT(conn->local_id_data_len), - SILC_STR_UI_XNSTRING(conn->local_id_data, - conn->local_id_data_len), - SILC_STR_UI_INT(conn->local_entry->mode), - SILC_STR_END); - - /* Save all joined channels */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void **)&chu)) { - unsigned char *chid = silc_id_id2str(chu->channel->id, SILC_ID_CHANNEL); - SilcUInt16 chid_len = silc_id_get_len(chu->channel->id, SILC_ID_CHANNEL); - - detach = silc_buffer_realloc(detach, detach->truelen + 2 + - strlen(chu->channel->channel_name) + - 2 + chid_len + 4); - silc_buffer_pull(detach, detach->len); - silc_buffer_format(detach, - SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)), - SILC_STR_UI_XNSTRING(chu->channel->channel_name, - strlen(chu->channel->channel_name)), - SILC_STR_UI_SHORT(chid_len), - SILC_STR_UI_XNSTRING(chid, chid_len), - SILC_STR_UI_INT(chu->channel->mode), - SILC_STR_END); - - silc_free(chid); - } - silc_hash_table_list_reset(&htl); - - silc_buffer_push(detach, detach->data - detach->head); - - SILC_LOG_HEXDUMP(("Detach data"), detach->data, detach->len); - - return detach; -} diff --git a/lib/silcclient/client_internal.h b/lib/silcclient/client_internal.h index 7de59e7f..be5c05b1 100644 --- a/lib/silcclient/client_internal.h +++ b/lib/silcclient/client_internal.h @@ -84,6 +84,12 @@ struct SilcClientInternalStruct { char *silc_client_version; }; +/* Session resuming callback */ +typedef void (*SilcClientResumeSessionCallback)(SilcClient client, + SilcClientConnection conn, + bool success, + void *context); + /* Macros */ /* Registers generic task for file descriptor for reading from network and @@ -202,5 +208,15 @@ void silc_client_connection_auth_request(SilcClient client, void silc_client_ftp(SilcClient client, SilcSocketConnection sock, SilcPacketContext *packet); +SilcBuffer silc_client_get_detach_data(SilcClient client, + SilcClientConnection conn); +bool silc_client_process_detach_data(SilcClient client, + SilcClientConnection conn, + unsigned char **old_id, + SilcUInt16 *old_id_len); +void silc_client_resume_session(SilcClient client, + SilcClientConnection conn, + SilcClientResumeSessionCallback callback, + void *context); #endif diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c index 24e8854e..1e777858 100644 --- a/lib/silcclient/client_notify.c +++ b/lib/silcclient/client_notify.c @@ -898,25 +898,53 @@ void silc_client_notify_by_server(SilcClient client, tmp = silc_argument_get_arg_type(args, 3, &tmp_len); if (tmp) { silc_free(client_id); - client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); - if (!client_id) + id = silc_id_payload_parse_id(tmp, tmp_len, &id_type); + if (!id) goto out; - /* Find killer's client entry and if not found resolve it */ - client_entry2 = silc_client_get_client_by_id(client, conn, client_id); - if (!client_entry2) { - silc_client_notify_by_server_resolve(client, conn, packet, - SILC_ID_CLIENT, client_id); - goto out; + /* Find Client entry */ + if (id_type == SILC_ID_CLIENT) { + /* Find Client entry */ + client_id = id; + client_entry2 = silc_client_get_client_by_id(client, conn, + client_id); + if (!client_entry) { + silc_client_notify_by_server_resolve(client, conn, packet, + SILC_ID_CLIENT, client_id); + goto out; + } + } else if (id_type == SILC_ID_SERVER) { + /* Find Server entry */ + server_id = id; + server = silc_client_get_server_by_id(client, conn, server_id); + if (!server) { + silc_client_notify_by_server_resolve(client, conn, packet, + SILC_ID_SERVER, server_id); + goto out; + } + + /* Save the pointer to the client_entry pointer */ + client_entry2 = (SilcClientEntry)server; } else { - if (client_entry2 != conn->local_entry) - silc_client_nickname_format(client, conn, client_entry2); + /* Find Channel entry */ + channel_id = id; + channel = silc_client_get_channel_by_id(client, conn, channel_id); + if (!channel) { + silc_client_notify_by_server_resolve(client, conn, packet, + SILC_ID_CHANNEL, channel_id); + goto out; + } + + /* Save the pointer to the client_entry pointer */ + client_entry2 = (SilcClientEntry)channel; + silc_free(channel_id); + channel_id = NULL; } } /* Notify application. */ client->internal->ops->notify(client, conn, type, client_entry, - comment, client_entry2); + comment, id_type, client_entry2); if (client_entry != conn->local_entry) /* Remove the client from all channels and free it */ diff --git a/lib/silcclient/client_ops_example.c b/lib/silcclient/client_ops_example.c index 5b5c1e3a..6662e855 100644 --- a/lib/silcclient/client_ops_example.c +++ b/lib/silcclient/client_ops_example.c @@ -117,7 +117,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, silc_client_close_connection. */ static void -silc_connect(SilcClient client, SilcClientConnection conn, int success) +silc_connect(SilcClient client, SilcClientConnection conn, + SilcClientConnectionStatus status); { } diff --git a/lib/silcclient/client_resume.c b/lib/silcclient/client_resume.c new file mode 100644 index 00000000..0d0d24ba --- /dev/null +++ b/lib/silcclient/client_resume.c @@ -0,0 +1,521 @@ +/* + + client_resume.c + + Author: Pekka Riikonen + + Copyright (C) 2002 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* $Id$ */ + +#include "silcincludes.h" +#include "silcclient.h" +#include "client_internal.h" + +SILC_CLIENT_CMD_REPLY_FUNC(resume); +SILC_CLIENT_CMD_FUNC(resume_identify); +SILC_CLIENT_CMD_FUNC(resume_cmode); +SILC_CLIENT_CMD_FUNC(resume_users); + +/* Generates the session detachment data. This data can be used later + to resume back to the server. */ + +SilcBuffer silc_client_get_detach_data(SilcClient client, + SilcClientConnection conn) +{ + SilcBuffer detach; + SilcHashTableList htl; + SilcChannelUser chu; + int ch_count; + + SILC_LOG_DEBUG(("Creating detachment data")); + + ch_count = silc_hash_table_count(conn->local_entry->channels); + + /* Save the nickname, Client ID and user mode in SILC network */ + detach = silc_buffer_alloc_size(2 + strlen(conn->nickname) + + 2 + conn->local_id_data_len + 4 + 4); + silc_buffer_format(detach, + SILC_STR_UI_SHORT(strlen(conn->nickname)), + SILC_STR_UI_XNSTRING(conn->nickname, + strlen(conn->nickname)), + SILC_STR_UI_SHORT(conn->local_id_data_len), + SILC_STR_UI_XNSTRING(conn->local_id_data, + conn->local_id_data_len), + SILC_STR_UI_INT(conn->local_entry->mode), + SILC_STR_UI_INT(ch_count), + SILC_STR_END); + + /* Save all joined channels */ + silc_hash_table_list(conn->local_entry->channels, &htl); + while (silc_hash_table_get(&htl, NULL, (void **)&chu)) { + unsigned char *chid = silc_id_id2str(chu->channel->id, SILC_ID_CHANNEL); + SilcUInt16 chid_len = silc_id_get_len(chu->channel->id, SILC_ID_CHANNEL); + + detach = silc_buffer_realloc(detach, detach->truelen + 2 + + strlen(chu->channel->channel_name) + + 2 + chid_len + 4); + silc_buffer_pull(detach, detach->len); + silc_buffer_pull_tail(detach, 2 + strlen(chu->channel->channel_name) + + 2 + chid_len + 4); + silc_buffer_format(detach, + SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)), + SILC_STR_UI_XNSTRING(chu->channel->channel_name, + strlen(chu->channel->channel_name)), + SILC_STR_UI_SHORT(chid_len), + SILC_STR_UI_XNSTRING(chid, chid_len), + SILC_STR_UI_INT(chu->channel->mode), + SILC_STR_END); + silc_free(chid); + } + silc_hash_table_list_reset(&htl); + + silc_buffer_push(detach, detach->data - detach->head); + + SILC_LOG_HEXDUMP(("Detach data"), detach->data, detach->len); + + return detach; +} + +/* Processes the detachment data. This creates channels and other + stuff according the data found in the the connection parameters. + This doesn't actually resolve any detailed information from the + server. To do that call silc_client_resume_session function. + This returns the old detached session client ID. */ + +bool silc_client_process_detach_data(SilcClient client, + SilcClientConnection conn, + unsigned char **old_id, + SilcUInt16 *old_id_len) +{ + SilcBufferStruct detach; + SilcUInt32 ch_count; + int i, len; + + SILC_LOG_DEBUG(("Start")); + + silc_free(conn->nickname); + silc_buffer_set(&detach, conn->params.detach_data, + conn->params.detach_data_len); + + SILC_LOG_HEXDUMP(("Detach data"), detach.data, detach.len); + + /* Take the old client ID from the detachment data */ + len = silc_buffer_unformat(&detach, + SILC_STR_UI16_NSTRING_ALLOC(&conn->nickname, + NULL), + SILC_STR_UI16_NSTRING_ALLOC(old_id, old_id_len), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(&ch_count), + SILC_STR_END); + if (len == -1) + return FALSE; + + silc_buffer_pull(&detach, len); + + for (i = 0; i < ch_count; i++) { + char *channel; + unsigned char *chid; + SilcUInt16 chid_len; + SilcUInt32 ch_mode; + SilcChannelID *channel_id; + SilcChannelEntry channel_entry; + + len = silc_buffer_unformat(&detach, + SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL), + SILC_STR_UI16_NSTRING(&chid, &chid_len), + SILC_STR_UI_INT(&ch_mode), + SILC_STR_END); + if (len == -1) + return FALSE; + + /* Add new channel */ + channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL); + channel_entry = silc_client_get_channel_by_id(client, conn, channel_id); + if (!channel_entry) { + channel_entry = silc_client_add_channel(client, conn, channel, ch_mode, + channel_id); + } else { + silc_free(channel); + silc_free(channel_id); + } + + silc_buffer_pull(&detach, len); + } + silc_buffer_push(&detach, detach.data - detach.head); + + return TRUE; +} + +/* Generic command reply callback */ + +SILC_CLIENT_CMD_REPLY_FUNC(resume) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + + SILC_LOG_DEBUG(("Start")); + + if (cmd->callback) + (*cmd->callback)(cmd->context, cmd); +} + +/* Resume session context */ +typedef struct { + SilcClient client; + SilcClientConnection conn; + SilcClientResumeSessionCallback callback; + void *context; + SilcUInt32 channel_count; +} *SilcClientResumeSession; + +/* This function is used to perform the resuming procedure after the + client has connected to the server properly and has received the + Client ID for the resumed session. This resolves all channels + that the resumed client is joined, joined users, users modes + and channel modes. The `callback' is called after this procedure + is completed. */ + +void silc_client_resume_session(SilcClient client, + SilcClientConnection conn, + SilcClientResumeSessionCallback callback, + void *context) +{ + SilcClientResumeSession session; + SilcIDCacheList list; + SilcIDCacheEntry entry; + SilcChannelEntry channel; + SilcBuffer tmp; + int i; + bool ret; + + SILC_LOG_DEBUG(("Resuming detached session")); + + session = silc_calloc(1, sizeof(*session)); + if (!session) { + callback(client, conn, FALSE, context); + return; + } + session->client = client; + session->conn = conn; + session->callback = callback; + session->context = context; + + /* First, send UMODE commandto get our own user mode in the network */ + SILC_LOG_DEBUG(("Sending UMODE")); + tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT); + silc_client_command_send(client, conn, SILC_COMMAND_UMODE, + conn->cmd_ident, 1, 1, tmp->data, tmp->len); + silc_buffer_free(tmp); + + /* Second, send IDENTIFY command of all channels we know about. These + are the channels we've joined to according our detachment data. */ + if (silc_idcache_get_all(conn->channel_cache, &list)) { + unsigned char **res_argv = NULL; + SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0; + + session->channel_count = silc_idcache_list_count(list); + + ret = silc_idcache_list_first(list, &entry); + while (ret) { + channel = entry->context; + tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); + res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1)); + res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) * + (res_argc + 1)); + res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) * + (res_argc + 1)); + res_argv[res_argc] = silc_memdup(tmp->data, tmp->len); + res_argv_lens[res_argc] = tmp->len; + res_argv_types[res_argc] = res_argc + 5; + res_argc++; + silc_buffer_free(tmp); + ret = silc_idcache_list_next(list, &entry); + } + silc_idcache_list_free(list); + + if (res_argc) { + /* Send the IDENTIFY command */ + SILC_LOG_DEBUG(("Sending IDENTIFY")); + silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL, + silc_client_command_reply_resume, + 0, ++conn->cmd_ident); + tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY, + res_argc, res_argv, res_argv_lens, + res_argv_types, conn->cmd_ident); + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + silc_client_command_resume_identify, + session); + silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, + NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE); + + for (i = 0; i < res_argc; i++) + silc_free(res_argv[i]); + silc_free(res_argv); + silc_free(res_argv_lens); + silc_free(res_argv_types); + silc_buffer_free(tmp); + } + } + + /* Now, we wait for replies to come back and then continue with USERS, + CMODE and TOPIC commands. */ +} + +/* Received identify reply for a channel entry */ + +SILC_CLIENT_CMD_FUNC(resume_identify) +{ + SilcClientResumeSession session = context; + SilcClientCommandReplyContext cmd = context2; + SilcClient client = session->client; + SilcClientConnection conn = session->conn; + unsigned char *tmp; + SilcUInt32 tmp_len; + SilcChannelEntry channel = NULL; + SilcChannelID *channel_id; + SilcIDPayload idp; + SilcIdType id_type; + + SILC_LOG_DEBUG(("Start")); + + tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); + if (!tmp) + goto err; + + if (cmd->error != SILC_STATUS_OK) { + /* Delete unknown channel from our cache */ + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) { + channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (channel_id) { + channel = silc_client_get_channel_by_id(client, conn, channel_id); + if (channel) + silc_client_del_channel(client, conn, channel); + silc_free(channel_id); + } + } + goto err; + } + + idp = silc_id_payload_parse(tmp, tmp_len); + if (!idp) { + return; + } + id_type = silc_id_payload_get_type(idp); + + switch (id_type) { + case SILC_ID_CHANNEL: + channel_id = silc_id_payload_get_id(idp); + channel = silc_client_get_channel_by_id(client, conn, channel_id); + silc_free(channel_id); + break; + default: + silc_id_payload_free(idp); + goto err; + break; + } + + /* Now, send CMODE command for this channel. We send only this one + because this will return also error if we are not currently joined + on this channel, plus we get the channel mode. USERS and TOPIC + commands are called after this returns. */ + if (channel) { + SILC_LOG_DEBUG(("Sending CMODE")); + silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL, + silc_client_command_reply_resume, 0, + ++conn->cmd_ident); + silc_client_command_send(client, conn, SILC_COMMAND_CMODE, + conn->cmd_ident, 1, 1, tmp, tmp_len); + silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident, + silc_client_command_resume_cmode, session); + } + + silc_id_payload_free(idp); + + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) + return; + + /* Unregister this command reply */ + silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL, + silc_client_command_reply_resume, + cmd->ident); + return; + + err: + session->channel_count--; + if (!session->channel_count) + session->callback(session->client, session->conn, FALSE, + session->context); +} + +/* Received cmode to channel entry */ + +SILC_CLIENT_CMD_FUNC(resume_cmode) +{ + SilcClientResumeSession session = context; + SilcClientCommandReplyContext cmd = context2; + SilcClient client = session->client; + SilcClientConnection conn = session->conn; + unsigned char *tmp; + SilcChannelID *channel_id; + SilcChannelEntry channel; + SilcUInt32 len; + + SILC_LOG_DEBUG(("Start")); + + /* Unregister this command reply */ + silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL, + silc_client_command_reply_resume, + cmd->ident); + + if (cmd->error != SILC_STATUS_OK) + goto err; + + /* Take Channel ID */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + if (!tmp) + goto err; + channel_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!channel_id) + goto err; + + /* Get the channel entry */ + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (channel) { + + /* Get channel mode */ + tmp = silc_argument_get_arg_type(cmd->args, 3, NULL); + if (tmp) + SILC_GET32_MSB(channel->mode, tmp); + + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + + /* And now, we will send USERS to get users on the channel */ + SILC_LOG_DEBUG(("Sending USERS")); + silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL, + silc_client_command_reply_users_i, 0, + ++conn->cmd_ident); + silc_client_command_send(client, conn, SILC_COMMAND_USERS, + conn->cmd_ident, 1, 1, tmp, len); + silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, + silc_client_command_resume_users, session); + } + + silc_free(channel_id); + return; + + err: + session->channel_count--; + if (!session->channel_count) + session->callback(session->client, session->conn, FALSE, + session->context); +} + +/* Received users reply to a channel entry */ + +SILC_CLIENT_CMD_FUNC(resume_users) +{ + SilcClientResumeSession session = context; + SilcClientCommandReplyContext cmd = context2; + SilcClient client = session->client; + SilcClientConnection conn = session->conn; + SilcBufferStruct client_id_list, client_mode_list; + unsigned char *tmp; + SilcUInt32 tmp_len, list_count; + SilcChannelEntry channel; + SilcChannelID *channel_id = NULL; + + SILC_LOG_DEBUG(("Start")); + + /* Unregister this command reply */ + silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL, + silc_client_command_reply_users_i, + cmd->ident); + + if (cmd->error != SILC_STATUS_OK) + goto err; + + /* Get channel ID */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); + if (!tmp) { + COMMAND_REPLY_ERROR; + goto err; + } + channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (!channel_id) { + COMMAND_REPLY_ERROR; + goto err; + } + + /* Get the list count */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); + if (!tmp) { + COMMAND_REPLY_ERROR; + goto err; + } + SILC_GET32_MSB(list_count, tmp); + + /* Get Client ID list */ + tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len); + if (!tmp) { + COMMAND_REPLY_ERROR; + goto err; + } + silc_buffer_set(&client_id_list, tmp, tmp_len); + + /* Get client mode list */ + tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len); + if (!tmp) { + COMMAND_REPLY_ERROR; + goto err; + } + silc_buffer_set(&client_mode_list, tmp, tmp_len); + + /* Get channel entry */ + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) + goto err; + + /* Send fake JOIN command reply to application */ + client->internal->ops->command_reply(client, conn, cmd->payload, TRUE, + SILC_COMMAND_JOIN, cmd->status, + channel->channel_name, channel, + channel->mode, 0, + NULL, NULL, NULL, NULL, + channel->hmac, list_count, + &client_id_list, client_mode_list); + + /* Send TOPIC for this channel to get the topic */ + SILC_LOG_DEBUG(("Sending TOPIC")); + tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); + silc_client_command_send(client, conn, SILC_COMMAND_TOPIC, + conn->cmd_ident, 1, 1, tmp, tmp_len); + + /* Call the completion callback after we've got reply to all of + our channels */ + session->channel_count--; + if (!session->channel_count) + session->callback(session->client, session->conn, TRUE, + session->context); + + silc_free(channel_id); + return; + + err: + silc_free(channel_id); + session->channel_count--; + if (!session->channel_count) + session->callback(session->client, session->conn, FALSE, + session->context); +} diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index fa0bfa63..f04e4510 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -1913,14 +1913,10 @@ SILC_CLIENT_CMD_FUNC(ban) chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); /* Send the command */ - if (ban) - buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, - 1, chidp->data, chidp->len, - type, ban, strlen(ban)); - else - buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, - 1, chidp->data, chidp->len); - + buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, + ++conn->cmd_ident, 2, + 1, chidp->data, chidp->len, + type, ban, ban ? strlen(ban) : 0); silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); @@ -1933,6 +1929,33 @@ SILC_CLIENT_CMD_FUNC(ban) silc_client_command_free(cmd); } +/* Command DETACH. This is used to detach from the server */ + +SILC_CLIENT_CMD_FUNC(detach) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientConnection conn = cmd->conn; + SilcBuffer buffer; + + if (!cmd->conn) { + SILC_NOT_CONNECTED(cmd->client, cmd->conn); + COMMAND_ERROR; + goto out; + } + + buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH, + ++conn->cmd_ident, 0); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + 0, NULL, NULL, buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + + /* Notify application */ + COMMAND; + + out: + silc_client_command_free(cmd); +} + /* LEAVE command. Leaves a channel. Client removes itself from a channel. */ SILC_CLIENT_CMD_FUNC(leave) @@ -2383,6 +2406,7 @@ void silc_client_commands_register(SilcClient client) SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5); SILC_CLIENT_CMD(kick, KICK, "KICK", 4); SILC_CLIENT_CMD(ban, BAN, "BAN", 3); + SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0); SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3); SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2); SILC_CLIENT_CMD(users, USERS, "USERS", 2); @@ -2416,6 +2440,7 @@ void silc_client_commands_unregister(SilcClient client) SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE"); SILC_CLIENT_CMDU(kick, KICK, "KICK"); SILC_CLIENT_CMDU(ban, BAN, "BAN"); + SILC_CLIENT_CMDU(detach, DETACH, "DETACH"); SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER"); SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE"); SILC_CLIENT_CMDU(users, USERS, "USERS"); diff --git a/lib/silcclient/command.h b/lib/silcclient/command.h index 0b8949e1..33479ec8 100644 --- a/lib/silcclient/command.h +++ b/lib/silcclient/command.h @@ -127,7 +127,6 @@ SILC_CLIENT_CMD_FUNC(invite); SILC_CLIENT_CMD_FUNC(quit); SILC_CLIENT_CMD_FUNC(kill); SILC_CLIENT_CMD_FUNC(info); -SILC_CLIENT_CMD_FUNC(connect); SILC_CLIENT_CMD_FUNC(ping); SILC_CLIENT_CMD_FUNC(oper); SILC_CLIENT_CMD_FUNC(join); @@ -137,11 +136,14 @@ SILC_CLIENT_CMD_FUNC(cmode); SILC_CLIENT_CMD_FUNC(cumode); SILC_CLIENT_CMD_FUNC(kick); SILC_CLIENT_CMD_FUNC(ban); -SILC_CLIENT_CMD_FUNC(close); -SILC_CLIENT_CMD_FUNC(shutdown); +SILC_CLIENT_CMD_FUNC(detach); SILC_CLIENT_CMD_FUNC(silcoper); SILC_CLIENT_CMD_FUNC(leave); SILC_CLIENT_CMD_FUNC(users); SILC_CLIENT_CMD_FUNC(getkey); +SILC_CLIENT_CMD_FUNC(shutdown); +SILC_CLIENT_CMD_FUNC(close); +SILC_CLIENT_CMD_FUNC(connect); + #endif diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index 097bdc09..85609d33 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -79,16 +79,6 @@ const SilcCommandStatusMessage silc_command_status_messages[] = { { 0, NULL } }; -/* Command reply operation that is called at the end of all command replys. - Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */ -#define COMMAND_REPLY(args) cmd->client->internal->ops->command_reply args -#define ARGS cmd->client, cmd->sock->user_data, \ - cmd->payload, TRUE, silc_command_get(cmd->payload), cmd->status - -/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */ -#define COMMAND_REPLY_ERROR cmd->client->internal->ops-> \ - command_reply(cmd->client, cmd->sock->user_data, cmd->payload, \ - FALSE, silc_command_get(cmd->payload), cmd->status) #define SAY cmd->client->internal->ops->say @@ -200,18 +190,15 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcClientID *client_id; SilcClientEntry client_entry = NULL; - int argc; SilcUInt32 len; unsigned char *id_data, *tmp; char *nickname = NULL, *username = NULL; char *realname = NULL; SilcUInt32 idle = 0, mode = 0; - SilcBuffer channels = NULL; + SilcBufferStruct channels; unsigned char *fingerprint; SilcUInt32 fingerprint_len; - argc = silc_argument_get_arg_num(cmd->args); - id_data = silc_argument_get_arg_type(cmd->args, 2, &len); if (!id_data) { if (notify) @@ -236,11 +223,8 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, } tmp = silc_argument_get_arg_type(cmd->args, 6, &len); - if (tmp) { - channels = silc_buffer_alloc(len); - silc_buffer_pull_tail(channels, SILC_BUFFER_END(channels)); - silc_buffer_put(channels, tmp, len); - } + if (tmp) + silc_buffer_set(&channels, tmp, len); tmp = silc_argument_get_arg_type(cmd->args, 7, &len); if (tmp) @@ -276,10 +260,7 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, /* Notify application */ if (!cmd->callback && notify) COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, - channels, mode, idle, fingerprint)); - - if (channels) - silc_buffer_free(channels); + &channels, mode, idle, fingerprint)); } /* Received reply for WHOIS command. This maybe called several times @@ -397,15 +378,12 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd, SilcClientEntry client_entry; SilcServerEntry server_entry; SilcChannelEntry channel_entry; - int argc; SilcUInt32 len; unsigned char *id_data; char *name = NULL, *info = NULL; SilcIDPayload idp = NULL; SilcIdType id_type; - argc = silc_argument_get_arg_num(cmd->args); - id_data = silc_argument_get_arg_type(cmd->args, 2, &len); if (!id_data) { if (notify) @@ -1379,6 +1357,36 @@ SILC_CLIENT_CMD_REPLY_FUNC(oper) silc_client_command_reply_free(cmd); } +SILC_CLIENT_CMD_REPLY_FUNC(detach) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcBuffer detach; + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_client_command_status_message(cmd->error)); + COMMAND_REPLY_ERROR; + goto out; + } + + /* Notify application */ + COMMAND_REPLY((ARGS)); + + /* Generate the detachment data and deliver it to the client in the + detach client operation */ + detach = silc_client_get_detach_data(cmd->client, conn); + if (detach) { + cmd->client->internal->ops->detach(cmd->client, conn, + detach->data, detach->len); + silc_buffer_free(detach); + } + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH); + silc_client_command_reply_free(cmd); +} + SILC_CLIENT_CMD_REPLY_FUNC(ban) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; @@ -1491,19 +1499,19 @@ static void silc_client_command_reply_users_cb(SilcClient client, silc_client_command_reply_users(context, NULL); } -/* Reply to USERS command. Received list of client ID's and theirs modes - on the channel we requested. */ - -SILC_CLIENT_CMD_REPLY_FUNC(users) +static int +silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd, + SilcCommandStatus status, + bool notify, + SilcGetChannelCallback get_channel, + SilcCommandCb get_clients) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcChannelEntry channel; SilcClientEntry client_entry; SilcChannelUser chu; SilcChannelID *channel_id = NULL; - SilcBuffer client_id_list = NULL; - SilcBuffer client_mode_list = NULL; + SilcBufferStruct client_id_list, client_mode_list; unsigned char *tmp; SilcUInt32 tmp_len, list_count; int i; @@ -1512,13 +1520,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) SILC_LOG_DEBUG(("Start")); - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } - /* Get channel ID */ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); if (!tmp) { @@ -1545,10 +1546,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) COMMAND_REPLY_ERROR; goto out; } - - client_id_list = silc_buffer_alloc(tmp_len); - silc_buffer_pull_tail(client_id_list, tmp_len); - silc_buffer_put(client_id_list, tmp, tmp_len); + silc_buffer_set(&client_id_list, tmp, tmp_len); /* Get client mode list */ tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len); @@ -1556,24 +1554,16 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) COMMAND_REPLY_ERROR; goto out; } - - client_mode_list = silc_buffer_alloc(tmp_len); - silc_buffer_pull_tail(client_mode_list, tmp_len); - silc_buffer_put(client_mode_list, tmp, tmp_len); + silc_buffer_set(&client_mode_list, tmp, tmp_len); /* Get channel entry */ channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); if (!channel) { /* Resolve the channel from server */ silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id, - silc_client_command_reply_users_cb, - cmd); + get_channel, cmd); silc_free(channel_id); - if (client_id_list) - silc_buffer_free(client_id_list); - if (client_mode_list) - silc_buffer_free(client_mode_list); - return; + return 1; } /* Cache the received Client ID's and modes. */ @@ -1583,22 +1573,22 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) SilcClientID *client_id; /* Client ID */ - SILC_GET16_MSB(idp_len, client_id_list->data + 2); + SILC_GET16_MSB(idp_len, client_id_list.data + 2); idp_len += 4; - client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL); + client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL); if (!client_id) continue; /* Mode */ - SILC_GET32_MSB(mode, client_mode_list->data); + SILC_GET32_MSB(mode, client_mode_list.data); /* Check if we have this client cached already. */ client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); if (!client_entry || !client_entry->username || !client_entry->realname) { if (client_entry) { if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) { - silc_buffer_pull(client_id_list, idp_len); - silc_buffer_pull(client_mode_list, 4); + silc_buffer_pull(&client_id_list, idp_len); + silc_buffer_pull(&client_mode_list, 4); continue; } client_entry->status |= SILC_CLIENT_STATUS_RESOLVING; @@ -1613,7 +1603,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) (res_argc + 1)); res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) * (res_argc + 1)); - res_argv[res_argc] = client_id_list->data; + res_argv[res_argc] = client_id_list.data; res_argv_lens[res_argc] = idp_len; res_argv_types[res_argc] = res_argc + 3; res_argc++; @@ -1629,8 +1619,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) } silc_free(client_id); - silc_buffer_pull(client_id_list, idp_len); - silc_buffer_pull(client_mode_list, 4); + silc_buffer_pull(&client_id_list, idp_len); + silc_buffer_pull(&client_mode_list, 4); } /* Query the client information from server if the list included clients @@ -1653,36 +1643,56 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) command reply we will reprocess this command reply by re-calling this USERS command reply callback. */ silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident, - silc_client_command_reply_users, cmd); + get_clients, cmd); silc_buffer_free(res_cmd); silc_free(channel_id); silc_free(res_argv); silc_free(res_argv_lens); silc_free(res_argv_types); - if (client_id_list) - silc_buffer_free(client_id_list); - if (client_mode_list) - silc_buffer_free(client_mode_list); - return; + return 1; } - silc_buffer_push(client_id_list, (client_id_list->data - - client_id_list->head)); - silc_buffer_push(client_mode_list, (client_mode_list->data - - client_mode_list->head)); + silc_buffer_push(&client_id_list, (client_id_list.data - + client_id_list.head)); + silc_buffer_push(&client_mode_list, (client_mode_list.data - + client_mode_list.head)); /* Notify application */ - COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list)); + if (notify) + COMMAND_REPLY((ARGS, channel, list_count, &client_id_list, + &client_mode_list)); + + out: + silc_free(channel_id); + return 0; +} + +/* Reply to USERS command. Received list of client ID's and theirs modes + on the channel we requested. */ + +SILC_CLIENT_CMD_REPLY_FUNC(users) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + SILC_LOG_DEBUG(("Start")); + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_client_command_status_message(cmd->error)); + COMMAND_REPLY_ERROR; + goto out; + } + + if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, + silc_client_command_reply_users_cb, + silc_client_command_reply_users)) + return; out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); silc_client_command_reply_free(cmd); - silc_free(channel_id); - if (client_id_list) - silc_buffer_free(client_id_list); - if (client_mode_list) - silc_buffer_free(client_mode_list); } /* Received command reply to GETKEY command. WE've received the remote @@ -1935,9 +1945,57 @@ SILC_CLIENT_CMD_REPLY_FUNC(info_i) silc_client_command_reply_free(cmd); } +static void silc_client_command_reply_users_i_cb(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry *channels, + SilcUInt32 channels_count, + void *context) +{ + if (!channels_count) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL; + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_client_command_status_message(cmd->error)); + COMMAND_REPLY_ERROR; + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); + silc_client_command_reply_free(cmd); + return; + } + + silc_client_command_reply_users_i(context, NULL); +} + +SILC_CLIENT_CMD_REPLY_FUNC(users_i) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + + SILC_LOG_DEBUG(("Start")); + + if (cmd->error != SILC_STATUS_OK) + goto out; + + /* Save USERS info */ + if (silc_client_command_reply_users_save( + cmd, cmd->status, FALSE, + silc_client_command_reply_users_i_cb, + silc_client_command_reply_users_i)) + return; + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); + + /* Unregister this command reply */ + silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS, + NULL, silc_client_command_reply_users_i, + cmd->ident); + + silc_client_command_reply_free(cmd); +} /* Private range commands, specific to this implementation (and compatible - with SILC Server). */ + with SILC Server >= 0.9). */ SILC_CLIENT_CMD_REPLY_FUNC(connect) { @@ -1998,4 +2056,3 @@ SILC_CLIENT_CMD_REPLY_FUNC(shutdown) SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN); silc_client_command_reply_free(cmd); } - diff --git a/lib/silcclient/command_reply.h b/lib/silcclient/command_reply.h index 1d1d2083..782b0aa2 100644 --- a/lib/silcclient/command_reply.h +++ b/lib/silcclient/command_reply.h @@ -44,6 +44,17 @@ struct SilcClientCommandReplyContextStruct { /* Macros */ +/* Command reply operation that is called at the end of all command replys. + Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */ +#define COMMAND_REPLY(args) cmd->client->internal->ops->command_reply args +#define ARGS cmd->client, cmd->sock->user_data, \ + cmd->payload, TRUE, silc_command_get(cmd->payload), cmd->status + +/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */ +#define COMMAND_REPLY_ERROR cmd->client->internal->ops-> \ + command_reply(cmd->client, cmd->sock->user_data, cmd->payload, \ + FALSE, silc_command_get(cmd->payload), cmd->status) + /* Macro used to declare command reply functions */ #define SILC_CLIENT_CMD_REPLY_FUNC(func) \ void silc_client_command_reply_##func(void *context, void *context2) @@ -64,6 +75,7 @@ void silc_client_command_reply_process(SilcClient client, SilcSocketConnection sock, SilcPacketContext *packet); char *silc_client_command_status_message(SilcCommandStatus status); +void silc_client_command_reply_free(SilcClientCommandReplyContext cmd); SILC_CLIENT_CMD_REPLY_FUNC(whois); SILC_CLIENT_CMD_REPLY_FUNC(whowas); SILC_CLIENT_CMD_REPLY_FUNC(identify); @@ -73,12 +85,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic); SILC_CLIENT_CMD_REPLY_FUNC(invite); SILC_CLIENT_CMD_REPLY_FUNC(kill); SILC_CLIENT_CMD_REPLY_FUNC(info); -SILC_CLIENT_CMD_REPLY_FUNC(links); SILC_CLIENT_CMD_REPLY_FUNC(stats); -SILC_CLIENT_CMD_REPLY_FUNC(users); -SILC_CLIENT_CMD_REPLY_FUNC(connect); SILC_CLIENT_CMD_REPLY_FUNC(ping); -SILC_CLIENT_CMD_REPLY_FUNC(pong); SILC_CLIENT_CMD_REPLY_FUNC(oper); SILC_CLIENT_CMD_REPLY_FUNC(join); SILC_CLIENT_CMD_REPLY_FUNC(motd); @@ -87,8 +95,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(cmode); SILC_CLIENT_CMD_REPLY_FUNC(cumode); SILC_CLIENT_CMD_REPLY_FUNC(kick); SILC_CLIENT_CMD_REPLY_FUNC(ban); -SILC_CLIENT_CMD_REPLY_FUNC(close); -SILC_CLIENT_CMD_REPLY_FUNC(shutdown); +SILC_CLIENT_CMD_REPLY_FUNC(detach); SILC_CLIENT_CMD_REPLY_FUNC(silcoper); SILC_CLIENT_CMD_REPLY_FUNC(leave); SILC_CLIENT_CMD_REPLY_FUNC(users); @@ -99,5 +106,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(quit); SILC_CLIENT_CMD_REPLY_FUNC(whois_i); SILC_CLIENT_CMD_REPLY_FUNC(identify_i); SILC_CLIENT_CMD_REPLY_FUNC(info_i); +SILC_CLIENT_CMD_REPLY_FUNC(users_i); + +SILC_CLIENT_CMD_REPLY_FUNC(connect); +SILC_CLIENT_CMD_REPLY_FUNC(close); +SILC_CLIENT_CMD_REPLY_FUNC(shutdown); #endif diff --git a/lib/silcclient/idlist.c b/lib/silcclient/idlist.c index 7549a62d..9f21b8a5 100644 --- a/lib/silcclient/idlist.c +++ b/lib/silcclient/idlist.c @@ -377,6 +377,7 @@ void silc_client_get_clients_by_list(SilcClient client, SilcUInt16 idp_len; SilcClientID *client_id; SilcClientEntry entry; + bool ret; /* Get Client ID */ SILC_GET16_MSB(idp_len, client_id_list->data + 2); @@ -388,16 +389,16 @@ void silc_client_get_clients_by_list(SilcClient client, } /* Check if we have this client cached already. */ - id_cache = NULL; - silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, - NULL, NULL, - silc_hash_client_id_compare, NULL, - &id_cache); + ret = + silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, + NULL, NULL, + silc_hash_client_id_compare, NULL, + &id_cache); /* If we don't have the entry or it has incomplete info, then resolve it from the server. */ - entry = id_cache ? (SilcClientEntry)id_cache->context : NULL; - if (!id_cache || !entry->nickname) { + if (!ret || !((SilcClientEntry)id_cache->context)->nickname) { + entry = ret ? (SilcClientEntry)id_cache->context : NULL; if (entry) { if (entry->status & SILC_CLIENT_STATUS_RESOLVING) { diff --git a/lib/silcclient/silcclient.h b/lib/silcclient/silcclient.h index 88370db6..928650c7 100644 --- a/lib/silcclient/silcclient.h +++ b/lib/silcclient/silcclient.h @@ -254,6 +254,28 @@ typedef enum { } SilcClientMessageType; /***/ +/****d* silcclient/SilcClientAPI/SilcClientConnectionStatus + * + * NAME + * + * typedef enum { ... } SilcClientConnectionStatus + * + * DESCRIPTION + * + * This type is returned to the `connect' client operation to indicate + * the status of the created connection. It can indicated if it was + * successful or whether an error occurred. + * + * SOURCE + */ +typedef enum { + SILC_CLIENT_CONN_SUCCESS, /* Successfully connected */ + SILC_CLIENT_CONN_SUCCESS_RESUME, /* Successfully connected and + resumed old detached session */ + SILC_CLIENT_CONN_ERROR, /* Error occurred during connecting */ +} SilcClientConnectionStatus; +/***/ + /****s* silcclient/SilcClientAPI/SilcClientOperations * * NAME @@ -346,9 +368,11 @@ typedef struct { /* Called to indicate that connection was either successfully established or connecting failed. This is also the first time application receives the SilcClientConnection object which it should save somewhere. - If the `success' is FALSE the application must always call the function + The `status' indicated whether the connection were successful. If it + is error value the application must always call the function silc_client_close_connection. */ - void (*connect)(SilcClient client, SilcClientConnection conn, int success); + void (*connect)(SilcClient client, SilcClientConnection conn, + SilcClientConnectionStatus status); /* Called to indicate that connection was disconnected to the server. */ void (*disconnect)(SilcClient client, SilcClientConnection conn); diff --git a/lib/silccore/silccommand.h b/lib/silccore/silccommand.h index a7842622..531f1935 100644 --- a/lib/silccore/silccommand.h +++ b/lib/silccore/silccommand.h @@ -144,6 +144,7 @@ typedef unsigned char SilcCommand; #define SILC_COMMAND_CUMODE 18 #define SILC_COMMAND_KICK 19 #define SILC_COMMAND_BAN 20 +#define SILC_COMMAND_DETACH 21 #define SILC_COMMAND_SILCOPER 23 #define SILC_COMMAND_LEAVE 24 #define SILC_COMMAND_USERS 25 diff --git a/lib/silccore/silcmode.h b/lib/silccore/silcmode.h index 63b0c2da..a56d5b4a 100644 --- a/lib/silccore/silcmode.h +++ b/lib/silccore/silcmode.h @@ -90,6 +90,7 @@ #define SILC_UMODE_ROBOT 0x00000080 /* Client is a robot */ #define SILC_UMODE_ANONYMOUS 0x00000100 /* Client is anonymous */ #define SILC_UMODE_BLOCK_PRIVMSG 0x00000200 /* Client blocks privmsgs */ +#define SILC_UMODE_DETACHED 0x00000400 /* Client is detached */ /***/ #endif diff --git a/lib/silccore/silcpacket.h b/lib/silccore/silcpacket.h index ea7dbbfb..a1e64a88 100644 --- a/lib/silccore/silcpacket.h +++ b/lib/silccore/silcpacket.h @@ -100,6 +100,7 @@ typedef unsigned char SilcPacketType; #define SILC_PACKET_KEY_AGREEMENT 25 /* Key Agreement request */ #define SILC_PACKET_RESUME_ROUTER 26 /* Backup router resume */ #define SILC_PACKET_FTP 27 /* File Transfer */ +#define SILC_PACKET_RESUME_CLIENT 28 /* Client resume */ #define SILC_PACKET_PRIVATE 200 /* Private range start */ #define SILC_PACKET_MAX 255 /* RESERVED */ -- 2.24.0