updates.
[silc.git] / apps / silcd / server.c
index 7593e7033b55b7afe9e4533c9b34fa5bb492e6be..4dde2b3669afbcfe10142922138560469e23b234 100644 (file)
@@ -267,30 +267,20 @@ int silc_server_init(SilcServer server)
     server->id_entry = id_entry;
   }
 
-  /* Register the task queues. In SILC we have by default three task queues. 
-     One task queue for non-timeout tasks which perform different kind of 
-     I/O on file descriptors, timeout task queue for timeout tasks, and,
-     generic non-timeout task queue whose tasks apply to all connections. */
-  silc_task_queue_alloc(&server->io_queue, TRUE);
-  if (!server->io_queue) {
-    goto err0;
-  }
-  silc_task_queue_alloc(&server->timeout_queue, TRUE);
-  if (!server->timeout_queue) {
-    goto err1;
-  }
-  silc_task_queue_alloc(&server->generic_queue, TRUE);
-  if (!server->generic_queue) {
-    goto err1;
-  }
-
   /* Register protocols */
   silc_server_protocols_register();
 
-  /* Initialize the scheduler */
-  silc_schedule_init(&server->io_queue, &server->timeout_queue, 
-                    &server->generic_queue, 
-                    SILC_SERVER_MAX_CONNECTIONS);
+  /* Initialize the scheduler. This will register the task queues as well.
+     In SILC we have by default three task queues. One task queue for
+     non-timeout tasks which perform different kind of I/O on file
+     descriptors, timeout task queue for timeout tasks, and, generic
+     non-timeout task queue whose tasks apply to all connections. */
+  server->schedule = silc_schedule_init(&server->io_queue, 
+                                       &server->timeout_queue, 
+                                       &server->generic_queue, 
+                                       SILC_SERVER_MAX_CONNECTIONS);
+  if (!server->schedule)
+    goto err0;
   
   /* Add the first task to the queue. This is task that is executed by
      timeout. It expires as soon as the caller calls silc_server_run. This
@@ -342,9 +332,6 @@ int silc_server_init(SilcServer server)
   /* We are done here, return succesfully */
   return TRUE;
 
-  silc_task_queue_free(server->timeout_queue);
- err1:
-  silc_task_queue_free(server->io_queue);
  err0:
   for (i = 0; i < sock_count; i++)
     silc_net_close_server(sock[i]);
@@ -467,8 +454,8 @@ void silc_server_stop(SilcServer server)
   /* Stop the scheduler, although it might be already stopped. This
      doesn't hurt anyone. This removes all the tasks and task queues,
      as well. */
-  silc_schedule_stop();
-  silc_schedule_uninit();
+  silc_schedule_stop(server->schedule);
+  silc_schedule_uninit(server->schedule);
 
   silc_server_protocols_unregister();
 
@@ -483,9 +470,11 @@ void silc_server_run(SilcServer server)
 {
   SILC_LOG_DEBUG(("Running server"));
 
+  SILC_LOG_INFO(("SILC Server started"));
+
   /* Start the scheduler, the heart of the SILC server. When this returns
      the program will be terminated. */
-  silc_schedule();
+  silc_schedule(server->schedule);
 }
 
 /* Timeout callback that will be called to retry connecting to remote
@@ -1008,6 +997,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      later when outgoing data is available. */
   SILC_REGISTER_CONNECTION_FOR_IO(sock);
 
+  SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
+                newsocket->ip));
+
   port = server->sockets[fd]->port; /* Listenning port */
 
   /* Check whether this connection is denied to connect to us. */
@@ -1017,6 +1009,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
                                          port);
   if (deny) {
     /* The connection is denied */
+    SILC_LOG_INFO(("Connection %s (%s) is denied", 
+                   newsocket->hostname, newsocket->ip));
     silc_server_disconnect_remote(server, newsocket, deny->comment ?
                                  deny->comment :
                                  "Server closed connection: "
@@ -1054,9 +1048,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 
   /* The connection is allowed */
 
-  SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
-                newsocket->ip));
-
   /* Allocate internal context for key exchange protocol. This is
      sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
@@ -1400,7 +1391,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
        back to only for input. When there is again some outgoing data 
        available for this connection it will be set for output as well. 
        This call clears the output setting and sets it only for input. */
-    SILC_SET_CONNECTION_FOR_INPUT(fd);
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
     SILC_UNSET_OUTBUF_PENDING(sock);
 
     silc_buffer_clear(sock->outbuf);
@@ -2076,8 +2067,16 @@ void silc_server_close_connection(SilcServer server,
                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
                  "Router"), sock->sock));
 
+  /* If any protocol is active cancel its execution */
+  if (sock->protocol) {
+    silc_protocol_cancel(sock->protocol, server->timeout_queue);
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->timeout_queue);
+    sock->protocol = NULL;
+  }
+
   /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
+  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
 
   /* Unregister all tasks */
   silc_task_unregister_by_fd(server->io_queue, sock->sock);
@@ -2165,7 +2164,7 @@ void silc_server_free_client_data(SilcServer server,
 
     silc_packet_send(sock, TRUE);
 
-    SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
     SILC_UNSET_OUTBUF_PENDING(sock);
     silc_buffer_clear(sock->outbuf);
   }
@@ -2402,7 +2401,8 @@ int silc_server_remove_clients_by_server(SilcServer server,
     SilcBuffer args;
 
     /* Send SERVER_SIGNOFF notify to our primary router */
-    if (!server->standalone && server->router) {
+    if (!server->standalone && server->router &&
+       server->router != entry) {
       args = silc_argument_payload_encode(1, argv, argv_lens,
                                          argv_types);
       silc_server_send_notify_args(server, 
@@ -2712,14 +2712,23 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = server->sockets[fd];
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (!sock)
     return;
 
+  /* If we have protocol active we must assure that we call the protocol's
+     final callback so that all the memory is freed. */
+  if (sock->protocol) {
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->timeout_queue);
+    return;
+  }
+
   if (sock->user_data)
     silc_server_free_sock_user_data(server, sock);
 
-  silc_server_disconnect_remote(server, sock, 
-                               "Server closed connection: "
+  silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                "Connection timeout");
 }
 
@@ -3283,7 +3292,9 @@ void silc_server_announce_get_channels(SilcServer server,
                                       SilcIDList id_list,
                                       SilcBuffer *channels,
                                       SilcBuffer *channel_users,
-                                      SilcBuffer *channel_users_modes)
+                                      SilcBuffer **channel_users_modes,
+                                      uint32 *channel_users_modes_c,
+                                      SilcChannelID ***channel_ids)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -3292,6 +3303,7 @@ void silc_server_announce_get_channels(SilcServer server,
   uint32 id_len;
   uint16 name_len;
   int len;
+  int i = *channel_users_modes_c;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -3321,15 +3333,24 @@ void silc_server_announce_get_channels(SilcServer server,
                           SILC_STR_END);
        silc_buffer_pull(*channels, len);
 
+       *channel_users_modes = silc_realloc(*channel_users_modes,
+                                           sizeof(**channel_users_modes) * 
+                                           (i + 1));
+       (*channel_users_modes)[i] = NULL;
+       *channel_ids = silc_realloc(*channel_ids, 
+                                   sizeof(**channel_ids) * (i + 1));
+       (*channel_ids)[i] = NULL;
        silc_server_announce_get_channel_users(server, channel,
                                               channel_users,
-                                              channel_users_modes);
-
-       silc_free(cid);
+                                              channel_users_modes[i]);
+       (*channel_ids)[i] = channel->id;
+       i++;
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
       }
+
+      *channel_users_modes_c += i;
     }
 
     silc_idcache_list_free(list);
@@ -3342,19 +3363,26 @@ void silc_server_announce_get_channels(SilcServer server,
 
 void silc_server_announce_channels(SilcServer server)
 {
-  SilcBuffer channels = NULL, channel_users = NULL, channel_users_modes = NULL;
+  SilcBuffer channels = NULL, channel_users = NULL;
+  SilcBuffer *channel_users_modes = NULL;
+  uint32 channel_users_modes_c = 0;
+  SilcChannelID **channel_ids = NULL;
 
   SILC_LOG_DEBUG(("Announcing channels and channel users"));
 
   /* Get channels and channel users in local list */
   silc_server_announce_get_channels(server, server->local_list,
                                    &channels, &channel_users,
-                                   &channel_users_modes);
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_ids);
 
   /* Get channels and channel users in global list */
   silc_server_announce_get_channels(server, server->global_list,
                                    &channels, &channel_users,
-                                   &channel_users_modes);
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_ids);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
@@ -3384,19 +3412,24 @@ void silc_server_announce_channels(SilcServer server)
   }
 
   if (channel_users_modes) {
-    silc_buffer_push(channel_users_modes, 
-                    channel_users_modes->data - channel_users_modes->head);
-    SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes->data, 
-                    channel_users_modes->len);
-
-    /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
-                           channel_users_modes->data, 
-                           channel_users_modes->len,
-                           FALSE);
-
-    silc_buffer_free(channel_users_modes);
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      silc_buffer_push(channel_users_modes[i], 
+                      channel_users_modes[i]->data - 
+                      channel_users_modes[i]->head);
+      SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes[i]->data, 
+                      channel_users_modes[i]->len);
+      silc_server_packet_send_dest(server, server->router->connection,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_users_modes[i]->data, 
+                                  channel_users_modes[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_users_modes[i]);
+    }
+    silc_free(channel_users_modes);
+    silc_free(channel_ids);
   }
 }