implemented KICK command
[silc.git] / apps / silcd / packet_send.c
index 661b16d5c04607a9e39cbf3ea8ba3fe9b004fe35..b8c720c61b429ec5bd8b04aec8bdb6813ad128de 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 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
@@ -139,7 +139,6 @@ void silc_server_packet_send_dest(SilcServer server,
   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + dst_id_len;
   packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-  packetdata.rng = server->rng;
 
   /* Prepare outgoing data buffer for packet sending */
   silc_packet_send_prepare(sock, 
@@ -180,52 +179,6 @@ void silc_server_packet_send_dest(SilcServer server,
     silc_free(packetdata.dst_id);
 }
 
-/* Forwards packet. Packets sent with this function will be marked as
-   forwarded (in the SILC header flags) so that the receiver knows that
-   we have forwarded the packet to it. Forwarded packets are handled
-   specially by the receiver as they are not destined to the receiver
-   originally. However, the receiver knows this because the forwarded
-   flag has been set (and the flag is authenticated). */
-
-void silc_server_packet_forward(SilcServer server,
-                               SilcSocketConnection sock,
-                               unsigned char *data, unsigned int data_len,
-                               int force_send)
-{
-  SilcIDListData idata;
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
-
-  SILC_LOG_DEBUG(("Forwarding packet"));
-
-  /* Get data used in the packet sending, keys and stuff */
-  idata = (SilcIDListData)sock->user_data;
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 0, 0, data_len);
-
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
-
-  /* Add the FORWARDED flag to packet flags */
-  sock->outbuf->data[2] |= (unsigned char)SILC_PACKET_FLAG_FORWARDED;
-
-  if (idata) {
-    cipher = idata->send_key;
-    hmac = idata->hmac;
-  }
-
-  /* Encrypt the packet */
-  silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
-
-  SILC_LOG_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
-
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
-}
-
 /* Broadcast received packet to our primary route. This function is used
    by router to further route received broadcast packet. It is expected
    that the broadcast flag from the packet is checked before calling this
@@ -243,7 +196,7 @@ void silc_server_packet_broadcast(SilcServer server,
 
   /* If the packet is originated from our primary route we are
      not allowed to send the packet. */
-  id = silc_id_str2id(packet->src_id, packet->src_id_type);
+  id = silc_id_str2id(packet->src_id, packet->src_id_len, packet->src_id_type);
   if (id && SILC_ID_SERVER_COMPARE(id, server->router->id)) {
     idata = (SilcIDListData)sock->user_data;
 
@@ -267,6 +220,33 @@ void silc_server_packet_broadcast(SilcServer server,
   silc_free(id);
 }
 
+/* Routes received packet to `sock'. This is used to route the packets that
+   router receives but are not destined to it. */
+
+void silc_server_packet_route(SilcServer server,
+                             SilcSocketConnection sock,
+                             SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Routing received packet"));
+
+  idata = (SilcIDListData)sock->user_data;
+
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+  silc_packet_send_prepare(sock, 0, 0, buffer->len); 
+  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+  silc_packet_encrypt(idata->send_key, idata->hmac, 
+                     sock->outbuf, sock->outbuf->len);
+
+  SILC_LOG_HEXDUMP(("Routed packet, len %d", sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_server_packet_send_real(server, sock, TRUE);
+}
+
 /* Internal routine to actually create the channel packet and send it
    to network. This is common function in channel message sending. If
    `channel_message' is TRUE this encrypts the message as it is strictly
@@ -319,9 +299,12 @@ silc_server_packet_send_to_channel_real(SilcServer server,
    the channel. Usually this is used to send notify messages to the
    channel, things like notify about new user joining to the channel. 
    If `route' is FALSE then the packet is sent only locally and will not
-   be routed anywhere (for router locally means cell wide). */
+   be routed anywhere (for router locally means cell wide). If `sender'
+   is provided then the packet is not sent to that connection since it
+   originally came from it. */
 
 void silc_server_packet_send_to_channel(SilcServer server,
+                                       SilcSocketConnection sender,
                                        SilcChannelEntry channel,
                                        SilcPacketType type,
                                        unsigned char route,
@@ -352,7 +335,6 @@ void silc_server_packet_send_to_channel(SilcServer server,
   packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
   packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
   packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = server->rng;
   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + packetdata.dst_id_len;
   packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
@@ -368,11 +350,14 @@ void silc_server_packet_send_to_channel(SilcServer server,
     sock = (SilcSocketConnection)router->connection;
     idata = (SilcIDListData)router;
     
-    SILC_LOG_DEBUG(("Sending channel message to router for routing"));
-
-    silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                           idata->send_key, idata->hmac, 
-                                           data, data_len, FALSE, force_send);
+    if (sock != sender) {
+      SILC_LOG_DEBUG(("Sending channel message to router for routing"));
+      
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             idata->send_key, idata->hmac, 
+                                             data, data_len, FALSE, 
+                                             force_send);
+    }
   }
 
   /* Send the message to clients on the channel's client list. */
@@ -398,6 +383,9 @@ void silc_server_packet_send_to_channel(SilcServer server,
       sock = (SilcSocketConnection)client->router->connection;
       idata = (SilcIDListData)client->router;
 
+      if (sender && sock == sender)
+       continue;
+
       /* Send the packet */
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                              idata->send_key, idata->hmac, 
@@ -414,6 +402,9 @@ void silc_server_packet_send_to_channel(SilcServer server,
       continue;
     }
 
+    if (client && client->router)
+      continue;
+
     /* Send to locally connected client */
     if (client) {
 
@@ -421,6 +412,9 @@ void silc_server_packet_send_to_channel(SilcServer server,
       sock = (SilcSocketConnection)client->connection;
       idata = (SilcIDListData)client;
 
+      if (sender && sock == sender)
+       continue;
+
       /* Send the packet */
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                              idata->send_key, idata->hmac, 
@@ -473,7 +467,6 @@ void silc_server_packet_relay_to_channel(SilcServer server,
   packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
   packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
   packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = server->rng;
   packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
                                          packetdata.src_id_len +
                                          packetdata.dst_id_len));
@@ -538,6 +531,13 @@ void silc_server_packet_relay_to_channel(SilcServer server,
        sock = (SilcSocketConnection)client->router->connection;
        idata = (SilcIDListData)client->router;
 
+       if (sender_sock && sock == sender_sock)
+         continue;
+
+       SILC_LOG_DEBUG(("Relaying packet to client ID(%s) %s (%s)", 
+                       silc_id_render(client->id, SILC_ID_CLIENT),
+                       sock->hostname, sock->ip));
+
        /* Send the packet */
        silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                                idata->send_key, idata->hmac, 
@@ -554,14 +554,19 @@ void silc_server_packet_relay_to_channel(SilcServer server,
        continue;
       }
 
-      /* XXX Check client's mode on the channel. */
+      if (client && client->router)
+       continue;
 
       /* Get data used in packet header encryption, keys and stuff. */
       sock = (SilcSocketConnection)client->connection;
       idata = (SilcIDListData)client;
 
-      SILC_LOG_DEBUG(("Sending packet to client %s", 
-                     sock->hostname ? sock->hostname : sock->ip));
+      if (sender_sock && sock == sender_sock)
+       continue;
+
+      SILC_LOG_DEBUG(("Sending packet to client ID(%s) %s (%s)", 
+                     silc_id_render(client->id, SILC_ID_CLIENT),
+                     sock->hostname, sock->ip));
 
       /* Send the packet */
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
@@ -598,7 +603,7 @@ void silc_server_packet_send_local_channel(SilcServer server,
   /* Send the message to clients on the channel's client list. */
   silc_list_start(channel->user_list);
   while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    if (chl->client) {
+    if (chl->client && !chl->client->router) {
       sock = (SilcSocketConnection)chl->client->connection;
 
       /* Send the packet to the client */
@@ -662,7 +667,7 @@ void silc_server_send_motd(SilcServer server,
     if (!motd)
       return;
 
-    silc_server_send_notify(server, sock, SILC_NOTIFY_TYPE_MOTD, 1,
+    silc_server_send_notify(server, sock, FALSE, SILC_NOTIFY_TYPE_MOTD, 1,
                            motd, motd_len);
     silc_free(motd);
   }
@@ -694,6 +699,7 @@ void silc_server_send_error(SilcServer server,
 
 void silc_server_send_notify(SilcServer server,
                             SilcSocketConnection sock,
+                            int broadcast,
                             SilcNotifyType type,
                             unsigned int argc, ...)
 {
@@ -708,10 +714,238 @@ void silc_server_send_notify(SilcServer server,
   silc_buffer_free(packet);
 }
 
+/* Send CHANNEL_CHANGE notify type. This tells the receiver to replace the
+   `old_id' with the `new_id'. */
+
+void silc_server_send_notify_channel_change(SilcServer server,
+                                           SilcSocketConnection sock,
+                                           int broadcast,
+                                           SilcChannelID *old_id,
+                                           SilcChannelID *new_id,
+                                           unsigned int id_len)
+{
+  SilcBuffer idp1, idp2;
+
+  idp1 = silc_id_payload_encode((void *)old_id, SILC_ID_CHANNEL);
+  idp2 = silc_id_payload_encode((void *)new_id, SILC_ID_CHANNEL);
+
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_CHANNEL_CHANGE,
+                         2, idp1->data, idp1->len, idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Send NICK_CHANGE notify type. This tells the receiver to replace the
+   `old_id' with the `new_id'. */
+
+void silc_server_send_notify_nick_change(SilcServer server,
+                                        SilcSocketConnection sock,
+                                        int broadcast,
+                                        SilcClientID *old_id,
+                                        SilcClientID *new_id,
+                                        unsigned int id_len)
+{
+  SilcBuffer idp1, idp2;
+
+  idp1 = silc_id_payload_encode((void *)old_id, SILC_ID_CLIENT);
+  idp2 = silc_id_payload_encode((void *)new_id, SILC_ID_CLIENT);
+
+  silc_server_send_notify(server, sock, broadcast, 
+                         SILC_NOTIFY_TYPE_NICK_CHANGE,
+                         2, idp1->data, idp1->len, idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Sends JOIN notify type. This tells that new client by `client_id' ID
+   has joined to the `channel'. */
+
+void silc_server_send_notify_join(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 int broadcast,
+                                 SilcChannelEntry channel,
+                                 SilcClientID *client_id,
+                                 unsigned int client_id_len)
+{
+  SilcBuffer idp1, idp2;
+
+  idp1 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  idp2 = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+  silc_server_send_notify(server, sock, broadcast, SILC_NOTIFY_TYPE_JOIN,
+                         2, idp1->data, idp1->len,
+                         idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Sends LEAVE notify type. This tells that `client_id' has left the
+   `channel'. The Notify packet is always destined to the channel. */
+
+void silc_server_send_notify_leave(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  int broadcast,
+                                  SilcChannelEntry channel,
+                                  SilcClientID *client_id,
+                                  unsigned int client_id_len)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
+                              SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_LEAVE,
+                              1, idp->data, idp->len);
+  silc_buffer_free(idp);
+}
+
+/* Sends CMODE_CHANGE notify type. This tells that `client_id' changed the
+   `channel' mode to `mode. The Notify packet is always destined to
+   the channel. */
+
+void silc_server_send_notify_cmode(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  int broadcast,
+                                  SilcChannelEntry channel,
+                                  unsigned int mode_mask,
+                                  SilcClientID *client_id,
+                                  unsigned int client_id_len)
+{
+  SilcBuffer idp;
+  unsigned char mode[4];
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  SILC_PUT32_MSB(mode_mask, mode);
+
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
+                              SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_CMODE_CHANGE,
+                              2, idp->data, idp->len,
+                              mode, 4);
+  silc_buffer_free(idp);
+}
+
+/* Sends CUMODE_CHANGE notify type. This tells that `client_id' changed the
+   `target' client's mode on `channel'. The Notify packet is always
+   destined to the channel. */
+
+void silc_server_send_notify_cumode(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   int broadcast,
+                                   SilcChannelEntry channel,
+                                   unsigned int mode_mask,
+                                   SilcClientID *client_id,
+                                   unsigned int client_id_len,
+                                   SilcClientID *target,
+                                   unsigned int target_len)
+{
+  SilcBuffer idp1, idp2;
+  unsigned char mode[4];
+
+  idp1 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  idp2 = silc_id_payload_encode((void *)target, SILC_ID_CLIENT);
+  SILC_PUT32_MSB(mode_mask, mode);
+
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
+                              SILC_ID_CHANNEL, 
+                              SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3, 
+                              idp1->data, idp1->len,
+                              mode, 4,
+                              idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Sends SIGNOFF notify type. This tells that `client_id' client has
+   left SILC network. This function is used only between server and router
+   traffic. This is not used to send the notify to the channel for
+   client. The `message may be NULL. */
+
+void silc_server_send_notify_signoff(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    int broadcast,
+                                    SilcClientID *client_id,
+                                    unsigned int client_id_len,
+                                    char *message)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_SIGNOFF,
+                         message ? 2 : 1, idp->data, idp->len,
+                         message, message ? strlen(message): 0);
+  silc_buffer_free(idp);
+}
+
+/* Sends SERVER_SIGNOFF notify type. This tells that `server_id' server
+   has quit SILC network. */
+
+void silc_server_send_notify_server_signoff(SilcServer server,
+                                           SilcSocketConnection sock,
+                                           int broadcast,
+                                           SilcServerID *server_id,
+                                           unsigned int server_id_len)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)server_id, SILC_ID_SERVER);
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                         1, idp->data, idp->len);
+  silc_buffer_free(idp);
+}
+
+/* Sends TOPIC_SET notify type. This tells that `client_id' changed
+   the `channel's topic to `topic'. The Notify packet is always destined
+   to the channel. This function is used to send the topic set notifies
+   between routers. */
+
+void silc_server_send_notify_topic_set(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      int broadcast,
+                                      SilcChannelEntry channel,
+                                      SilcClientID *client_id,
+                                      unsigned int client_id_len,
+                                      char *topic)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                         topic ? 2 : 1, 
+                         idp->data, idp->len, 
+                         topic, topic ? strlen(topic) : 0);
+  silc_buffer_free(idp);
+}
+
+/* Send KICKED notify type. This tells that the `client_id' on `channel'
+   was kicked off the channel.  The `comment' may indicate the reason
+   for the kicking. This function is used only between server and router
+   traffic. */
+
+void silc_server_send_notify_kicked(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   int broadcast,
+                                   SilcChannelEntry channel,
+                                   SilcClientID *client_id,
+                                   unsigned int client_id_len,
+                                   char *comment)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
+                              SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_KICKED,
+                              comment ? 2 : 1, idp->data, idp->len,
+                              comment, comment ? strlen(comment) : 0);
+  silc_buffer_free(idp);
+}
+
 /* Sends notify message destined to specific entity. */
 
 void silc_server_send_notify_dest(SilcServer server,
                                  SilcSocketConnection sock,
+                                 int broadcast,
                                  void *dest_id,
                                  SilcIdType dest_id_type,
                                  SilcNotifyType type,
@@ -730,11 +964,14 @@ void silc_server_send_notify_dest(SilcServer server,
 }
 
 /* Sends notify message to a channel. The notify message sent is 
-   distributed to all clients on the channel. If `router_notify' is TRUE
+   distributed to all clients on the channel. If `route_notify' is TRUE
    then the notify may be routed to primary route or to some other routers.
-   If FALSE it is assured that the notify is sent only locally. */
+   If FALSE it is assured that the notify is sent only locally. If `sender'
+   is provided then the packet is not sent to that connection since it
+   originally came from it. */
 
 void silc_server_send_notify_to_channel(SilcServer server,
+                                       SilcSocketConnection sender,
                                        SilcChannelEntry channel,
                                        unsigned char route_notify,
                                        SilcNotifyType type,
@@ -746,7 +983,7 @@ void silc_server_send_notify_to_channel(SilcServer server,
   va_start(ap, argc);
 
   packet = silc_notify_payload_encode(type, argc, ap);
-  silc_server_packet_send_to_channel(server, channel, 
+  silc_server_packet_send_to_channel(server, sender, channel, 
                                     SILC_PACKET_NOTIFY, route_notify,
                                     packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
@@ -796,7 +1033,6 @@ void silc_server_send_notify_on_channels(SilcServer server,
   packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
   packetdata.src_id_len = SILC_ID_SERVER_LEN;
   packetdata.src_id_type = SILC_ID_SERVER;
-  packetdata.rng = server->rng;
 
   silc_list_start(client->channels);
   while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
@@ -854,6 +1090,9 @@ void silc_server_send_notify_on_channels(SilcServer server,
        continue;
       }
 
+      if (c && c->router)
+       continue;
+
       /* Send to locally connected client */
       if (c) {
        
@@ -915,96 +1154,6 @@ void silc_server_send_new_id(SilcServer server,
   silc_buffer_free(idp);
 }
 
-/* Sends Replace ID payload to remote end. This is used to replace old
-   ID with new ID sent in the packet.  This is called for example when
-   user changes nickname and we create new ID for the user.  If the 
-   argument `broadcast' is TRUE then the packet is sent as
-   broadcast packet. */
-/* XXX It would be expected that the new id is same type as the old
-   ID. :) */
-
-void silc_server_send_replace_id(SilcServer server,
-                                SilcSocketConnection sock,
-                                int broadcast,
-                                void *old_id, SilcIdType old_id_type,
-                                unsigned int old_id_len,
-                                void *new_id, SilcIdType new_id_type,
-                                unsigned int new_id_len)
-{
-  SilcBuffer packet;
-  unsigned char *oid;
-  unsigned char *nid;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  oid = silc_id_id2str(old_id, old_id_type);
-  if (!oid)
-    return;
-
-  nid = silc_id_id2str(new_id, new_id_type);
-  if (!nid)
-    return;
-
-  packet = silc_buffer_alloc(2 + 2 + 2 + 2 + old_id_len + new_id_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(old_id_type),
-                    SILC_STR_UI_SHORT(old_id_len),
-                    SILC_STR_UI_XNSTRING(oid, old_id_len),
-                    SILC_STR_UI_SHORT(new_id_type),
-                    SILC_STR_UI_SHORT(new_id_len),
-                    SILC_STR_UI_XNSTRING(nid, new_id_len),
-                    SILC_STR_END);
-
-  silc_server_packet_send(server, sock, SILC_PACKET_REPLACE_ID, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(oid);
-  silc_free(nid);
-  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;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  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);
-}
-
 /* Send New Channel Payload to notify about newly created channel in the
    SILC network. Normal server nevers sends this packet. Router uses this
    to notify other routers in the network about new channel. This packet
@@ -1036,6 +1185,7 @@ void silc_server_send_new_channel(SilcServer server,
                     SILC_STR_UI_XNSTRING(cid, channel_id_len),
                     SILC_STR_END);
 
+
   silc_server_packet_send(server, sock, SILC_PACKET_NEW_CHANNEL, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          packet->data, packet->len, FALSE);
@@ -1044,78 +1194,67 @@ void silc_server_send_new_channel(SilcServer server,
   silc_buffer_free(packet);
 }
 
-/* Send New Channel User payload to notify routers in the network about new
-   user on the channel. The packet is may be broadcasted. Normal server
-   can send this but must not receive. Router can send and receive it. */
-
-void silc_server_send_new_channel_user(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      int broadcast,
-                                      void *channel_id, 
-                                      unsigned int channel_id_len,
-                                      void *client_id,
-                                      unsigned int client_id_len)
-{
-  SilcBuffer packet;
-  unsigned char *clid, *chid;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  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 + channel_id_len + client_id_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(channel_id_len),
-                    SILC_STR_UI_XNSTRING(chid, channel_id_len),
-                    SILC_STR_UI_SHORT(client_id_len),
-                    SILC_STR_UI_XNSTRING(clid, client_id_len),
-                    SILC_STR_END);
-
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_CHANNEL_USER, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(clid);
-  silc_free(chid);
-  silc_buffer_free(packet);
-}
-
 /* Send Channel Key payload to distribute the new channel key. Normal server
    sends this to router when new client joins to existing channel. Router
-   sends this to the local server who forwarded join command in case where
-   the channel did not exist yet.  Both normal and router servers uses this
+   sends this to the local server who sent the join command in case where
+   the channel did not exist yet. Both normal and router servers uses this
    also to send this to locally connected clients on the channel. This
-   must not be broadcasted packet. */
+   must not be broadcasted packet. Routers do not send this to each other. 
+   If `sender is provided then the packet is not sent to that connection since
+   it originally came from it. */
 
 void silc_server_send_channel_key(SilcServer server,
-                                 SilcSocketConnection sock,
+                                 SilcSocketConnection sender,
                                  SilcChannelEntry channel,
                                  unsigned char route)
 {
   SilcBuffer packet;
   unsigned char *chid;
   unsigned int tmp_len;
-
   SILC_LOG_DEBUG(("Start"));
-
   chid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
   if (!chid)
     return;
-
   /* Encode channel key packet */
   tmp_len = strlen(channel->channel_key->cipher->name);
   packet = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, chid, tmp_len,
-                                          channel->channel_key->cipher->name,
-                                          channel->key_len / 8, channel->key);
-
-  silc_server_packet_send_to_channel(server, channel, SILC_PACKET_CHANNEL_KEY,
-                                    route, packet->data, packet->len, FALSE);
+                                           channel->channel_key->cipher->name,
+                                           channel->key_len / 8, channel->key);
+  silc_server_packet_send_to_channel(server, sender, channel, 
+                                    SILC_PACKET_CHANNEL_KEY,
+                                     route, packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
   silc_free(chid);
 }
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct form in correct order. */
+
+void silc_server_send_command(SilcServer server, 
+                             SilcSocketConnection sock,
+                             SilcCommand command, 
+                             unsigned int argc, ...)
+{
+  SilcBuffer packet;
+  va_list ap;
+
+  va_start(ap, argc);
+
+  packet = silc_command_payload_encode_vap(command, 0, argc, ap);
+  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND, 0,
+                         packet->data, packet->len, TRUE);
+  silc_buffer_free(packet);
+}
+
+/* Send the heartbeat packet. */
+
+void silc_server_send_heartbeat(SilcServer server,
+                               SilcSocketConnection sock)
+{
+  silc_server_packet_send(server, sock, SILC_PACKET_HEARTBEAT, 0,
+                         NULL, 0, FALSE);
+}