Support for SILC 1.2 INVITE and BAN commands. Client supports
[silc.git] / apps / silcd / packet_receive.c
index bb778149996cdef8143acaa207efbcb2d37d436c..fbb5a9d0badb8c9c2740340781030b1223050572 100644 (file)
@@ -612,7 +612,8 @@ void silc_server_notify(SilcServer server,
        if (channel->founder_key)
          silc_pkcs_public_key_free(channel->founder_key);
        channel->founder_key = NULL;
-       silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
+       silc_pkcs_public_key_payload_decode(tmp, tmp_len,
+                                           &channel->founder_key);
       }
 
       break;
@@ -718,7 +719,7 @@ void silc_server_notify(SilcServer server,
       if (channel->founder_key)
        silc_pkcs_public_key_free(channel->founder_key);
       channel->founder_key = NULL;
-      silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
+      silc_pkcs_public_key_payload_decode(tmp, tmp_len, &channel->founder_key);
 
       if (!channel->founder_key || 
          (client && client->data.public_key && 
@@ -938,8 +939,8 @@ void silc_server_notify(SilcServer server,
        if (channel->founder_key) {
          /* Get public key that must be present in notify */
          tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
-         if (!tmp || !silc_pkcs_public_key_decode(tmp, tmp_len,
-                                                  &founder_key)) {
+         if (!tmp || !silc_pkcs_public_key_payload_decode(tmp, tmp_len,
+                                                          &founder_key)) {
            chl->mode = mode &= ~SILC_CHANNEL_UMODE_CHANFO;
            silc_server_force_cumode_change(server, sock, channel, chl, mode);
            notify_sent = TRUE;
@@ -948,6 +949,7 @@ void silc_server_notify(SilcServer server,
 
          /* Now match the public key we have cached and public key sent.
             They must match. */
+#if 0 /* The key may be other than the client's in 1.2 */
          if (client && client->data.public_key && 
              !silc_pkcs_public_key_compare(channel->founder_key,
                                            client->data.public_key)) {
@@ -956,6 +958,7 @@ void silc_server_notify(SilcServer server,
            notify_sent = TRUE;
            break;
          }
+#endif
          if (!silc_pkcs_public_key_compare(channel->founder_key,
                                            founder_key)) {
            chl->mode = mode &= ~SILC_CHANNEL_UMODE_CHANFO;
@@ -1064,46 +1067,33 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
 
-    /* Get the added invite */
+    /* Get the invite action */
     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
-    if (tmp) {
-      if (!channel->invite_list)
-       channel->invite_list = silc_calloc(tmp_len + 2, 
-                                          sizeof(*channel->invite_list));
-      else
-       channel->invite_list = silc_realloc(channel->invite_list, 
-                                           sizeof(*channel->invite_list) * 
-                                           (tmp_len + 
-                                            strlen(channel->invite_list) + 
-                                            2));
-      if (tmp[tmp_len - 1] == ',')
-       tmp[tmp_len - 1] = '\0';
-      
-      strncat(channel->invite_list, tmp, tmp_len);
-      strncat(channel->invite_list, ",", 1);
-    }
+    if (tmp && tmp_len == 1) {
+      SilcUInt8 action = (SilcUInt8)tmp[0];
+      SilcUInt16 iargc = 0;
+      SilcArgumentPayload iargs;
 
-    /* Get the deleted invite */
-    tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
-    if (tmp && channel->invite_list) {
-      char *start, *end, *n;
-      
-      if (!strncmp(channel->invite_list, tmp, 
-                  strlen(channel->invite_list) - 1)) {
-       silc_free(channel->invite_list);
-       channel->invite_list = NULL;
-      } else {
-       start = strstr(channel->invite_list, tmp);
-       if (start && strlen(start) >= tmp_len) {
-         end = start + tmp_len;
-         n = silc_calloc(strlen(channel->invite_list) - tmp_len, sizeof(*n));
-         strncat(n, channel->invite_list, start - channel->invite_list);
-         strncat(n, end + 1, ((channel->invite_list + 
-                               strlen(channel->invite_list)) - end) - 1);
-         silc_free(channel->invite_list);
-         channel->invite_list = n;
-       }
-      }
+      /* Get invite list */
+      tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+      if (!tmp)
+       goto out;
+
+      /* Parse the arguments to see they are constructed correctly */
+      SILC_GET16_MSB(iargc, tmp);
+      iargs = silc_argument_payload_parse(tmp + 2, tmp_len - 2, iargc);
+      if (!iargs)
+       goto out;
+
+      if (action == 0 && !channel->invite_list)
+       channel->invite_list = silc_hash_table_alloc(0, silc_hash_ptr,
+                                                    NULL, NULL, NULL,
+                                                    NULL, NULL, TRUE);
+
+      /* Proces the invite action */
+      silc_server_inviteban_process(server, channel->invite_list, action,
+                                   iargs);
+      silc_argument_payload_free(iargs);
     }
 
     break;
@@ -1639,41 +1629,33 @@ void silc_server_notify(SilcServer server,
     }
     silc_free(channel_id);
 
-    /* Get the new ban and add it to the ban list */
+    /* Get the ban action */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (tmp) {
-      if (!channel->ban_list)
-       channel->ban_list = silc_calloc(tmp_len + 2, 
-                                       sizeof(*channel->ban_list));
-      else
-       channel->ban_list = silc_realloc(channel->ban_list, 
-                                        sizeof(*channel->ban_list) * 
-                                        (tmp_len + 
-                                         strlen(channel->ban_list) + 2));
-      strncat(channel->ban_list, tmp, tmp_len);
-      strncat(channel->ban_list, ",", 1);
-    }
+    if (tmp && tmp_len == 1) {
+      SilcUInt8 action = (SilcUInt8)tmp[0];
+      SilcUInt16 iargc = 0;
+      SilcArgumentPayload iargs;
 
-    /* Get the ban to be removed and remove it from the list */
-    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (tmp && channel->ban_list) {
-      char *start, *end, *n;
-      
-      if (!strncmp(channel->ban_list, tmp, strlen(channel->ban_list) - 1)) {
-       silc_free(channel->ban_list);
-       channel->ban_list = NULL;
-      } else {
-       start = strstr(channel->ban_list, tmp);
-       if (start && strlen(start) >= tmp_len) {
-         end = start + tmp_len;
-         n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
-         strncat(n, channel->ban_list, start - channel->ban_list);
-         strncat(n, end + 1, ((channel->ban_list + 
-                               strlen(channel->ban_list)) - end) - 1);
-         silc_free(channel->ban_list);
-         channel->ban_list = n;
-       }
-      }
+      /* Get ban list */
+      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+      if (!tmp)
+       goto out;
+
+      /* Parse the arguments to see they are constructed correctly */
+      SILC_GET16_MSB(iargc, tmp);
+      iargs = silc_argument_payload_parse(tmp + 2, tmp_len - 2, iargc);
+      if (!iargs)
+       goto out;
+
+      if (action == 0 && !channel->ban_list)
+       channel->ban_list = silc_hash_table_alloc(0, silc_hash_ptr,
+                                                 NULL, NULL, NULL,
+                                                 NULL, NULL, TRUE);
+
+      /* Proces the ban action */
+      silc_server_inviteban_process(server, channel->ban_list, action,
+                                   iargs);
+      silc_argument_payload_free(iargs);
     }
     break;
 
@@ -2318,7 +2300,7 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   idata->status |= SILC_IDLIST_STATUS_REGISTERED;
   client->nickname = nickname;
   client->username = username;
-  client->userinfo = realname ? realname : strdup(" ");
+  client->userinfo = realname ? realname : strdup(username);
   client->id = client_id;
   id_len = silc_id_get_len(client_id, SILC_ID_CLIENT);
 
@@ -2860,9 +2842,10 @@ void silc_server_new_channel(SilcServer server,
   char *channel_name;
   SilcUInt32 name_len;
   unsigned char *id;
-  SilcUInt32 id_len;
+  SilcUInt32 id_len, cipher_len;
   SilcServerEntry server_entry;
   SilcChannelEntry channel;
+  const char *cipher;
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
       packet->src_id_type != SILC_ID_SERVER ||
@@ -2983,10 +2966,10 @@ void silc_server_new_channel(SilcServer server,
       /* Send the new channel key to the server */
       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
       id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+      cipher = silc_cipher_get_name(channel->channel_key);
+      cipher_len = strlen(cipher);
       chk = silc_channel_key_payload_encode(id_len, id,
-                                           strlen(channel->channel_key->
-                                                  cipher->name),
-                                           channel->channel_key->cipher->name,
+                                           cipher_len, cipher,
                                            channel->key_len / 8, 
                                            channel->key);
       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
@@ -3049,11 +3032,10 @@ void silc_server_new_channel(SilcServer server,
        /* Send to the server */
        id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
        id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+       cipher = silc_cipher_get_name(channel->channel_key);
+       cipher_len = strlen(cipher);
        chk = silc_channel_key_payload_encode(id_len, id,
-                                             strlen(channel->channel_key->
-                                                    cipher->name),
-                                             channel->channel_key->
-                                             cipher->name,
+                                             cipher_len, cipher,
                                              channel->key_len / 8, 
                                              channel->key);
        silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
@@ -3064,10 +3046,6 @@ void silc_server_new_channel(SilcServer server,
 
       silc_free(channel_id);
 
-      /* Update statistics */
-      server->stat.channels++;
-      server->stat.cell_channels++;
-
       /* Since the channel is coming from server and we also know about it
         then send the JOIN notify to the server so that it see's our
         users on the channel "joining" the channel. */
@@ -3436,6 +3414,7 @@ void silc_server_resume_client(SilcServer server,
   SilcHashTableList htl;
   SilcChannelClientEntry chl;
   SilcServerResumeResolve r;
+  const char *cipher;
 
   ret = silc_buffer_unformat(buffer,
                             SILC_STR_UI16_NSTRING(&id_string, &id_len),
@@ -3630,6 +3609,7 @@ void silc_server_resume_client(SilcServer server,
     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->data.status |= SILC_IDLIST_STATUS_LOCAL;
     detached_client->data.status &= ~SILC_IDLIST_STATUS_RESUME_RES;
     detached_client->mode &= ~SILC_UMODE_DETACHED;
     server->stat.my_detached--;
@@ -3646,6 +3626,9 @@ void silc_server_resume_client(SilcServer server,
     silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
                            SILC_PACKET_RESUME_CLIENT, 0, 
                            buf->data, buf->len, TRUE);
+    silc_server_backup_send(server, detached_client->router,
+                           SILC_PACKET_RESUME_CLIENT, 0,
+                           buf->data, buf->len, TRUE, TRUE);
 
     /* As router we must deliver this packet directly to the original
        server whom this client was earlier. */
@@ -3668,6 +3651,8 @@ void silc_server_resume_client(SilcServer server,
     if (!silc_idlist_del_client(server->local_list, client))
       silc_idlist_del_client(server->global_list, client);
     client = detached_client;
+    silc_free(client->servername);
+    client->servername = strdup(server->server_name);
 
     /* 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)) {
@@ -3764,24 +3749,27 @@ void silc_server_resume_client(SilcServer server,
       }
 
       id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+      cipher = silc_cipher_get_name(channel->channel_key);
       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,
+                                       strlen(cipher), cipher,
                                        channel->key_len / 8, channel->key);
       silc_free(id_string);
 
-      /* Send the key packet to client */
+      /* Send the channel key to the client */
       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
                              keyp->data, keyp->len, FALSE);
 
-      if (created && server->server_type == SILC_SERVER)
-       silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server), 
-                               SILC_PACKET_CHANNEL_KEY, 0, 
-                               keyp->data, keyp->len, FALSE);
+      /* Distribute the channel key to channel */
+      if (created) {
+       silc_server_send_channel_key(server, NULL, channel,
+                                    server->server_type == SILC_ROUTER ? 
+                                    FALSE : !server->standalone);
+       silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
+                               keyp->data, keyp->len, FALSE, TRUE);
+      }
 
       silc_buffer_free(keyp);
     }
@@ -3855,20 +3843,12 @@ void silc_server_resume_client(SilcServer server,
 
     /* Client is detached, and now it is resumed.  Remove the detached
        mode and mark that it is resumed. */
+    silc_idlist_del_data(detached_client);
     detached_client->mode &= ~SILC_UMODE_DETACHED;
     detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
     detached_client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
     id_cache->expire = 0;
 
-    /* Update channel information regarding global clients on channel. */
-    if (server->server_type == SILC_SERVER) {
-      silc_hash_table_list(detached_client->channels, &htl);
-      while (silc_hash_table_get(&htl, NULL, (void **)&chl))
-       chl->channel->global_users = 
-         silc_server_channel_has_global(chl->channel);
-      silc_hash_table_list_reset(&htl);
-    }
-
     silc_schedule_task_del_by_context(server->schedule, detached_client);
 
     /* Get the new owner of the resumed client */
@@ -3895,7 +3875,7 @@ void silc_server_resume_client(SilcServer server,
     }
 
     if (server->server_type == SILC_ROUTER &&
-       sock->type == SILC_SOCKET_TYPE_ROUTER && 
+       sock->type == SILC_SOCKET_TYPE_ROUTER &&
        server_entry->server_type == SILC_ROUTER)
       local = FALSE;
 
@@ -3904,17 +3884,27 @@ void silc_server_resume_client(SilcServer server,
                                     detached_client))
       silc_idcache_del_by_context(server->global_list->clients,
                                  detached_client);
-    silc_idcache_add(local && server->server_type == SILC_ROUTER ? 
-                    server->local_list->clients : 
-                    server->global_list->clients, 
+    silc_idcache_add(local && server->server_type == SILC_ROUTER ?
+                    server->local_list->clients :
+                    server->global_list->clients,
                     detached_client->nickname,
                     detached_client->id, detached_client, FALSE, NULL);
 
     /* Change the owner of the client */
     detached_client->router = server_entry;
 
+    /* Update channel information regarding global clients on channel. */
+    if (server->server_type != SILC_ROUTER) {
+      silc_hash_table_list(detached_client->channels, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void **)&chl))
+       chl->channel->global_users = 
+         silc_server_channel_has_global(chl->channel);
+      silc_hash_table_list_reset(&htl);
+    }
+
     silc_free(server_id);
   }
 
   silc_free(client_id);
+    silc_idlist_del_data(detached_client);
 }