updates.
authorPekka Riikonen <priikone@silcnet.org>
Thu, 11 Apr 2002 13:36:05 +0000 (13:36 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 11 Apr 2002 13:36:05 +0000 (13:36 +0000)
34 files changed:
CHANGES
TODO
apps/irssi/src/silc/core/client_ops.c
apps/irssi/src/silc/core/client_ops.h
apps/irssi/src/silc/core/silc-servers.c
apps/silcd/command.c
apps/silcd/command.h
apps/silcd/command_reply.c
apps/silcd/idlist.h
apps/silcd/packet_receive.c
apps/silcd/packet_receive.h
apps/silcd/packet_send.c
apps/silcd/packet_send.h
apps/silcd/server.c
apps/silcd/server_util.c
apps/silcd/server_util.h
doc/draft-riikonen-silc-commands-03.nroff
doc/draft-riikonen-silc-pp-05.nroff
doc/draft-riikonen-silc-spec-05.nroff
lib/silcclient/Makefile.am
lib/silcclient/client.c
lib/silcclient/client_internal.h
lib/silcclient/client_notify.c
lib/silcclient/client_ops_example.c
lib/silcclient/client_resume.c [new file with mode: 0644]
lib/silcclient/command.c
lib/silcclient/command.h
lib/silcclient/command_reply.c
lib/silcclient/command_reply.h
lib/silcclient/idlist.c
lib/silcclient/silcclient.h
lib/silccore/silccommand.h
lib/silccore/silcmode.h
lib/silccore/silcpacket.h

diff --git a/CHANGES b/CHANGES
index 65a89453e38becd6aa2dd263851af40180a321c7..3a357a62b00f83e4108f0c597081f05acb80cb77 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,32 @@
+Thu Apr 11 16:32:08 EEST 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * 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 <priikone@silcnet.org>
+
+       * 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 <priikone@silcnet.org>
 
        * Added new user modes ANONYMOUS for special anonymous servers
diff --git a/TODO b/TODO
index 25f39441d4899195b8aee07e0236262e96d7ba0f..afa8ad7445eb2783a697ae0979c0098f038b241b 100644 (file)
--- 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.
 
index 35c9a64e1e94d325e29d7c74c98312d82bdfd292..b4f71907dd2d8a07d499755ef17cf8d1476d4bf3 100644 (file)
@@ -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);
 }
 
 
index 505b63437f9ad0f4feee353c6a8158f8b30775ba..60fda7ae1b9452001b67f9088a92738519e2614f 100644 (file)
@@ -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);
index e92ae9ba7ded428b2c93c5f863dd2a712ce199e3..fdcbd431f52320990d0057aec74901ece9f0baea 100644 (file)
@@ -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(&params, 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, &params.detach_data_len);
+
+  /* Add connection to the client library */
+  conn = silc_client_add_connection(silc_client, &params,
                                    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 [<nickname>] */
 /* SYNTAX: FILE */
 /* SYNTAX: JOIN <channel> [<passphrase>] [-cipher <cipher>] [-hmac <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,
index 34f8ee292ba38483ee879e57c526af588def64a9..afb9e5394e9f8c6e8e789113d783209bd9951dbe 100644 (file)
@@ -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. */
 
index b8441b7f11329e06286c2b3398c21272c035c710..229a37597f81fab7f037398662907e168c37df97 100644 (file)
@@ -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);
index bd4052a47e619e037e1c0c1b76e0006c54014339..ccb425f00d51b480495018e18a811caa1b1aecba 100644 (file)
@@ -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;
   }
index 30fbd3a15e6470bc418e8abb58a14b9e1d7b1bb8..888e509b804a2ae3ad54514046664807552f3106 100644 (file)
@@ -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.
index 1a1be10d11fd3ceadf491f74c762d5ea2ac04cc3..29752416068c1b32778e492cfe1fc2561a041ca0 100644 (file)
@@ -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);
+}
index 0aed4ca934ff71230f655e1fddf9e7e45b8fd270..b28d3de498a0197bc4d14a2258d688fbd6e625d4 100644 (file)
@@ -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
index ab5b6219e96f24d9c07f432b06bbbb6a4a100b31..019df0699a68c825613d772a9ab75ff9b2794593 100644 (file)
@@ -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,
index 17dc349e1a7601002da0b587584830f7d0642290..c40c888ca1a2c1aaa5cc2d08727ecb5c78625934 100644 (file)
@@ -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,
index eb2c0a95340812b9662662235dbaf7e4c221cfca..bb633a65ecd6c4839e404b2f6153dd013f1450c4 100644 (file)
@@ -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);
index 5e9bdbd7c41d6b9db8c8f74ad6d4e0f85a6b8f66..2fe705f32b5c10064f8f541f15207b2a56552e49 100644 (file)
@@ -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);
+}
index 1545c1fd72053cdc88080114a4fa76326ec02340..5a531037f7e6dfddba77861227531daee756e702 100644 (file)
@@ -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 */
index edc4cdbbfa25d1c10617430ee3e6f4cefe4a0ec6..c915a498d4965d017a492a6ef0a0afc6f4d24556 100644 (file)
@@ -284,11 +284,12 @@ List of all defined commands in SILC follows.
         <count> option were defined in the query there will be only
         <count> 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 <fingerprint> is the
@@ -965,7 +966,7 @@ List of all defined commands in SILC follows.
    16   SILC_COMMAND_UMODE
 
         Max Arguments:  2
-            Arguments:  (1) <Client ID>  (2) <client mode mask>
+            Arguments:  (1) <Client ID>  (2) [<client mode mask>]
 
         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 <client mode mask> was not provided this command merely
+        returns the mode mask to the client.
+
+
         Reply messages to the command:
 
         Max Arguments:  2
index 4c35ed82b3f527f6b4538c9123401bc7e8dd3416..b991f5a263e7f729e12ef81991c429b6cf9720d5 100644 (file)
@@ -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
index 72369b8def050fc6e12d0344f96442d492d2fa16..e8ee48427382e80bc2a9dc68f862e696239eae76 100644 (file)
@@ -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
 
index b4ec607b1a67ed9656675ce356d11e087ed31e39..68d9f9d7d5356d58e4f87e97b45423fc704f5dc1 100644 (file)
@@ -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 \
index efcc29dad86fe5028a8f22e83f6bf0cbee3cde03..2619df2671b66c5d6e6b82c61bb2948f21dbcd41 100644 (file)
@@ -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;
-}
index 7de59e7f76df2679ee0f8cd8acb593a2beb735e9..be5c05b168efa490478f25d77756f550123888b2 100644 (file)
@@ -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
index 24e8854eb3173e3381cc466faeb796dc735628f7..1e7778583f6e864b8ab307ff57f2484d4bf980c9 100644 (file)
@@ -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 */
index 5b5c1e3ab958370ea5a29d6977db77e99b396239..6662e8550d1c84dcacae20570f953ab73854dcd7 100644 (file)
@@ -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 (file)
index 0000000..0d0d24b
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+
+  client_resume.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  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);
+}
index fa0bfa633d8414828223d9658839023c45cb11a4..f04e4510aaff84f3c8d490c76b0bb5cbe4bf70df 100644 (file)
@@ -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");
index 0b8949e15132da54cb230e7e20face8ce73f0354..33479ec86062e51c6f3ec9237548ed2e4cc6c0a9 100644 (file)
@@ -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
index 097bdc093ec6266d227895d1eccfa3cbcf4fdd24..85609d337d0db7c21173c9f91208d93c359e9d22 100644 (file)
@@ -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);
 }
-
index 1d1d20834522dcbe1a14a84ab71833a4d9f77cdc..782b0aa244c2f106087641f8deed76ac454b979b 100644 (file)
@@ -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
index 7549a62ddfb12826517b00548674820306533f36..9f21b8a5fd06908b6ba18d4d8e021998fdaf6412 100644 (file)
@@ -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) {
index 88370db685a77b071ef8978f547fd40737278cd4..928650c788e8bfff98e8dd81c6b9bbec974bf0ad 100644 (file)
@@ -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);
index a7842622b271e56296f4e61455fdc7f7fea8de23..531f1935aa114e9c66b939c02db1d810fad226a9 100644 (file)
@@ -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
index 63b0c2da7373692fc9811133ecd128fa45a93a24..a56d5b4a5241024d4741d447f209f85acda12c58 100644 (file)
@@ -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
index ea7dbbfb9d792b0dd42a91aa73c8ef17450a7c6c..a1e64a88c60fd3bc20a90931090ac3d5d2855d20 100644 (file)
@@ -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 */