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
 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?
 
 
  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.
 
  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);
       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);
       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);
                         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,
       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);
                         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,
       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_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) {
   
     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 = 
     } 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);
       }
 
        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;
 
     }
     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. */
 
    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;
 
 {
   SILC_SERVER_REC *server = conn->context;
 
-  if (!server && !success) {
+  if (!server && status == SILC_CLIENT_CONN_ERROR) {
     silc_client_close_connection(client, conn);
     return;
   }
 
     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);
     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));
     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);
   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,
 
   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)
                 "SILC Operator" : "[Unknown mode]");
        }
        if (mode & SILC_UMODE_GONE)
-         strcat(buf, " away");
+         strcat(buf, " [away]");
        if (mode & SILC_UMODE_INDISPOSED)
        if (mode & SILC_UMODE_INDISPOSED)
-         strcat(buf, " indisposed");
+         strcat(buf, " [indisposed]");
        if (mode & SILC_UMODE_BUSY)
        if (mode & SILC_UMODE_BUSY)
-         strcat(buf, " busy");
+         strcat(buf, " [busy]");
        if (mode & SILC_UMODE_PAGE)
        if (mode & SILC_UMODE_PAGE)
-         strcat(buf, " page to reach");
+         strcat(buf, " [page to reach]");
        if (mode & SILC_UMODE_HYPER)
        if (mode & SILC_UMODE_HYPER)
-         strcat(buf, " hyper active");
+         strcat(buf, " [hyper active]");
        if (mode & SILC_UMODE_ROBOT)
        if (mode & SILC_UMODE_ROBOT)
-         strcat(buf, " robot");
+         strcat(buf, " [robot]");
        if (mode & SILC_UMODE_ANONYMOUS)
        if (mode & SILC_UMODE_ANONYMOUS)
-         strcat(buf, " anonymous");
+         strcat(buf, " [anonymous]");
        if (mode & SILC_UMODE_BLOCK_PRIVMSG)
        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);
 
        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)
 {
 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_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);
 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);
   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;
     return;
-  
+  }
+
   silc_client_send_channel_message(silc_client, server->conn, rec->entry, 
                                   NULL, 0, msg, strlen(msg), TRUE);
 }
   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;
 static void sig_connected(SILC_SERVER_REC *server)
 {
   SilcClientConnection conn;
+  SilcClientConnectionParams params;
+  char file[256];
   int fd;
 
   if (!IS_SILC_SERVER(server))
     return;
 
   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;
                                    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));
   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();
   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: 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)
 
 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("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");
 }
 
   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("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,
 }
 
 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(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),
   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 {                                                                         \
    of arguments. */
 #define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
 do {                                                                         \
-  SilcUInt32 _argc;                                                                  \
+  SilcUInt32 _argc;                                                          \
                                                                              \
   SILC_LOG_DEBUG(("Start"));                                                 \
                                                                              \
                                                                              \
   SILC_LOG_DEBUG(("Start"));                                                 \
                                                                              \
@@ -2013,6 +2014,11 @@ SILC_SERVER_CMD_FUNC(nick)
                                   cmd->server->md5hash, nick,
                                   &new_id)) {
     nickfail++;
                                   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);
   }
 
     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;
 
   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;
   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,
 
   /* 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);
 
  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;
   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);
   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);
 
   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);
 
  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);
 
   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,
                                           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;
   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);
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  bool set_mask = FALSE;
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
 
   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);
 
   /* 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;
     }
       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 */
 
   /* 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,
   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);
   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_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. */
 
 /* 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(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);
 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;
     }
 
     client->data.public_key = public_key;
+    public_key = NULL;
   } else if (id_type == SILC_ID_SERVER) {
     server_id = silc_id_payload_get_id(idp);
 
   } 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;
     }
 
     server_entry->data.public_key = public_key;
+    public_key = NULL;
   } else {
     goto out;
   }
   } 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_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.
 
 /*
    Generic ID list data structure.
index 1a1be10d11fd3ceadf491f74c762d5ea2ac04cc3..29752416068c1b32778e492cfe1fc2561a041ca0 100644 (file)
@@ -25,8 +25,6 @@
 #include "serverincludes.h"
 #include "server_internal.h"
 
 #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. */
 
 /* 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) {
       /* 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 (!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) {
                                                  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;
     }
 
       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;
 
     /* 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) {
                                          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 */
     /* 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;
 
     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++;
   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);
   }
 
     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);
   
   /* 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),
   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_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;
 }
 
   return client;
 }
@@ -2849,3 +2811,372 @@ void silc_server_ftp(SilcServer server,
                           idata->hmac_send, idata->psn_send++,
                           packet, FALSE);
 }
                           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_ftp(SilcServer server,
                     SilcSocketConnection sock,
                     SilcPacketContext *packet);
+void silc_server_resume_client(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet);
 
 #endif
 
 #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;
 
     /* 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);
     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,
                                    SilcSocketConnection sock,
                                    bool broadcast,
                                    SilcClientID *client_id,
-                                   char *comment,
-                                   SilcClientID *killer)
+                                   const char *comment,
+                                   void *killer, SilcIdType killer_type)
 {
   SilcBuffer idp1;
   SilcBuffer idp2;
 
 {
   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,
   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,
                                    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,
 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;
 
     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. */
   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;
                         (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->router = NULL;
   client->connection = NULL;
-  client->mode = 0;
 }
 
 /* Frees user_data pointer from socket connection object. This also sends
 }
 
 /* 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"));
 
 
   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);
   /* 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);
   }
 
     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);
   /* 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"
 
 #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
 /* 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;
 }
 
   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);
 
                                    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 */
 #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.
 
         <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
 
         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
    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,
 
         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.
 
 
               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
         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.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
   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 21:  Key Agreement Payload
 Figure 22:  Resume Router Payload
 Figure 23:  File Transfer Payload
+Figure 24:  Resume Client Payload
 
 
 .ti 0
 
 
 .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
 
 
           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.
 
 
           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
 
 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
 
 .in 5
 .nf
@@ -2392,6 +2409,64 @@ o Data (variable length) - Arbitrary file transfer data.  The
 .in 3
 
 
 .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
 
 
 .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.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
 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.
 
 
 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
 
 .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_prvmsg.c \
        client_channel.c \
        client_ftp.c    \
+       client_resume.c \
        command.c \
        command_reply.c \
        idlist.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->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;
     return;
   }
   conn->sock->protocol = protocol;
@@ -465,7 +465,8 @@ SILC_TASK_CALLBACK(silc_client_connect_failure)
     (SilcClientKEInternalContext *)context;
   SilcClient client = (SilcClient)ctx->client;
 
     (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);
   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 */
       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;
       silc_client_del_connection(client, conn);
     }
     return;
@@ -670,26 +671,71 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
     return;
   }
 
     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;
 
   /* 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->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. */
 
 /* 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);
   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;
 
   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) {
                   (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);
 }
                           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;
 };
 
   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
 /* 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);
 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
 
 #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);
       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;
 
          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 {
        } 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, 
        }
       }
 
       /* 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 */
 
       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_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 */
   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);
   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);
 }
 
   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)
 /* 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(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);
   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(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");
   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(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);
 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(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(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
 #endif
index 097bdc093ec6266d227895d1eccfa3cbcf4fdd24..85609d337d0db7c21173c9f91208d93c359e9d22 100644 (file)
@@ -79,16 +79,6 @@ const SilcCommandStatusMessage silc_command_status_messages[] = {
 
   { 0, NULL }
 };
 
   { 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
 
 
 #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;
   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;
   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;
   
   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)
   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);
   }
 
   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)
 
   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, 
   /* 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
 }
 
 /* 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;
   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;
   
   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)
   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_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;
 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);
 }
 
   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;
   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;
   unsigned char *tmp;
   SilcUInt32 tmp_len, list_count;
   int i;
@@ -1512,13 +1520,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
 
   SILC_LOG_DEBUG(("Start"));
 
 
   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) {
   /* 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;
   }
     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);
 
   /* 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;
   }
     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,
 
   /* 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);
     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. */
   }
 
   /* Cache the received Client ID's and modes. */
@@ -1583,22 +1573,22 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
     SilcClientID *client_id;
 
     /* Client ID */
     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;
     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 */
     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) {
 
     /* 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;
          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_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++;
       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_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
   }
 
   /* 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,
        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);
 
     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 */
 
   /* 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);
 
  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
 }
 
 /* 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);
 }
 
   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
 
 /* Private range commands, specific to this implementation (and compatible
-   with SILC Server). */
+   with SILC Server >= 0.9). */
 
 SILC_CLIENT_CMD_REPLY_FUNC(connect)
 {
 
 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);
 }
   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 */
 
 
 /* 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)
 /* 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);
                                       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);
 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(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(stats);
-SILC_CLIENT_CMD_REPLY_FUNC(users);
-SILC_CLIENT_CMD_REPLY_FUNC(connect);
 SILC_CLIENT_CMD_REPLY_FUNC(ping);
 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);
 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(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);
 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(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
 
 #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;
     SilcUInt16 idp_len;
     SilcClientID *client_id;
     SilcClientEntry entry;
+    bool ret;
 
     /* Get Client ID */
     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
 
     /* 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. */
     }
 
     /* 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. */
 
     /* 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) {
 
       if (entry) {
        if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
index 88370db685a77b071ef8978f547fd40737278cd4..928650c788e8bfff98e8dd81c6b9bbec974bf0ad 100644 (file)
@@ -254,6 +254,28 @@ typedef enum {
 } SilcClientMessageType;
 /***/
 
 } 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
 /****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.
   /* 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. */
      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);
 
   /* 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_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
 #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_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
 /***/
 
 #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_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 */
 
 #define SILC_PACKET_PRIVATE              200     /* Private range start  */
 #define SILC_PACKET_MAX                  255     /* RESERVED */