updates.
[silc.git] / lib / silcclient / client_notify.c
index 0aa343ee5680cf1f5afa481cdb4b0d07736d6dce..2bd0aeb695d992758fece77a7d64433fdef91c20 100644 (file)
@@ -33,6 +33,7 @@ static void silc_client_notify_by_server_pending(void *context)
 {
   SilcPacketContext *p = (SilcPacketContext *)context;
   silc_client_notify_by_server(p->context, p->sock, p);
+  silc_socket_free(p->sock);
 }
 
 /* Destructor for the pending command callback */
@@ -53,7 +54,7 @@ static void silc_client_notify_by_server_resolve(SilcClient client,
   SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
 
   p->context = (void *)client;
-  p->sock = conn->sock;
+  p->sock = silc_socket_dup(conn->sock);
 
   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
                           1, 3, idp->data, idp->len);
@@ -75,6 +76,7 @@ void silc_client_notify_by_server(SilcClient client,
   SilcNotifyType type;
   SilcArgumentPayload args;
 
+  SilcIDPayload idp;
   SilcClientID *client_id = NULL;
   SilcChannelID *channel_id = NULL;
   SilcClientEntry client_entry;
@@ -83,7 +85,7 @@ void silc_client_notify_by_server(SilcClient client,
   SilcChannelUser chu;
   SilcIDCacheEntry id_cache = NULL;
   unsigned char *tmp;
-  unsigned int tmp_len, mode;
+  uint32 tmp_len, mode;
 
   payload = silc_notify_payload_parse(buffer);
   if (!payload)
@@ -423,15 +425,28 @@ void silc_client_notify_by_server(SilcClient client,
     if (!tmp)
       goto out;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
+    idp = silc_id_payload_parse_data(tmp, tmp_len);
+    if (!idp)
       goto out;
 
     /* Find Client entry */
-    client_entry = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
+    if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
+      client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (!client_id) {
+       silc_id_payload_free(idp);
+       goto out;
+      }
+
+      client_entry = silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry) {
+       silc_id_payload_free(idp);
+       goto out;
+      }
+    } else {
+      client_entry = NULL;
+    }
+
+    silc_id_payload_free(idp);
 
     /* Get the mode */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
@@ -584,7 +599,6 @@ void silc_client_notify_by_server(SilcClient client,
     channel = (SilcChannelEntry)id_cache->context;
 
     /* Free the old ID */
-    silc_free(channel_id);
     silc_free(channel->id);
 
     /* Get the new ID */
@@ -675,9 +689,7 @@ void silc_client_notify_by_server(SilcClient client,
     /* Get comment */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
+    /* Notify application. */
     client->ops->notify(client, conn, type, client_entry, tmp);
 
     if (client_entry != conn->local_entry) {
@@ -700,6 +712,68 @@ void silc_client_notify_by_server(SilcClient client,
 
     break;
     
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    {
+      /*
+       * A server quit the SILC network and some clients must be removed
+       * from channels as they quit as well.
+       */
+      SilcClientEntry *clients = NULL;
+      uint32 clients_count = 0;
+      int i;
+
+      for (i = 1; i < silc_argument_get_arg_num(args); i++) {
+       /* Get Client ID */
+       tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
+       if (tmp) {
+         client_id = silc_id_payload_parse_id(tmp, tmp_len);
+         if (!client_id)
+           goto out;
+         
+         /* Get the client entry */
+         client_entry = silc_client_get_client_by_id(client, conn, client_id);
+         if (client_entry) {
+           clients = silc_realloc(clients, sizeof(*clients) * 
+                                  (clients_count + 1));
+           clients[clients_count] = client_entry;
+           clients_count++;
+         }
+         silc_free(client_id);
+       }
+      }
+      client_id = NULL;
+
+      /* Notify application. We don't keep server entries so the server
+        entry is returned as NULL. The client's are returned as array
+        of SilcClientEntry pointers. */
+      client->ops->notify(client, conn, type, NULL, clients, clients_count);
+
+      for (i = 0; i < clients_count; i++) {
+       /* Remove client from all channels */
+       client_entry = clients[i];
+       if (client_entry == conn->local_entry)
+         continue;
+
+       silc_client_remove_from_channels(client, conn, client_entry);
+       silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
+                              client_entry->id);
+       if (client_entry->nickname)
+         silc_free(client_entry->nickname);
+       if (client_entry->server)
+         silc_free(client_entry->server);
+       if (client_entry->id)
+         silc_free(client_entry->id);
+       if (client_entry->send_key)
+         silc_cipher_free(client_entry->send_key);
+       if (client_entry->receive_key)
+         silc_cipher_free(client_entry->receive_key);
+       silc_free(client_entry);
+      }
+      silc_free(clients);
+
+    }
+    break;
+
   default:
     break;
   }