Created SILC Client Libary by moving stuff from silc/ directory.
[silc.git] / apps / silcd / server.c
index c17d2c7ffe30f1103c5846f40b61c12777af11d9..e652099db0e49938bd042146594b8e630d364fe6 100644 (file)
 /*
  * $Id$
  * $Log$
+ * Revision 1.13  2000/08/21 14:21:21  priikone
+ *     Fixed channel joining and channel message sending inside a
+ *     SILC cell. Added silc_server_send_remove_channel_user and
+ *     silc_server_remove_channel_user functions.
+ *
+ * Revision 1.12  2000/07/26 07:05:11  priikone
+ *     Fixed the server to server (server to router actually) connections
+ *     and made the private message work inside a cell. Added functin
+ *     silc_server_replace_id.
+ *
+ * Revision 1.11  2000/07/20 10:17:25  priikone
+ *     Added dynamic protocol registering/unregistering support.  The
+ *     patch was provided by cras.
+ *
  * Revision 1.10  2000/07/17 11:47:30  priikone
  *     Added command lagging support. Added idle counting support.
  *
@@ -238,11 +252,14 @@ int silc_server_init(SilcServer server)
   server->local_list->servers = silc_idcache_alloc(0);
   server->local_list->channels = silc_idcache_alloc(0);
 
-  if (server->server_type == SILC_ROUTER) {
-    server->global_list->clients = silc_idcache_alloc(0);
-    server->global_list->servers = silc_idcache_alloc(0);
-    server->global_list->channels = silc_idcache_alloc(0);
-  }
+  /* XXX for now these are allocated for normal server as well as these
+     hold some global information that the server has fetched from its
+     router. For router these are used as they are supposed to be used
+     on router. The XXX can be remoevd later if this is the way we are
+     going to do this in the normal server as well. */
+  server->global_list->clients = silc_idcache_alloc(0);
+  server->global_list->servers = silc_idcache_alloc(0);
+  server->global_list->channels = silc_idcache_alloc(0);
 
   /* Allocate the entire socket list that is used in server. Eventually 
      all connections will have entry in this table (it is a table of 
@@ -315,6 +332,9 @@ int silc_server_init(SilcServer server)
     goto err1;
   }
 
+  /* Register protocols */
+  silc_server_protocols_register();
+
   /* Initialize the scheduler */
   silc_schedule_init(server->io_queue, server->timeout_queue, 
                     server->generic_queue, 
@@ -367,6 +387,8 @@ void silc_server_stop(SilcServer server)
   silc_schedule_stop();
   silc_schedule_uninit();
 
+  silc_server_protocols_unregister();
+
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
@@ -1497,12 +1519,47 @@ void silc_server_packet_parse_type(SilcServer server,
   case SILC_PACKET_NEW_SERVER:
     /*
      * Received new server packet. This includes Server ID and some other
-     * information that we may save. This is after server as connected to us.
+     * information that we may save. This is received after server has 
+     * connected to us.
      */
     SILC_LOG_DEBUG(("New Server packet"));
     silc_server_new_server(server, sock, packet);
     break;
 
+  case SILC_PACKET_NEW_CHANNEL:
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_USER:
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_LIST:
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_USER_LIST:
+    break;
+
+  case SILC_PACKET_REPLACE_ID:
+    /*
+     * Received replace ID packet. This sends the old ID that is to be
+     * replaced with the new one included into the packet. Client must not
+     * send this packet.
+     */
+    SILC_LOG_DEBUG(("Replace ID packet"));
+    silc_server_replace_id(server, sock, packet);
+    break;
+
+  case SILC_PACKET_REMOVE_ID:
+    break;
+
+  case SILC_PACKET_REMOVE_CHANNEL_USER:
+    /*
+     * Received packet to remove user from a channel. Routers notify other
+     * routers about a user leaving a channel.
+     */
+    SILC_LOG_DEBUG(("Remove Channel User packet"));
+    silc_server_remove_channel_user(server, sock, packet);
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -2083,10 +2140,8 @@ void silc_server_packet_relay_command_reply(SilcServer server,
   SILC_LOG_DEBUG(("Start"));
 
   /* Source must be server or router */
-  /* XXX: actually it must be only router */
   if (packet->src_id_type != SILC_ID_SERVER &&
-      (sock->type != SILC_SOCKET_TYPE_SERVER ||
-       sock->type != SILC_SOCKET_TYPE_ROUTER))
+      sock->type != SILC_SOCKET_TYPE_ROUTER)
     goto out;
 
   /* Destination must be client */
@@ -2177,7 +2232,7 @@ void silc_server_disconnect_remote(SilcServer server,
 }
 
 /* Free's user_data pointer from socket connection object. As this 
-   pointer maybe anything we wil switch here to find the corrent
+   pointer maybe anything we wil switch here to find the correct
    data type and free it the way it needs to be free'd. */
 
 void silc_server_free_sock_user_data(SilcServer server, 
@@ -2255,6 +2310,18 @@ void silc_server_remove_from_channels(SilcServer server,
        /* If this client is last one on the channel the channel
           is removed all together. */
        if (channel->user_list_count == 1) {
+
+         /* However, if the channel has marked global users then the 
+            channel is not created locally, and this does not remove the
+            channel globally from SILC network, in this case we will
+            notify that this client has left the channel. */
+         if (channel->global_users)
+           silc_server_send_notify_to_channel(server, channel,
+                                              "Signoff: %s@%s",
+                                              client->nickname,
+                                              sock->hostname ?
+                                              sock->hostname : sock->ip);
+
          silc_idlist_del_channel(server->local_list, channel);
          break;
        }
@@ -2265,8 +2332,10 @@ void silc_server_remove_from_channels(SilcServer server,
        /* Send notify to channel about client leaving SILC and thus
           the entire channel. */
        silc_server_send_notify_to_channel(server, channel,
-                                          "Signoff: %s",
-                                          client->nickname);
+                                          "Signoff: %s@%s",
+                                          client->nickname,
+                                          sock->hostname ?
+                                          sock->hostname : sock->ip);
       }
     }
   }
@@ -2279,12 +2348,14 @@ void silc_server_remove_from_channels(SilcServer server,
 /* Removes client from one channel. This is used for example when client
    calls LEAVE command to remove itself from the channel. Returns TRUE
    if channel still exists and FALSE if the channel is removed when
-   last client leaves the channel. */
+   last client leaves the channel. If `notify' is FALSE notify messages
+   are not sent. */
 
 int silc_server_remove_from_one_channel(SilcServer server, 
                                        SilcSocketConnection sock,
                                        SilcChannelEntry channel,
-                                       SilcClientEntry client)
+                                       SilcClientEntry client,
+                                       int notify)
 {
   int i, k;
   SilcChannelEntry ch;
@@ -2306,6 +2377,16 @@ int silc_server_remove_from_one_channel(SilcServer server,
        /* If this client is last one on the channel the channel
           is removed all together. */
        if (channel->user_list_count == 1) {
+         /* Notify about leaving client if this channel has global users,
+            ie. the channel is not created locally. */
+         if (notify && channel->global_users)
+           silc_server_send_notify_to_channel(server, channel,
+                                              "%s@%s has left channel %s",
+                                              client->nickname, 
+                                              sock->hostname ?
+                                              sock->hostname : sock->ip,
+                                              channel->channel_name);
+
          silc_idlist_del_channel(server->local_list, channel);
          return FALSE;
        }
@@ -2314,10 +2395,12 @@ int silc_server_remove_from_one_channel(SilcServer server,
        channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
 
        /* Send notify to channel about client leaving the channel */
-       silc_server_send_notify_to_channel(server, channel,
-                                          "%s has left channel %s",
-                                          client->nickname,
-                                          channel->channel_name);
+       if (notify)
+         silc_server_send_notify_to_channel(server, channel,
+                                            "%s@%s has left channel %s",
+                                            client->nickname, sock->hostname ?
+                                            sock->hostname : sock->ip,
+                                            channel->channel_name);
       }
     }
   }
@@ -2438,9 +2521,15 @@ void silc_server_private_message(SilcServer server,
     /* If we are router and the client has router then the client is in
        our cell but not directly connected to us. */
     if (server->server_type == SILC_ROUTER && client->router) {
+      /* We are of course in this case the client's router thus the real
+        "router" of the client is the server who owns the client. Thus
+        we will send the packet to that server. */
+      router = (SilcServerEntry)dst_sock->user_data;
+      //      assert(client->router == server->id_entry);
+
       silc_server_private_message_send_internal(server, dst_sock,
-                                               client->router->send_key,
-                                               client->router->hmac,
+                                               router->send_key,
+                                               router->hmac,
                                                packet);
       goto out;
     }
@@ -2489,7 +2578,8 @@ void silc_server_private_message(SilcServer server,
   silc_buffer_free(buffer);
 }
 
-/* Process received channel message. */
+/* Process received channel message. The message can be originated from
+   client or server. */
 
 void silc_server_channel_message(SilcServer server,
                                 SilcSocketConnection sock,
@@ -2498,7 +2588,7 @@ void silc_server_channel_message(SilcServer server,
   SilcChannelEntry channel = NULL;
   SilcClientEntry client = NULL;
   SilcChannelID *id = NULL;
-  SilcClientID *sender = NULL;
+  void *sender = NULL;
   SilcBuffer buffer = packet->buffer;
   int i;
 
@@ -2519,15 +2609,21 @@ void silc_server_channel_message(SilcServer server,
     goto out;
   }
 
-  /* See that this client is on the channel */
+  /* See that this client is on the channel. If the message is coming
+     from router we won't do the check as the message is from client that
+     we don't know about. Also, if the original sender is not client
+     (as it can be server as well) we don't do the check. */
   sender = silc_id_str2id(packet->src_id, packet->src_id_type);
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
-    if (client && !SILC_ID_CLIENT_COMPARE(client->id, sender))
-      break;
+  if (sock->type != SILC_SOCKET_TYPE_ROUTER && 
+      packet->src_id_type == SILC_ID_CLIENT) {
+    for (i = 0; i < channel->user_list_count; i++) {
+      client = channel->user_list[i].client;
+      if (client && !SILC_ID_CLIENT_COMPARE(client->id, sender))
+       break;
+    }
+    if (i >= channel->user_list_count)
+      goto out;
   }
-  if (i >= channel->user_list_count)
-    goto out;
 
   /* Distribute the packet to our local clients. This will send the
      packet for further routing as well, if needed. */
@@ -2780,6 +2876,120 @@ void silc_server_send_replace_id(SilcServer server,
   silc_buffer_free(packet);
 }
 
+/* This function is used to send Remove Channel User payload. This may sent
+   by server but is usually used only by router to notify other routers that
+   user has left a channel. Normal server sends this packet to its router
+   to notify that the router should not hold a record about this client
+   on a channel anymore. Router distributes it further to other routers. */
+
+void silc_server_send_remove_channel_user(SilcServer server,
+                                         SilcSocketConnection sock,
+                                         int broadcast,
+                                         void *client_id, void *channel_id)
+{
+  SilcBuffer packet;
+  unsigned char *clid, *chid;
+
+  clid = silc_id_id2str(client_id, SILC_ID_CLIENT);
+  if (!clid)
+    return;
+
+  chid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
+  if (!chid)
+    return;
+
+  packet = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN + SILC_ID_CHANNEL_LEN);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
+                    SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
+                    SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                    SILC_STR_UI_XNSTRING(chid, SILC_ID_CHANNEL_LEN),
+                    SILC_STR_END);
+
+  silc_server_packet_send(server, sock, SILC_PACKET_REMOVE_CHANNEL_USER, 
+                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
+                         packet->data, packet->len, FALSE);
+  silc_free(clid);
+  silc_free(chid);
+  silc_buffer_free(packet);
+}
+
+/* Received packet to replace a ID. This checks that the requested ID
+   exists and replaces it with the new one. */
+
+void silc_server_replace_id(SilcServer server,
+                           SilcSocketConnection sock,
+                           SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  unsigned char *old_id = NULL, *new_id = NULL;
+  SilcIdType old_id_type, new_id_type;
+  unsigned short old_id_len, new_id_len;
+  void *id = NULL, *id2 = NULL;
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type == SILC_ID_CLIENT)
+    return;
+
+  SILC_LOG_DEBUG(("Replacing ID"));
+
+  silc_buffer_unformat(buffer,
+                      SILC_STR_UI_SHORT(&old_id_type),
+                      SILC_STR_UI16_NSTRING_ALLOC(&old_id, &old_id_len),
+                      SILC_STR_UI_SHORT(&new_id_type),
+                      SILC_STR_UI16_NSTRING_ALLOC(&new_id, &new_id_len),
+                      SILC_STR_END);
+
+  if (old_id_type != new_id_type)
+    goto out;
+
+  if (old_id_len != silc_id_get_len(old_id_type) ||
+      new_id_len != silc_id_get_len(new_id_type))
+    goto out;
+
+  id = silc_id_str2id(old_id, old_id_type);
+  if (!id)
+    goto out;
+
+  id2 = silc_id_str2id(new_id, new_id_type);
+  if (!id2)
+    goto out;
+
+  /* Replace the old ID */
+  switch(old_id_type) {
+  case SILC_ID_CLIENT:
+    if (silc_idlist_replace_client_id(server->local_list, id, id2) == NULL)
+      if (server->server_type == SILC_ROUTER)
+       silc_idlist_replace_client_id(server->global_list, id, id2);
+    break;
+
+  case SILC_ID_SERVER:
+    if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL)
+      if (server->server_type == SILC_ROUTER)
+       silc_idlist_replace_server_id(server->global_list, id, id2);
+    break;
+
+  case SILC_ID_CHANNEL:
+    /* XXX Hmm... Basically this cannot occur. Channel ID's cannot be
+       re-generated. */
+    silc_free(id2);
+    break;
+
+  default:
+    silc_free(id2);
+    break;
+  }
+
+ out:
+  if (id)
+    silc_free(id);
+  if (old_id)
+    silc_free(old_id);
+  if (new_id)
+    silc_free(new_id);
+}
+
 /* Creates new channel. */
 
 SilcChannelEntry silc_server_new_channel(SilcServer server, 
@@ -3023,14 +3233,18 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
                        SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
+  SilcIDList id_list;
+  SilcServerEntry tmpserver, router;
+  SilcSocketConnection router_sock;
   SilcIdType id_type;
   unsigned char *id_string;
-  void *id;
+  void *id, *tmpid;
 
   SILC_LOG_DEBUG(("Processing new ID"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER)
+      server->server_type == SILC_SERVER ||
+      packet->src_id_type != SILC_ID_SERVER)
     return;
 
   silc_buffer_unformat(buffer,
@@ -3046,11 +3260,24 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
   if (!id)
     goto out;
 
-  /* XXX Do check whether the packet is coming outside the cell or
-     from someone inside the cell.  If outside use global lists otherwise
-     local lists. */
-  /* XXX If using local list set the idlist->connection to the sender's
-     socket connection as it is used in packet sending */
+  /* If the packet is originated from the one who sent it to us we know
+     that the ID belongs to our cell, unless the sender was router. */
+  tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
+  tmpserver = (SilcServerEntry)sock->user_data;
+
+  if (!SILC_ID_SERVER_COMPARE(tmpid, tmpserver->id) &&
+      sock->type == SILC_SOCKET_TYPE_SERVER) {
+    id_list = server->local_list;
+    router_sock = sock;
+    router = sock->user_data;
+    /*    router = server->id_entry; */
+  } else {
+    id_list = server->global_list;
+    router_sock = (SilcSocketConnection)server->id_entry->router->connection;
+    router = server->id_entry->router;
+  }
+
+  silc_free(tmpid);
 
   switch(id_type) {
   case SILC_ID_CLIENT:
@@ -3059,9 +3286,9 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
 
       /* Add the client to our local list. We are router and we keep
         cell specific local database of all clients in the cell. */
-      idlist = silc_idlist_add_client(server->local_list, NULL, NULL, NULL,
-                                     id, sock->user_data, NULL, NULL, 
-                                     NULL, NULL, NULL, sock);
+      idlist = silc_idlist_add_client(id_list, NULL, NULL, NULL,
+                                     id, router, NULL, NULL, 
+                                     NULL, NULL, NULL, router_sock);
     }
     break;
 
@@ -3071,17 +3298,16 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
 
       /* Add the server to our local list. We are router and we keep
         cell specific local database of all servers in the cell. */
-      idlist = silc_idlist_add_server(server->local_list, NULL, 0,
-                                     id, server->id_entry, NULL, NULL, 
-                                     NULL, NULL, NULL, sock);
+      idlist = silc_idlist_add_server(id_list, NULL, 0,
+                                     id, router, NULL, NULL, 
+                                     NULL, NULL, NULL, router_sock);
     }
     break;
 
   case SILC_ID_CHANNEL:
     /* Add the channel to our local list. We are router and we keep
        cell specific local database of all channels in the cell. */
-    silc_idlist_add_channel(server->local_list, NULL, 0, id, 
-                           server->id_entry, NULL);
+    silc_idlist_add_channel(id_list, NULL, 0, id, router, NULL);
     break;
 
   default:
@@ -3092,3 +3318,63 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
  out:
   silc_free(id_string);
 }
+
+/* Received packet to remove a user from a channel. Routers notify other
+   routers that user has left a channel. Client must not send this packet. 
+   Normal server may send this packet but ignores if it receives one. */
+
+void silc_server_remove_channel_user(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  unsigned char *tmp1 = NULL, *tmp2 = NULL;
+  SilcClientID *client_id = NULL;
+  SilcChannelID *channel_id = NULL;
+  SilcChannelEntry channel;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Removing user from channel"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      server->server_type == SILC_SERVER)
+    return;
+
+  silc_buffer_unformat(buffer,
+                      SILC_STR_UI16_STRING_ALLOC(&tmp1),
+                      SILC_STR_UI16_STRING_ALLOC(&tmp2),
+                      SILC_STR_END);
+
+  if (!tmp1 || !tmp2)
+    goto out;
+
+  client_id = silc_id_str2id(tmp1, SILC_ID_CLIENT);
+  channel_id = silc_id_str2id(tmp2, SILC_ID_CHANNEL);
+  if (!client_id || !channel_id)
+    goto out;
+
+  /* XXX routers should check server->global_list as well */
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+  if (!channel)
+    goto out;
+  
+  /* XXX routers should check server->global_list as well */
+  /* Get client entry */
+  client = silc_idlist_find_client_by_id(server->local_list, client_id);
+  if (!client)
+    goto out;
+
+  /* Remove from channel */
+  silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+
+ out:
+  if (tmp1)
+    silc_free(tmp1);
+  if (tmp2)
+    silc_free(tmp2);
+  if (client_id)
+    silc_free(client_id);
+  if (channel_id)
+    silc_free(channel_id);
+}