New silcconfig library and server parser. Merged silc-newconfig-final.patch.
authorGiovanni Giacobbi <johnny@silcnet.org>
Wed, 13 Feb 2002 11:56:01 +0000 (11:56 +0000)
committerGiovanni Giacobbi <johnny@silcnet.org>
Wed, 13 Feb 2002 11:56:01 +0000 (11:56 +0000)
14 files changed:
1  2 
CHANGES
apps/silcd/command.c
apps/silcd/packet_receive.c
apps/silcd/packet_send.c
apps/silcd/protocol.c
apps/silcd/server.c
apps/silcd/server_backup.c
apps/silcd/serverconfig.c
apps/silcd/serverconfig.h
apps/silcd/silcd.c
doc/example_silcd.conf.in
lib/silcutil/silcconfig.c
lib/silcutil/silcconfig.h
lib/silcutil/silclog.h

diff --combined CHANGES
index 65d6f219f68dd8c9b0a8adf5fed58f62761c3821,0000000000000000000000000000000000000000..f3617b11b2e10d5f8d45abfe4e17c6211a4ed18b
mode 100644,000000..100644
--- /dev/null
+++ b/CHANGES
@@@ -1,7005 -1,0 +1,7018 @@@
++Wed Feb 13 12:46:25 CET 2002  Johnny Mnemonic <johnny@themnemonic.org>
++
++      * Merged the new SILC Config library, with the server parsing
++        support.  Read the header file silcconfig.h or the toolkit
++        documentation for the news.  Affected files are
++        doc/example_silcd.conf.in lib/silcutil/silcconfig.[ch]
++        silcd/command.c silcd/packet_receive.c silcd/packet_send.c
++        silcd/protocol.c silcd/server.c silcd/server_backup.c
++        silcd/serverconfig.[ch] silcd/silcd.c.
++
++      * Fixed some silclog documentation.  Affected file is
++        lib/silcutil/silclog.h.
++
 +Sun Feb 10 18:11:30 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * The silc_cipher_register, silc_hash_register and
 +        silc_hmac_register now checks if the object to be registered
 +        is registered already.  Affected files are
 +        lib/silccrypt/silccipher.c, silchash.c and silchmac.c.
 +
 +Sun Feb 10 15:48:38 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Merged new irssi from irssi.org's CVS, the version 0.7.99.
 +
 +Sat Feb  9 14:54:33 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Allow zero length channel messages inside the Channel Message
 +        Payload.  Affected file lib/silccore/silcchannel.c.
 +
 +      * Fixed scripts/silcdoc/silcdoc to support all kinds of filenames
 +        as header filenames.
 +
 +      * Removed lib/silcclient/README and created HTML file
 +        lib/silcclient/silcclient_using.html, which is now included
 +        as part of Toolkit documentation.
 +
 +Thu Feb  7 10:12:25 CET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed CUMODE_CHANGE notify handling to change the mode of
 +        correct client.  Affected file lib/silcclient/client_notify.c.
 +
 +      * Make silc_rng_alloc fail if it cannot allocate the sha1
 +        hash algorithm.  Affected file lib/silccrypt/silcrng.c.
 +
 +Sun Feb  3 17:20:52 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the file transfer's key agreement payload to include
 +        zero port also if the hostname is NULL because it could not
 +        be bound.  
 +
 +        Call file transfer monitor callback now also if error occurs
 +        during key agreement protocol.
 +
 +        Changed the silc_client_file_send interface to return the
 +        SilcClientFileError instead of session id.  The session ID
 +        is returned into pointer provided as argument.
 +
 +        Check that the file exists locally before sending the
 +        file transfer request at all.
 +
 +        Affected file lib/silcclient/client_ftp.c, silcapi.h.
 +
 +      * Added SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED file transfer
 +        error than can occur while key agreement protocol.  Affected
 +        file lib/silcclient/silcapi.h.
 +
 +      * Fixed the event_mode CMODE handler to not crash when mode
 +        is changed and +k mode is set in the channel.  Affected file
 +        irssi/src/silc/core/silc-channels.c.
 +
 +      * Fixed SILC_LOG_ERROR to give out Error and not Warning, and
 +        SILC_LOG_WARNING to give out Warning and not Error.  Affected
 +        file lib/silcutil/silclog.c.
 +
 +      * Fixed the channel message payload decryption in the function
 +        silc_channel_message_payload_decrypt to not modify the original
 +        buffer before it is verified that the message decrypted
 +        correctly.  Otherwise, next time it is called with correct
 +        channel key it won't encrypt since the payload is corrupted.
 +        Affected file lib/silccore/silcchannel.c.
 +
 +Sun Feb  3 11:46:12 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not constantly resize the window.  A fix patch by cras.
 +        Affected file irssi/src/fe-text/screen.c.
 +
 +Sat Feb  2 16:54:18 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Applied IPv6 fix patch from Jun-ichiro itojun Hagino.
 +        Affected file lib/silcutil/silcnet.c.
 +
 +Fri Feb  1 22:33:11 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a bug in hash table internal routine for traversing
 +        the table with foreach callback.  The current entry may
 +        become invalid in the callback but it was referenced after
 +        the callback returned.
 +
 +        Do not allow auto rehashing of hash table during the
 +        silc_hash_table_foreach operation, for same reasons as it is
 +        not allowed for SilcHashTableList.  Affected files are
 +        lib/silcutil/silchashtable.[ch].
 +
 +Fri Feb  1 14:55:00 CET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Defined DLLAPI into silcincludes.h and silcwin32.h for
 +        Win32 DLL.  extern's in header files are now declared with
 +        DLLAPI.
 +
 +Thu Jan 31 23:34:33 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed private message handling.  It used some old code that
 +        caused the client to crash.  Affecte file is
 +        lib/silcclient/client_prvmsg.c.
 +
 +Thu Jan 31 19:06:22 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added function silc_client_add_channel, 
 +        silc_client_replace_channel_id, and removed functions
 +        silc_client_new_channel_id and silc_idlist_get_channel_by_id
 +        from client library.
 +
 +      * Added cross reference of the joined channels to the 
 +        SilcClientEntry, and changed the SilcChannelEntry's
 +        users list to SilcHashTable.  The affected files are
 +        lib/silcclient/idlist.[ch].
 +
 +      * Fixed a bug in hash table tarversing.  While the hash table
 +        is traversed with SilcHashTableList the table must not be
 +        rehashed.  It is now guaranteed that auto rehashable tables
 +        are not rehashed while tarversing the list.  Also defined that
 +        silc_hash_table_rehash must not be called while tarversing
 +        the table.  Added function silc_hash_table_list_reset that must
 +        be called after the tarversing is over.  The affected files are
 +        lib/silcutil/silchashtable.[ch].
 +
 +      * Changed all hash table traversing to call the new
 +        silc_hash_table_list_reset in server and in client library.
 +
 +      * Added function silc_client_on_channel to return the 
 +        SilcChannelUser entry if the specified client entry is joined
 +        on the specified channel.  This is exported to application as
 +        well.  Affected files lib/silcclient/client_channel.c, silcapi.h.
 +
 +Wed Jan 30 19:14:31 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed founder regaining problem with JOIN command on normal
 +        server.  The notify for mode change must be sent always and
 +        not only if !cmd->pending.  Affected file silcd/command.c.
 +
 +      * Fixed the WHOWAS command's reply sending to support the
 +        lists correctly.  Affected file silcd/command.c.
 +
 +Wed Jan 30 11:11:47 CET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * When sending JOIN command to router for processing the
 +        sender's old command identifier was not saved back to the
 +        sender's command context, fixed now.  The affected file is
 +        silcd/command.c.
 +
 +      * Create the key in JOIN command of the router did not return
 +        the channel key, added check for this.  Affected file is
 +        silcd/command.c.
 +
 +      * Fixed a channel ID update bug in JOIN command reply.  Do
 +        not directly upgrade the ID but call the function
 +        silc_idlist_replace_channel_id if the ID was changed.
 +        Affected file silcd/command_reply.c.
 +
 +      * Fixed memory leaks from command calling if it would fail.
 +        Affected file silcd/command.c.
 +
 +Tue Jan 29 19:49:31 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Applied patches from cras:
 +
 +        Memory leak fixes around libaries, irssi window resize fix,
 +        new silclist.h and silcdlist.h, all extern inline changed to
 +        static inline.
 +
 +      * Removed dotconf from lib/dotconf, not needed anymore.
 +
 +      * Removed TRQ from lib/trq, not needed anymore.
 +
 +      * Do more frequent heartbeats (5 minutes instead of 10 minutes)
 +        with server connections.  Later this will be configurable
 +        in config file after new config file is done.  Affected file
 +        silcd/server.c.
 +
 +Tue Jan 29 10:35:03 CET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a crash in server related to channel announcements.
 +        Affected file silcd/server.c.
 +
 +Mon Jan 28 17:49:42 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed memory leaks in silc_server_create_new_channel*
 +        functions.  Affected file silcd/server.c.
 +
 +      * Fixed the CHANNEL_CHANGE notify to re-announce the channel
 +        which ID was changed.  This way the router will send the
 +        user list for the channel again, and server won't be in 
 +        desync in some rare circumstances.  Affected file is
 +        silcd/packet_receive.c.
 +
 +Sun Jan 27 21:04:19 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Check for NULL socket pointer in the function
 +        silc_server_packet_send_to_channel_real.  Affected file
 +        silcd/packet_send.c.
 +
 +      * Fixed the BAN notify handling to correctly remove ban
 +        list.  Affected file silcd/packet_receive.c.
 +
 +Sat Jan 26 23:01:03 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed some header addition to Toolkit distribution in
 +        lib/silcutil/Makefile.am and lib/trq/Makefile.am.
 +
 +      * Added lib/silcclient/client_ops_example.h as an template
 +        file for application programmers to quickly start using
 +        the SilcClientOperation functions in their application.
 +        Updated the lib/silcclient/README as well to tell about this
 +        nice file made available.
 +
 +Sat Jan 26 10:45:41 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Call silc_server_remove_from_channels when removing client
 +        entry when NO_SUCH_CLIENT_ID was received.  Affected file
 +        is silcd/command_reply.c.
 +
 +Fri Jan 25 19:12:36 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added server & router operator statistics updating.  Affected
 +        file silcd/packet_receive.c and silcd/command.c.
 +
 +      * Fixed the SERVER_SIGNOFF notify handling on normal server
 +        not to save the history information for clients.  Same was
 +        fixed earlier in remove_clients_by_server function, but not
 +        here.  Affected file silcd/packet_receive.c.
 +
 +      * Raised the default connection-retry count from 4 to 7 in
 +        server.  Affected file silcd/server.h.
 +
 +      * Cancel any possible reconnect timeouts when we start the
 +        key exchange.  Affected file silcd/server.c.
 +
 +      * Do not reconnect on connection failure when SCONNECT was
 +        given.  Affected files silcd/server.[ch].
 +
 +Tue Jan 22 18:19:36 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Removed assert()'s from the lib/silcclient/client_keyagr.c.
 +
 +      * Fixed the NICK command to always give the unformatted
 +        nickname to the one giving the NICK command.  If unformatted
 +        nickname is cached already it will be formatted and the
 +        local entry will always get the unformatted nickname.
 +        Affected file lib/silcclient/idlist.c.
 +
 +      * Fixed some double frees from client library commands.
 +        Affected file is lib/silcclient/command.c.
 +
 +      * Fixed CUMODE command in server to assure that no one can
 +        change founder's mode than the founder itself, there was a
 +        little bug.  Affected file silcd/command.c.
 +
 +Mon Jan 21 19:07:53 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Removed the SilcClientCommandDestructor from the client
 +        libary, it is not needed anymore.  Affected files are
 +        lib/silcclient/silcapi.h, command[_reply].[ch],
 +        client_notify, idlist.c.
 +
 +      * Fixed GETKEY command to first resolve client, and then
 +        resolve the server only if the client was not found, instead
 +        of resolving both at the same time.  Affected file is
 +        lib/silcclient/command.c.
 +
 +      * Added silc_client_start_key_exchange_cb and lookup the
 +        remote hostname and IP address before starting the key
 +        exchange with server.  The affected file is 
 +        lib/silcclient/client.c.
 +
 +      * The server's public key is now saved using the IP address
 +        of the server and not the servername for the filename.
 +        The hostname public key filename is checked as an fall back
 +        method if the IP address based filename is not found.
 +
 +        Fixed the GETKEY command to save the fetched server key
 +        in correct filename.
 +
 +        Print the remote server's hostname now when new key is
 +        received during connection process.  Affected file is
 +        irssi/src/silc/core/client_ops.c.
 +
 +      * Return always our own public key to the client if it asks
 +        for it with GETKEY command.  Affected file silcd/command.c.
 +
 +      * Removed the use_auto_addr variable from default config
 +        file since it was in wrong section.  Affected file is
 +        irssi/src/config.
 +
 +      * Fixed TOPIC_CHANGE notification to not route it when it
 +        was sent using silc_server_send_notify_to_channel function.
 +        Affected file silcd/command.c.
 +
 +      * Fixed silc_server_send_notify_kicked to send the kicker's
 +        Client ID also, it was missing.  Affected files are
 +        silcd/command.c, silcd/packet_send.[ch].
 +
 +Thu Jan 17 18:59:11 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not save client history information in SERVER_SIGNOFF.
 +        Fixes the bug in normal server that it does not detect
 +        the client becoming valid after the server becomes back
 +        online.  Affected file silcd/server_util.c.
 +
 +      * Added `sock_error' field  into the SilcSocketConnection
 +        context.  When error occurs during socket operation (read
 +        or write) the error is saved.  Added also new function
 +        silc_socket_get_error to return human readable socket error
 +        message.  Affected files are lib/silcutil/silcsockconn.[ch], 
 +        lib/silcutil/unix/silcunixsockconn.c, and
 +        lib/silcutil/win32/silcwin32sockconn.c.
 +
 +      * The server now prints the socket error message in the
 +        signoff for client.  Affected file silcd/server.c.
 +
 +      * Fixed the `created' channel information sending from router
 +        to server in JOIN command.  Checks now whether the channel
 +        really was created or not and set it according that. 
 +
 +        Fixed the JOIN command to use the client entry's current
 +        ID during the joining procedure instead of the one it sent
 +        in the command (it is checked though), since it can change
 +        between the packet processing and command processing, and 
 +        would just case unnecessary pain in the client end.  Affected
 +        file silcd/command.c.
 +
 +      * Fixed a channel key payload sending to use correct channel
 +        ID when the server was forced to change the channel's ID by
 +        router.  Router sent the key payload with the old Channel ID.
 +        Affected file silcd/packet_receive.c.
 +
 +Wed Jan 16 22:26:30 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Call silc_server_save_channel_key only if the key payload
 +        was provided in the JOIN command's command reply.  Affected
 +        file silcd/command_reply.c.
 +
 +Tue Jan 15 18:49:41 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed silc_mp_sizeinbase to return the value correctly with
 +        MPI.  Affected file lib/silcmath/mp_mpi.c.
 +
 +      * Fixed the stop_server signal to correctly stop the scheduler
 +        and gracefully stop the server when SIGTERM or SIGINT signals
 +        are received.  Affected file silcd/silcd.c.
 +
 +Mon Jan  7 23:38:19 CET 2002  Johnny Mnemonic <johnny@themnemonic.org>
 +
 +      * Simple handling of TERM and HUP signals. Also added some log
 +        flushing call around.  Affected file is
 +        silcd/silcd.c.
 +
 +      * Fixed small bugs in silclog.c. Now buffering output will take
 +        effect after 10 seconds since startup: This will ensure that
 +        no important startup messages are lost. Also output redirection
 +        will preserve original format ([Date] [Type] message).
 +        Affected file is lib/silcutil/silclog.c.
 +
 +      * Added two options to the config file, in the logging section:
 +        quicklogs:<yes/no>: and flushdelay:<seconds>:.  Affected files
 +        lib/silcutil/silclog.[ch], silcd/serverconfig.[ch].
 +
 +Sun Jan  6 12:49:40 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not print the warning about log files not being initialized
 +        more than once to avoid excess logging.  Affected file is
 +        lib/silcutil/silclog.c.
 +
 +      * Fixed the SIM compilation in lib/silcsim/Makefile.am.  Fixed
 +        the SIM copying in make install in Makefile.am.pre.
 +
 +Sun Jan  6 01:10:21 CET 2001  Johnny Mnemonic <johnny@themnemonic.org>
 +
 +      * Rewritten silclog APIs. Globally interesting changes follows:
 +        silc_log_set_files() changed to silc_log_set_file().
 +        silc_log_set_callbacks() changed to silc_log_set_callback().
 +        ROBOdoc documented silclog header file.
 +        SilcLogCb now returns bool to wether inihibit the default
 +        handler or not (to keep the old behaviour return always TRUE).
 +        The new APIs should also fix the problem of the
 +        silcd_error.log file that was written in the current directory.
 +
 +        New features:
 +        Log files streams will remain opened after silc_log_set_file()
 +        call, means less CPU usage notably on high traffic servers.
 +        File streams are now full buffered, and flushed to the disk
 +        every 5 minutes, lesses HD activity and CPU usage.
 +        Messages can be redirected, allowing admins to configure
 +        one single logfile for all server messages.
 +        the silc_log_quick global variable to activate fast-logging.
 +        Affected files lib/silcutil/silclog.[ch]
 +
 +      * Changed some code to conform new silclog APIs. Affected
 +        files are doc/example_silcd.conf.in, silcd/server.c
 +        irssi/src/silc/core/silc-core.c, silcd/serverconfig.[ch],
 +        silcd/silcd.c.
 +
 +      * Fixed a memory leak that could occur in some situations.
 +        Affected file silcd/serverconfig.c.
 +
 +Sat Jan  5 13:37:29 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added the silc_client_del_client to remove the client from
 +        all channels as well.  Affected file lib/silcclient/idlist.c.
 +
 +      * Fixed the client library to correctly remove the client
 +        from all channels when the client entry is being destroyed.
 +        Affected file lib/silcclient/client_notify.c, command.c.
 +
 +      * Added auto-nicking support to the client library.  If the
 +        applicatio now sets client->nickname it will be sent to the
 +        server after connecting by the library.  This way for example
 +        SILCNICK (or IRCNICK) environment variables will have effect
 +        and always change the nickname automatically to whatever
 +        it is wanted.  Affected file lib/silcclient/client.[ch].
 +
 +      * Renamed silc_server_command_bad_chars to the
 +        silc_server_name_bad_chars and moved it to the
 +        silcd/server_util.[ch].  Added also new function
 +        silc_server_name_modify_bad to return nickname that
 +        includes bad characters as new nickname without those
 +        bad characters.  This check and modify is now used in
 +        silc_server_new_client when the username is initially set
 +        as nickname, so it must be checked to be valid nickname.
 +        Affected file silcd/packet_receive.c.
 +
 +      * The nickname length is now taken from the packet for real
 +        and not trusted to strlen() since it clearly can return
 +        wrong length for nickname including bad characters.  This
 +        also applies to channel names.  Affected file silcd/command.c.
 +
 +      * Removed the lib/silcsilm/modules directory.  Modules are now
 +        compiled into the lib/silcsim.  Fixed the copying of the
 +        modules to follow symbolic links in Makefile.am.pre.
 +
 +Wed Jan  2 18:56:21 EET 2002  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed silc_string_regexify list creation.  Fixes bugs with
 +        BAN and INVITE commands in server.  The affected file is
 +        lib/silcutil/unix/silcunixutil.c.
 +
 +Sun Dec 30 13:41:34 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Removed the command destructor entirely from the server's
 +        command and command reply routines.  It is not needed, and
 +        its usage was buggy and caused crashes.  Affected files are
 +        silcd/command[_reply].[ch].
 +
 +Fri Dec 28 12:43:22 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Cancel protocol and NULL sock->protocol if timeout
 +        occurred during protocol.  Affected file silcd/server.c.
 +
 +      * Cancel protocol timeouts always before calling the final
 +        callback, to assure that after final callback is called
 +        no other state will be called for the protocol anymore.
 +        Affected file silcd/protocol.c.
 +
 +      * Print error log if incoming connection configuration could
 +        not be found.  Affected file silcd/server.c.
 +
 +      * Fixed JOIN command to correctly save the founder mode
 +        to the client on normal SILC server, when the channel
 +        was created by the router.  Affected file silcd/command.c.
 +
 +      * Fixed LIST command (hopefully) to send correct reply
 +        packets.  Affected file silcd/command.c.
 +
 +Thu Dec 20 16:14:52 CET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * The silc_packet_receive_process now returns FALSE if the
 +        read data was invalid packet, and TRUE if it was ok.
 +
 +        The server now checks that if unauthenticated connection
 +        sends data and its processing fails the server will close
 +        the connection since it could be a malicious flooder. 
 +
 +        Affected files lib/silccore/silcpacket.[ch], silcd/server.c.
 +
 +Wed Dec 19 21:31:25 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Make sure the warning about error opening a log file is
 +        printed only once and not everytime it fails (produces
 +        too much useless log).  Affected file lib/silcutil/silclog.c.
 +
 +Wed Dec 19 18:21:51 CET 2001  Johnny Mnemonic <johnny@themnemonic.org>
 + 
 +      * Made the silc_server_daemonise() function more readable.
 +        Affected file silcd/server.c.
 + 
 +      * Pid file is now optional, the user may comment it out from
 +        the config file. Removed define SILC_SERVER_PID_FILE, we
 +        don't need a default any longer.  Affected file
 +        configure.in.pre, lib/Makefile.am.pre.
 + 
 +      * Make some use of the pid file. The server now dies at startup
 +        if it detects a valid pid file on his path. The server would
 +        die anyway in this circumstance, because of the bind() failure.
 +        Affected file silcd/silcd.c.
 + 
 +      * No longer compiling lib/dotconf.
 +
 +Mon Dec 17 18:24:27 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed JOIN command parsing not to crash.  Affected file
 +        lib/silcclient/command.c.
 +
 +      * Fied the NICK_CHANGE notify to add the new client entry
 +        even it is resolved.  This removes an <[unknown]> nick
 +        thingy bug in the client.  Affected file is 
 +        lib/silcclient/client_notify.c.
 +
 +      * Do not try to allocate 0 bytes (efence does not like it)
 +        in lib/silccore/silccomand.c when encoding payload.
 +
 +      * Do not take IRCNICK as nickname in Irssi SILC client since
 +        it is not possible to set nickname before hand connecting
 +        the server (TODO has an entry about adding auto-nicking
 +        support).
 +
 +      * Changed the silc_server_command_pending to check whether
 +        there already exists an pending entry with the specified
 +        command, command identifier and pending callback.  This is
 +        to fix IDENTIFY and WHOIS related crashes that may register
 +        multiple pending commands with same identifier.  Affected
 +        file silcd/command.c.
 +
 +      * Fixed the server to reconnect to the router even if it
 +        was already reconnecting and EOF was received.  This to
 +        fix a possibility that the server wouldn't ever try to
 +        auto-reconnect to the router.  Affected file silcd/server.c.
 +
 +Sat Dec 15 20:31:50 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the server's password authentication to use the
 +        length of the locally saved password, and not the one
 +        sent in the packet.  Affected file silcd/protocol.c.
 +
 +      * Fixed same password authentication problem in the
 +        Authentication Payload handling routines in
 +        lib/silccore/silcauth.c.
 +
 +      * Yet another password authentication problem fixed with
 +        channel password handling in silcd/command.c.
 +
 +Mon Dec 10 19:57:40 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * If first character of string in silc_parse_userfqdn is '@'
 +        then do not parse it.  Affected file is
 +        lib/silcutil/silcutil.c.
 +
 +Sun Dec  9 22:18:50 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed minor bug in IDENTIFY command reply sending, which
 +        caused various weird problems during JOIN when it was
 +        resolving names for users.  Affected file silcd/command.c.
 +
 +Sun Dec  9 19:18:41 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the IDENTIFY command reply sending to chech better valid
 +        clients.  It was possible to send incomplete list of replies.
 +        Affected file silcd/command.c.
 +
 +Sat Dec  8 15:58:31 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added silc_client_command[s]_[un]register functions now to
 +        dynamically register the commands in client library.  Removed
 +        the static table of commands.  This allows the client library
 +        to call commands without causing the application to know about
 +        what commands library has called.
 +
 +        Removed the INFO command reply kludge to detect when the command
 +        was called by library.  Now library use its own command reply
 +        function for INFO command.
 +
 +        Added function silc_client_command_call to call a command.
 +        Application can use it to call command, not access the structure
 +        directly.
 +
 +        Now all commands that are sent by the client library (not
 +        explicitly sent by application) use own command reply functions.
 +
 +        Affected files around lib/silcclient/ and in
 +        irssi/src/silc/core/.
 +
 +      * Fixed the WHOIS command reply sending to chech better valid
 +        clients.  It was possible to send incomplete list of replies.
 +
 +        Fixed the WHOIS and IDENTIFY to send the request to router
 +        if normal server did not do it and did not find any results.
 +
 +        Affected file silcd/command.c.
 +
 +Thu Dec  6 17:21:06 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Moved the internal data from SilcClient context into its
 +        own file, not accesible to application.  Affected files
 +        lib/silcclient/client.h and lib/silcclient/client_internal.h,
 +        and other files in client library.
 +
 +Thu Dec  6 10:37:55 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added doc/examples installation target in Makefile.am.pre.
 +        A patch by salo.
 +
 +Tue Dec  4 17:43:19 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * If NO_SUCH_CLIENT_ID notify is received for WHOIS or IDENTIFY
 +        commands the found client entry will be removed from the
 +        cache, after notifying application about the error.  Affected
 +        file lib/silcclient/command_reply.c.
 +
 +      * Changed the /MSG to check for exact nickname user gave, and
 +        not let `nick' match `nick@host' if it is only one found.  Now,
 +        user must type the exact nickname (like nick@host2) even if
 +        there are no more than one same nicks found.  This is to avoid
 +        a possibility of sending nickname to wrong nickname since
 +        `nick' could match `nick@host'.  Affected file is
 +        irssi/src/core/silc-servers.c.
 +
 +Mon Dec  3 18:49:45 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not print "you are now server operator" or similar when
 +        giving /away command.  Affected files are
 +        irssi/src/silc/core/client_ops.c, silc-servers.h.
 +
 +      * Made the silc_server_command_pending_error_check to send
 +        the same command reply payload it received back to the
 +        original sender of the command.  This way all arguments
 +        that was received by the server will be received by the
 +        client too.  Affected file silcd/command.c.
 +
 +      * Added the silc_idcache_add to return the created cache entry
 +        to a pointer.  Affected file lib/silccore/silcidcache.[ch].
 +
 +      * Add global clients to expire if they are not on any channel.
 +        This is because normal server will never know if they signoff
 +        if they are not on any channel.  The cache expiry will take
 +        case of these entries.  This is done by normal servers only.
 +        The affected files are silcd/command_reply.c,
 +        silcd/idlist.[ch], silcd/server and silcd/packet_receive.c.
 +
 +      * If server receives invalid ID notification for WHOIS or
 +        IDENTIFY and the ID exists in the lists, it is removed.
 +        Affected file silcd/command_reply.c.
 +
 +      * If NO_SUCH_CLIENT_ID is received for WHOIS or IDENTIFY command
 +        in client then client entry that it matches is searched and
 +        the nickname is printed on the screen for user.  Affected
 +        file irssi/src/silc/core/client_ops.c.
 +
 +Mon Dec  3 11:56:59 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Use cache entry expire time in the LIST command reply to
 +        purge old entries from the cache after the LIST command
 +        reply has been received.  This way we don't have non-existent
 +        entries in the cache for too long.  Affected file is
 +        silcd/command_reply.c.
 +
 +Sun Dec  2 23:29:07 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * If we are normal server, and we've not resolved client info
 +        in WHOIS or IDENTIFY from router, and it is global client,
 +        we'll check whether it is on some channel.  If it is not
 +        then we cannot be sure about its validity and will resolve it
 +        from router.  Fixes a bug in WHOIS and IDENTIFY.  Affected 
 +        file silcd/command.c.
 +
 +      * Search channel by name (if possible) rather than by ID
 +        in IDENTIFY command's command reply.  Affected file is
 +        silcd/command_reply.c.
 +
 +Sun Dec  2 13:48:46 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Distribute to the channel passphrase in CMODE_CHANGE notify.
 +        Updated specs and implemented it.  Affected file silcd/command.c,
 +        silcd/packet_send.c and silcd/packet_receive.c.
 +
 +      * Implemented the <founder auth> payload handling in the JOIN
 +        command.  If provided all conditions for channel joining
 +        except requirement to provide correct passphrase can be 
 +        overrided by the channel founder.  Updated the protocol specs.
 +        Affected file silcd/command.c.
 +
 +        Added support for founder auth in JOIN command in client
 +        library.  Fixed the parsing of the JOIN command now to support
 +        all options as they should be.  The affected file is
 +        lib/silcclient/command.c.
 +
 +      * Optimized the WHOIS and IDENTIFY commands to send the request
 +        to router only if it includes nicknames or other names.  If
 +        they include only IDs then check the local cache first before
 +        routing.  Affected file is silcd/command.c.
 +
 +      * Added channels topic announcements.  Affected file is
 +        silcd/packet_receive.c and silcd/server.c.
 +
 +      * Fixed the silc_server_send_notify_topic_set to really destine
 +        the packet to channel.  Affected file silcd/packet_send.c.
 +
 +      * Fixed a crash in CHANNEL_CHANGE notify handling in the client
 +        library.  Affected file lib/silcclient/client_notify.c.
 +
 +      * Added UMODE announcements.  Affected file silcd/server.c.
 +
 +Sat Dec  1 12:52:39 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Memory leak fixes in:
 +
 +        lib/silcutil/silcsockconn.c
 +        lib/silcske/silcske.c
 +        lib/silcske/groups.c
 +        lib/silccrypt/rsa.c
 +        lib/silccrypt/silcpkcs.c
 +        lib/silccore/silccommand.c
 +        lib/silccore/silcidcache.c
 +        silcd/idlist.c
 +        silcd/packet_send.c
 +        silcd/command.c
 +
 +      * ROBOdoc documented the lib/silcske/groups.h file and a
 +        bit changed the interface for better.
 +
 +Thu Nov 29 22:12:50 EET 2001  Pekka Riikonen <priikone@silcnet.org>'
 +
 +      * Update the client entry context in the ID cache after
 +        nick change.  Affected file lib/silcclient/command.c.
 +        Fixes the CUMODE command when regaining founder privileges,
 +        and a little WHOIS problem.
 +
 +      * Fixed silc_net_gethostbyname to correctly call the
 +        inet_ntop.  Affected file lib/silcutil/silcnet.c.
 +
 +Thu Nov 29 19:31:23 EET 2001  Pekka Riikonen <priikone@silcnet.org>'
 +
 +      * Added IPv6 support checking to the configure.in.pre, added
 +        also --enable-ipv6 option to override the check.  Affected
 +        file configure.in.pre.
 +
 +      * The silc_thread_create now calls the start function
 +        directly if threads support is not compiled in.  Removes
 +        ugly #ifdef's from generic code.  Affected files are
 +        lib/silcutil/unix/silcunixthread, win32/silcwin32thread.c.
 +
 +      * Added silc_net_gethostby[name/addr]_async to asynchronously
 +        resolve.  Affected files are lib/silcutil/silcnet.[ch].
 +
 +      * Added support for rendering IPv6 based server, client and
 +        channel IDs.  Affected file lib/silcutil/silcutil.c.
 +
 +      * Added support for creating IPv6 based server IDs.  Affected
 +        file is silcd/serverid.c.
 +
 +Wed Nov 28 23:46:09 EET 2001  Pekka Riikonen <priikone@silcnet.org>'
 +
 +      * Added silc_net_gethostby[addr/name] into the
 +        lib/silcutil/silcnet.[ch].  Added IPv6 support to Unix network
 +        routines.  Added silc_net_is_ip[4/6].  Affected file is
 +        lib/silcutil/unix/silcunixnet.c.  All routines that take
 +        address as argument now supports both IPv4 and IPv6 addresses.
 +
 +Mon Nov 26 18:09:48 EET 2001  Pekka Riikonen <priikone@silcnet.org>'
 +
 +      * Fixed LIST command reply sending in server.  Affected file
 +        silcd/command.c.
 +
 +      * Server now sends the kicker's client ID in the KICK notify
 +        to the kicked client.  Affected file silcd/command.c.
 +
 +      * The client library now parses the kickers client ID and
 +        UI displays it.  Affected files lib/silcclient/client_notify.c
 +        and irssi/src/silc/core/silc-channels.c, module-formats.c.
 +
 +      * Made all payload parsing function prototypes consistent.
 +        They all take now const unsigned char * and uint32 pair as
 +        the payload data instead of SilcBuffer.  Changes all around
 +        the source tree.  Other unsigned char* -> const unsigned char*
 +        changes around the tree as well.
 +
 +      * Optimized SFTP client and server packet sending not to
 +        allocate new buffer for each packet but to recycle the
 +        first allocated buffer.  Affected files are
 +        lib/silcsftp/sftp_client.c, sftp_server.c, sftp_util.[ch].
 +
 +      * Optimized the SFTP client to use SilcList instead of
 +        SilcDList for requests, because it is faster.  Affected file
 +        is lib/silcsftp/sftp_client.c.
 +
 +      * Moved the ID Payload routines from lib/silccore/silcpayload.[ch]
 +        into lib/silccore/silcid.[ch].
 +
 +        Renamed silcpayload.[ch] into silcargument.[ch].
 +
 +Mon Nov 26 15:01:53 CET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * If client entry is deleted with active key agreement
 +        session, abort the session.
 +
 +        The silc_client_abort_key_agreement now calls the completion
 +        callback with new SILC_KEY_AGREEMENT_ABORTED status.
 +
 +        Affected file lib/silcclient/silcapi.h, client_keyagr.c and
 +        idlist.c.
 +
 +Sun Nov 25 18:01:45 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Don't use __restrict in older GCC's.  Affected file is
 +        lib/silcmath/mpi/mpi-priv.h.  A patch by salo.
 +
 +      * silc_net_localhost now attempts to reverse lookup the
 +        IP/hostname.  Affected file lib/silcutil/silcnet.c.
 +
 +      * Defined <founder auth> argument to the SILC_COMMAND_JOIN
 +        command.  It can be used to gain founder privileges at 
 +        the same time when joining the channel.
 +
 +        Defined that the SILC_NOTIFY_TYPE_KICKED send the 
 +        kicker's client ID as well.  Updated protocol specs.
 +
 +        Defined that the server must send SILC_COMMAND_IDENTIFY
 +        command reply with error status to client who sent
 +        private message with invalid client ID.
 +
 +        Updated the protocol specification.
 +
 +      * Added silc_server_send_command_reply to send any
 +        command reply.  Affected file silcd/packet_send.[ch].
 +
 +      * Added silc_id_payload_encode_data to encode ID payload
 +        from raw ID data.  Affected file lib/silccore/silcpayload.[ch].
 +
 +      * The server now send IDENTIFY command reply with error
 +        status if client ID in private message is invalid.  Affected
 +        file silcd/packet_receive.c.
 +
 +      * Save the server key file with server's IP address in
 +        the filename instead of hostname.  The affected file is
 +        irssi/src/silc/core/client_ops.c.
 +
 +Sat Nov 24 20:08:22 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Typo fixes in irssi/src/fe-common/silc/module-formats.c.
 +        A patch by Sunfall.
 +
 +      * Added libtool support for compiling shared objects in
 +        lib/silcsim.  Affected file configure.in.pre and
 +        lib/silcsim/Makefile.am.  Original patch by cras.
 +
 +Fri Nov 23 23:30:59 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Pid file configuration, and server's config file fixes
 +        patch by toma.  Updated CREDITS file. 
 +
 +Sun Nov 18 01:34:41 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed silc_client_channel_message to not try to decrypt
 +        the message twice if it resolved the destination client
 +        information.  This could cause of dropping one channel
 +        message.  Affected file lib/silcclient/client_channel.c.
 +
 +Wed Nov 14 23:44:56 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added silc_client_run_one into lib/silcclient/silcapi.h and
 +        lib/silcclient/client.c. This function is used when the SILC
 +        Client is run under some other scheduler, or event loop or
 +        main loop.  On GUI applications, for example this may be
 +        desired to used to run the client under the GUI application's
 +        main loop.  Typically the GUI application would register an
 +        idle task that calls this function multiple times in a second
 +        to quickly process the SILC specific data.
 +
 +Wed Nov 14 19:16:52 CET 2001  Johnny Mnemonic <johnny@themnemonic.org>
 +
 +        * Fixed silc_server_drop() for dropping the supplementary
 +          groups as well, this could cause a security hole on some
 +          systems.
 +
 +Wed Nov 14 16:22:25 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * __pid_t -> pid_t in lib/silccrypt/silcrng.c.  A patch by
 +        johnny.
 +
 +      * Write PID file after dropping privileges.  Added -F option
 +        to run server on foreground.  A patch by debolaz.
 +        Affected files silcd/server.c, silcd/silcd.c.
 +
 +      * Fixed MOTD to return the MOTD file server name.  Affected
 +        file silcd/command.c.
 +
 +      * Added INFO command reply handling to the Irssi SILC Client.
 +        Affected file irssi/src/silc/core/client_ops.c.
 +
 +Wed Nov 14 00:18:08 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the silc_idcache_list_* routines to really support
 +        the dynamic list.  Fixes a crash.  Affected file is
 +        lib/silccore/silcidcache.c.
 +
 +      * Fixed the LIST command reply to really call LIST command's
 +        pending callbacks.  Affected file silcd/command_reply.c.
 +
 +Tue Nov 13 00:49:17 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Update conn->local_entry->nickname after giving NICK
 +        command.  Affected file lib/silcclient/command.c.
 +
 +Sun Nov 11 23:43:02 PST 2001  Brian Costello <bc@wpfr.org>
 +
 +      * Added the [pid] option to the silcd configuration file
 +
 +        Affected files: serverconfig.[ch] and silcd.c
 +
 +Sun Nov 11 23:56:39 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Save fingerprint in WHOIS command reply in server.
 +        Affected file silcd/command_reply.c.
 +
 +      * Fixed NICK commands pending callback registration.
 +        Affected file lib/silcclient/command.c.
 +
 +Sun Nov 11 10:49:10 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Use ++server->cmd_ident when sending commands in server,
 +        instead of random number.  Affected file silcd/command.c.
 +
 +      * Fixed GETKEY command reply to call actually GETKEY pending
 +        command callbacks.  Affected file silcd/command_reply.c.
 +
 +      * A bit stricter check for nicknames.  Check for same nickname
 +        in NICK command also.  Affected file silcd/command.c.
 +
 +      * Do not call INFO command everytime client ID changes, only
 +        during first connecting.  Affected file lib/silcclient/client.c.
 +
 +      * Set the new nickname only after successful command reply for
 +        NICK command is returned by server.  Affected file
 +        lib/silcclient/command.c.
 +
 +      * Remove nicknames from nicklist during server_signoff notify.
 +        Should fix /NAMES bit more.  The affected file is
 +        irssi/src/silc/core/silc-channels.c.
 +
 +      * Added `fingerprint' field to the SilcIDListData in the 
 +        silcd/idlist.h to hold the fingerprint of the client's
 +        public key.
 +
 +        Send the fingerprint of the client's public key in WHOIS
 +        command reply.
 +
 +        Affected files silcd/command.c, and silcd/idlist.[ch].
 +
 +      * Added silc_fingerprint into lib/silcutil/silcutil.[ch] to
 +        create fingerprint from given data.
 +
 +      * Show the fingerprint of the client's public key in WHOIS.
 +        Affected files irssi/src/module-formats.[ch] and
 +        irssi/src/silc/core/client_ops.c.
 +
 +      * Format the multiple same nicknames also during JOIN and
 +        NICK_CHANGE notifys.  Affected file is
 +        lib/silcclient/client_notify.c.
 +
 +      * Do not print error on screen for invalid private message
 +        payload since it can come if someone is sending private
 +        messages with wrong key.  Affected file
 +        lib/silccore/silcprivate.c.
 +
 +      * Fixed multiple concurrent /PING crash.  Affected file
 +        lib/silcclient/command.c.
 +
 +      * Changed the wrong ID encoding.  All IP addresses must be
 +        in MSB first order in encoded format.  They were encoded
 +        wrong and was in LSB format.  Affected files are
 +        silcd/serverid.c, lib/silcutil/silcutil.c.
 +
 +      * Remove silc_net_addr2bin_ne from lib/silcutil/silcnet.[ch].
 +
 +      * Call the `connect' client operation through the scheduler
 +        in case of error.  Affected file lib/silcclient/client.c.
 +
 +      * Call the `failure' client operation even if the error
 +        occurred locally during a protocol.  Affected file is
 +        lib/silcclient/protocol.c.
 +
 +      * Added support of sending LIST command to router from normal
 +        server.  This way normal server can get list of all channels
 +        in the network too.  Fixed the channel list sending in the
 +        server too.  Affected files are silcd/command.c, and
 +        silcd/command_reply.[ch].
 +
 +      * Added silc_server_update_channels_by_server and
 +        silc_server_remove_channels_by_server.  They are used during
 +        disconnection of primary router and in backup router protocol.
 +        Affected file silcd/server_util.[ch], silcd/server.c and
 +        silcd/server_backup.c.
 +
 +      * Fixed channel adding to global list in IDENTIFY command
 +        reply in server.  Affected file silcd/command_reply.c.
 +
 +Sat Nov 10 21:39:22 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * If the incoming packet type is REKEY or REKEY_DONE process
 +        that packet always synchronously.  Fixes yet another MAC
 +        failed error on slow (dialup) connections.  Affected file
 +        lib/silcclient/client.c and silcd/server.c.
 +
 +Thu Nov  8 22:21:09 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Call check_version SKE callback for initiator too.  Affected
 +        file lib/silcske/silcske.c.
 +
 +      * Implemented fix for security hole found in the SKE that was
 +        fixed in the specification few days back; the initiator's
 +        public key is now added to the HASH value computation.
 +        Added backwards support for the old way of doing it too, for
 +        old clients and old servers.  Affected file is
 +        lib/silcske/silcske.c.
 +
 +      * Enabled mutual authentication by default in SKE.  If initiator
 +        is not providing mutual authentication the responder will
 +        force it.  This will provide the proof of posession of the
 +        private key for responder.  The affected files are
 +        lib/silcclient/protocol.c and silcd/protocol.c.
 +
 +      * Do not cache anymore the server's public key during SKE.
 +        We do mutual authentication so the proof of posession of
 +        private key is done, and if the server is authenticated in
 +        conn auth protocol with public key we must have the public
 +        key already.  Affected file silcd/protocol.c.
 +
 +      * Added new global debug variable: silc_debug_hexdump.  If
 +        it is set to TRUE SILC_LOG_HEXDUMP will be printed.  Affected
 +        file lib/silcutil/silclog.[ch].
 +
 +      * Fixed compilation warning due to char * -> const char *.
 +        Affected files lib/silcutil/silcnet.h, and
 +        lib/silccore/silcauth.[ch].
 +
 +Wed Nov  7 20:43:03 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed CMODE command when new channel key was created.  If
 +        the creation failed the old key was removed.  Next time giving
 +        same command would crash the server since the old key was
 +        freed already.  Affected file silcd/command.c.
 +
 +      * Fixed the silc_server_announce_get_channels to not crash
 +        on reconnect.  Affected file silcd/server.c.
 +
 +Wed Nov  7 17:15:07 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added silc_log_set_debug_string function to set a regex
 +        string to match for debug output.  Only the function names,
 +        or filenames matching the given debug string is actually
 +        printed.  This way it is possible to filter out those debug
 +        strings that user is not interested in.
 +
 +        Fixed a bug in silc_string_regexify.
 +
 +        Affected files lib/silcutil/silclog.[ch], and
 +        lib/silcutil/unix/silcunixutil.c.
 +
 +      * Changed the -d options in both server and Irssi SILC client
 +        to take the debug string as argument.  Affected files
 +        silcd/silcd.c and irssi/src/silc/core/silc-core.c.
 +
 +Tue Nov  6 21:31:54 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added silc_hash_babbleprint to create a Bubble Babble
 +        Encoded fingerprint.  The encoding is developed by Antti
 +        Huima (draft-huima-babble-01.txt), and it creates human
 +        readable strings out of binary data.  Affected file
 +        lib/silccrypt/silchash.[ch].
 +
 +      * Print the babble print now in addition of fingerprint as well
 +        in Irssi SILC client.  Affected files are
 +        irssi/src/fe-common/silc/module-formats.[ch],
 +        irssi/src/fe-common/silc/core/client_ops.c.
 +
 +Sun Nov  4 23:37:28 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a security problem found in SKE.  The initiator's
 +        public key too is now added to the HASH hash value creation
 +        which is signed by the responder to create the SIGN value.
 +        This will prevent anyone in the middle to lie to the responder
 +        about the initiator's public key.  If this is done now, the
 +        man in the middle will get caught.  Updated the protocol
 +        specification.
 +
 +Sun Nov  4 11:43:53 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Better installation directory handling.  Configure module
 +        paths and other paths automatically to example_silc* files
 +        in doc/.  A patch by toma.
 +
 +      * Fixed compiler warning from MPI library, and from SILC RNG.
 +        A patch by johnny.
 +
 +      * Added SILC_SERVER_PID_FILE to define the pid file for server.
 +        It can be configured with ./configure.  A patch by toma.
 +
 +Sat Nov  3 23:48:23 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Find correct make to use in prepare-clean.  A patch by
 +        toma.  Affected file prepare-clean.
 +
 +Sat Nov  3 22:04:00 PST 2001  Brian Costello <bc@mksecure.com>
 +
 +      * Added irssi variables use_auto_addr, auto_bind_ip,
 +          auto_bind_port and auto_public_ip.
 +
 +      * Changed the interface for silc_client_send_key_agreement
 +          in lib/silcclient/silcapi.h
 +
 +      Affected files:
 +
 +         irssi/src/silc/core/silc-core.c
 +         irssi/config
 +         lib/silcclient/silcapi.h
 +         irssi/src/silc/core/silc-channels.c
 +         lib/silcclient/client_keyagr.c
 +         irssi/docs/help/key
 +
 +Sat Nov  3 17:48:55 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added silc_pkcs_public_key_compare to compare two 
 +        public keys.  Affected file lib/silccrypt/silcpkcs.[ch].
 +
 +      * Check that the client who set the founder mode on the
 +        channel is the same client that is giving the founder
 +        mode to itself.  It is done by comparing the saved public
 +        key (it is saved even in the authentication is passphrase).
 +        Affected file silcd/command.c.
 +
 +Fri Nov  2 18:52:08 EST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not process packet for disconnected socket connection.
 +        Affected file lib/silccore/silcpacket.c.
 +
 +      * Process the DISCONNECT packet through scheduler in the
 +        client library.  Affected file lib/silcclient/client.c.
 +
 +      * Fixed the silc_client_packet_parse to not to increase
 +        the packet sequence number if the conn->sock and the 
 +        current socket connection is not same.  This can happen
 +        for example during key agreement when the conn includes
 +        multiple socket connections (listeners).  Affected file
 +        lib/silcclient/client.c.
 +
 +      * The sender of the file transfer request now provides also
 +        the pointer (listener) for the key exchange protocol.  If
 +        the listener cannot be created then it sends empty key
 +        agreement and lets the receiver provide the listener.
 +
 +        Added `local_ip' and `local_port' arguments to the
 +        silc_client_file_send.  If they are provided they are used,
 +        if not then it will attempt to find local IP address, if
 +        not found or bind fails then the remote client will provide
 +        the listener.
 +
 +        Affected files are lib/silcclient/client_ftp.c and
 +        lib/silcclient/silcapi.h.
 +
 +      * Extended the FILE SEND command to support defining the
 +        local IP and port for key exchange listener.  They are
 +        optional.  Affected file irssi/src/silc/core/silc-servers.c.
 +
 +Thu Nov  1 22:10:07 EST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Defined to WHOIS command reply the sending of fingerprint
 +        of the client's public key (if the proof of posession of the
 +        corresponding private key is verified by the server).
 +        Updated to the protocol specification.
 +
 +      * Added support of receiving the client's public key's 
 +        fingerprint in command reply in client library.  Affected
 +        file is lib/silcclient/command_reply.c, and
 +        lib/silcclient/idlist.[ch].
 +
 +Thu Nov  1 18:06:12 EST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not send over 128 chars long nickname to the server
 +        in NICK command.  Affected file lib/silcclient/command.c.
 +
 +      * Do not send over 256 chars long channel names to the server
 +        in JOIN command.  Affected file lib/silcclient/command.c.
 +
 +Tue Oct 30 22:48:59 EST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Assure that silc_server_close_connection cannot be called
 +        twice for same socket context.  Affected file is
 +        silcd/server.c.
 +
 +Tue Oct 30 16:58:14 EST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Send error message to application if opening file for
 +        writing during file transfer fails.  Affected file is
 +        lib/silcclient/client_ftp.c.
 +
 +        Remove all file transfer sessions for a client that we're
 +        removing from ID cache.
 +
 +        Affected file is lib/silcclient/client_ftp.c.
 +
 +      * Fixed silc_net_addr2bin to return correct address.  Affected
 +        file lib/silcutil/[unix/win32]/silc[unix/win32]net.c.
 +
 +      * Fixed file transfer session removing on signoff notify.
 +        Affected file irssi/src/silc/core/silc-servers.c.
 +
 +      * Added the SilcClientFileError to be returned in the monitor
 +        callback.  Added NO_SUCH_FILE and PERMISSION_DENIED errors.
 +        Affected file lib/silcclient/silcapi.h.
 +
 +Mon Oct 29 17:43:04 EST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a crash in silc_client_ftp_free_sessions and
 +        silc_client_ftp_session_free_client.  Affected file
 +        lib/silcclient/client_ftp.c.
 +
 +      * Added `disabled' field in the SilcChannelEntry in the server
 +        to indicate if the server entry is disabled.  Affected file
 +        silcd/idlist.h, silcd/command[_reply].c.
 +
 +      * SILC server adds now /var/run/silcd.pid everytime it is
 +        started.  Affected file silcd/silcd.c.
 +
 +      * Added silc_server_packet_send_clients to send a packet to
 +        the provided table of client entries.  Affected file
 +        silcd/packet_send.[ch].
 +
 +      * Fixed a crash in client resolving in client_prvmsg.c in 
 +        client library.  Affected file lib/silcclient/client_prvmsg.c.
 +
 +      * Do not actually remove the client directly from ID cache
 +        during SERVER_SIGNOFF, but invalidate it.  This way we
 +        preserve the WHOWAS info for the client.  Affected file
 +        silcd/server_util.c.
 +
 +      * Fixed SERVER_SIGNOFF notify handling in the server.  The
 +        server is now able to process incoming SERVER_SIGNOFF notify
 +        for a server that it doesn't even know about.  It will remove
 +        the clients provided in the notify.  Affected file
 +        silcd/packet_receive.c.
 +
 +      * Check for partial packet in data queue after every packet that
 +        was found from the queue.  Return and wait for more data if 
 +        there is partial data in queue.  Affected file is
 +        lib/silccore/silcpacket.c.
 +
 +Sun Oct 28 18:46:27 EST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added SilcClietFileError enum to indicate error in
 +        file transfer.  Added SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT
 +        and SILC_CLIENT_FILE_MONITOR_ERROR new monitor statuses.
 +        Affected files lib/silcclient/silcapi.h and
 +        lib/silcclient/client_ftp.c.
 +
 +      * Check that newsize in silc_buffer_realloc is larger than
 +        the old buffer's size.  Affected file lib/silcutil/silcbufutil.h.
 +
 +      * Added better monitor of file transfers.  It now monitors
 +        key agreement protocol during the file transfer too.  Added
 +        error reporting too.  Affected files
 +        irssi/src/silc/core/silc-servers.c,
 +        irssi/src/fe-common/silc/module-formats.[ch].
 +
 +      * Wrote a help file for FILE command.
 +
 +      * Added silc_rng_global_get_byte_fast to get not-so-secure
 +        random data as fast as possible.  Random data is read from
 +        /dev/urandom if available and from the SILC RNG if not
 +        available.  It is used in padding generation.  Affected file
 +        lib/silccrypt/silcrng.[ch].
 +
 +      * All packets in client library are now processed synchronously.
 +        Optimized packet processing a lot.  Affected file
 +        lib/silcclient/client.c.
 +
 +      * All server connection packets are processing synchronously
 +        now in server, to optimize packet processing.  Affected file
 +        silcd/server.c.
 +
 +      * Include files are installed now only in Toolkit distribution
 +        if make install is given.  Affected files: all Makefile.am's.
 +
 +Thu Oct 25 22:44:06 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Assure that silc_client_notify_by_server_resolve does not
 +        resolve the client information multiple times.  If it cannot
 +        be found by the first it cannot be found at all.  Affected
 +        file lib/silcclient/client_notify.c.
 +
 +      * Fixed WHOWAS command reply calling.  Affected file
 +        lib/silcclient/command_reply.c.
 +
 +      * Removed all references to silc_idlist_get_client from the
 +        Irssi SILC client since that call is internal call used by
 +        the library.  The Irssi SILC client will use now client
 +        retrieval functions found in silcapi.h.
 +
 +      * Fixed a bug in resolving nickname info before sending
 +        private message.  It used freed memory.  Affected file
 +        irssi/src/silc/core/silc-servers.c.
 +
 +Thu Oct 25 19:04:49 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Assure my_channels statistics cannot go negative in server.
 +        Affected files silcd/server.c, silcd/server_util.c.
 +
 +Wed Oct 24 19:53:05 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Upgraded dotconf 1.0.2 to 1.0.6 in lib/dotconf.
 +
 +Tue Oct 23 13:51:19 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Win32 Toolkit changes.  Affected files
 +        win32/silc.dsw, win32/libsilc/libsilc.def,
 +        win32/libsilcclient/libsilc.def,
 +        lib/silcutil/silcutil.c, and
 +        lib/sftp/sftp_fs_memory.c.
 +
 +Mon Oct 22 16:35:05 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added silc_net_localip to return local host's IP address.
 +        Affected file lib/silcutil/silcnet.[ch].
 +
 +      * If key exchange or rekey protocol is active for a connection
 +        parse all packets syncronously since there might be packets
 +        in packet queue that we are not able to process without first
 +        processing packets before them.  Affected file silcd/server,
 +        lib/silcclient/client.c.
 +
 +      * SilcPacketParserCallback now returns TRUE or FALSE to indicate
 +        whether library should continue processing the packet. 
 +        Affected file lib/silccore/silcpacket.h.
 +
 +      * Added SilcSFTPMonitor callback, SilcSFTPMonitors and
 +        SilcSFTPMonitorData to SFTP server to monitor various
 +        SFTP client requests.  Affected file lib/silcsftp/silcsftp.h,
 +        lib/silcsftp/sftp_server.c.
 +
 +      * Added silc_file_size to return file size.  Affected file
 +        lib/silcutil/silcutil.[ch].
 +
 +      * Implemented the file transfer support for the client library.
 +        Added preliminary support for simple client to client one-file
 +        transmission.  Affected file lib/silcclient/client_ftp.c,
 +        lib/silccilent/client.[ch].
 +
 +      * Added new local command FILE to the Irssi SILC Client.
 +        It is used to perform the file transfer.  It has subcommands
 +        SEND, RECEIVE, SHOW and CLOSE.  Affected files
 +        irssi/src/silc/core/client_ops.c, 
 +        irssi/src/silc/core/silc-server.[ch].
 +
 +Mon Oct 22 12:50:08 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Relay the SILC_PACKET_FTP in the server.  Affected files
 +        silcd/server.c and silcd/packet_receive.c.
 +
 +Sun Oct 21 20:21:02 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Renamed silc_file_read and silc_file_write to functions
 +        silc_file_readfile and silc_file_writefile.  Added function
 +        silc_file_open and silc_file_close.  Affected files 
 +        lib/silcutil/silcutil.[ch].
 +
 +Thu Oct 18 20:58:13 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Resolve the client info when received private message or
 +        channel message for a client which nickname we don't know.
 +        Affected files lib/silcclient/client_prvmsg.c and
 +        lib/silcclient/client_channel.c.
 +
 +      * Do not crash in /KEY if client is not connected.  Affected
 +        file irssi/src/silc/core/silc-channels.c.
 +
 +      * Added SilcClientStatus field to the SilcClientEntry in the
 +        lib/silcclient/idlist.h.
 +
 +        Added SILC_CLIENT_STATUS_RESOLVING to mark that the entry
 +        is incomplete and is being resolved, it won't be resolved
 +        twice.
 +
 +        Make sure also that USERS command reply does not resolve
 +        twice information.  Affected file is
 +        lib/silcclient/command_reply.c.
 +
 +        Make sure that silc_client_get_clients_by_list does not
 +        resolve twice same information.
 +
 +      * Check for valid client->id in the silc_server_free_client_data.
 +        Affected file silcd/server.c.
 +
 +      * Fixed /GETKEY nick@server not to crash if the server entry
 +        is not found.  Affected file lib/silcclient/command.c.
 +
 +      * Fixed the silc_server_check_cmode_rights to check the
 +        requested modes correctly.  Affected file silcd/command.c.
 +
 +Thu Oct 18 12:10:22 CEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Better checks for non-printable chars in nick added.
 +        Affected file silcd/command.c.
 +
 +Thu Oct 18 09:18:58 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Call the silc_server_udpate_servers_by_server in the
 +        primary router that comes back online in the backup resuming
 +        protocol.  Otherwise it routes packets wrong.  Affected file
 +        silcd/server_util.[ch], silcd/server_backup.c.
 +
 +Wed Oct 17 16:51:18 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added SILC_STR_UI8_[N]STRING[_ALLOC] formats to the
 +        lib/silcutil/silcbuffmt.[ch].
 +
 +      * Redefined the SILC packet header to include the padding
 +        length.  Affected file lib/silccore/silcpacket.[ch].
 +
 +      * Added SILC_PACKET_PADLEN_MAX macro to return the padding
 +        length for maximum padding up to 128 bytes).  Affected
 +        file lib/silccore/silcpacket.h.
 +
 +      * Removed all backwards support for old 0.5.x MAC thingies.
 +        The SILC packet header change makes it impossible to be
 +        backwards compatible.
 +
 +      * Send the ENDING packet with timeout in the backup resuming
 +        protocol.  This is to assure that all routers has connected
 +        to the primary router.  Affected file silcd/server_backup.c.
 +
 +      * Changed the RNG to take the first IV from random data.  It
 +        used to take it from zero actually.  Changed the RNG also
 +        to use /dev/urandom during session.  /dev/random is used
 +        in initialization.  Affected file lib/silccrypt/silcrng.[ch].
 +
 +Tue Oct 16 20:45:49 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Changed the SILC packet header to have the first two bytes
 +        (the packet length) encrypted.  Affected files aroung the
 +        code tree, lib/silccore/silcpacket.[ch].  Removed the
 +        SilcPacketCheckDecrypt callback.  It is not needed anymore
 +        since the silc_packet_receive_process will determine now
 +        whether the packet is normal or special.
 +
 +      * Implemented the unidirectional MAC keys.  Affected files
 +        lib/silcske/silcske.c, silcd/protocol.c and
 +        lib/silcclient/protocol.c.
 +
 +      * Implemented the packet sequence number to the MAC computation.
 +        Affected files lib/silccore/silcpacket.c, silcd/protocol.c,
 +        silcd/packet_send.c, silcd/server.c, lib/silcclient/client.c,
 +        lib/silcclient/protocol.c.
 +
 +Mon Oct 15 17:42:55 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Allow backup router to announce servers.  All servers
 +        announced by backup router are added to the global list
 +        automatically.  Update hte server's socket to our primary
 +        router also when backup router announces a server.
 +        Affected file silcd/packet_receive.c.
 +
 +      * Do not update the client->router in the function
 +        silc_server_udpate_clients_by_server if the client is on
 +        global list.  We might fail to find any specific server
 +        for locally connected clients and local cell clients.  They
 +        should still use the `from' and not `to' as client->router.
 +        This fixes backup router resuming protocol.  Affected file
 +        silcd/server_util.c.
 +
 +      * Decrease channel statistics count only if the channel
 +        deletion worked.  Affected files are silcd/server.c and
 +        silcd/server_util.c.
 +
 +      * Added silc_server_update_servers_by_server to update origin
 +        of all server entries.  Used during backup router protocol.
 +        Affected files silcd/server_util.[ch], silcd/server.c. and
 +        silcd/backup_router.c.
 +
 +      * ROBODoc documented the lib/silccrypt/silchmac.h.  Added new
 +        function silc_hmac_init, silc_hmac_update, silc_hmac_final,
 +        silc_hmac_get_hash and silc_hmac_get_name.  Affected file
 +        lib/silccrypt/silchmac.c.
 +
 +Sun Oct 14 18:28:22 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Assure that router cannot reroute the same channel message
 +        to the sender.  Affected file silcd/packet_receive.c.
 +
 +Sat Oct 13 12:46:18 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Made better checks that the channel message is not sent
 +        to the router it came from.  Affected file is
 +        silcd/packet_send.c.  Fixed memory leak too.
 +
 +      * Announce informations for incoming router connection, but
 +        only after checking if it is replaced by backup router.
 +        Affected file silcd/packet_receive.c.
 +
 +Fri Oct 12 18:37:24 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the backup resuming protocol to work in multiple
 +        router environment.  Affected file silcd/server_backup.c.
 +
 +      * Route packet only to one router in the function
 +        silc_server_packet_send_to_channel.  Affected file is
 +        silcd/packet_send.c.
 +
 +      * Fixed silc_server_send_notify_dest to set the broadcast
 +        flag.  Fixed the silc_server_send_notify_topic to actually
 +        send the TOPIC_CHANGE notify and not SERVER_SIGNOFF notify.
 +        Affected file silcd/packet_send.c.
 +
 +      * Changed the SFTP Filesystem interface.  Changed the
 +        SilcSFTPFilesystemStruct to SilcSFTPFilesystemOps to include
 +        the filesystem operation function.  The SilcSFTPFilesystem
 +        is now a context that is allocated by all filesystem allocation
 +        functions and it already includes the operations structure
 +        and filesystem specific context.  It is given as argument
 +        now to the silc_sftp_server_start.  This made the interface
 +        a bit cleaner.  Affected file lib/silcsftp/silcsftp[_fs].h,
 +        lib/silcsftp/sftp_fs_memory.c and sftp_server.c.
 +
 +Thu Oct 11 22:19:26 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Changed the backup router adding and getting interfaces
 +        in the server.  The router that will be replaced by the
 +        specified backup router is now sent as argument.  Affected
 +        files silcd/serverconfig.[ch], silcd/backup_router.[ch], and
 +        silcd/server.c.
 +
 +      * Added silc_net_addr2bin_ne to return the binary form of
 +        the IP address in network byte order.  Affected files
 +        lib/silcutil/[unix/win32].silc[unix/win32]net.[ch].
 +
 +Thu Oct 11 12:14:19 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Check for existing server ID in silc_server_new_server
 +        and in silc_server_connect_to_router_final and remove the
 +        old entry if it exists.  Affected file silcd/packet_receive.c,
 +        silcd/server.c.
 +
 +      * Send the channel message always to only one router, either
 +        in upstream or downstream.  Affected file is
 +        silcd/packet_send.c.
 +
 +Tue Oct  9 17:45:43 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Wrote the definition of the backup resuming protocol to the
 +        protocol specification.
 +
 +      * Removed one redundant channel key generation from normal
 +        server during joining procedure.  Removed one redundant
 +        channel key sending from server to router during joining
 +        procedure.  Affected file silcd/command.c.
 +
 +      * Made minor bugfixes to the backup router resuming protocol.
 +        Affected file silcd/server_backup.c, server.c.
 +
 +Mon Oct  8 16:47:42 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added --disable-asm configuration option.  Affected files
 +        configure.in.pre, lib/silcmath/mpi/configure.in.  A patch
 +        by salo.
 +
 +      * Implemented the backup resuming protocol that is used to
 +        resume the primary router position in the cell after the
 +        primary router comes back online.  Affected files
 +        silcd/server_backup.[ch], silcd/server, silcd/packet_receive.c,
 +        and silcd/server_util.[ch].
 +
 +Sun Oct  7 12:29:25 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Sleep two (2) seconds after sending QUIT command to server.
 +        Affected file lib/silcclient/command.c.
 +
 +      * Assure that if outgoing data buffer is pending do not force
 +        send any data.  Affected file silcd/packet_send.c.
 +
 +      * Assure that if outgoing data buffer is pending do not force
 +        send any data.  Affected file lib/silcclient/client.c.
 +
 +      * Implemented the backup router support when the primary router
 +        goes down.  The servers and routers can now use the backup
 +        router as new primary router without loosing connectivity.
 +
 +Sat Oct  6 21:18:54 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added new SILC_IDLIST_STATUS_DISABLED flag for entries
 +        in the server to indicate disabled entry.  All data read
 +        from the connection will be ignored and no data is sent
 +        for entry that is disabled.  Affected files are
 +        silcd/idlist.h, silcd/server.c.
 +
 +Fri Oct  5 00:03:29 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Created SFTP client and server test programs in the
 +        lib/silcsftp/tests directory.
 +
 +Wed Oct  3 23:31:42 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Implemented memory filesystem (virtual filesystem) for
 +        SFTP server.  Affected file lib/silcsftp/silcsftp_fs.h,
 +        sftp_fs_memory.c.
 +
 +Sun Sep 30 22:10:57 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Implemented the SFTP (SSH File Transfer Protocol) to the
 +        lib/silcsftp.  It includes SFTP client and SFTP server
 +        implementations.
 +
 +Sun Sep 30 10:35:44 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Moved lib/silccore/silcprotocol.[ch] to the
 +        lib/silcutil library.
 +
 +      * Added silc_buffer_format_vp and silc_buffer_unformat_vp to
 +        take variable argument list pointer as argument.  Affected
 +        file lib/silcutil/silcbuffmt.[ch].
 +
 +      * Added silc_buffer_set function that is used to set data
 +        to a SilcBuffer that is not allocated at all (SilcBufferStruct).
 +        Affected file lib/silcutil/silcbuffer.h.
 +
 +      * Changed various routines in the core library to use the new
 +        silc_buffer_set instead of allocating new buffer only for
 +        temporary purposes.
 +
 +      * Added 64-bit value formatting and unformatting support to the
 +        silc_buffer_[un]format routines.  Affected file is
 +        lib/silcutil/silcbuffmt.[ch].
 +
 +        Added also 64-bit macros: SILC_GET64_MSB and SILC_PUT64_MSB,
 +        to includes/bitmove.h.
 +
 +Fri Sep 28 21:30:10 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed channel user mode saving in client library.  Affected
 +        file lib/silcclient/command[_reply].c.
 +
 +Thu Sep 27 22:52:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Defined the file transfer to the SILC Protocol.  Added
 +        new packet type SILC_PACKET_FTP and defined File Transfer
 +        Payload.  The mandatory file transfer protocol is SFTP
 +        (SSH File Transfer Protocol).  Affected file in addition
 +        of the internet draft is lib/silccore/silcpacket.h.
 +
 +      * Deprecated the SILC_PACKET_CELL_ROUTERS and defined new 
 +        packet SILC_PACKET_RESUME_ROUTER instead.  The new packet
 +        is used as part of backup router protocol when the primary
 +        router of the cell is back online and wishes to resume
 +        the position as primary router.
 +
 +      * Redefined the MAC generation keys in the protocol.  The
 +        same key is not used anymore in both direction.  Both
 +        direction will now use different keys for sending and
 +        receiving.  This fixes a potential security flaw.  This
 +        change causes incompatibilities in the protocol.
 +
 +      * Redefined also the MAC computation from the packet.
 +        An packet sequence number is now added to the MAC 
 +        computation.  This prevents possible replay attacks against
 +        the protocol.  This change too causes incompatibilities
 +        in the protocol.
 +
 +        Added `sequence' field to the SilcPacketContext to hold
 +        the current sequence number for the packet.
 +
 +Wed Sep 26 20:15:22 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added `created' field to the SilcIDListData in the file
 +        silcd/idlist.h to indicate the time when the entry was
 +        created.
 +
 +      * Added `created' field to the SilcChannelEntry too.  Affected
 +        file silcd/idlist.h.
 +
 +      * Added `creation_time' aguments to all the announcement functions
 +        in the server.  If it is provided then only the entries that
 +        was created after the provided time frame are actually
 +        announced.  Affected file silcd/server.[ch].
 +
 +      * The protocol says that the Channel ID's IP address must be
 +        based on the router's IP address.  Added check for this in
 +        the silc_server_new_channel when processing incoming New Channel
 +        Payload.  Affected file silcd/packet_receive.c.
 +
 +      * Print out the correct version with --version in SILC client.
 +        Affected file irssi/src/silc/core/silc-core.c.
 +
 +Mon Sep 24 17:19:00 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed WHOWAS command to check for completnes of the client
 +        entry always, not just when the command is coming from client.
 +        Affected file silcd/command.c.
 +
 +      * Added new function silc_server_packet_queue_purge to purge the
 +        outgoing data queue to the network.  After the function returns
 +        it is guaranteed that the outgoing packet queue is empty.
 +        Affected file silcd/packet_send.[ch].
 +
 +      * Purge the outgoing packet queue in the rekey protocol's final
 +        callback to assure that all rekey packets go to the network
 +        before quitting the protocol.  Affected file silcd/server.c.
 +
 +      * Added silc_client_packet_queue_parse as similar function as
 +        in server to the client library.  The affected file is
 +        lib/silcclient/client.c.
 +
 +Sun Sep 23 15:15:53 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Splitted silcd/server.c and created silcd/server_util.[ch]
 +        for utility functions.
 +
 +      * Added new socket flag SILC_SF_DISABLED to indicate that the
 +        connection is open but nothing can be sent to or received from
 +        the connection.  Affected file lib/silcutil/silsockconn.[ch].
 +        The checking for disabled socket is checked in the low level
 +        silc_socket_write and silc_socket_read functions.
 +
 +Thu Sep 20 23:11:28 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Allow only nicknames and channel names that fits into the
 +        7-bit unsigned char ASCII set.  Affected file silcd/command.c.
 +
 +Thu Sep 20 18:04:12 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * When processing JOIN command reply in server check that if
 +        the channel exists in our global list we'll move it the local
 +        list.  Affected file silcd/command_reply.c.
 +
 +      * Fixed the check whether client is joined on the channel already
 +        in JOIN command.  Affected file lib/silcclient/command.c.
 +
 +      * Fixed the JOIN command reply to check whether the channel
 +        already exists.  Affected file lib/silcclient/command_reply.c.
 +
 +Wed Sep 19 22:58:32 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added silc_ske_status_string to map the SKE error numbers
 +        to readable strings.  The affected files are
 +        lib/silcske/silcske[_status].[ch].
 +
 +Tue Sep 18 22:50:41 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not show the private channels on the WHOIS channel list
 +        as it is not allowed by the protocol.  The affected file is
 +        silcd/server.c.
 +
 +Sun Sep 16 12:32:58 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Assure that the packet length digged from the actual packet
 +        is something sensible in the silc_packet_decrypt_rest_special
 +        in lib/silccrypt/silcpacket.c.
 +
 +      * Free and NULL the allocated pointer in silc_hmac_alloc if
 +        the HMAC allocation fails.  The affected file is
 +        lib/silccrypt/silchmac.c.
 +
 +      * Print the selected security properties to the log files in
 +        the server.  Affected file silcd/protocol.c.
 +
 +      * Add SKE's reference counter even if calling the completion
 +        callback manually.  Otherwise it goes negative, although it
 +        does not cause any problems.  The affected file is
 +        lib/silcske/silcske.c.
 +
 +      * Remove the client entry with short timeout after giving the
 +        KILL command.  Affected file lib/silcclient/command.c.
 +
 +      * Fixed to send error reply in WHOIS and IDENTIFY commands in
 +        case all found clients are already disconnected (WHOWAS would
 +        found them) in the server.  Affected file silcd/command.c.
 +
 +      * Update the last_receive (time of last data received) to be 
 +        updated only when received private or channel message so that
 +        the idle time showed in WHOIS makes more sense.
 +
 +      * Added boolean field `valid' in to the SilcClientEntry in the
 +        client library to indicate whether the entry is valid or not.
 +        This fixes the nickname change bug on channel when changing
 +        the nickname to be same than the old (like nick to Nick) the
 +        nickname formatter doesn't set the new nick anymore to Nick@host.
 +        Affected file lib/silcclient/idlist.[ch].
 +
 +      * Now actually fixed the nickname changing on disconnection.
 +        Added new function silc_change_nick to the Irssi SILC Client.
 +        Affected file irssi/src/silc/core/client_ops.c,
 +        irssi/src/silc/core/silc-nicklist.[ch].
 +
 +Sat Sep 15 13:29:17 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Check that the public key exists in the GETKEY command before
 +        trying to encode it.  Affected file silcd/command.c.
 +
 +      * Print some notifications on received public keys with GETKEY
 +        command in the Irssi SILC Client.  Affected files are
 +        irssi/src/fe-common/silc/module-formats.[ch],
 +        irssi/src/silc/core/client_ops.c.
 +
 +      * Use IDENTIFY command to resolve the server information in the
 +        GETKEY command instead of INFO command.  Affected file
 +        lib/silcclient/command.c.
 +
 +      * All command reply functions in the client library now calls
 +        the pending command reply callbacks even if an error has
 +        occurred.  The server has done this a long time and now it was
 +        time to move the client library to this as well.  Now all
 +        errors can be delivered back to the pending command reply
 +        callbacks if necessary.  Affected files are
 +        lib/silcclient/command[_reply].[ch].
 +
 +      * Change the nickname on disconnection back to the username
 +        because in reconnect the server will enforce it to it anyway.
 +        Affected file irssi/src/silc/core/silc-servers.c.
 +
 +      * Fixed a config file parsing bug in the Irssi SILC client.
 +        Affected file irssi/src/silc/core/clientconfig.c.
 +
 +Thu Sep 13 23:11:18 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * When printing the channel mode on JOIN, verify that the
 +        channel key and channel's HMAC are valid.  Affected file
 +        irssi/src/silc/core/client_ops.c.
 +
 +Thu Sep 13 20:24:52 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added defines SILC_DEFAULT_CIPHER, SILC_DEFAULT_HMAC,
 +        SILC_DEFAULT_HASH and SILC_DEFAULT_PKCS in the file
 +        lib/silccrypt/[silccipher.h|silchmac.h|silchash.h|silcpkcs.h].
 +
 +      * Removed channel key rekey task deleting from the function
 +        silc_server_save_channel_key.  Affected file silcd/server.c.
 +        Added explicit timeout task context instead that is used to   
 +        delete the task if we are registering a new task before the
 +        new task has elapsed.
 +
 +      * When channel key rekey occurs the client library now saves
 +        the old channel key for a short period of time (10 seconds) and
 +        is able to use it in case some is still sending channel
 +        messages encrypted with the old key after the rekey.  Affected
 +        file lib/silcclient/[idlist.h|client_channel.c].
 +
 +Sun Sep  9 15:49:16 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added check to the silc_server_new_id_real to not accept
 +        new ID if it is the sender's own ID.  Affected file is
 +        silcd/packet_receive.c.
 +
 +      * Assure that we do not announce ourself or the one we've
 +        sending our announcements when we're router and are announcing
 +        servers to our primary router.  Affected file silcd/server.c.
 +
 +      * Fixed silc_server_command_identify_check_client to assemble
 +        correct WHOIS packet.  It send corrupted WHOIS packet and
 +        caused problem with router to router connections.  Affected
 +        file silcd/command.c.
 +
 +        Fixed also silc_server_command_whois_check the same way
 +        as for the IDENTIFY command.
 +
 +      * Added new SilcIDListStatus to the server in the SilcIDListData
 +        structure.   The status now includes the current status of
 +        the entry (like registered, resolved etc.).  Affected file
 +        silcd/idlist.[ch].  Defined a bunch of different status types
 +        as well.  This replaced the old boolean registered field as well.
 +
 +        Added resolve_cmd_ident field to the SilcClientEntry structure
 +        too so that if the entry is for example being resolved so 
 +        another command may attach to the same pending command reply
 +        without requiring to resolve the same entry again.  This concept
 +        should optimize the WHOIS and the IDENTIFY resolving under
 +        heavy load by taking away unnecessary resolving for entries
 +        that are being resolved already.
 +
 +        Added support for adding multiple pending commands for one
 +        command idenfier.  Affected file silcd/command[_reply].[ch].
 +
 +      * Fixed WHOIS and IDENTIFY save to remove the cache entry
 +        before deleting the data.  Otherwise the hash table will have
 +        freed data in comparison functions.  Affected file is
 +        silcd/command_reply.c.
 +
 +      * Fixed silc_idlist_replace_client_id to add the new entry to
 +        the cache with NULL nickname.  Otherwise there will be invalid
 +        memory as the nickname after the nickname is freed.  Affected
 +        file silcd/packet_receive.c.
 +
 +      * Fixed the silc_idlist_get_clients_by_hash.  The entries was
 +        saved into wrong slots because the previous number of entries
 +        was not taken into account.  Affected file silcd/idlist.c.
 +        Fixed same thing in silc_idlist_get_clients_by_nickname too.
 +
 +      * If we are router and we receive JOIN notify to a channel that
 +        does not have any users then notified client is marked as the
 +        channel founder, as it is it.  The affected file is
 +        silcd/packet_receive.c
 +
 +      * Added to the extended hash table API's table_del_*ext functions
 +        the destructor as argument too, so that the caller can decide
 +        which destructor to use or whether to use destructor at all.
 +        Affected file lib/silcutil/silchashtable.[ch].
 +
 +      * Fixed ID Cache purging.  It actually deleted the entries from
 +        the hash table after the data was freed.  The hash table ended
 +        up comparing freed memory.  The affected file is
 +        lib/silccore/silcidcache.c.
 +
 +Sat Sep  8 10:22:10 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed Irssi SILC client's KILL command's HELP syntax.
 +
 +      * The USERS command now resolves the detailed user information
 +        if the userinfo field is missing.  Affected file is
 +        lib/silcclient/command_reply.c.
 +
 +      * Do not print error in silc_file_read if the read file does
 +        not exist.  Just silently return NULL.  Affected file is
 +        lib/silcutil/silcutil.c.
 +
 +      * Fixed the silc_log_output to not wine about NULL filename
 +        and to not create some bogus " " filename.  Affected file is
 +        lib/silcutil/silclog.c.
 +
 +Fri Sep  7 22:16:38 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed various printing bugs on the user interface in the
 +        Irssi SILC Client.  Minor changes that were forgotten from
 +        the release.
 +
 +Fri Sep  7 17:28:37 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the configure.in.pre and the compilation and distribution
 +        environment to support the new autoconf 2.52.  That version is
 +        now required to compile the CVS trunk.
 +
 +Thu Sep  6 12:47:37 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Renamed function silc_parse_nickname to silc_parse_userfqdn
 +        to generally parse user@fqdn format strings.  Affected file
 +        lib/silcutil/silcutil.c.
 +
 +      * Added nickname_format and nickname_force_format fields to the
 +        SilcClientParams structure.  The first one defines the format
 +        for the nicknames that the library will enforce if the receives
 +        multiple same nicknames.  The second one is boolean value and
 +        can be used to force the library to always enforce the format
 +        to the nicknames regardles whether there are multiple nicknames
 +        or not.  This configurable formatting was employed to flexibly
 +        support accessing multiple nicknames from the user interface.
 +        The userinterface can now set the nicknames to what ever format
 +        they prefer.  Affected file lib/silcclient/silcapi.h.
 +
 +        Added function silc_client_nickname_format to the file
 +        lib/silcclient/idlist.c.  It performs the nickname formatting.
 +
 +        Added new field `hostname´ to the SilcClientEntry context.
 +        It holds the hostname of the client.  Affected file is
 +        lib/silcclient/idlist.h.
 +
 +      * Irssi SILC Client sets the nicknames in nick@hostn format.
 +        Fe. priikone@otaku, priikone@otaku2 etc.  Affected file
 +        irssi/src/silc/core/silc-core.c.
 +
 +        The WHOIS printing now also shows both the real nickname and
 +        the formatted nickname so that user knows how to access the
 +        user if there are multiple same nicknames cached.  Affected
 +        file irssi/src/silc/core/client_ops.c.  Changed the WHOIS
 +        printing formatting too to take the hostname now as a separate
 +        argument.  The Affected file is
 +        irssi/src/fe-common/silc/modules-formats.[ch].
 +
 +      * Changed the silc_client_get_clients_local to accept the formatted
 +        nickname as argument.  It accepts the real nickname too but the
 +        formatted nickname can be used to find the true entry from 
 +        multiple entries.  Affected file lib/silcclient/silcapi.h and
 +        lib/silcclient/idlist.c.
 +
 +      * Added nickname_format_parse field to the SilcClientParams.
 +        It is a callback function provided by the application to parse
 +        the nickname out of the formatted nickname string. The library
 +        calls it to get the nickname from the formatted string. Since
 +        the application generally knows better the format of the nickname
 +        string it parses it instead of the library, even though library
 +        encodes the formatted string.  If the callback function is not
 +        provided then the library will use the string as is.  The
 +        affected file is lib/silcclient/silcapi.h.
 +
 +      * All the nickname strings passed to the client library in 
 +        commands are now expected to be formatted nickname strings.
 +        If the command does not support the formatted nickname string
 +        it will assume that the sent string is the actual nickname.
 +        Affected file lib/silcclient/command.c.
 +
 +Tue Sep  4 22:31:28 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added public key authentication support to OPER and SILCOPER
 +        commands in the client library.  Affected file is
 +        lib/silcclient/command.c.
 +
 +Tue Sep  4 12:39:17 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Changed the get_auth_methdod client operation to be asynchronous.
 +        It can be async if the application resolves the authentication
 +        method from the server during the negotiation.  Added new
 +        SilcGetAuthMeth completion callback that the application will
 +        call after resolving the authentication method.
 +
 +        Added function silc_client_request_authentication_method that
 +        the application can use to resolve the authentication method
 +        from the server.  Added also SilcConnectionAuthRequest callback
 +        that the library will call after the server has replied.  The
 +        application can call this function if it does not know the
 +        current authentication method.
 +
 +        Affected files are lib/silcclient/client.c and 
 +        lib/silcclient/silcapi.h.
 +
 +      * The Irssi SILC client now automatically resolves the authentication
 +        method incase any configuration information is not present (and
 +        currently there never is).  The affected file is
 +        irssi/src/silc/core/client_ops.c.
 +
 +      * Fixed public key authentication from the client library.
 +        Affected file lib/silcclient/protocol.c.  Changed also the
 +        protocol specification about the public key authentication in
 +        the connection authentication protocol.  The actual data to be
 +        signed is now computed with a hash function before signing.
 +
 +      * Fixed the public key authentication from the server as well.
 +        Affected file silcd/protocol.c.
 +
 +      * Removed the mlock()'s from the memory allocation routines.
 +        Affected file lib/silcutil/silcmemory.c.  The ./configure does
 +        not check anymore for the mlock().  Affected file is
 +        configure.in.pre.
 +
 +      * Fixed USERS command in server to allow the execution of the
 +        command for private and secret channels if the client sending
 +        the command is on the channel.  Affected file silcd/command.c.
 +
 +      * Fixed silc_client_get_clients_local to return the clients
 +        count correctly.  It could return wrong value.  Affected file
 +        lib/silcclient/idlist.c.
 +
 +Mon Sep  3 20:09:59 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the lib/silcmath/mpi/mpi.h to always use 32-bit data
 +        types.  The assembler optimizations seemed not to like 64-bit
 +        data types.  The assmebler optimizations thus are now enabled
 +        also for BSD systems as opposed to only enable them for Linux.
 +
 +      * Do not check for threads at all on BSD systems.  Affected
 +        file configure.in.pre.
 +
 +      * Removed -n and -h options from the Irssi SILC Client since
 +        they are not used in silc.
 +
 +      * Fixed the prime generation to assure that the first digit
 +        of the generated random number is not zero since our conversion
 +        routines does not like number strings that starts with zero
 +        digit.  If zero digit is seen the random number is regenerated.
 +        This caused some corrupted RSA keys when the zero first digit
 +        was met.  Affected file lib/silcmath/silcprimegen.c.
 +
 +Sun Sep  2 17:17:24 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed WIN32 configuration in the ./configure script.
 +        Fixed to include xti.h on environments that has it.
 +        Patches by Carsten Ilchmann and andrew.
 +
 +Sat Sep  1 00:29:33 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Changed the silc_id_create_client_id to be collision
 +        resistant.  It is now assured that there cannot be created
 +        two same client ID's.  I suspect that some weird bugs in 
 +        the server were actually caused by duplicate Client IDs.
 +        Affected file silcd/serverid.[ch].  A router receiving
 +        new ID now also assures and informs the sending server
 +        if the ID caused collision.
 +
 +      * Changed the silc_id_create_channel_id to also assure that
 +        there are no collisions.
 +
 +Wed Aug 29 17:55:01 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Statement about ignoring the Mutual Authentication flag when
 +        performing rekey with PFS was a bit misleading.  It is ignored
 +        if it was set in the initial negotiation, it cannot be even
 +        set in the rekey.  Fixed in the ke-auth draft.  Started the
 +        new versions of the protocol drafts in the doc/.
 +
 +Sun Aug 26 14:59:15 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a bug in silc_client_command_identify_save when saving
 +        new channel information.  The channel name was no duplicated
 +        and caused crash on exit.  Affected file is
 +        lib/silcclient/command_reply.c.
 +
 +Fri Aug 17 23:07:45 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the getkey command handling in the server.  Send just
 +        empty OK reply to the sender if the key could not be fetched
 +        (but everything else was ok, like the key just was not available).
 +        Changed the public key parameter to optional in the protocol
 +        specs so that empty OK reply can be sent.  Affected file
 +        silcd/command.c.
 +
 +        Added a message to Irssi SILC client to tell to user if the
 +        server did not return a public key.
 +
 +Tue Aug 14 07:29:27 CEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a channel key regeneration bug.  It registered new
 +        timeout tasks exponentially until all system resources were
 +        used.  Affected file silcd/server.c.
 +
 +Sun Aug 12 20:48:14 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added the SILC Document generator to the scripts/silcdoc.
 +        It can be used to generate the Toolkit Reference Manual out
 +        of the source tree.  Internally it will also use the RoboDoc
 +        generator now imported in util/robodoc.
 +
 +Sun Aug 12 12:28:17 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added couple of return's in rekey protocol if error orccurred
 +        during the protocol.  The execution must be terminated.
 +        Affected file silcd/protocol.c.  Also, terminate the protocol
 +        always with timeout.
 +
 +Sat Aug 11 12:36:02 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * The client's Client ID was created initally from the wrong
 +        nickname (it could have been in format nick@host) in the
 +        silc_server_new_client.  Affected file silcd/packet_receive.c
 +
 +Sat Aug 11 00:29:57 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added some SILC_LOG_ERROR's to various error conditions
 +        if client could not be added to ID cache.  Affected files
 +        silcd/packet_receive.c and silcd/server.c.
 +
 +      * When client's sock->user_data is freed, NULL also the 
 +        client->router and client->connection pointers.  Added check
 +        for these pointers being NULL to various places around the
 +        code.  Affected file silcd/server.c.
 +
 +      * Added client->data.registered == TRUE checks to various
 +        places around the code to assure that unregistered client's
 +        are not handled when it is not allowed.  Affected file
 +        silcd/server.c.
 +
 +      * Added `bool registered' fields to all 
 +        silc_idlist_[server|client]_get_* routines to indicate whether
 +        the fetched client needs to be registered or not.  Affected
 +        file silcd/idlist.[ch].
 +
 +      * Add your own entry as registered to the ID cache in the
 +        server.  Affected file server.c.
 +
 +      * Fixed a bug in silc_server_new_server.  The SilcServer was
 +        set as the new server's context instead of SilcServerEntry.
 +        This naturally caused some weird bugs.
 +
 +Thu Aug  9 18:28:37 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not delete the channel rekey task when adding it
 +        for in silc_server_create_channel_key.
 +
 +      * Changed the silc_server_create_channel_key to return
 +        TRUE or FALSE to indicate the success of the channel key
 +        creation.
 +
 +Thu Jul 26 11:32:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed MSVC++ project files and added missing files to
 +        Makefiles under win32/.
 +
 +Wed Jul 25 18:43:54 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not add TCP_NODELAY flag if the operating system
 +        does not have it defined.  Affected files are
 +        lib/silcutil/[unix/win32]/silc[unix/win32]net.c.
 +
 +      * Fixed buffer overflow from Irssi SILC Client.  Affected
 +        file irssi/src/fe-common/core/themes.c.
 +
 +      * Fixed double free in client library in the file
 +        lib/silcclient/client.c when disconnecting from server.
 +
 +      * Applied double free patch from cras to Irssi SILC client.
 +        Affected files irssi/src/core/[modules/expandos].c
 +
 +      * Fixed the disconnection handling to Irssi SILC Client.
 +        The application must call silc_client_close_connection
 +        in ops->connect client operation in case of failure of
 +        the connection.  Affected file is
 +        irssi/src/silc/core/client_ops.c.
 +
 +      * Do not set sock->protocol to NULL in the function
 +        silc_client_close_connection after executing the protocol's
 +        final callback since the sock might not be valid anymore.
 +        Affected file lib/silcclient/client.c.
 +
 +Wed Jul 25 16:04:35 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not enable SILC_THREADS if the linking with libpthread
 +        did not happen.  Affected file configure.in.pre.
 +
 +      * Added notion to protocol specification that server must
 +        verify the sent authentication payload with CMODE when
 +        setting the channel founder key.  Implemented it to the
 +        server.  Affected file silcd/command.c.
 +
 +Mon Jul 23 18:31:43 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added _EXTRA_DIST SILC distribution variable to the
 +        distributions file.  It is used to conditionally add extra
 +        files or directories to the specific distribution.  Affected
 +        files ./prepare, Makefile.am.pre and distributions.
 +
 +        Removed the `_' from the start of the distribution names.
 +        It is redundant.
 +
 +      * Added README.WIN32 for instructions to compile the Toolkit
 +        under WIN32.
 +
 +Mon Jul 23 10:12:37 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a double free in disconnection in the server.  Affected
 +        file is silcd/server.c.
 +
 +      * Fixed the lib/silcske/groups.c to work now also with GMP
 +        MP library.  The string conversion did not work when using
 +        specific base and the base is indicated in the string as well.
 +
 +      * Created win32/ directory which now includes MSVC++ specific
 +        stuff so that toolkit (DLLs) may be compiled with MSVC++.
 +        It will appear only in the toolkit distribution
 +
 +Sun Jul 22 19:40:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Changed the key material distribution function in case when
 +        the hash output is too short.  The data is now concatenated
 +        a bit differently than it used to.  Made the change to the
 +        SKE protocol specification.
 +
 +      * Added better GMP detection to configure.in.pre.  A patch
 +        by salo.
 +
 +Fri Jul 20 13:16:00 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a minor bug in SKE that might cause some problem on
 +        some platforms.  Affected file lib/silcske/silcske.c.
 +
 +      * Added the cookie checking for initiator in the SKE.  It checks
 +        that the responder returns the sent cookie unmodified.  The
 +        affected file is lib/silcske/silcske.c.  Added new SKE
 +        error type INVALID_COOKIE that can be sent during the
 +        negotiation.  Fixed some memory leaks as well.
 +
 +      * Added the "invalid cookie" error message to Irssi SILC client's
 +        message formats.
 +
 +Thu Jul 19 21:44:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added `task_max' field to the SilcClientParams to indicate
 +        the maximum tasks the scheduler can handle.  If set to zero,
 +        default values are used.  Affected file lib/silcclient/silcapi.h.
 +
 +      * Fixed memory leaks in silc_client_close_connection.  Affected
 +        file lib/silcclient/client.c.
 +
 +      * Added silc_client_del_client_entry to client library to free
 +        all memory of given client entry.  Affected file is
 +        lib/silcclient/idlist.[ch].
 +
 +      * Added new functions silc_client_del_channel and
 +        silc_client_del_server to delete channel and server entries.
 +        Affected file lib/silcclient/[silcapi.h/idlist.c].
 +
 +      * Removed silc_client_del_client_by_id from silcapi.h.
 +
 +      * Fixed the INFO command to return the server's own info
 +        correctly when querying by Server ID.  Affected file is
 +        silcd/command.c.
 +
 +Thu Jul 19 14:47:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Removed the non-blocking settings in WIN32 code in the
 +        silc_sock_[read/write] and added SleepEx instead.  Affected
 +        file lib/silcutil/win32/silcwin32sockconn.c.  The availability
 +        of input data is now checked with FIONREAD and ioctlsocket.
 +
 +Wed Jul 18 18:34:01 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Call silc_schedule_task_del_by_context in the 
 +        silc_protocol_cancel instead of silc_schedule_task_del_by_callback.
 +        Affected file lib/silccore/silcprotocol.c.
 +
 +      * Call silc_protocol_cancel for active protocols in the
 +        silc_server_close_connection if the funtion
 +        silc_server_free_sock_user_data has not been called.
 +        Affected file silcd/server.c.
 +
 +      * Generic tasks cannot be deleted using the del_by_fd
 +        task deleting function since generic tasks does not match
 +        any specific fd.  Affected file lib/silcutil/silcschedule.[ch].
 +
 +      * Added a notion to SILCOPER help file that the SILCOPER works
 +        only on router server, not on normal server.
 +
 +Wed Jul 18 09:40:04 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added for WIN32 support for the new scheduler as well.
 +        Affected file lib/silcutil/win32/silcwin32schedule.c.
 +
 +      * Fixed the SHA1 implementation to work on various platforms.
 +
 +Tue Jul 17 23:04:10 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Rewrote the SILC Scheduler entirely.  Removed the old SILC Task
 +        API.  It is part of the scheduler now.  Everything else is
 +        as previously but some functions has changed their names.
 +        Checkout the lib/silcutil/silcschedule.h for the interface.
 +        Updated all applications to use the new interface.  Affected
 +        files are lib/silcutil/silcschedule.[ch].
 +
 +Tue Jul 17 16:53:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Found a bug in the SKE implementation.  The HASH value,
 +        specified by the protocol, was not computed correctly.  The
 +        public key of the responder was not added to the computation
 +        even though it is mandatory.  Affected file lib/silcske/silcske.c.
 +        This unfortunately causes incompatibilities with older
 +        clients and servers.
 +
 +      * Added WIN32 specific network init and uninit functions:
 +        silc_net_win32_init and silc_net_win32_uninit to init and uninit
 +        the Winsock2.  Affected file lib/silcutil/silcnet.h and
 +        lib/silcutil/win32/silcwin32net.c.
 +
 +      * Set the socket always to nonblocking mode on WIN32 after
 +        reading data or writing data.  Affected file is
 +        lib/silcutil/win32/silcwin32sockconn.c.
 +
 +Mon Jul 16 22:55:26 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed various compilation problems under WIN32.  Affected
 +        files lib/silcutil/win32/silcwin32thread.c and
 +        lib/silcutil/win32/silcwin32schedule.c.
 +
 +      * Removed all _internal.h #includes from public header
 +        files.  Internal headers must never be included from
 +        public headers.
 +
 +        Removed also the lib/silcske/payload_internal.h file.
 +
 +      * All include files that may be needed (public and some others
 +        included by the public headers) by application developers are
 +        now copied to the ./includes directory.  It does not copy any
 +        internal headers.  Affected file Makefile.defines.pre and all
 +        Makefile.am's under lib/ and subdirs.
 +
 +Thu Jul 12 17:49:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not change the ~/.silc directory's permissions automatically.
 +        Affected file irssi/src/silc/core/clientutil.c.
 +
 +Thu Jul 12 10:18:40 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not cancel the protocol in silc_server_close_connection
 +        it might cause recursion.  Now cancelled in the function
 +        silc_server_free_sock_user_data.  Affected file silcd/server.c.
 +
 +      * Fixed the silc_server_remove_clients_by_server to regenerate
 +        the channel keys correctly finally.  Added also new function
 +        silc_server_remove_clients_channels to actually do it.
 +        Affected file silcd/server.c.
 +
 +      * Fixed the silc_server_new_channel to not crash by giving
 +        wrong router to the new channel.  Affected file is
 +        silcd/packet_receive.c.
 +
 +Wed Jul 11 18:31:57 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added SilcClientParams structure to the lib/silcclient/silcapi.h
 +        which is given as argument to the silc_client_alloc now.
 +        It can be used to configure the client and set various parameters
 +        that affect the function of the client.
 +
 +      * The USERS command in server did not check whether the channel
 +        is private or secret.  Affected file silcd/command.c.
 +
 +      * Added new argument to the USERS command in protocol specification.
 +        The USERS command now can take the channel name as argument
 +        as well.  Added support for this in client and server and
 +        updated the protocol specs.
 +
 +      * Completed the GETKEY command in client. It can be now used
 +        to fetch also servers public key not only some clients. 
 +        Affected files lib/silcclient/command[_reply].c.
 +
 +      * Added silc_client_get_server to return server entry by the
 +        server name.  Affected files lib/silcclient/silcapi.h and
 +        idlist.c.
 +
 +      * Redefined the IDENTIFY command in protocol specification to be
 +        more generic.  It now can be used to query information about
 +        any entity in the SILC Network, including clients, servers and
 +        channels.  The query may be based either the entity's name
 +        or the ID.  Added support for this in both client and server.
 +
 +        Affected files silcd/command.c and lib/silcclient/command.c
 +        and command_reply.c.
 +
 +      * Optimized the WHOIS and WHOWAS commands in the server. Removed
 +        the _from_client and _from_server functions.  Affected file
 +        silcd/command.c.
 +
 +      * Added silc_client_get_channel_by_id_resolve to the file
 +        lib/silcclient/silcapi.h to resolve channel information by
 +        its ID.  Added also silc_client_get_channel_by_id that
 +        does not resolve it from the server.
 +
 +Tue Jul 10 18:05:38 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added SilcServerEntry context into the client library
 +        to represent one server.  The INFO command now allocates
 +        these to save the resolved server info.  For now on the
 +        client library will also keep information about servers,
 +        connected and resolved with INFO.
 +
 +        The INFO command now allocates the SilcServerEntry context
 +        and saves the server info there.  The COMMAND_REPLY in
 +        the INFO now returns the parameters to application in 
 +        same order as defined in the protocol specification.
 +
 +        The entries are cached in the client->server_cache.
 +
 +      * The INFO command is now issued after received the Client ID
 +        from the server.  Affected file lib/silcclient/client.c.
 +
 +      * The CMODE_CHANGE notify may now return also an SilcServerEntry
 +        to the application as the mode changer might be server.
 +        It is guaranteed that NULL is not returned anymore to the
 +        application.  Affected file lib/silcclient/client_notify.c.
 +
 +        The ID Type is now also passed to the application so that
 +        it can check whether the returned entry is SilcClientEntry
 +        or SilcServerEntry.
 +
 +        Added new function silc_client_get_server_by_id to return
 +        the server entry by ID.  Affected files are the
 +        lib/silcclient/silcapi.h and lib/silcclient/idlist.c.
 +
 +      * Do not create the channel in the Irssi SILC Client when issuing
 +        the JOIN command but when received the sucessful JOIN command
 +        reply.  Otherwise the channel might get created even though we
 +        could not join it.  The Affected file is
 +        irssi/src/silc/core/[silc-channels.c/client_ops.c].
 +
 +      * Fixed a channel joining bug in router.  The router must also
 +        check the channel modes, invite and ban lists etc. when serving
 +        the JOIN command sent by normal server.  Affected file is
 +        silcd/command.c.  The router now resolves the client's 
 +        information from the server who sent the JOIN command if it
 +        does not know it, and processes the JOIN command only after
 +        that.
 +
 +      * Changed the SilcCommandCb to take new argument; void *context2.
 +        Affected file lib/silccore/silccommand.h
 +
 +        The second argument in the command callbacks in the server now
 +        includes the SilcServerCommandReplyContext if the command was
 +        called as pending command callback from the command reply.
 +        Otherwise it is NULL. When called as pending the status of the
 +        command reply will be checked and if it was erronous the
 +        error will be sent to the original sender of the command.
 +        This way the client always receives the error messages even
 +        though the server was actually the one who received the error
 +        when it resent the command to router, for example.  Affected
 +        files silcd/command[_reply].[ch].
 +
 +      * Fixed sending WHOWAS command's error message to client if
 +        the requested client could not be found.  It was missing.
 +        silcd/command.c.
 +
 +      * Changed the CMODE and CUMODE commands reply arguments in the
 +        protocol specification.  The Channel ID is now sent in both
 +        of the commands to identify the channel.  Implemented this
 +        new feature to the client and server.  Affected files
 +        lib/silcclient/command_reply.c and silcd/command.c.
 +
 +      * Made better checks for invite and ban lists in the JOIN
 +        command in server.  Affected file silcd/command.c.
 +
 +Mon Jul  9 18:28:34 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * The server now performs the incoming host IP/DNS lookup
 +        using the silc_socket_host_lookup and thus does not block
 +        the server anymore.  Affected file silcd/server.c.
 +
 +      * Completed the multi-thread support for SILC Scheduler in
 +        the lib/silcutil/silcschedule.c.
 +
 +      * Fixed the configure.in.pre to detect the pthread correctly
 +        on various systems.
 +
 +      * Fixed a deadlock in silc_task_queue_wakeup in the file
 +        lib/silcutil/silctask.c.
 +
 +Mon Jul  9 13:40:03 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added new function silc_schedule_wakeup that is used in
 +        multi-threaded environment to wakeup the main thread's
 +        schduler. It needs to be used when a thread adds a new task
 +        or removes a task from task queues. After waking up, the
 +        scheduler will detect the task queue changes. If threads
 +        support is not compiled in this function has no effect.
 +        Implemented the wakeup mechanism to both Unix and WIN32
 +        systems.  Affected files are lib/silcutil/silcschedule.[ch],
 +        lib/silcutil/unix/silcunixschedule.c and the
 +        lib/silcutil/win32/silcwin32schedule.c.
 +
 +      * Added new function silc_task_queue_wakeup to wakeup the
 +        scheduler by the specified task queue.  Affected file
 +        lib/silcutil/silctask.[ch].
 +
 +      * The silc_socket_host_lookup_start now wakes up the scheduler
 +        after adding the timeout task.  Affected file is
 +        lib/silcutil/silcsockconn.c.
 +
 +      * The silc_socket_host_lookup is synchronous now if the threads
 +        support is not compiled in.  However, the callback is still
 +        called asyncronously through the scheduler, anyway.  Affected
 +        file lib/silcutil/silcsockconn.c.
 +
 +Mon Jul  9 00:24:45 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added new function silc_socket_host_lookup to perform
 +        asynchronous IP and FQDN lookups for the socket connection.
 +        Affected files lib/silcutil/silcsockconn.[ch].
 +
 +Sun Jul  8 18:44:53 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added SILC_MUTEX_DEFINE to define the mutex on environments
 +        that may or may not compile the mutex support in.
 +      
 +        Changed the silc_mutex_alloc interface. It allocates the
 +        mutex now to the sent pointer and returns TRUE or FALSE.
 +
 +        Affected file lib/silcutil/silcmutex.h.
 +
 +      * Wrote the SILC Task Queue interface to support multi-threads.
 +        Affected file lib/silcutil/silctask.[ch].
 +
 +      * Wrote the SILC Scheduler to support multi-threads.  Affected
 +        file lib/silcutil/silcschedule.c.
 +
 +Sun Jul  8 11:16:01 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Implemented the SILC Mutex API and SILC Thread API for WIN32
 +        in lib/silcutil/win32/.
 +
 +Sun Jul  8 00:18:15 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Defined SILC Mutex API and SILC Thread API and implemented
 +        them for Unix.  Affected files are
 +        lib/silcutil/silcmutex.h, lib/silcutil/silcthread.h,
 +        lib/silcutil/unix/silcunixmutex.c and
 +        lib/silcutil/unix/silcunixthread.c.
 +
 +Sat Jul  7 14:40:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed the silc_server_remove_clients_by_server's channel
 +        key re-generation.  The hash table handling was incorrect
 +        and would not work with many channels.  Affected file is
 +        silcd/server.c.
 +
 +      * Fixed some memory leaks around the server code.
 +
 +      * Rewrote the silc_server_get_users_on_channel to support IPv6
 +        based Client ID's.  Affected file silcd/server.c.
 +
 +      * Defined the SILC_MESSAGE_FLAG_SIGNED to the protocol
 +        specification.  However, a separate document must be written
 +        to define the detailed signing procedure and the payload
 +        associated with the flag.  Defined the flag to the
 +        lib/silccore/silcchannel.h as well.
 +
 +Fri Jul  6 18:26:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Changed the dynamic tables to static size tables in the
 +        lib/silccrypt/silchmac.c.
 +
 +      * Removed GCC dependencies from the code.  A patch by cras.
 +
 +Fri Jul  6 09:39:35 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not show the error "Error receiving packet bla bla"
 +        in server if it really was not an error (-2 means that reading
 +        is pending).  Affected file silcd/server.c.
 +
 +Thu Jul  5 21:22:32 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a possible crash in silc_server_remove_clients_by_server
 +        in silcd/server.c.  Fixed there also some memory leaks.
 +
 +      * Fixed the silc_idlist_replace_client_id.  It could replace
 +        wrong key in the hash table.  Affected file silcd/idlist.c.
 +
 +      * Do not check whether there are global users on the channel
 +        if the channel->global_users is FALSE.  Affected functions
 +        silc_server_remove_from_one_channel and
 +        silc_server_remove_from_channels in silcd/server.c.  Also,
 +        do not check if the removed client is local as we can be
 +        sure that global client was not removed from the channel
 +        and checking for global users is not needed.
 +
 +      * The silc_server_remove_clients_by_server now re-generates
 +        the channel keys correctly for those channels that had
 +        clients removed from them.  Affected file silcd/server.c.
 +
 +Tue Jul  3 11:39:20 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Found the reason of random crashes in the server.  We weren't
 +        ignoring the SIGPIPE signal (which can be sent in write())
 +        and it crashed the server.  Affected file silcd/silcd.c.
 +
 +Fri Jun 29 20:05:25 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Assure that sock->user_data is not NULL in the function
 +        silc_server_packet_send in silcd/packet_send.c.
 +
 +      * Disconnect the remote connection if it could not be added
 +        to any ID lists in the server.  The affected file is
 +        silcd/server.c.
 +
 +      * Check in silc_server_packet_send[_real/dest] that the
 +        socket is not disconnecting and ignore the data if it is.
 +        Affected file silcd/packet_send.c.
 +
 +      * Define inline to __inline on native WIN32 compilation.
 +        Affected file includes/silcwin32.h.
 +
 +      * Added some explicit type casts for inline code since MSVC
 +        require them.  Affected files lib/silcutil/silcbuffer.h,
 +        lib/trq/silcdlist.h and lib/trq/silclist.h.
 +
 +      * Print warning in log files from now on if the packet
 +        decryption fails.  Affected file silcd/server.c.
 +
 +Thu Jun 28 21:30:39 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Changed the `say' client operation's interface to accept
 +        new `type' argument to indicate the type of the message sent
 +        by the library.  The application may filter the library's
 +        messages according the type.  The affected file is the
 +        lib/silcclient/silcapi.h.
 +
 +      * Added two new functions to lib/silcclient/silcapi.h:
 +        silc_client_del_client and silc_client_del_client_by_id.
 +        Affected file lib/silcclient/idlist.c.
 +
 +      * Moved the clientincludes.h from includes/ to silc/ and
 +        serverincludes.h from includes/ to silcd/.
 +
 +      * The modes for the CMODE and CUMODE are now passed as
 +        uint32 for application with COMMAND_REPLY.  The affected
 +        file is lib/silcclient/command_reply.c.
 +
 +Wed Jun 27 22:24:47 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * /WHOIS without arguments shows client's own information.
 +        Affected file lib/silcclient/command.c.
 +
 +      * Changed PING to not accept any arguments.  The specs
 +        says that client can ping only the connected server so
 +        requiring an argument is not needed.  Affected file is
 +        lib/silcclient/command.c.
 +
 +Wed Jun 27 00:10:33 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Fixed a fatal bug in private message sending and reception
 +        encryption and decryption when using private message keys.
 +        The implementation was incorrect and did not follow the
 +        specification.  It causd that some of the message were
 +        lost since it did not use the sending and receiving keys
 +        as the protocol suggests.  This has been fixed and will cause
 +        incompatibilities with older clients when sending private
 +        message encrypted with private message keys.  Affected files
 +        lib/silcclient/client_prvmsg.c, lib/silcclient/client_keyagr.c
 +        and various other in Irssi SILC Client.
 +
 +        Added `responder' boolean argument to the functions
 +        silc_client_add_private_message_key[_ske] to indicate when
 +        the key is added as responder or initiator of the key
 +        negotiation.
 +
 +Tue Jun 26 19:23:07 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Removed the silc_ske_check_version function and created
 +        a SilcSKECheckVersion callback.  Added also a function
 +        silc_ske_set_callbacks that is now used to set all SKE
 +        callbacks.  The callback functions are not given to
 +        the SKE functions anymore, but this function is used to
 +        set the callbacks.
 +
 +      * Fixed the WIN32 DLL generation in lib/Makefile.am.pre.
 +
 +      * Added `silc_version' argument to the silc_client_alloc
 +        to define the version of the application for the library.
 +        The library will use the version string to compare it
 +        against the remote host's (usually a server) version
 +        string.  Affected file lib/silcclient/silcapi.h
 +
 +      * Added the KE protocol context to Key Agreement context
 +        in client library so that we can abort the SKE if it
 +        is in process when we get timeout.  Affected file is
 +        lib/silcclient/client_keyagr.c.
 +
 +      * Do not resolve the client ID forever if it returns in the
 +        first time that such client does not exist.  This was done
 +        for example with private message.  Affected file is
 +        lib/silcclient/client_prvmsg.c.
 +
 +Mon Jun 25 21:42:51 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not add regex.h for WIN32.  The affected file
 +        includes/silcincludes.h.
 +
 +      * Added WIN32 DLL generation to lib/Makefile.am.pre.  It might
 +        not work yet 100%.  It generates the DLL's automatically
 +        when compiling with --with-win32 under cygwin.
 +
 +Sun Jun 24 19:49:23 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * lib/contrib/regex.c is not compiled on WIN32.
 +
 +      * Added silc_net_get_socket_opt function to the
 +        lib/silcutil/silcnet.h.
 +
 +      * Added includes/silcwin32.h for WIN32 specific includes
 +        and definitions.
 +
 +      * Do not use ptime structure or any of the posix process
 +        functions on WIN32 in lib/silccrypt/silrng.c.
 +
 +      * Added silc_gettimeofday to provide generic function
 +        for struct timeval on all platforms.  Added the function
 +        to lib/silcutil/silcutil.h.
 +
 +Sun Jun 24 12:19:52 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Moved the lib/silccore/silcsockconn.[ch] to the utility
 +        library as they clearly belong there.  As a plus side we
 +        can make the actual socket connection routines platform
 +        specific.
 +
 +        Added also new generic function silc_socket_read and
 +        silc_socket_write (that used to be silc_packet_[read/write].
 +        The implementation of these are platform specific.
 +
 +      * Added WIN32 specific routines of silc_socket_[read/write]
 +        to lib/silcutil/win32/silcwin32sockconn.c.
 +
 +Sat Jun 23 16:01:00 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Added preliminary support for native WIN32 compilation under
 +        cygwin (using the -mno-cygwin option for GCC) to the
 +        ./configure.in.pre.  The --with-win32 now prepares the
 +        compilation for native WIN32.
 +
 +      * Rewrote the SILC Scheduler interface in the file
 +        lib/silcutil/silcschedule.h.  The scheduler is now context
 +        based and does not have anymore any global static scheduler.
 +        Moved the Unix scheduler to the lib/silcutil/unix/ directory
 +        and created lib/silcutil/win32 directory for WIN32 based
 +        scheduler.
 +
 +      * Added Unix specific network routines to the
 +        lib/silcutil/unix/silcunixnet.c and the old
 +        lib/silcutil/silcnet.c includes now only generic routines.
 +
 +        Added WIN32 specific network routines to the
 +        lib/silcutil/win32/silcwin32net.c.
 +
 +      * Added Unix specific utility functions from the
 +        lib/silcutil/silcutil.c to lib/silcutil/unix/silcunixutil.c.
 +
 +      * Added WIN32 SILC Scheduler to the file
 +        lib/silcutil/win32/silcwin32schedule.c. The code is of course
 +          untested.
 +
 +Fri Jun 22 10:44:14 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 +
 +      * Do not handle JOIN notify in the server if the target client
 +        is not registered (idata->registered == FALSE).  The affected
 +        file is silcd/packet_receive.c.
 +
 +      * Update the nickrec->founder in event_cumode in the Irssi SILC
 +        client.  Affected file irssi/src/silc/core/silc-channels.c.
 +
 +      * Fixed the CUMODE_CHANGE notify handling in the server when
 +        server and router are announcing their clients on channels.
 +        Now the mode changes are saved and notified correctly.  The
 +        affected file is /silcd/packet_receive.c.
 +
 +      * Fixed silc_idlit_replace_[server/client/channel]_id functions.
 +        They really did not replace the cache entry in the ID Cache.
 +        Now they do that.  Affected file silcd/idlist.c.
 +
 +      * Fixed the KICK notify handling in the Irssi SILC client to
 +        update the channel records so that the kicked client does not
 +        appear to be on the channel.  The affected file is 
 +        irssi/src/silc/core/silc-channels.c.
 +
 +      * Always update the conn->current_channel when executing command
 +        on a channel.  Affected file irssi/src/silc/core/silc-servers.c.
 +
 +      * Fixed the KILL notify handling in Irssi SILC client to remove
 +        the killed client on all channels.
 +
 +Thu Jun 21 17:10:08 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the silc_parse_command_line to remove extra spaces
 +        from the start and end of the arguments.  Affected file is
 +        lib/silcutil/silcutil.c.
 +
 +      * Cancel and free any active protocol in the function
 +        silc_server_close_connection.  Affected file silcd/server.c.
 +
 +      * Cancel and free any active protocol in the function
 +        silc_client_close_connction.  Affected file is
 +        lib/silcclient/client.c.
 +
 +      * Do not execute the KILL command for clients that are in
 +        history (ie. they are not in the network).  Affected file is
 +        silcd/command.c.
 +
 +      * Fixed KILL notify handling, client does not crash anymore.
 +        Affected file irssi/src/silc/core/silc-channels.c.
 +
 +      * Reduced the default packet buffer size from 2048 to 1024 in   
 +        lib/silccore/silcpacket.c.
 +
 +      * Added SILC_SKE_STATUS_FREED SKE status type and a reference
 +        counter to the SKE context that is incresed when the SKE library
 +        performs async operation outside the library.  If the outside
 +        process frees the SKE context and FREED status will be set 
 +        and the library will detect after the sync operation that the
 +        libary is freed.  The affected files are
 +        lib/silcske/silcske[_status].[ch].
 +
 +      * Resolve the client entry information in the function
 +        silc_client_channel_message to assure that NULL pointer is not
 +        passed as client entry to the application. */
 +
 +      * Fixed the task timeout calculation to assure that there is
 +        never negative timeouts.  The affected file is 
 +        lib/silcutil/silcschedule.c.
 +
 +      * Fixed the channel user mode notification sending in server.
 +        It was sent point-to-point to the router (or to server by router)
 +        but it needs to be destined to a channel.  The routines now
 +        supports sending the channel user mode notifys to the channels
 +        when announcing clients and channels.  Affected files are
 +        silcd/server.c and silcd/packet_receive.c.
 +
 +      * Fixed the CHANNEL_CHANGE notify handling in the client libary.
 +        It did not actually replace the old channel entry in the cache.
 +        Affected file lib/silcclient/client_notify.c.
 +
 +Tue Jun 19 22:10:36 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a possible crash in silc_packet_send_prepare.  It now
 +        assures always that there is enough space in the buffer and
 +        at the tail area of the buffer (for MAC). 
 +
 +        Fixed the inbound buffer reallocation in silc_packet_read.
 +        It was old code and did not handle the reallocation correctly.
 +        Affected
 +
 +        The affected file is lib/silccore/silcpacket.c.
 +
 +      * Fixed buffer overflow in silc_parse_nickname in the file
 +        lib/silcutil/silcutil.c.
 +
 +Tue Jun 19 13:40:09 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * make install generates new server keys only if there is not
 +        keys already.
 +
 +Mon Jun 18 18:49:07 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Set SILC_MESSAGE_FLAG_NOREPLY when sending the away message.
 +        Added check that if the NOREPLY is set then we will not send
 +        the away message.  This avoids infinite loop of away messages
 +        if both clients are away.  The affected file is
 +        lib/silcclient/client_prvmsg.c.
 +
 +      * Fixed client crash if /NICK was given without arguments.
 +        Affected file lib/silcclient/command.c.
 +
 +      * Server does not send the invite list in INVITE command back
 +        to the client if the list was not altered.  Added this notion
 +        to the protocol spec as well.  Affected file silcd/command.c.
 +
 +        Fixed possible crash in INVITE command by checking the
 +        value of silc_server_get_client_route command.
 +
 +      * Fixed the INVITE notify type handling.  The arguments are now
 +        taken in correct order and client does not crash.  The affected
 +        file is irssi/src/silc/core/silc-channels.c.
 +
 +        Removed the "Inviting xxx to channel" message from the
 +        client library away and let the application handle it.
 +        Affected file lib/silcclient/command.c.  Added that message
 +        to Irssi SILC client's message formats.
 +
 +      * Fixed CMODE command crash in client.  It now checks the
 +        amount of arguments correctly and does not crash.  The affected
 +        file is lib/silcclient/command.c.
 +
 +      * Do not create new channel automatically in silc_channels_join
 +        but check whether the channel by that name already exists.
 +        Affected file irssi/silc/core/silc-channels.c.
 +
 +      * Do not send the SERVER_SIGNOFF to router if the disconnected
 +        entity was the router.  Affected file silcd/server.c.
 +
 +      * Added the handling of the SERVER_SIGNOFF notify to the Irssi
 +        SILC client as it was missing from there.
 +
 +        Added the handling of the KICK notify to the Irssi SILC client
 +        as it was missing.  Added "you have been kicked" message to
 +        Irssi SILC client's message modules formats.
 +
 +        Added the handing of the KILL notify to the Irssi SILC client
 +        as it was missing.  Added the kill message module formats 
 +        as well.
 +
 +        The affected file is irssi/src/silc/core/silc-channels.c.
 +
 +      * The router did not save the channel mode the server announced.
 +        Affected file silcd/packet_receive.c.
 +
 +      * Fixed a possible crash in INFO command in server.  If the
 +        server did not provide the server info it crashed.  Affected
 +        file silcd/command.c.
 +
 +Sun Jun 17 15:26:05 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the GETKEY command in the server to check also the
 +        global list.  Otherwise the GETKEY would not work correctly
 +        in normal SILC server.  Affected file silcd/command.c.
 +
 +Sat Jun 16 18:00:00 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed GETKEY crash, it crashed if the command did not succseed.
 +
 +Tue Jun 12 21:36:18 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Redefined the SILC MP API in lib/silcmath/silcmp.h. The API
 +        is now real and not just an macro interface to GMP.
 +
 +        Removed the entire GMP from the source tree and imported new
 +        NSS MPI library instead.  Reason for removing GMP is that it is
 +        extremely large and compiles extremely slow.  The NSS MPI
 +        is only a few files and compiles in less than 10 seconds.
 +        The speed is also about the same as GMP.  The MPI is imported
 +        to lib/silcmath/mpi.
 +
 +        If the system has GMP installed we will still use the GMP.
 +        If it is not then the NSS MPI will be compiled.
 +
 +Mon Jun 11 18:07:24 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Merged a long nickname (127 characters long) crash bugfix from
 +        Irssi CVS tree.  Affected file irssi/src/core/misc.c.
 +
 +      * Merged a freed memory reference bugfix from Irssi CVS tree.
 +        Affected file irssi/src/core/commands.c.
 +
 +Sun Jun 10 16:08:35 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added the server's public key sving and verification to the
 +        server when performing the SKE.  This was missing and the
 +        remote server's (or router's) public key was accepted without
 +        checking whether we have it previously or trust it at all.
 +        Affected file silcd/protocol.c.
 +
 +Sat Jun  9 20:17:30 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Check in the silc_server_timeout_remote if protocol is active
 +        and make sure that the protocol's final callback is called so
 +        that all memory if freed.  Affected file silcd/server.c.
 +
 +Sat Jun  9 12:51:27 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * silc_server_whois_send_reply crashed the server if the nickname
 +        was 127 characters long.  Affected file silcd/command.c.
 +
 +Thu Jun  7 16:29:56 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added sanity check to the silc_server_new_client. If the hostname
 +        is provided inside username then check that the provided hostname
 +        really is the same as the resolved one.  If the hostname was not
 +        resolved then check it from the public key.  Affected file is
 +        silcd/packet_receive.c.
 +
 +      * Fixed a fatal bug in Irssi SILC client. Do not send QUIT command
 +        if the server disconnected us and the connection is not valid
 +        anymore.  Affected file irssi/src/silc/core/silc-channels.c.
 +
 +      * Moved the silc_client_[chmode|chumode|chumode_char] away from
 +        the library to the lib/silcutil/silcutil.[ch].
 +
 +Thu Jun  7 08:57:16 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Close log file after open.  Affected file 
 +        lib/silcutil/silclog.c.
 +
 +      * Check whether sock == NULL in silc_client_send_packet and return
 +        if it is.  Affected file lib/silcclient/silcclient.c.
 +
 +      * Check rec->entry == NULL in the Irssi SILC Client before
 +        sending the channel message.  Affecte file is
 +        irssi/src/silc/core/silc-servers.c.
 +
 +Tue Jun  5 08:08:21 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Merged a splitted window bugfix from Irssi CVS tree.  The 
 +        affected file is irssi/src/fe-text/textbuffer-view.c.
 +
 +      * Fixed the ME, ACTION and NOTICE printing in Irssi Client.
 +        It did not print nickname.
 +
 +      * Improved the distributions system a bit.
 +
 +Mon Jun  4 17:57:16 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Merged /WINDOW bugfix from irssi CVS tree. Affected file is
 +        irssi/src/fe-text/gui-window.c.
 +
 +      * Fixed a fatal bug in Irssi SILC client. Crashed if sent message
 +        to in-active server.  The affected file is
 +        irssi/src/silc/core/client_ops.c.
 +
 +      * Resolve the client in USERS command reply if the entry does
 +        not have username resolved.  The affected file is
 +        lib/silcclient/command_reply.c.  Also, changed the IDENTIFY
 +        command to WHOIS command to really resolve stuff.  The USERS
 +        is not used any more in any critical section so WHOIS can
 +        be used even though it might be slower than IDENTIFY.
 +
 +      * Changed the lib/silcutil/silchashtable.h header to ROBODoc
 +        format.
 +
 +Sun Jun  3 14:21:32 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed the protocol API a bit more consistent in the
 +        lib/silccore/silcprotocol.[ch].
 +
 +      * Changed the following headers to ROBODoc format:
 +
 +              lib/silccore/silcpayload.h
 +              lib/silccore/silcprotocol.h
 +              lib/silccore/silcsockconn.h
 +
 +        All core library headers are now formatted.
 +
 +Sat Jun  2 10:45:09 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a bug in Irssi SILC client; do not show that you are
 +        server/router operator if you really are not.  Affected file is
 +        irssi/src/silc/core/client_ops.c.
 +
 +      * Renamed silc_command_free_payload to silc_command_payload_free.
 +        Affected file lib/silccore/silccommand.h
 +
 +      * Added silcmath.h to include the prototoypes of various routines
 +        in the lib/silcmath.  Removed the old modinv.h, mpbin.h and
 +        silcprimegen.h.
 +
 +      * Changed the following headers to ROBODoc format:
 +
 +              lib/silccore/silcchannel.h
 +              lib/silccore/silccommand.h
 +              lib/silccore/silcid.h
 +              lib/silccore/silcidcache.h
 +              lib/silccore/silcmode.h
 +              lib/silccore/silcnotify.h
 +              lib/silccore/silcpacket.h
 +              lib/silcmath/silcmath.h
 +
 +Fri Jun  1 22:19:37 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added checking to the server code not to start the server if
 +        ciphers and stuff are not configured properly.  Affected files
 +        silcd/serverconfig.[h] and silcd/server.c.
 +
 +      * Changed the layout of the header files of the public interfaces
 +        in the SILC libraries.  The new layout supports ROBODoc 
 +        documentation tool (and some others) so that it is easy to create
 +        a library reference manual.  All the other headers and source
 +        code must still follow the CodingStyle document.  Also source
 +        code must not include these ROBODoc stuffs, only the headers.
 +        Furthermore, all public interface headers must now be named
 +        by using `silc' prefix, example: silcapi.h, silccipher.h.
 +        Some files were renamed due to this.  All the other headers
 +        must not be used as public interfaces.  I will update the
 +        CodingStyle document later.  Changed following headers, so far:
 +
 +              lib/silcclient/silcapi.h
 +              lib/silccore/silcauth.h
 +              lib/silccore/silcprivate.h
 +              lib/silccrypt/silcdh.h
 +
 +Fri Jun  1 10:28:09 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Updated TODO.
 +
 +      * Removed silc_client_packet_send_flush from the client library
 +        as it is not needed.  Affected file lib/silcclient/client.[ch].
 +
 +      * Added printing of message of unresolved authentication method
 +        to the Irssi SILC client.  Added it to the module formats.
 +        Removed the same message from the client library.
 +
 +Thu May 31 13:57:33 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new distribution feature, DISTLABEL.  Every distribution
 +        can define own preprocessor label that can be used in the
 +        source code.  For example: #ifdef SILC_DIST_CLIENT.  Affected
 +        file distributions, acconfig.h.pre and prepare.
 +
 +Tue May 29 22:16:40 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added Makefile.defines_int to include the actual definitions
 +        for Makefile.defines.in.  Tested the new distribution system,
 +        created distributions and tested installation.
 +
 +      * Added AWAY message printing to the Irssi SILC client.  Added
 +        the messages to the irssi/src/fe-common/silc/module-formats.[ch].
 +
 +      * Added SCONNECT command to call the SILC's CONNECT command.
 +        Cannot use CONNECT directly since Irssi uses that internally.
 +        Affected file irssi/src/silc/core/silc-servers.c.
 +
 +        Added ACTION local command.  It is same as ME command but takes
 +        the channel as mandatory argument.
 +
 +        Rewrote some of the Irssi's help files to suite for SILC
 +        protocol.
 +
 +Mon May 28 19:05:22 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added Makefile.defines[.in] that should for now on be included
 +        in all Makefile.am file in the source tree.  That file includes
 +        all common compilation definitions for SILC source tree.
 +
 +Mon May 28 10:30:51 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Minor changes to the ./prepare script to change the package
 +        name according the distribution name to the configure.in.
 +
 +Sun May 27 22:24:57 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Created new distribution system.  Added file `distributions'
 +        that defines all the distributions that can be created out of
 +        the SILC source tree.  The ./prepare script now reads that
 +        file to determine how to prepare the distributions.  The
 +        first argument to the ./prepare is the name of the distribution
 +        and second is the version of the distribution.  If given
 +        without arguments it creates the default (toolkit) distribution
 +        with the default version (defined in ./prepare).
 +
 +        All Makefile.am files that are subject to the distributions
 +        are now named as Makefile.am.pre.  These are ./Makefile.am
 +        and lib/Makefile.am.  Others may be changed later.
 +
 +Sun May 27 15:57:17 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added invite list, ban list, some key management and connection
 +        error message printing to module formats in the Irssi SILC client.
 +
 +      * Added new silc_client_set_away_message to set the away message
 +        that is back to the person who sent private message.  The 
 +        affected file lib/silcclient/silcapi.h and the
 +        lib/silcclient/client_prvmsg.c.
 +
 +Sun May 27 12:39:48 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the private message sending in the Irssi SILC client,
 +        added local command KEY to the Irssi SILC client.
 +
 +        Added key management and key agreement message formats to the
 +        irssi/src/fe-common/silc/module-formats.[ch].
 +
 +        Added USERS (alias WHO) printing, server/router operator
 +        indication and LIST command printing to the module formats.
 +
 +Sat May 26 17:43:42 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed channel joining notify handling, cumode notify handling
 +        from Irssi SILC client.
 +
 +      * Added SILC specific module-formats to the Irssi SILC client so
 +        that SILC specific message hilighting, colors etc is possible.
 +        Affected file irssi/src/fe-common/silc/module-formats.[ch].
 +
 +        Added channel mode, channel user mode, actions, notices,
 +        whois and whowas printing to the the module-formats.c.
 +
 +      * Fixed a bug in channel deletion in the server.  The channel
 +        is not left to the cache even if the channel founder auth mode
 +        is set when there are no users anymore on the channel.  Affected
 +        file silcd/server.c.
 +
 +      * The silc_net_localhost now resolves the entire hostname including
 +        the domain name.  Affected file lib/silcutil/silcnet.c.
 +
 +Sat May 26 12:13:37 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed the ask_passphrase client operation to be ascynchronous.
 +        It has now a completion callback and a context that the 
 +        application must call after it has got the passphrase from
 +        the user.  Affected files lib/silcclient/silcapi.h,
 +        lib/silcclient/protocol.c, lib/silcclient/command.c and
 +        silc/client_ops.c.
 +
 +        Added SilcAskPassphrase callback that the application calls
 +        to deliver the passphrase to the library.
 +
 +      * Changed the SKE protocol's SilcSKEVerifyCb to be asynchronous.
 +        The public key verification and especially a certificate
 +        verification is asynchronous procedure.
 +
 +        Added new SILC_SKE_STATUS_PENDING status to indicate the
 +        request is pending and a callback will be called to finalize
 +        the request.
 +
 +        Added also SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED status to
 +        indicate that remote end did not send its public key (or
 +        certificate), even though we require it.  Added check for this
 +        condition in the SKE.  This was a security bug, now fixed.
 +
 +        Defined new SilcSKEVerifyCbCompletion callback that is called
 +        when the verification process is completed.
 +
 +        The affected files lib/silcske/silcske_status.h and
 +        lib/silcske/silcske.[ch].
 +
 +      * Changed the verify_public_key client operation to be async
 +        as well.  Defined SilcVerifyPublicKey callback that is used to
 +        indicate the success of the public key verification process.
 +
 +        Changed the server and client to use the new async client 
 +        operations.
 +
 +      * Changed the Irssi SILC client's internal scheduler to be called
 +        twice as many times as it used to be.  As a result the client
 +        should be a bit faster now.  Affected file is
 +        irssi/src/silc/core/silc-core.c.
 +
 +      * Added support to Irssi SILC client of asynchronous public key
 +        verification and passphrase inquiry.  Affected file is
 +        irssi/src/silc/core/silc-core.c.
 +
 +Fri May 25 14:38:38 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Do not say "You have left channel %s" in client library.
 +        Moved it to the application.  Affected files are
 +        lib/silcclient/command.c and silc/client_ops.c.
 +
 +      * Fixed silc_client_get_clients.  Command context was not
 +        duplicated and was freed memory in the callback.  Affected
 +        file lib/silcclient/idlist.c.
 +
 +      * Do not say "you are now talking..." on JOIN command in the
 +        client library.  The appliation must handle it.
 +
 +      * Do not say ".. changed topic to" in command reply in the
 +        client libary.  The application must handle it.
 +
 +      * Fixed TOPIC command sending in the client library.
 +
 +      * Fixed a memory leak in silc_client_command_free in the file
 +        lib/silcclient/command.c.
 +
 +Thu May 24 19:08:55 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Imported a modified version of Irssi client to the source tree.
 +        The Irssi will be used to create a new client called
 +        Irssi SILC.  Imported to irssi/.
 +
 +        Added silc_core_init_finish function to the Irssi.  Affected
 +        file irssi/configure.in.
 +
 +        A lot changes in the Makefile.ams around the irssi tree.
 +
 +Tue May 22 22:23:49 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Do not rehash if the new size is same as the old size of the
 +        hash table, in the silc_hash_table_rehash*.  The affected file
 +        lib/silcutil/silchashtable.c.
 +
 +      * Replaced hash_table_del_by_context calls from the server
 +        (when channel->user_list and client->channels) to the
 +        hash_table_del as it is sufficient and faster.
 +
 +Tue May 22 17:27:16 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_hash_table_list, silc_hash_table_get and the
 +        SilcHashTableList structure to provide an alternative way to
 +        traverse the hash table.  The affected files are
 +        lib/silcutil/silchashtable.[ch].
 +
 +      * Changed the server's idlist routines to use the hash table
 +        routines to optimize the code.
 +
 +Mon May 21 21:46:20 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Replaced the client entry's `channel' list and channel entry's
 +        `user_list' list to hash tables for optimized lookup.  Changed
 +        the code to use the hash table interface around the code. 
 +        Affected file lib/silcd/idlist.[ch].
 +
 +      * Added `auto_rehash' boolean argument to the function
 +        silc_hash_table_alloc to indicate whether the hash table should
 +        auto-rehash when it thinks is appropriate time.  It will
 +        increase the hash table size if the there is twice as much
 +        entries in the table than the size of the table, and will
 +        decrease the size if there are twice as less entries than
 +        the size of the table.
 +
 +Mon May 21 09:51:11 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed silc_xxx_get_supported to not crash at some circumstances.
 +
 +Sun May 20 13:45:58 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * silc_idcache_purge_by_context deletes the entry now by context
 +        as it is supposed to do.  Affected file lib/silccore/idcache.c.
 +
 +      * Send the ERR_NO_SUCH_NICK in the WHOIS command reply if the
 +        client is not anymore valid (WHOWAS givens the info) and not
 +        the ERR_NO_SUCH_CLIENT_ID if the nickname still exists.
 +
 +Sat May 19 16:30:03 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Removed the `data' and `data_len' arguments from the ID Cache
 +        interfaces and added `name' argument.  ID Cache does not handle
 +        anymore the binary data only a names associated with given ID.
 +
 +      * When hashing a Client ID with silc_hash_id the entire ID is
 +        not hashed anymore, instead only the hash of the Client ID is
 +        hashed.  This way we can access the Client ID from the cache
 +        with Client ID but with the hash of the ID (which is a hash of
 +        the nickname) as well without any difference in performance.
 +
 +        Added also silc_idcache_find_by_id_one_ext to do one on one 
 +        searching when we have the actual ID.  Added also function
 +        silc_hash_client_id_compare.  The affected files are
 +        lib/silccore/idcache.[ch] and lib/silcutil/silcutil.[ch].
 +
 +      * When hashing the name associated with a ID it is always done
 +        in lowercase.  This way we can access the cache without worrying
 +        about case-sensitivity, even though, for example nicknames are
 +        case sensitive.
 +
 +      * Fixed a bug in server with channel message sending.  It put
 +        wrong ID type as destination ID.  The affected file 
 +        silcd/packet_send.c.
 +
 +      * silc_idcache_del_by_context now deletes from all hash tables
 +        by context.  Affected file lib/silccore/idcache.c.
 +
 +Fri May 18 17:42:00 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed the client library to use the new ID Cache interface.
 +        Changes around the source tree.
 +
 +      * Added silc_hash_table_rehash_ext to rehash with specific
 +        hash function.  Affected file lib/silcutil/silchashtable.[ch].
 +
 +      * Added silc_hash_string_compare to compare two strings in the
 +        hash table.  Affected file lib/silcutil/silcutil.[ch].
 +
 +Fri May 18 11:18:45 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new function silc_idcache_del_by_context into the
 +        lib/silccore/idcache.[ch].
 +
 +      * Changed the server's ID list routines to use the new ID Cache
 +        interface.  Changes around the source tree.
 +
 +Fri May 18 08:35:31 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_hash_table_del[_by_context]_ext functions in to the
 +        lib/silcutil/silchashtable.[ch].
 +
 +        Removed silc_hash_table_find_all* routines and added new
 +        silc_hash_table_find_foreach to replace them.
 +
 +        Added silc_hash_table_replace_ext function as extended
 +        replacing function.  Separated the simple hash table interface
 +        from the extended hash table interface in the file
 +        lib/silcutil/silchashtable.h.
 +
 +      * Fixed minor bugs and changed it to use some of the new
 +        hash table functions in lib/silccore/idcache.c
 +
 +Thu May 17 18:15:12 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new function silc_hash_table_find_all to return all keys
 +        in the hash table by the specified key.  As the hash table is
 +        collision resistant it also makes it possible to have several
 +        duplicate keys in the hash table.  This function may be used to
 +        find all of the keys from the hash.
 +
 +        Added user_context arguments to the SilcHashFunction,
 +        SilcHashCompare and SilcHashDestructor to deliver user specified
 +        context.
 +
 +        Added new fuctions silc_hash_table_find[_all]_ext to do
 +        extended lookup with specified hash and compare functions and
 +        specified user contexts.
 +
 +        Added new function silc_hash_table_add_ext to add the key
 +        with specified hash function and user context.
 +
 +        Added new function silc_hash_table_foreach to traverse all
 +        entrys in the hash table.  Added SilcHashForeach callback
 +        function.
 +
 +        Added new function silc_hash_table_del_by_context to delete
 +        the entry only if the context associated with the key matches.
 +
 +        Affected files are lib/silcutil/silchashtable.[ch].
 +
 +      * Removed silc_hash_[server/client/channel]_id and added just
 +        silc_hash_id to the lib/silcutil/silcutil.[ch].  Added also
 +        silc_hash_id_compare to compare two ID's using as the hash table
 +        comparison function.  Added also silc_hash_data to hash
 +        binary data and silc_hash_data_compare to compare it.
 +
 +      * Removed silc_idlist_find_client_by_hash as it is not needed
 +        anymore.  Affected file silcd/idlist.[ch].
 +
 +      * Rewrote the entire ID Cache system (in lib/silccore/idcache.[ch])
 +        to use internally the SilcHashTable.  The new ID Cache is a lot
 +        faster than the old one.  Some of the ID Cache interface was also
 +        rewritten and obsolete and stupid functions were removed.
 +
 +Wed May 16 23:03:30 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added entry_count field to the SilcHashTable to keep the number
 +        of the entries in the table.  Implemented the function
 +        silc_hash_table_rehash.  Added new function
 +        silc_hash_table_count.  Affected file lib/silcutil/silchashtable.c.
 +
 +        Fixed a minor bug in silc_hash_table_free.
 +
 +      * Added silc_hash_string, silc_hash_uint, silc_hash_ptr,
 +        silc_hash_client_id, silc_hash_server_id and silc_hash_channel_id
 +        into the lib/silcutil/silcutil.[ch].
 +
 +Wed May 16 20:02:47 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented a collision resistant hash table into the
 +        lib/silcutil/silchashtable[ch].  See the header and the source
 +        for the SilcHashTable API.
 +
 +Tue May 15 22:05:46 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Merged dotconf version 1.0.2 into lib/dotconf.
 +
 +Sun May 13 19:32:09 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Do not compile anything in lib/silcsim/* if the SIM support
 +        is not enabled.  The tree should now compile without problems
 +        under cygwin.
 +
 +Thu May 10 22:49:51 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Compiled the SILC under cygwin.  Compiled and tested briefly
 +        without problems.  More tests needed.  The SIMs didn't compile
 +        though.
 +
 +      * Added various #ifdef HAVE_* stuff to lib/silccrypt/silrng.c.
 +
 +      * Fixed possible crash in silc_get_username in the
 +        lib/silcutil/silcutil.c.
 +
 +Tue May  8 09:04:03 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a va_arg in silc/client_ops.c.
 +
 +      * Oops, RC5 routines were named AES and caused some problems
 +        when not using SIM's.  Affected file lib/silccrypt/rc5.c.
 +
 +Sun May  6 13:59:48 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new SilcIDIP structure into the lib/silccore/id.h and
 +        replaced the old `ip' fields from all SILC ID's to that type.
 +        This is a step towards IPv6 support.
 +
 +        The silc_id_get_len takes now the ID as an extra argument.
 +        The silc_id_id2str, silc_id_str2id and silc_id_dup now supports
 +        both IPv4 and IPv6 based ID's.
 +
 +        The affected files are lib/silccore/id.[ch] and other files
 +        around the tree using these routines.
 +
 +      * Removed the ID length arguments in server from various 
 +        silc_server_send_notify_* routines -> they are not needed 
 +        anymore.
 +
 +Sat May  5 13:56:33 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed memory leak in silc_encode_pem_file in the file
 +        lib/silcutil/silcutil.c.
 +
 +Thu May  3 21:23:50 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Check minor version as well in the SKE.  Affected files are
 +        silcd/protocol.c and lib/silcclient/protocol.c.
 +
 +      * Added --identifier option to the server so that an identifier
 +        can be when creating the public key for the server.  Affected
 +        file is silcd/silcd.c.
 +
 +      * Fixed minor decoding bug in silc_pkcs_decode_identifier in
 +        lib/silccrypt/silcpkcs.c.
 +
 +Wed May  2 20:50:49 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Register default ciphers and stuff when using -C option with
 +        the server.  Affected file sildc/silcd.c.
 +
 +      * Put back the servers public key filename format, it is better
 +        than the new one.  For now, the client keys are saved with the
 +        new filename format.  The affected file silc/client_ops.c.
 +
 +      * Implemented the Cipher API for the rest of the ciphers that
 +        did not implement it or implemented it the wrong way.
 +
 +Wed May  2 13:31:26 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Register default ciphers and stuff when using the -S option
 +        in the client.  Affected file silc/silc.c.  Same also when
 +        creating new key pair with -C option.
 +
 +Tue May  1 14:18:13 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the silc_verify_public_key client operation function to
 +        save the public keys differently.  The fingerprint is now 
 +        used as filename and not the hostname.  This way also the
 +        client keys are saved uniquely and not with hostnames.  The
 +        affected file is silc/client_ops.c.
 +
 +      * Trimmed the silc_hash_fingerprint function to remove extra
 +        whitespaces from the end of the fingerprint.  The affected
 +        file is lib/silccrypt/silchash.c.
 +
 +      * Updated TODO.
 +
 +      * Added silc_cipher_register_default function to register all
 +        default ciphers.  It can be used when configuration files
 +        does not exist and the application does not want any specific
 +        ciphers in any specific order.
 +
 +        The SilcDList is now used as silc_cipher_list dynamically
 +        allocated cipher list.  Removed the static list all together
 +        and now all ciphers must be allocated to the dynamic list.
 +        The silc_cipher_alloc routine was changed to check only the
 +        dynamic list.
 +
 +        All silc_cipher_* routines that used to return int returns
 +        now bool.
 +
 +        The affected files lib/silccrypt/silccrypt.[ch].
 +
 +      * The same thing was done to silc_hash_* as for silc_cipher_*
 +        routines.  Affected files lib/silccrypt/silchash.[ch].
 +
 +      * The same thing was done to silc_pkcs_* as for silc_cipher_*
 +        routines.  Affected files lib/silccrypt/silcpkcs.[ch].
 +        Added also silc_pkcs_[un]register[_default] functions.
 +        Removed the data_context from the PKCS API.
 +
 +      * Added silc_hmac_register_default function to register default
 +        hmacs.  Affected files lib/silccrypt/silchmac.[ch].  Added also
 +        SILC_ALL_HMACS macro that can be used with silc_hmac_unregister
 +        to unregister all hmacs at once.
 +
 +      * Register the default ciphers, hash functions, PKCSs and HMACs
 +        if client's configuration file does not exist.  The affected
 +        file silc/silc.c.
 +
 +      * The client did not load the hash functions from the SIM
 +        modules at all.  Added support for this.  Affected file is
 +        silc/clientconfig.c.
 +
 +      * When decoding public key with silc_pkcs_public_key_decode, check
 +        the supported algorithm only if PKCS are registered.  Affected
 +        file lib/silccrypt/silcpkcs.c.  The same was done with the
 +        silc_pkcs_private_key_decode.
 +
 +      * Fixed the SILC List routines to keep the list always in order.
 +        It used to change the list's order when traversing the list but
 +        not it preserves the order.  Affected file lib/trq/silclist.h.
 +
 +Mon Apr 30 17:29:03 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added the client library to use the SilcSocketConnection's
 +        reference counter (by silc_socket_dup) to prevent the bug that
 +        the socket object may be freed underneath async operation.
 +
 +      * The name resolv library checking fixes in the configure.in.pre.
 +        The patch by salo.
 +
 +      * Created new version of the protocol drafts for future
 +        development. The -03 drafts are the ones that will be changed
 +        in the trunk now and the -02 will remain as they are.
 +
 +      * Send list of CUMODE notifys to the router when announcing
 +        the channel users to the router.  Affected file silcd/server.c.
 +        If the router receiving channel founder CUMODE for a channel
 +        that already has channel founder it will send CUMODE notify
 +        to the sender to remove the channel founder rights from the
 +        announced client.  Affected file silcd/packet_receive.c.
 +
 +      * The CUMODE notify may now use Server ID as well as the entity
 +        who changes the mode.  Updated protocool specs.
 +
 +      * Updated INSTALL and README files.
 +
 +Sun Apr 29 23:17:50 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * New web pages in the http://silc.pspt.fi.  The pages was
 +        designed by salo.
 +
 +      * Updated CREDITS.
 +
 +Sun Apr 29 13:33:41 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented the [DenyConnectin] config section in the server.
 +        Added silc_server_config_denied_conn to check whether incoming
 +        connection is denied.  Affected file silcd/serverconfig.[ch].
 +
 +      * Do not check the ports when checking the incoming configuration
 +        data if the port is 0, meaning any.  Affected file is
 +        silcd/serverconfig.c.
 +
 +Fri Apr 20 18:58:43 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed buffer overflow in silc_string_compare in the file
 +        lib/silcutil/silcutil.c.
 +
 +      * Fixed double free in silc_server_command_leave in the file
 +        silcd/command.c.
 +
 +Fri Apr 20 14:00:11 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the version checking in the server.  Affected file is
 +        silcd/protocol.c.
 +
 +Thu Apr 19 19:52:46 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the configuration data fetching when accepting new
 +        connections in the server.  Affected file silcd/server.c.
 +
 +Thu Apr 19 11:40:20 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added `sender_entry' argument to the function
 +        silc_server_packet_relay_to_channel so that we can check
 +        whether some destination actually belongs to the same route
 +        the sender belongs (ie, we must not resend the packet to the
 +        sender).  Affected file silcd/packet_send.[ch].
 +
 +      * Added `servername' field to the SilcClientEntry in the server
 +        to hold the name of the server where client is from.  Affected
 +        file is silcd/idlist.h.
 +
 +Wed Apr 18 22:19:03 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Moved the channel message encrypting in the router betwen
 +        router connections from silc_server_channel_message to the
 +        silc_server_packet_relay_to_channel since we want to check
 +        whether we have anybody channel before encrypting anything.
 +        Affected files silcd/packet_[receive/send].c.
 +
 +Tue Apr 17 21:18:19 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the [AdminConnection] server config section to support
 +        multiple entries.  Affected file silcd/serverconfig.c.
 +
 +      * Added support into the server to check the validity of the
 +        incoming connection before executing any KE or authentication
 +        protocols.
 +
 +      * The connection configuration is now saved to the KE and 
 +        connection auth protocol contexts and not fetched anymore in 
 +        the protocol.  Affected files silcd/server.c, silcd/protocol.[ch].
 +
 +      * The local hosts listenning address and port is also resolved
 +        now when starting the server.  We want to have the socket object
 +        to include the real address and port for the listener.  Added
 +        new function silc_net_check_local_by_sock into the files
 +        lib/silcutil/silcnet.[ch].
 +
 +      * Fixed a broadcast bug in server -> do not broadcast if we
 +        are standalone.
 +
 +      * Fixed a routing bug.  Do not route broadcast packets ever.
 +        Broadcast packets must be processed always and not routed since
 +        they may be destined to some other host than yourself and thus
 +        would get routed without no good reason.  Affected file is
 +        silcd/server.c.
 +
 +      * Added function silc_server_config_is_primary_route to check
 +        whether primary router connection has been configured (a router
 +        configuration that we are initiating).  If there is not, we 
 +        will assume that there is only two routers in the SILC network
 +        and we will use the incoming router connection as our primary
 +        route.  Affected files silcd/serverconfig.[ch], silcd/server.c.
 +
 +      * Changed the order of the broadcasting.  Broadcast _after_ the
 +        packet has been processed not before.  Affected file is
 +        silcd/server.c.
 +
 +      * Fixed a [ClientConnection] parsing bug.  The port was never
 +        parsed correctly thus resulting to port 0.  Affected file
 +        silcd/serverconfig.c.
 +
 +      * Fixed silc_server_send_notify_args -> it ignored the `broadcast'
 +        argument and did not set the broadcast packet flag.  Affected
 +        file silcd/packet_send.c.  Fixed same bug in the function
 +        silc_server_send_notify as well.
 +
 +      * If we receive NEW_ID packet for our own ID in the server, ignore
 +        the packet.
 +
 +Mon Apr 16 12:10:33 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Updated TODO.
 +
 +      * Removed the nickname from the Private Message Payload.
 +        Updated the code and the protocol specs.
 +
 +      * Updated protocol specs for submitting to the IETF.
 +
 +      * Tweaked the Random Number Generator a bit.  Affected file
 +        lib/silccrypt/silcrng.c.  Exported a new function
 +        silc_rng_[global]_add_noise which can be used to add more
 +        noise to the RNG.
 +
 +Sat Apr 14 16:21:32 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Do not parse packets with different timeout when protocol
 +        is active -> may cause problem with rekey.  Affected file
 +        silcd/server.c.
 +
 +      * When server receives signoff notify it must not create
 +        new channel key if the client is on any channels since the
 +        sender of the signoff notify will create it.
 +
 +Fri Apr 13 17:12:46 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added printing of error messages during SKE protocol from the
 +        failure packet sent by server during SKE.  Affected file
 +        silc/client_ops.c.
 +
 +      * Removed the client's failure_callback handling with timeout
 +        and handle it immediately when received.
 +
 +      * The SKE library returned wrong type in SUCCESS and FAILURE 
 +        packets.  They must be 32 bit MSB not 16 bit MSB.
 +
 +Fri Apr 13 00:09:08 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Ok, rewrote the logic of the re-key and now it seems to work.
 +        I tested it on high traffic with frequent re-keys without
 +        problems.  Added hmac_receive (and renamed hmac to hmac_send)
 +        in SilcClientConnection in lib/silcclient/client.h and
 +        in SilcIDListData in silcd/idlist.h.  Also, removed the
 +        SilcPacketParserContext's cipher and hmac fields as they are
 +        not needed anymore and actually caused some problems when
 +        the ciphers and hmac's changed underneath the packet parser.
 +
 +Thu Apr 12 14:42:51 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * If re-key protocol is active then process the incoming packets
 +        synchronously since we must assure that icoming packets encrypted
 +        with the old key is processed before the new keys is set to
 +        use.  This is true other packets than for REKEY packets.
 +        Affected file silcd/server.c.  The same was done to client library
 +        as well, affected file lib/silcclient/client.c.
 +
 +Thu Apr 12 12:01:52 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed bug in client and server to accept the force send if
 +        the packet is send from silc_[server/client]_packet_process
 +        function.  Otherwise the packets are never delivered, oops.
 +
 +Wed Apr 11 22:10:15 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Disable force sending of packets when REKEY protocol is active.
 +        We must assure that no packet is sent directly when rekey is
 +        performed.  All packets must be sent through packet queue.
 +        Added macro SILC_SERVER_IS_REKEY to silcd/server.h and
 +        SILC_CLIENT_IS_REKEY to lib/silcclient/client.h.  Affected
 +        function is silc_[server/client]_packet_send_real to check
 +        the situation.
 +
 +      * Replaced the SIM paths from example config files to 
 +        /usr/local/modules.  Also, make install creates now
 +        /usr/local/silc/logs directory to hold all the SILC server
 +        logs.
 +
 +Wed Apr 11 16:59:59 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Made the configure.in.pre work on Solaris.  Patch by salo.
 +
 +      * Made all ciphers compatible with non-x86 machines.  Defined
 +        CBC mode macros into lib/silccrypt/ciphers_def.h.
 +
 +Tue Apr 10 20:32:44 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the make install.
 +
 +Tue Apr 10 16:20:34 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * When MAC computation fails the silc_packet_decrypt returned 0
 +        even though it was supposed to return -1.  Fixed this.  The
 +        affected file is lib/silccore/silcpacket.c.
 +
 +      * Do not replace the config files in /etc/silc (in make install)
 +        if they already exist.  Affected file ./Makefile.am.
 +
 +      * Do not send re-key packets immediately but through packet queue.
 +        Affected file silcd/protocol.c and lib/silcclient/protocol.c.
 +
 +      * Changed silc_net_check_host_by_sock to return FALSE if the
 +        IP/DNS could not be resolved.  Though, it returns the IP address
 +        now even if it could not resolve it (but returns also FALSE).
 +        Affected file lib/silcutil/silcnet.[ch].
 +
 +Mon Apr  9 21:54:44 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_pkcs_decode_identifier to decode the public key's
 +        identifier.  Affected file lib/silccrypt/silpkcs.[ch].
 +        Added also silc_pkcs_free_identifier.  Added also new context
 +        SilcPublicKeyIdentifier.
 +
 +      * Added -S option to the silc client.  It is used to dump the
 +        contents of the specified public key file.
 +
 +      * Changed the PKCS api to return the public key length when
 +        setting the public key.
 +
 +      * Fixed a fatal bug in the public and private key file loading.
 +        Affected file lib/silccrypt/silcpkcs.c.
 +
 +      * Execute the packet parsing for client with zero (0) timeout
 +        if the protocol is active.  Affected file silcd/server.c.
 +
 +Sun Apr  8 19:30:56 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Made the key generation options to the silcd program.  Added
 +        -C option, equivalent to client's option.
 +
 +      * Added new [ServerKeys] config section to the server.  It
 +        configures the server's public and private key.
 +
 +      * Defined generic Public Key Payload into the protocol
 +        specification to send specific type of public keys and
 +        certificates.
 +
 +      * Defined new command SILC_COMMAND_GETKEY to fetch a client's
 +        public key or certificate.
 +
 +      * Implemented the GETKEY command to the server and to the
 +        client library and on user interface.
 +
 +Sun Apr  8 01:37:21 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Made preliminary `make install' work.
 +
 +Thu Apr  5 17:42:30 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added SilcServerRekey context into silcd/idlist.h.
 +
 +      * Added the PFS support as defined in the specification to the
 +        SKE protocol.  Affected files lib/silcske/*.c.
 +
 +      * Added `ske_group' field to the SilcServerRekey context to hold
 +        the number of the SKE group that is used with PFS in re-key.
 +        Affected file silcd/idlist.h.
 +
 +      * Added PFS re-key support to the server.  Affected file is
 +        silcd/protocol.c.
 +
 +      * Added silc_protocol_cancel to cancel execution of the next
 +        state of the protocol.  Affected file is
 +        lib/silccore/silcprotocol.[ch].
 +
 +      * Added the re-key support with and without PFS to the client
 +        library.  Re-key is performed once in an hour, by default.
 +
 +        Added new protocol type SILC_PROTOCOL_CLIENT_REKEY.
 +        Added silc_client_rekey_callback and silc_client_rekey_final.
 +        Affected files are lib/silcclient/protocol.[ch] and
 +        lib/silcclient/client.[ch].
 +
 +      * Removed the `hmac_key' and `hmac_key_len' fields from the
 +        SilcClientConnection structure; not needed.  Affected file is
 +        lib/silcclient/client.h.
 +
 +      * Updated TODO.
 +
 +Wed Apr  4 16:32:31 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Do not ask whether user wants to use the negotiated private key
 +        for private messages, just use it.  Affected file is 
 +        silc/local_command.c.
 +
 +      * Added `send_enc_key' and `enc_key_len' fields to the 
 +        SilcIDListData structure since they are needed in the re-key
 +        phase.  Affected file is silcd/idlist.[ch].
 +
 +      * Implemented the simple re-key protocol into the server.
 +        Affected files silcd/server.c and silcd/protocol.[ch].  The
 +        re-key will be performed once in an hour, by default.
 +
 +        Added new protocol type SILC_PROTOCOL_SERVER_REKEY.
 +        Added silc_server_rekey, silc_server_rekey_callback and
 +        silc_server_rekey_final.
 +
 +      * Removed Tunneled flag from the protocol.  Updated the code
 +        and the specifications.
 +
 +      * Adde `pfs' field to the SilcIDListData to indicate whether
 +        the PFS is to be performed in the re-key.  Affected file is
 +        silcd/idlist.h.
 +
 +Tue Apr  3 21:52:42 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Defined uint8, int8, uint16, int16, uint32, int32, uint64 and
 +        int64 of at least the xintXX size.  If void * is less that 4
 +        bytes uint32 * will be used.  Defined bool as boolean.
 +
 +      * Changed _ALL_ unsigned long and unsigned int to uint32, 
 +        unsgined short to uint16 in the source tree.
 +
 +      * Fixed a fatal bug in silc_server_remove_clients_by_server.  Do
 +        not handle clients that has entry->data.registered == FALSE.
 +        They are not in the network anymore.  Affected file is
 +        silcd/server.c.
 +
 +Tue Apr  3 16:39:19 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented the sending of the SERVER_SIGNOFF notify in the
 +        server.  Affected file is silcd/server.c.
 +
 +      * Added silc_server_send_notify_args into silcd/packet_send.[ch].
 +        Added also silc_notify_payload_encode_args into the
 +        lib/silccore/silcnotify.[ch].
 +
 +      * Implemented ther SERVER_SIGNOFF notify handling in the server.
 +        Affected file silcd/packet_receive.c.
 +
 +      * Implemented the SERVER_SIGNOFF notify handling in the client
 +        library.  Affected file lib/silcclient/client_notify.c.  Also,
 +        implemnted the printing of the SERVER_SIGNOFF info to the
 +        application.  Affected file silc/client_ops.c.
 +
 +      * The silc_idlist_del_server now returns TRUE or FALSE to indicate
 +        if the deleting was successful.  Affected file silcd/idlist.[ch].
 +
 +      * Added support for public key authentication in the connection
 +        authentication protocol in the client library.  Affected file
 +        lib/silcclient/protocol.c.
 +
 +      * Changed the server's silc_idlist_get_clients_by_* interface
 +        to support already allocated array so that new entries may be
 +        added to pre-allocated array.  Affected file silcd/idlist.[ch].
 +        This fixes some bugs with WHOIS, WHOWAS and IDENTIFY commands
 +        and command replies.
 +
 +      * All command reply functions in the server now calls the 
 +        pending command callback even if error occured.  This way the
 +        error will be delivered to the client as well.  Affected files
 +        silcd/command.c and silcd/command_reply.c.
 +
 +      * Fixed INFO command to return local server's info if no server
 +        was provided.  Affected file lib/silcclient/command.c.
 +
 +      * Removed RESTART command for good.  Updated the code and the
 +        protocol specs.
 +
 +      * Rewrote parts of the task system.  It is a bit simpler now.
 +        Removed unsued task priorities. The affected files are
 +        lib/silcutil/silctask.[ch].
 +
 +Mon Apr  2 20:02:33 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Moved the USERS printing from the library to the application.
 +        Affected files lib/silcclient/command.c and silc/client_ops.c.
 +
 +Mon Apr  2 13:13:23 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Updated TODO.
 +
 +      * Added channel key re-key support.  The re-key is perfomed
 +        only by the router and is done once in an hour.  Added `rekey'
 +        field to the SilcChannelEntry in the server.  Affected files
 +        silcd/server.c and silcd/idlist.h.
 +
 +      * Added silc_task_unregister_by_context into the file
 +        lib/silcutil/silctask.[ch].
 +
 +Sun Apr  1 19:49:34 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added SILC_UMODE_GONE mode to indicate when the client is not
 +        present in the SILC network.  Added also support to the local
 +        command AWAY that will set this mode.  Added support of showing
 +        "xxx is gone" in WHOIS command.  The USERS command shows the
 +        gone status as well.
 +
 +      * Fixed setting server and router operator privileges in the
 +        server's UMODE command.  Affected file silcd/command.c.
 +
 +      * Merged the SKE KE1 and KE2 payloads into one payload.  The
 +        new KE payload is equivalent to the old KE2 payload.
 +
 +        Cleaned up the SKE Start Payload parsing.  It now uses the
 +        simple buffer unformatting to do the parsing.  A lot faster
 +        now.
 +
 +        Added new Mutual Authentication flag (SILC_SKE_SP_FLAG_MUTUAL)
 +        to the SKE that is used to indicate whether both of the SKE
 +        parties should perform authentication.  By default only the
 +        responder performs authentication.  By setting this flag also
 +        the initiator must do authentication.  By default it is unset
 +        since in normal SKE case, client to server connection, only
 +        the responder should do authentication.  When doing SKE between
 +        two clients both should perform authentication.  Updated the
 +        code and the protocol specs.
 +
 +      * A little fix to IDENTIFY command in the server.  Search the
 +        client first by hash not nickname.  Affected file is 
 +        silcd/command.c.
 +
 +      * Fixed the silc_client_close_connection to support closing
 +        the client to client connections wihtout deleting too much
 +        data.  Affected file lib/silcclient/client.c.
 +
 +      * Fixed a fatal bug in server and client; if KE1 or KE2 packets
 +        are received if protocol used to be active but is not anymore
 +        the application would crash due to NULL pointer dereference.
 +        Affected files silcd/server.c and lib/silcclient/client.c.
 +
 +      * Added `hash' field to the SilcClientConnection to include
 +        the hash function negotiated in the SKE protocol.
 +
 +      * Added new channel mode SILC_CMODE_FOUNDER_AUTH that is used
 +        to set the channel founder authentication data.  A client can
 +        claim the founder rights later by providing the authentication
 +        data to the CUMODE command using SILC_CUMODE_FOUNDER mode.
 +        This way the channel founder can regain the channel founder
 +        privileges even it is left the channel.  This works only on
 +        local server and the client must be connected to the same
 +        server to be able to regain the founder rights.  Updated the
 +        protocol specs accordingly.
 +
 +        Added support to the CMODE command in the client to set the
 +        founder auth data.  Read the README to see how to set it.
 +
 +        Added support to the CUMODE command to claim the founder
 +        rights.  Read the README to see how to do it.
 +
 +        Added support for the founder authentication to the Channel
 +        Entry in the server.  Affected file silcd/idlist.h.
 +
 +        Added support for the SILC_CMODE_FOUNDER_AUTH mode in the
 +        server's CMODE command.  Affected file silcd/command.c.
 +
 +      * Added the following new functions into lib/silccore/silcauth.[ch]:
 +        silc_auth_get_method and silc_auth_get_data.    
 +
 +      * The server now saves the remote hosts public key to the
 +        SilcIDListData pointer.  Affected file silcd/protocol.c.
 +
 +      * The normal server now does not remove the channel entry from
 +        the cache if the founder authentication data is set.  It used
 +        to remove it if the founder was the last one on the channel on 
 +        the server and left the channel.  The auth data is saved and
 +        if the channel is re-joined later the old entry is used with
 +        the old auth data.  Affected files silcd/command_reply.c and
 +        silcd/server.c.
 +
 +      * Removed the `pkcs' field from the SilcIDListData structure
 +        in the server; it is not used.  Affected file silcd/idlist.h.
 +
 +Sat Mar 31 15:38:36 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed packet processing on slow links.  Partial packets were
 +        never re-processed because the incoming data buffer was cleared
 +        by the application.  Application must not directly clear the
 +        sock->inbuf, the packet processing routines handle it.  Fixed
 +        this in client library and in server.
 +
 +Fri Mar 30 16:35:27 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the WHOIS and IDENTIFY send reply function to really
 +        check whether to send list or just one entry.  Affected file
 +        silcd/command.c.
 +
 +      * Cleaned up the LEAVE command's channel key distribution.  The
 +        affected file silcd/command.c.
 +
 +      * Changed CMODE_CHANGE's <Client ID> to <ID Payload> as server
 +        can enforce the channel mode as well.  In that case the ID
 +        includes the ID of the server.  The code now enforces the
 +        mode change if the router have different mode than the server.
 +
 +      * The notify client operation with CMODE_CHANGE notify can now
 +        return NULL client_entry pointer if the CMODE was not changed
 +        by client.  Application must check for this.
 +
 +      * Added <Server ID> argument to INFO command to support server
 +        info fetching by Server ID.
 +
 +      * Added silc_server_announce_get_channel_users to get assembled
 +        packets of channel users of the specified channel.  Affected
 +        file silcd/server.[ch].
 +
 +      * Fixed bug in CHANNEL_CHANGE notify in the server.  The new ID
 +        was freed underneath the ID Cache.
 +
 +      * Re-announce clients when the server received CHANNEL_CHANGE
 +        notify from the router.  Affected file silcd/packet_send.c.
 +
 +Thu Mar 29 19:10:28 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a fatal bug when client does /join 1 2 3 4 5 6 the server
 +        crashed since it did not handle the fact that there is no cipher
 +        called "3" and didn't check the error condition.  Now fixed.
 +
 +      * Added SILC_MESSAGE_FLAG_REQUEST message flag as generic request
 +        flag.  It can be used to send message requests.
 +
 +Thu Mar 29 12:26:25 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented the RESTART command in the client.
 +
 +      * Added SILC_MESSAGE_FLAG_NOTICE message flag for informational
 +        notice type messages.  Added notice printing to the user
 +        interface.
 +
 +      * The channel keys are not re-generated if the channel's mode
 +        is PRIVKEY, ie private key on the channel exists.  Affected
 +        files silcd/server.c and silcd/command.c.
 +
 +      * Fixed a little bug in channel message delivery when channel
 +        private keys are set in the server.  Affected file is
 +        silcd/packet_send.c.
 +
 +      * Changed the setting on channel->on_channel = TRUE from the
 +        silc_client_save_channel_key to the JOIN command reply.  The
 +        key payload is not received if the private channel key is set.
 +        Affected file lib/silcclient/command_reply.c and the
 +        lib/silcclient/client_channel.c.
 +
 +      * When the CMODE_CHANGE notify is sent and the channel private
 +        key mode is removed the channel key must be re-generated in
 +        other cells as well.  Added this support for the router in the
 +        silcd/packet_receive.c.
 +
 +      * Added new local command NOTICE to send notice message on
 +        channel.  Affected file silc/local_command.[ch].
 +
 +Wed Mar 28 23:55:54 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new local command ME to the client.  It is used to send
 +        message to a channel with SILC_MESSAGE_FLAG_ACTION to indicate
 +        some action.  Affected file silc/local_command.[ch].
 +
 +      * Changed channel_message and private_message client operations 
 +        to deliver the message flags to the application.  Added also
 +        the `flags' arguments to the silc_client_send_channel_message
 +        and silc_client_send_private_message functions.  Affected file
 +        silcapi.h.
 +
 +Wed Mar 28 20:50:47 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Redefined the Private Message Payload to support private message
 +        keys and to support the new private message flags.  Updated
 +        the protocol specs.  Flags makes it possible to have for example
 +        CTCP style messages.
 +
 +      * Added new type SilcPrivateMessagePayload and defined an API
 +        for it in the lib/silcclient/silcprivate.[ch].
 +
 +      * Tested private message private keys successfully.  Tested the
 +        private message key set, unset and list commands with the new
 +        KEY command.
 +
 +      * Redefined the Channel Message Payload to include the channel
 +        message flags (equal with private message flags) to support
 +        for example CTCP style messages.
 +
 +      * Defined some of the message (for channel and private message)
 +        flags.  Updated the protocol specs and added the flags to the
 +        lib/silccore/silcchannel.h.  The type is SilcMessageFlags.
 +
 +Wed Mar 28 15:52:36 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added SilcKeyAgreementStatus type to the key agreement routines
 +        to indicate the current status and error if one occured.
 +        The status types are defined in the lib/silcclient/silcapi.h.
 +
 +      * Added new local command KEY that is used to set and unset private
 +        keys for channels, set and unset private keys for private messages
 +        with remote clients and to send key agreement requests and
 +        negotiate the key agreement protocol with remote client.  The
 +        key agreement is supported only to negotiate private message keys,
 +        it currently cannot be used to negotiate private keys for channels,
 +        as it is not convenient for that purpose.
 +
 +      * Fixed a minor pending callback setting bug in the function
 +        silc_client_get_client_by_id_resolve, now the function works.
 +        Affected file lib/silcclient/idlist.c.
 +
 +      * Added function silc_net_get_local_port to get local bound
 +        port by socket.  Added to lib/silcutil/silcnet.[ch].
 +
 +      * Added `sockets' and `sockets_count' fields to the SilcClient
 +        object.  They hold the sockets of the listenning sockets in
 +        the client.  Listenning sockets may be for example the key 
 +        agreement server.  Affected file lib/silcclient/client.[ch].
 +        Added functions the silc_client_add_socket and the
 +        silc_client_del_socket.  They are exported to the application
 +        as well.
 +
 +      * Added ~./silc/clientkeys to support other client's public keys.
 +
 +      * Renamed verify_server_key client operation to verify_public_key
 +        and added one argument to indicate the type of the connection
 +        (server, client etc.).
 +
 +Tue Mar 27 22:22:38 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_server_connection_auth_request to handle the
 +        incoming CONNECTION_AUTH_REQUEST packet.  Affected file is
 +        silcd/packet_receive.[ch].
 +
 +      * Added silc_server_send_connection_auth_request into the
 +        silcd/packet_send.c to send the connection auth request packet.
 +
 +      * Cleaned up the silcd/protocol.c a bit and fixed some memory
 +        leaks.
 +
 +      * Fixed the public key authentication in responder side in the
 +        server.  The `auth_data' pointer includes the SilcPublicKey
 +        not the path to the public key.  Affected file silcd/protocol.c.
 +
 +      * Implemented the public key authentication in the initiator side
 +        in the server.  Affected file silcd/protocol.c.
 +
 +      * Removed the [RedirectClient] config section from the server
 +        configuration.  Is not needed and I don't want to implement it.
 +
 +Tue Mar 27 12:49:56 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Cleaned up the CMODE command in the server.  It now works
 +        correctly and supports all the modes defined in the protocol.
 +        Affected file is silcd/command.c.
 +
 +      * Added `hmac_name' field to the SilcChannelEntry in the server
 +        to hold the default HMAC of the channel.  It can be set when
 +        creating the channel (with JOIN command).  Affected files
 +        silcd/idlist.[ch].
 +
 +      * Added <cipher> and <hmac> argument to the CMODE_CHANGE notify
 +        type to indicate the change of the current cipher and hmac
 +        on the channel.  Client can safely ignore the <cipher> argument
 +        (if it chooses to do so) since the CHANNEL_KEY packet will 
 +        force the channel key change anyway.  The <hmac> argument is
 +        important since the client is responsible of setting the new
 +        HMAC and the hmac key into use.
 +
 +      * Fixed the CMODE command in the client library as well.
 +
 +      * Tested CMODE command in router environment successfully.
 +
 +Mon Mar 26 14:39:48 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Show the version of the remote client (or server) when connecting
 +        to the server.  It is logged to the log file.  Affected file
 +        is silcd/protocol.c.
 +
 +      * Fixed the KILLED notify handling in the client library.  The
 +        client must be removed from all channels when receiving the
 +        KILLED notify.
 +
 +        Also, do not remove the client entry when giving the KILL 
 +        command but when the KILLED notify is received.
 +
 +      * Removed silc_idlist_find_client_by_nickname from the server.
 +        Not needed anymore.  Affected files silcd/idlist.[ch].
 +
 +      * Implemented the CHANNEL_CHANGE notify type handling to the
 +        server.  Affected file silcd/server.c.
 +
 +      * Updated TODO.
 +
 +Mon Mar 26 12:11:14 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_server_send_notify_invite to send the INVITE
 +        notify between routers.
 +
 +      * Implemented the INVITE command correctly to the server.
 +
 +      * Implemented the INVITE notify type handling in the server.
 +
 +      * Implemented the INVITE command to the client library and on the
 +        user interface.
 +
 +Sun Mar 25 20:27:09 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added function silc_server_get_client_resolve to find the
 +        client entry by ID from all ID lists and then resolve it
 +        (using WHOIS) if it cannot be found.  Affected file is
 +        silcd/server.[ch].
 +
 +Sun Mar 25 13:52:51 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented the BAN command to the client library.
 +
 +      * The JOIN command in the server now checks the invite list
 +        and the ban list.
 +
 +      * Changed the silc_command_reply_payload_encode_va and the
 +        silc_command_payload_encode_va to support that if argument is
 +        NULL it ignores and checks the next argument.  Affected file
 +        lib/silccore/silccommand.c.
 +
 +      * Added silc_server_send_notify_ban to send the BAN notify
 +        type between routers.
 +
 +      * Chaned the silc_notify_payload_encode to support that if 
 +        argument is NULL it ignores and checks the next argument.
 +        Affected file lib/silccore/silcnotify.c.
 +
 +      * Tested ban lists in router environment successfully.
 +
 +Sat Mar 24 14:47:25 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented BAN command to the server, in silcd/command.[ch].
 +
 +      * Removed the BAN and INVITE_LIST modes from the CMODE command
 +        in the server code.
 +
 +      * Added function silc_string_match to regex match two strings.
 +        Affected files lib/silcutil/silcutil.[ch].
 +
 +Fri Mar 23 22:02:40 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Redefined parts of the SilcChannelEntry in the server to support
 +        the new ban and invite lists.
 +
 +Fri Mar 23 16:25:11 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Redefined the INVITE command.  The same command can be used to
 +        invite individuals to the channel but also to manage the invite
 +        list of the channel (to add to and remove from the invite list).
 +        Updated the protocol specs.
 +
 +      * Added new command SILC_COMMAND_BAN that can be used to manage
 +        the ban list of the channel.  Updated the protocol specs.
 +
 +      * Removed the channel modes: the SILC_CMODE_BAN and the 
 +        SILC_CMODE_INVITE_LIST as they were a bit kludge to be included
 +        in the CMODE command.  The equivalent features are now available
 +        using INVITE and BAN commands.  Updated the protocol specs.
 +
 +      * Added new SILC_NOTIFY_TYPE_BAN notify type to notify routers
 +        in the network about change in the current ban list.  The notify
 +        type is not used by the client.
 +
 +      * Redefined parts of the SILC_NOTIFY_TYPE_INVITE command to 
 +        support the invite lists.
 +
 +Thu Mar 22 22:52:23 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new function silc_string_regexify that converts string
 +        including wildcard characters into regex string that can
 +        be used by the GNU regex library.  Added into the file
 +        lib/silcutil/silcutil.[ch].
 +
 +        Added silc_string_regex_combine to combine to regex strings
 +        into one so that they can be used as one regex string by
 +        the GNU regex library.  Added into the file
 +        lib/silcutil/silcutil.[ch].
 +
 +        Added silc_string_regex_match to match two strings.  It returns
 +        TRUE if the strings match.  Added into lib/silcutil/silcutil.[ch].
 +
 +Thu Mar 22 15:29:42 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Imported GNU regex to the soruce tree into lib/contrib.
 +        Fixed some compiler warning from the regex.c.
 +
 +Wed Mar 21 15:27:58 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed MOTD command in the server to work in router environment.
 +
 +      * Fixed the MOTD command in the client library to support
 +        the server argument in the command.
 +
 +      * Added `nickname_len' argument to the silc_idlist_add_client
 +        in the server, as the `nickname' argument may be binary data
 +        (it may be hash).
 +
 +      * Added silc_idlist_get_channels to return all channels from
 +        the ID list.
 +
 +      * Implemented LIST command to the server.  Affected file is
 +        silcd/command.c.
 +
 +      * Implemented the LIST command to the client library and on the
 +        user interface.
 +
 +      * Added [<user count>] argument to the LIST command reply.
 +        With private channels the user count is not shown.
 +
 +      * Updated TODO and README.
 +
 +Tue Mar 20 21:05:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * The client entry's data.registered must be TRUE even with
 +        global client entry on global client list.  The data.registered
 +        is used to check whether the client is anymore in the network,
 +        for example with WHOWAS command so it must be valid.
 +
 +      * Fixed the WHOWAS command in the server.  It now actually works
 +        in router environment.  Added function into silcd/command_reply.c
 +        silc_server_command_reply_whowas_save.
 +
 +      * Added silc_idlist_purge function to the silcd/idlist.c
 +        to periodically purge the ID Cache.
 +
 +      * Fixed INFO command in the server.  It works now in router
 +        environment.  Added <server name> argument to the INFO command
 +        reply.  Updated the protocol specs.
 +
 +      * Fixed minor bug in silc_idcache_purge to not purge if the
 +        expire value is zero.
 +
 +      * Fixed various bugs in WHOIS and IDENTIFY command handling as
 +        they were buggy because of the WHOWAS information.
 +
 +      * Fixed local command MSG to handle the async resolving of 
 +        the remote client properly.  It used to fail the first MSG.
 +        Affected file silc/local_command.c.
 +
 +      * Added `data_len' field to SilcIDCache context.
 +
 +Tue Mar 20 16:29:00 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Update TODO.  Todo in commands in the server.
 +
 +Tue Mar 20 15:45:14 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new notify type SILC_NOTIFY_TYPE_UMODE_CHANGE that is
 +        used by routers as broadcast packet to inform other routers
 +        about the changed user mode.
 +
 +        Implemented the notify handling in the server.  Affected file is
 +        silcd/packet_receive.c.  Added the function 
 +        silc_server_send_notify_umode to the silcd/packet_send.[ch].
 +
 +      * Added new generic Channel Payload and deprecated the New Channel
 +        Payload.  The New Channel Payload is now the generic Channel
 +        Payload.
 +
 +      * Added new argument `mode' to the silc_server_send_new_channel
 +        as it is required in the Channel Payload now.
 +
 +      * Renamed the SilcChannelPayload to SilcChannelMessagePayload
 +        and created a new and real SilChannelPayload to represent the
 +        new generic Channel Payload.  Implemented the encode/decode
 +        for Channel Payload.  Affected file lib/silccore/silcchannel.[ch].
 +
 +      * Added silc_server_get_client_channel_list to return the list
 +        of channels the client has joined for WHOIS command reply.
 +        Affected file silcd/server.[ch].
 +
 +      * Implemented the channel list sending in the WHOIS command reply
 +        in server and in the client.
 +
 +        Implemented the channel list displaying on the user interface
 +        as well.  Affected file silc/client_ops.c.
 +
 +      * Added silc_channel_payload_parse_list to parse list of Channel
 +        Payloads.  It returns SilcDList list of SilcChannelPayloads.
 +        Client for example can use this function to parse the list of
 +        channels it receives in the WHOIS command reply.  The caller
 +        must free the list by calling silc_channel_payload_list_free.
 +        Affected files lib/silccore/silcchannel.[ch].
 +
 +Mon Mar 19 21:39:15 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added one new argument <user mode> to the WHOIS command reply
 +        to return the mode of the user in SILC.  Updated the protocol
 +        specs.
 +
 +        Implemented it to the server and client.
 +
 +Mon Mar 19 18:43:06 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the mode printing on the user interface on joining.
 +        Affected file silc/client_ops.c.
 +
 +      * Implemented the UMODE command and user modes in general to the
 +        client library and to the user interface.
 +
 +      * Implemented the UMODE command to the server.
 +
 +      * The server now sends UNKNOWN_COMMAND error status if client sends
 +        unknown command.  Affected file silcd/command.c.
 +
 +      * All server commands now handle the command identifier the right
 +        way when sending the command reply to the client.  The client can
 +        use to identify the command replies with the identifier.
 +
 +Mon Mar 19 16:13:07 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_server_get_client_route to resolve the route to
 +        the client indicated by the client ID.  Affected file is
 +        silcd/server.[ch].
 +
 +      * Added silc_server_relay_packet as general function to relay
 +        packet to arbitrary destination.  This deprecates functions
 +        like _send_private_message_key, _relay_notify etc.  Affected
 +        file is silcd/packet_send.[ch].
 +
 +        Removed silc_server_send_key_agreement, 
 +        silc_server_send_private_message_key and
 +        silc_server_packet_relay_notify functions from the file
 +        silcd/packet_send.[ch].
 +
 +      * Updated TODO.
 +
 +      * Implemented the SILC_NOTIFY_TYPE_KILLED notify handling in the
 +        server.  Affected file silcd/packet_receive.[ch].
 +
 +      * Implemented the KILL command to the client.  Implemented the
 +        SILC_NOTIFY_TYPE_KILLED notify handling in the client library.
 +        Affected files lib/silcclient/command[_reply].c and
 +        lib/silcclient/client_notify.c.  Implemented the KILL notify
 +        printing in the user inteface.
 +
 +      * Fixed a lot silc_parse_nick memory leaks from the client
 +        library in the file lib/silcclient/command.c.
 +
 +      * Changed the silc_server_send_notify_on_channels's `sender'
 +        argument from SilcSocketConnection to SilcClientEntry to 
 +        check the sender as entry and not as connection object and not
 +        to send to the client provided as argument.  The affected file
 +        is silcd/packet_send.[ch].
 +
 +      * The notify packets that are destined directly to the client used
 +        to not to be processed by the server.  Now changed that and the
 +        server processes all notify packets.  After relaying the packet
 +        to the client the notify packet is processed in the server.
 +
 +      * The silc_server_free_client_data now checks whether there is
 +        pending outgoing traffic for the client and purges the data to
 +        the network before removing the client entry.
 +
 +Sun Mar 18 21:02:47 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added SILC_NOTIFY_TYPE_KILLED notify type.  It is sent when
 +        an client is killed from the SILC Network.  Updated the protocol
 +        specs accordingly.
 +
 +        Added new function silc_server_send_notify_killed to the
 +        silcd/packet_send.[ch].
 +
 +      * Added function silc_server_packet_relay_notify to relay notify
 +        packets that are destined directly to a client.  In this case
 +        the server does not process the notify packets but merely relays
 +        it to the client.  Affected file silcd/packet_send.[ch].
 +
 +        Added also silc_server_packet_process_relay_notify to check
 +        whereto relay the notify.  Affected file is 
 +        silcd/packet_receive.[ch].
 +
 +      * Implemented the KILL command to the server.
 +
 +      * Updated TODO.
 +
 +      * Added the backup schema desgined last fall to the protocol
 +        specs for everyone to see.  The specification is in the
 +        *-spec-xx.txt draft and the packet type definitions for the
 +        backup routers is in *-pp-xx.txt draft.  Thusly, added also
 +        new packet type SILC_PACKET_CELL_ROUTERS.
 +
 +      * A big security problem in the implementation discovered.  The
 +        signoff of an client did not cause new channel key generation
 +        which it of course should've done.  The channel keys must be
 +        always re-generated when client leaves (or signoffs) the channel.
 +        The silc_server_remove_from_channels funtion now handles
 +        the channel key re-generation.
 +
 +      * Added `sender' argument to the silc_server_send_notify_on_channels
 +        to not to send the client provided as argument.  Affected file
 +        silcd/packet_send.[ch].
 +
 +Fri Mar 16 15:52:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented OPER and SILCOPER commands into the server and
 +        the client library.
 +
 +      * Added silc_auth_verify and silc_auth_verify_data to verify
 +        the authentication directly from the authentication payload.
 +        It supports verifying both passphrase and public key based
 +        authentication.  Affected file lib/silccore/silcauth.[ch].
 +
 +      * Added `hash' field to the SilcIDListData structure.  It is the
 +        hash negotiated in the SKE protocol.  Affected file is
 +        silcd/idlist.[ch].
 +
 +      * Slight redesigning of the SilcAuthPayload handling routines.
 +        Do not send SilcPKCS but SilcPublicKey as argument.
 +
 +      * Implemented the public key authentication support to the
 +        serverconfig.  The public key is loaded from the provided path
 +        and saved as authentication data to void * pointer.  Thus,
 +        changed the unsigned char *auth_data to void *auth_data;
 +
 +      * Fixed SHUTDOWN command to send the reply before the server
 +        is shutdown. :)  Affected file silcd/command.c.
 +
 +      * Fixed fatal bug in CONNECT command.  The hostname was invalid
 +        memory and server crashed.  Affected file silcd/command.c.
 +
 +      * Fixed fatal bug in CLOSE command.  The server_entry became
 +        invalid but was referenced later in the command.  Affected file
 +        silcd/command.c.
 +
 +Thu Mar 15 12:46:58 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed fatal bug in failure packet handling.  Server ignored
 +        the failure and thus crashed when it came.
 +
 +      * Updated TODO.
 +
 +Wed Mar 14 20:37:35 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new SILC_CF_LAG_STRICT command flag that strictly forces
 +        that the command may be executed only once in (about) 2 seconds.
 +        The old SILC_CF_LAG flag is same but allows command bursts up
 +        to five before limiting.
 +
 +        Added the support for CF_LAG and CF_LAG_STRICT flags to the
 +        server code.  Various commands now includes the CF_LAG_STRICT
 +        flag to disallow any kind of miss-use of the command.
 +
 +      * Fixed the silc_buffer_unformat to not to allocate any data
 +        if the length of the data is zero.  It used to allocate the
 +        length + 1.  Affected file lib/silcutil/silcbuffmt.c.
 +
 +Wed Mar 14 16:10:30 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed the format of AdminConnection configuration section
 +        in the server.  Added username of the admin to the format.
 +        Affected files silcd/serverconfig.[ch].
 +
 +        Added silc_server_config_find_admin into silcd/serverconfig.[ch]
 +        to return admin configuration data by host, username and/or
 +        nickname.
 +
 +Wed Mar 14 13:18:16 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented WHOWAS command to the server.  Added the functions:
 +
 +        silc_server_command_whowas_parse,
 +        silc_server_command_whowas_send_reply,
 +        silc_server_command_whowas_from_client and
 +        silc_server_command_whowas_from_server
 +
 +      * Added <Client ID> argument to the WHOWAS command reply.  Updated
 +        the protocol specs accordingly.
 +
 +      * Implemented WHOWAS command and command_reply to the client
 +        library.
 +
 +        Implemented the WHOWAS printing on the user interface.
 +
 +Tue Mar 13 22:17:34 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new argument to the WHOWAS command reply, the real name.
 +        It is an optional argument.  Updated the protocol specs.
 +
 +      * Added SilcIDCacheDestructor callback that is registered when
 +        the SilcIDCache is allocated.  The callback is called when
 +        an cache entry in the ID Cache expires, or is purged from the
 +        cache.  Added into lib/silccore/idcache.[ch].
 +
 +        Added silc_idlist_client_destructor to the silcd/idlist.[ch]
 +        to destruct the client entries when the cache entry expires.
 +        Other ID Cache's in server and in the client library ignores
 +        the destructor.
 +
 +      * If the ID Cache entry's `expire' field is zero then the entry
 +        never expires.  Added boolean `expire' argument to the
 +        silc_idcache_add function in the lib/silccore/idcache.[ch].
 +        If it is TRUE the default expiry value is used.
 +
 +      * Added silc_server_free_client_data_timeout that is registered
 +        when client disconnects.  By default for 5 minutes we preserve
 +        the client entry for history - for WHOWAS command.
 +
 +Tue Mar 13 13:26:18 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added support to the server to enforce that commands are not
 +        executed more than once in 2 seconds.  If server receives 
 +        commands from client more frequently, timeout is registered
 +        to process the commands.  Affected file silcd/command.c.
 +        Added new function silc_server_command_process_timeout.
 +
 +      * Changed NICK_NOTIFY handling in client library to check that
 +        if the client's nickname was changed, so there is no need to
 +        resolve anything from the server.
 +
 +      * Removed error printing from the WHOIS and IDENTIFY commands.
 +        If error occurs then it is ignored silently in the client library.
 +        The application, however, may map the received error to 
 +        human readable error string.  The application currently maps
 +        the NO_SUCH_NICKNAME error to string.
 +
 +      * Made the command status message public to the application.  Moved
 +        them from lib/silcclient/command_reply.c to 
 +        lib/silcclient/command_reply.h.  The application can map the
 +        received command status to the string with the
 +        silc_client_command_status_message function.
 +
 +      * Added check to the server to check that client's ID is same
 +        as the Source ID in the packet the client sent.  They must
 +        match.
 +
 +Tue Mar 13 12:49:21 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added dist-bzip hook to the Makefile.am to make bzip2
 +        compressed distributions.
 +
 +Mon Mar 12 18:43:38 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Server now enforces the maximum length for the nickname and
 +        the channel as protocol specification dictates.  128 bytes for
 +        nickname and 256 bytes for channel name.
 +
 +      * Moved the WHOIS printing to the application.  The client libary
 +        does not print out the WHOIS information anymore, the application
 +        must do it.  Renamed silc_client_command_reply_whois_print to
 +        the silc_client_command_reply_whois_save.
 +
 +        The client's idle time is also sent to the application now, and
 +        the idle is shown on screen.
 +
 +      * Added silc_client_command_reply_identify_save to save the
 +        received IDENTIFY entries.
 +
 +      * Do not check for channel private keys in message sending and
 +        reception if the channel does not have the PRIVKEY mode set.
 +        Affected file lib/silclient/client_channel.c.
 +
 +Sun Mar 11 20:25:06 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a minor bug if WHOIS and IDENTIFY command parsing that
 +        just surfaced after chaning the JOIN procedure.
 +
 +Sun Mar 11 14:59:05 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_client_get_clients_by_list to get client entries
 +        from Client ID list, that is returned for example by JOIN
 +        and USERS command replies.  The application should use this
 +        function for example when JOIN command reply is received to
 +        resolve the clients already on the channel (library does not
 +        do that anymore as USERS command reply is not used in the JOIN
 +        procedure anymore).  Affected files lib/silcclient/silcapi.h and
 +        lib/silcclient/idlist.c.
 +
 +      * JOIN command reply and USERS command reply returns now SilcBuffer
 +        pointers instead of unsigned char pointers when returning
 +        the client list and mode list.
 +
 +      * Added <Client ID> argument to the JOIN command reply, mainly
 +        for the server to identify for which client the command was
 +        originally sent.  Updated protocol specs accordingly.
 +
 +      * Added SilcDlist private_key pointer to the SilcChannelEntry
 +        in the client to support the channel private keys.  Affected
 +        file is lib/silcclient/idlist.h.
 +
 +      * Added SilcChannelPrivateKey argument to the function
 +        silc_client_send_channel_message so that application can choose
 +        to use specific private ke if it wants to.  If it is not provided,
 +        the normal channel key is used, unless private keys are set. 
 +        In this case the first (key that was added first) is used 
 +        as the encryption key.
 +
 +      * Implemented the support for channel private key handling.
 +        Implemented the following functions:
 +
 +        silc_client_add_channel_private_key,
 +        silc_client_del_channel_private_keys,
 +        silc_client_del_channel_private_key,
 +        silc_client_list_channel_private_keys and
 +        silc_client_free_channel_private_keys
 +
 +        Affected file lib/silcclient/client_channel.c.
 +
 +      * Added the support for the private keys in the channel message
 +        sending and encryption and in the message reception and
 +        decryption.  Affected funtions are
 +        silc_client_send_channel_message and silc_client_channel_message.
 +
 +Sat Mar 10 21:36:22 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added SKE's key verify callback to the client library's
 +        KE protocol context. Affected files lib/silcclient/protocol.[ch].
 +
 +      * Removed the statement that server (or router) must send USERS
 +        command reply when joining to the channel so that the client
 +        knows who are on the channel.  Instead, the client list and 
 +        client's mode list is now sent in the JOIN command reply to the
 +        client who joined channel.  This is better solution.
 +
 +      * Added function silc_server_get_users_on_channel and function
 +        silc_server_save_users_on_channel to the silcd/server.[ch].
 +
 +      * Removed function silc_server_command_send_users from the
 +        silcd/command.c.
 +
 +      * Do not show topic on the client library anymore.  The topic is
 +        sent in the command reply notify to the application and the
 +        application must show the topic now.
 +
 +Sat Mar 10 00:07:37 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added client searching by nickname hash into the IDENTIFY and
 +        WHOIS commands in the server as they were clearly missing from
 +        them.  Affected file is silcd/command.c.
 +
 +      * Fixed a bug in private message receiving in the client library.
 +        The remote ID was freed and it wasn't supposed, now it is
 +        duplicated.
 +
 +Fri Mar  9 12:40:42 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Minor fix to the channel payload; allocate the data area, as it
 +        needs to be of specific length.
 +
 +      * If the key agreement port is zero then the operating
 +        system will define the bound port.  Affected files are
 +        lib/silcclient/silcapi.h and lib/silcclient/client_keyagr.c.
 +
 +      * Added new function silc_channel_payload_decrypt into the file
 +        lib/silccore/silcchannel.[ch].
 +
 +      * Moved the channel message etc, check from silc_packet_decrypt
 +        to applications.  The library calls now a generic 
 +        SilcPacketCheckDecrypt callback which is to return TRUE or FALSE
 +        when the packet is either normal or special.  This was done to
 +        allow more wide range of checking that was not allowed when
 +        the code was in library.  Now applications can do virtually any
 +        checks to the packet and return to the library the decision how
 +        the packet should be processed.  Affected files are
 +        lib/silccore/silcpacket.[ch].
 +
 +        Added silc_server_packet_decrypt_check to the server and
 +        silc_client_packet_decrypt_check to the client library.
 +
 +      * Added silc_server_packet_send_srcdest into silcd/packet_send.[ch]
 +        to send with specified source and destination information.
 +
 +      * Channel message delivery between routers was broken after the
 +        channel key distribution was fixed earlier.  The channel key
 +        was used be to distributed to other routers as well which is not
 +        allowed by the protocol.  Now this is fixed and channel keys
 +        really are cell specific and the channel message delivery between
 +        routers comply with the protocol specification.
 +
 +      * Fixed various commands in server to check also the global list
 +        for the channel entry and not just the local list.  The affected
 +        file silcd/command.c.
 +
 +Thu Mar  8 21:39:03 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added assert()s to buffer formatting and unformatting routines
 +        to assert (if --enable-debug) when error occurs.  Affected
 +        file: lib/silcutil/silcbuffmt.c.
 +
 +      * Changed to auto-reconnect to check whether the remote host is
 +        router and register the re-connect timeout if it is.  It used 
 +        to check that whether we are normal server, but router must do
 +        auto-reconnect with another router as well.  Affected file
 +        silcd/server.c.
 +
 +      * Removed the [<key len>] option from CMODE command as the cipher
 +        name decides the key length, nowadays.  See the defined ciphers
 +        from the protocol specification.
 +
 +      * Added [<hmac>] option to the CMODE command to define the HMAC
 +        for the channel.  Added SILC_CMODE_HMAC channel mode.
 +
 +      * Added [<hmac>] option for the JOIN command so that user can
 +        select which HMAC is used to compute the MACs of the channel
 +        messages.
 +
 +      * Added Hmac field to the Channel Message Payload.  The integrity
 +        of plaintext channel messages are now protected by computing
 +        MAC of the message and attaching the MAC to the payload.  The
 +        MAC is encrypted.  Now, it is clear that this causes some
 +        overhead to the size of the packet but rationale for this is that
 +        now the receiver can verify whether the channel message decrypted
 +        correctly and also when private keys are set for the channel the
 +        receiver can decrypt the packet with several keys and check from
 +        the MAC which key decrypted the message correctly.
 +
 +      * Added silc_cipher_encrypt and silc_cipher_decrypt into the
 +        lib/silccrypt/silccipher.[ch].
 +
 +      * Added silc_hash_len to return the digest length into the
 +        lib/silcrypt/silchash.[ch].
 +
 +      * Rewrote parts of Silc Channel Payload interface in the
 +        lib/silccore/silcchannel.[ch].  The encode function now also
 +        encrypts the packet and parse function decrypts it.
 +
 +Wed Mar  7 20:58:50 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a minor formatting bug in the SKE's key material processing.
 +        It actually might have processed the keys wrong way resulting
 +        into wrong keys.
 +
 +      * Redefined the mandatory HMAC algorithms and added new algorithms.
 +        Added hmac-sha1-96 and hmac-md5-96 which are normal hmac-sha1
 +        and hmac-md5 truncated to 96 bits.  The mandatory is now 
 +        hmac-sha1-96.  Rest are optional (including the one that used
 +        to be mandatory).  Rationale for this is that the truncated HMAC
 +        length is sufficient from security point of view and can actually
 +        make the attack against the HMAC harder.  Also, the truncated
 +        HMAC causes less overhead to the packets.  See the RFC2104 for
 +        more information.
 +
 +      * Added new [hmac] configuration section.  The SKE used to use
 +        the hash names (md5 and sha1) in the SKE proposal as HMCAS which
 +        is of course wrong.  The official names that must be proposed in
 +        the SKE are the ones defined in the protocol specification
 +        (hmac-sha1-96 for example).  The user can configure any hmac
 +        using any hash function configured in the [hash] section.  At
 +        least, the mandatory must be configured.
 +
 +        Rewrote the HMAC interface in lib/silccrypt/silchmac.[ch].
 +
 +      * Added HMAC list to the SKE proposal list.  It has now both
 +        hash algorithm list and HMAC list.  This makes the protocol
 +        incompatible with previous versions.  The SKE now seems to work
 +        the way it is supposed to work, for the first time actually.
 +
 +      * Defined plain Hash algorithms to the protocol specification.
 +        Added sha1 and md5.
 +
 +Tue Mar  6 15:36:11 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented support for key agreement packets into the server.
 +        Added functions silc_server_key_agreement and
 +        silc_server_send_key_agreement.  Other than these functions,
 +        server has nothing to do with this packet.
 +
 +      * Added support for private message key packets into the server.
 +        Added functions silc_server_private_message_key and
 +        silc_server_send_private_message_key.
 +
 +      * Updated TODO.
 +
 +      * Changed the silc_[client|server]_protocol_ke_set_keys to be
 +        called in the protocol's final callback instead in the END
 +        protocol state.  This makes a little more sense and in the same
 +        time in client we can use the same protocol routines for normal
 +        key exchange and to key agreement packet handling as well.
 +
 +      * Added to both client's and server's KE protocol context the
 +        SilcSKEKeyMaterial pointer to save the key material.  We will
 +        bring the key material to the protocol's final callback by doing
 +        this.  The final callback must free the key material.
 +
 +      * Added SKE's packet_send callback into client's KE protocol
 +        context so that the caller can choose what packet sending function
 +        is used.  This way we can use different packet sending when
 +        doing normal SKE when doing key agreement packet handling (in
 +        the key agreement packet handling we do not want to encrypt
 +        the packets).
 +
 +      * Implemented the responder side of the key agreement routines
 +        in the client.  The client can now bind to specified port and
 +        accept incoming key negotiation.  The key material is passed
 +        to the application after the protocol is over.
 +
 +      * Implemented the processing of incoming Key Agreement packet
 +        in the client.  Added function silc_client_key_agreement to
 +        process the packet.
 +
 +      * Implemented the intiator side of the key agreement routines
 +        in the client.  The client can now initiate key agreement with
 +        another remote client.  The key material is passed to the
 +        application after the protocol is over.
 +
 +      * Created client_keyagr.c to include all the key agreement 
 +        routines.
 +
 +      * Added macro SILC_TASK_CALLBACK_GLOBAL which is equal to the
 +        SILC_TASK_CALLBACK except that it is not static.
 +
 +      * Created client_notify.c and moved the Notify packet handling
 +        from the client.[ch] into that file.
 +
 +      * Created client_prvmsg.c and moved all private message and
 +        private message key routines from the client.[ch] into that file.
 +
 +      * Create client_channel.c and moved all channel message and
 +        channel private key routines from the client.[ch] into that file.
 +
 +      * Changed silc_client_get_client_by_id_resolve to resolve with
 +        WHOIS command instead of IDENTIFY command, in the file
 +        lib/silclient/idlist.c.
 +
 +Mon Mar  5 18:39:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented the SKE's responder side to the Client library.
 +
 +      * When FAILURE is received to the protocol do not trust it
 +        blindly.  Register a timeout to wait whether the remote closes
 +        the connection as it should do it, only after that process the
 +        actual failure.  This was changed to both client and server.
 +
 +      * Added client_internal.h to include some of the structures
 +        there instead of client.h in lib/silcclient/.
 +
 +      * Added function silc_task_unregister_by_callback to unregister
 +        timeouts by the callback function.
 +
 +Sat Mar  3 19:15:43 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Some "Incomplete WHOIS info" errors has been appearing on the
 +        log files.  Took away the entry->userinfo check from WHOIS
 +        reply sending.  The entry->userinfo is now " " if client did not
 +        provide one.  I thought this was fixed earlier but something
 +        is wrong still.  Let's see if the error still appears.
 +
 +Wed Feb 28 20:56:29 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a minor bug in the login when the channel key is
 +        re-generated in the server.  It used to generate the key in
 +        wrong order and thus caused problems in the channel traffic.
 +
 +      * Fixed a minor bug in channel key distsribution after
 +        KICK command.  The key was not sent to the router even though
 +        it should've been.
 +
 +Tue Feb 27 20:24:25 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_ske_process_key_material_data as generic routine
 +        to process any key material as the SILC protocol dictates.  The
 +        function is used by the actual SKE library but can be used by
 +        applications as well.  This relates to the private message keys
 +        and the channel private keys since they must be processed the
 +        same way the normal SILC session keys.  The protocol dictates
 +        this.  Affected files: lib/silcske/silcske.[ch].
 +
 +        Added also silc_ske_free_key_material to free the
 +        SilcSKEKeyMaterial structure.
 +
 +      * Defined silc_cipher_set_key function to set the key for
 +        cipher without using the object's method function.  The affected
 +        files: lib/silccrypt/silccipher.[ch].
 +
 +      * Implemented silc silc_client_add_private_message_key,
 +        silc_client_add_private_message_key_ske, 
 +        silc_client_del_private_message_key,
 +        silc_client_list_private_message_keys and
 +        silc_client_free_private_message_keys functions in the
 +        client library.
 +
 +        Added functions silc_client_send_private_message_key to send
 +        the Private Message Key payload and silc_client_private_message_key
 +        to handle incoming Private Message Key payload.
 +
 +      * Added Cipher field to the Private Message Key payload to set
 +        the cipher to be used.  If ignored, the default cipher defined
 +        in the SILC protocol (aes-256-cbc) is used.
 +
 +Tue Feb 27 13:30:52 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Removed lib/silcclient/ops.h file.
 +
 +        Redefined parts of the SILC Client Library API. Created new
 +        file silcapi.h that deprecates the ops.h file and defines the
 +        published Client Library API.  Defined also private message key
 +        API and channel private key API into the file.
 +
 +        This is the file that the application must include from the
 +        SILC Client Library.  Other files need not be included by
 +        the application anymore.
 +
 +      * Added new key_agreement client operation callback and also
 +        defined the Key Agreement library API for the application.
 +
 +Tue Feb 27 11:28:31 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new packet type: SILC_PACKET_KEY_AGREEMENT.  This packet
 +          is used by clients to request key negotiation  between another
 +          client in the SILC network.  If the negotiation is started it
 +          is performed using the SKE protocol.  The result of the
 +          negotiation, the secret key material, can be used for example
 +          as private message key.
 +
 +        Implemented the Key Agreement payload into the files
 +        lib/silccore/silauth.[ch].
 +
 +Mon Feb 26 12:13:58 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Redefined ciphers for the SILC protocol.  Added some new ciphers
 +        and defined the key lengths for the algorithms.  Changed the
 +        code accordingly.  The default key length is now 256 bits.
 +
 +      * Fixed SKE key distribution function silc_ske_process_key_material
 +        when the key length is more than 128 bits.  The default key 
 +        length in SILC is now 256 bits.
 +
 +      * Added new command status type: SILC_STATUS_ERR_UNKOWN_ALGORITHM
 +        to indicate unsupported algorithm.
 +
 +      * Renamed rijndael.c to aes.c and all functions as well.
 +
 +      * Fixed a long standing channel key setting bug in client library.
 +        Weird that it has never surfaced before.
 +
 +      * Fixed bug in channel deletion.  If the entire channel is removed
 +        then it must also delete the references of the channel entry
 +        from the client's channel list as the client's channel entry and
 +        the channel's client entry share same memory.
 +
 +Sun Feb 25 20:47:29 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented CONNECT and SHUTDOWN commands in the client.
 +
 +      * Implemented CLOSE command to the client.
 +
 +      * Added the function silc_idlist_find_server_by_name into the
 +        files silcd/idlist.[ch].
 +
 +        Added the function silc_idlist_find_server_by_conn into the
 +        files silcd/idlist.[ch].
 +
 +Sat Feb 24 23:45:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * DIE command was renamed to SHUTDOWN.  Updated the both code
 +        and protocol specs.
 +
 +      * Defined SILC_UMODE_NONE, SILC_UMODE_SERVER_OPERATOR and
 +        SILC_UMODE_ROUTER_OPERATOR modes into lib/silccore/silcmode.h.
 +
 +      * Implemented CONNECT, CLOSE and SHUTDOWN commands to the server
 +        side.
 +
 +      * Added function silc_server_create_connection function to create
 +        connection to remote router.  My server implementation actually
 +        does not allow router to connect to normal server (it expects
 +        that normal server always initiates the connection to the router)
 +        so the CONNECT command is only good for connecting to another
 +        router.
 +
 +Sat Feb 24 16:03:45 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added SILC_NOTIFY_TYPE_KICKED to indicate that the client
 +        or some other client was kicked from the channel.
 +
 +        Implemented the handling of the notify type to both client
 +        and server.
 +
 +        Implemented silc_server_send_notify_kicked to send the KICKED
 +        notify.  It is used to send it to the server's primary router.
 +
 +      * Implemented the KICK command into server and client.
 +
 +      * Added `query' argument to the silc_idlist_get_client function
 +        to indicate whether to query the client from server or not if
 +        it was not found.
 +
 +      * Added new command status type SILC_STATUS_ERR_NO_CHANNEL_FOPRIV
 +        to indicate that the client is not channel founder.
 +
 +      * Updated TODO.
 +
 +Sat Feb 24 00:00:55 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Removed the rng context from SilcPacketContext structure and
 +        changed that the packet routine uses the Global RNG API.
 +
 +Fri Feb 23 11:22:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added support for quit message that client can "leave" on the
 +        channel when it quits the SILC.  It is ditributed inside the
 +        SILC_NOTIFY_TYPE_SIGNOFF notify type.
 +
 +        Added silc_server_free_client_data that will take the
 +        signoff message as argument.
 +
 +      * Changed SKE routines to use the silc_pkcs_sign/verify routines.
 +
 +Thu Feb 22 23:05:36 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Updated parts of the protocol specification to keep it up
 +        to date.
 +
 +Thu Feb 22 15:08:20 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added List flag (SILC_PACKET_FLAG_LIST) to indicate list of
 +        payloads in one packet.
 +
 +      * Deprecated following packet types: NEW_ID_LIST, NEW_CHANNEL_LIST,
 +        NEW_CHANNEL_USER_LIST, SET_MODE and SET_MODE_LIST.  List packets
 +        use now the new List flag.
 +
 +      * Also deprecated the following packet types: REPLACE_ID,
 +        NEW_CHANNEL_USER and REMOVE_CHANNEL_USER packet types.
 +       
 +      * Added list support for Notify packet in server.
 +
 +      * Added silc_server_send_notify_channel_change to send the
 +        CHANNEL_CHANGE notify type to replace channel ID's.  Deprecates
 +        the silc_server_send_replace_id.
 +
 +      * Added silc_server_send_notify_nick_change to send the
 +        NICK_CHANGE notify type.  Deprecates the function
 +        silc_server_send_replace_id.
 +
 +      * Added silc_server_send_notify_join to send the JOIN notify type.
 +        Deprecates the function silc_server_send_new_channel_user.
 +
 +      * Added silc_server_send_notify_leave to send LEAVE notify type.
 +        Deprecates the function silc_server_send_remove_channel_user.
 +
 +      * Added silc_server_send_notify_cmode and 
 +        silc_server_send_notify_cumode to send CMODE and CUMODE notify
 +        types.  Deprecates the silc_server_send_set_mode function.
 +
 +      * Added SERVER_SIGNOFF notify type to indicate that server has
 +        quit.  This means that all clients on the channel from that 
 +        server will drop.  This can be also used when netsplit happens.
 +
 +        Deprecated REMOVE_ID packet type since it is not needed anymore
 +        even from server.
 +
 +        Added silc_server_send_notify_server_signoff to send the
 +        SERVER_SIGNOFF notify type.  Deprecates the function
 +        silc_server_send_remove_id.
 +
 +        Added also silc_server_send_notify_signoff to send the
 +        SIGNOFF notify type.
 +
 +      * Employed the PKCS #1. It is the mandatory way to do RSA in the
 +        SILC protocol from this day on.  Changed the protocol 
 +        specification as well.
 +
 +      * Added silc_server_send_notify_topic_set to send TOPIC_SET
 +        notify type.  It is used between routers to notify about
 +        topic changes on a channel.
 +
 +      * Added silc_id_dup into lib/silccore/id.[ch] to duplicate
 +        ID data.
 +
 +      * Partly updated the protocol specification to comply with the
 +        changes now made.  It is still though a bit outdated.
 +
 +      * The JOIN notify type now takes one extra argument <Channel ID>.
 +        The packet used to be destined to the channel but now the
 +        JOIN type may be sent as list thus it is impossible to 
 +        destine it to any specific channel.  By adding this argument
 +        it is again possible.
 +
 +Wed Feb 21 22:39:30 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added CREDITS file.  The CHANGES and CREDITS file will appear
 +        in the distribution as well.
 +
 +Wed Feb 21 14:17:04 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented CMODE_CHANGE, CUMODE_CHANGE and TOPIC_SET notify
 +        types in the server's silcd/packet_receive.c.
 +
 +      * Implemented CMODE and CUMODE to work in router environment.
 +
 +      * Fixed minor encoding and decoding buglet from the
 +        lib/silccore/silcmode.c.
 +
 +      * Fixed buffer overflow from lib/silcclient/command.c in USERS
 +        command parsing.
 +
 +Wed Feb 21 12:44:00 EET 2001  Mika Boström <bostik@lut.fi>
 +
 +      * Changed all SilcConfigServer* and silc_config_server* to
 +        SilcServerConfig* and silc_server_config*, respectively.
 +        Patch by Bostik.
 +
 +Wed Feb 21 00:10:00 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Associated the ID (client or server ID) to the Authentication
 +        Payload to avoid any possibility of forging.  Updated the
 +        protocol specification and the code accordingly.
 +
 +Tue Feb 20 14:14:14 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * The RSA key length is now save to the RsaKey context in the
 +        key generation process in lib/silccrypt/rsa.c.  The key length
 +        is now used to figure out the maximum size of the block allowed
 +        to be encrypted/signed.
 +
 +      * Added silc_mp_mp2bin_noalloc into lib/silcmath/mpbin.[ch].  It
 +        is equivalent to the silc_mp_mp2bin but does not allocate any
 +        memory.
 +
 +      * Changed silc_mp_mp2bin API to take length argument.  If it is
 +        non-zero then the buffer is allocated that large.  If zero, then
 +        the size is approximated using silc_mp_sizeinbase, which however
 +        is not relieable.
 +
 +      * Created Global RNG API which is global RNG that application can
 +        initialize.  After initializing, any routine anywhere in the
 +        code (including library) can use RNG without allocating a new
 +        RNG object.  This was done to allow this sort of use of the 
 +        RNG in code that has no chance to allocate RNG object.  All
 +        applications currently allocate this and many routines in the
 +        library use this.  Affected file lib/silccrypt/silcrng.[ch].
 +
 +      * Removed the RNG kludge from lib/silcmath/primegen.c and changed
 +        it to use the Global RNG API.
 +
 +      * Defined Authentication Payload into protocol specification that
 +        is used during SILC session to authenticate entities.  It is
 +        used for example by client to authenticate itself to the server
 +        to obtain server operator privileges.
 +
 +        Implemented this payload into the lib/silccore/silcauth.[ch].
 +        Implemented also routines for public key based authentication
 +        as the new protocol specification dictates.
 +
 +        Moved definitions of different authentication methods from
 +        lib/silccore/silcprotocol.h into lib/silccore/silcauth.h.
 +
 +      * Added silc_pkcs_encrypt, silc_pkcs_decrypt, silc_pkcs_sign,
 +        silc_pkcs_verify and silc_pkcs_sign_with_hash and
 +        silc_pkcs_verify_with_hash functions into the file 
 +        lib/silccrypt/silcpkcs.[ch].
 +
 +Mon Feb 19 19:59:28 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * The client entry's userinfo pointer must be always valid. 
 +        Otherwise the [<unknown>] bug will surface beacuse the WHOIS
 +        will fail since it requires the userinfo.  Now, the userinfo
 +        is allocated as "" if actual userinfo does not exist.  Actually,
 +        it must exist and it is totally Ok to drop client connections
 +        that does not announce the userinfo.  However, we will make
 +        this workaround for now.
 +
 +      * Added silc_net_get_remote_port into lib/silcutil/silcnet.[ch]
 +        to return the remote port by socket.
 +
 +Mon Feb 19 14:26:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed SILC_SERVER_COMMAND_EXEC_PENDING macro to the name
 +        SILC_SERVER_PENDING_EXEC and added an new macro
 +        SILC_SERVER_PENDING_DESTRUCTOR which is called to free the
 +        data or when error occurs while processing the pending command.
 +
 +        Added new argument `destructor' into silc_server_command_pending
 +        and to the SilcServerCommandPending object.  This destructor is
 +        now called after calling the pending callback or if error occurs
 +        immediately.  If error occurs the actual pending callback won't
 +        be called at all - only the destructor.  The destructor may be
 +        NULL if destructor is not needed.
 +
 +        All this applies for client library code as well.  Similar
 +        changes were made there as well for the pending commands.
 +
 +        In the client, the application must now allocate the 
 +        SilcClientCommandContext with the silc_client_command_alloc
 +        function.
 +
 +      * Added reference counter to the SilcServerCommandContext.  Added
 +        function silc_server_command_alloc and silc_server_command_dup 
 +        functions.
 +
 +        Same type of functions added to the client library for the same
 +        purpose as well.
 +
 +      * Removed the cmd_ident from IDListData away since it is now 
 +        global for all connections.  It is the command identifier used
 +        in command sending and with pending commands.  The affected file
 +        is silcd/idlist.h.
 +
 +      * Added reference counter to the SilcSocketConnection objecet to
 +        indicate the usage count of the object.  The object won't be
 +        freed untill the reference counter hits zero.  Currently only
 +        server uses this, and client ignores it.  The client must be
 +        set to use this too later.  The affected files are
 +        lib/silccore/silcsockconn.[ch].  Added also the function
 +        silc_socket_dup to increase the reference counter.
 +
 +        This was mainly added because it is possible that the socket
 +        is removed underneath of pending command or other async
 +        operation.  Now it won't be free'd and proper DISCONNECTING
 +        flags, etc. can be set to avoid sending data to connection that
 +        is not valid anymore.
 +
 +      * Added SILC_SET_DISCONNECTING to server.c when EOF is read from
 +        the connection.  After that it sets SILC_SET_DISCONNECTED.
 +        It is, however, possible that the socket data is not still freed.
 +        The silc_server_packet_process now checks that data is not
 +        read or written to connection that is DISCONNECTED.  The socket
 +        get's freed when the reference counter hits zero.
 +
 +Mon Feb 19 00:50:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed the client operation API: channel_message operation's
 +        `sender' is now the client entry of the sender, not the nickname
 +        and the `channel' is the channel entry, not the channel name.
 +
 +        In the private_message operation the `sender' is now also the
 +        client entry of the sender not the nickname.
 +
 +        Affected file is lib/silcclient/ops.h and all applications
 +        using the client operations.
 +
 +Sat Feb 17 22:11:50 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Moved the calling of ops->connect() from connect_to_server_final
 +        into receive_new_id functin since that is the point when the
 +        client is actually allowed to send traffic to network.  The
 +        affected file is lib/silcclient/client.c.
 +
 +Sat Feb 17 13:15:35 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * When receiving NEW_CHANNEL_LIST, NEW_CHANNEL_USER_LIST,
 +        NEW_ID_LIST and SET_MODE_LIST packets, broadcast the list packet
 +        (if needs broadcasting) instead of broadcasting the packets one
 +        by one which would make a burst in the network traffic.
 +
 +      * Added `broadcast' argument to the functions in silcd/server.[ch]
 +        silc_server_create_new_channel[_with_id] to indicate whether
 +        to send New Channel packet to primary router.
 +
 +Sat Feb 17 01:06:44 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new function into the silcd/server.[ch] files:
 +        silc_server_create_new_channel_with_id to create new channel with
 +        already existing Channel ID.
 +
 +      * Added new packet type SILC_PACKET_SET_MODE_LIST into the file
 +        lib/silccore/silcpacket.h.  This packet is used t send list of
 +        Set Mode payloads inside one packet.  Server uses this to set
 +        the modes for the channels and clients on those channels, that it
 +        announced to the router when it connected to it.  The protocol
 +        specification has been updated accordingly.
 +
 +      * The silc_server_new_channel did not handle the packet coming
 +        from normal server as it normally does not send that.  However,
 +        when it announces its channels it does send it.  Implemented
 +        the support for that.
 +
 +      * Added SILC_ID_CHANNEL_COMPARE macro to compare to Channel ID's
 +        into the file lib/silccore/id.h.
 +
 +Fri Feb 16 23:57:29 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed memory leaks in the functions silc_idlist_del_client,
 +        silc_idlist_del_channel and silc_idlist_del_server in the file
 +        silcd/idlist.c.  All of those leaked like a sieve.
 +
 +      * Fixed some small memory leaks in the client's function
 +        silc_client_notify_by_server.
 +
 +      * Added functions into silcd/server.c: silc_server_announce_clients,
 +        silc_server_announce_channels and silc_server_announce_server.
 +        These functions are used by normal and router server to announce
 +        to its primary router about clients, channels and servers (when
 +        router) that we own.  This is done after we've connected to the
 +        router.
 +
 +        These functions effectively implements the following packet types:
 +        SILC_PACKET_NEW_CHANNEL_LIST, SILC_PACKET_NEW_CHANNEL_USER_LIST
 +        and SILC_PACKET_NEW_ID_LIST.
 +
 +      * Added new functions into the silcd/packet_receive.[ch]:
 +        silc_server_new_id_list, silc_server_new_channel_list and
 +        silc_server_new_channel_user_list to handle the incoming 
 +        NEW_ID_LIST, NEW_CHANNEL_LIST and NEW_CHANNEL_USER_LIST packets.
 +
 +      * Added support of changing Channel ID in the function
 +        silc_server_replace_id.  If the server that announces a channel
 +        to the router already exists in the router (with same name but
 +        with different Channel ID), router is responsible to send
 +        Replace ID packet to the server and force the server to change
 +        the Channel ID to the one router has.
 +
 +      * Added new notify type SILC_NOTIFY_TYPE_CHANNEL_CHANGE to notify
 +        client that the Channel ID has been changed by the router.  The
 +        normal server sends this to the client.  Client must start using
 +        the new Channel ID as the channel's ID.
 +
 +        Implemented handling of this new type into lib/silcclient/client.c
 +        into the function silc_client_notify_by_server.
 +
 +      * Added new function silc_idlist_replace_channel_id into the files
 +        silcd/idlist.[ch] to replace the Channel ID.
 +
 +Fri Feb 16 14:14:00 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Call silc_server_command_identify_check always when processing
 +        the IDENTIFY command in silcd/command.c
 +
 +Thu Feb 15 20:07:37 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new packet type SILC_PACKET_HEARTBEAT that is used to
 +        send keepalive packets.  The packet can be sent by clients, 
 +        servers and routers.
 +
 +        Added function silc_socket_set_heartbeat into the file
 +        lib/silccore/silcsockconn.[ch] to set the heartbeat timeout.
 +        If not set, the heartbeat is not performed.  The actual 
 +        heartbeat is implemented in the low level socket connection
 +        library.  However, application is responsible of actually
 +        sending the packet.
 +
 +        Added silc_server_send_heartbeat to send the actual heartbeat
 +        packet into silcd/packet_send.[ch].  Server now performs
 +        keepalive with all connections.
 +
 +      * Added silc_task_get_first function into lib/silcutil/silctask.c
 +        to return the timeout task with shortest timeout.  There was a bug
 +        in task unregistration that caused problems.  TODO has been
 +        updated to include that task system must be rewritten.
 +
 +      * The client library will now resolve the client information when
 +        receiving JOIN notify from server for client that we know but
 +        have incomplete information.
 +
 +      * Rewrote parts of silc_server_remove_from_channels and
 +        silc_server_remove_from_one_channel as they did not remove the
 +        channel in some circumstances even though they should've.
 +
 +      * Encryption problem encountered in server:
 +
 +        The LEAVE command used to send the Channel Key packet to the
 +        router immediately after generating it.  However, the code
 +        had earlier sent Remove Channel user packet but not immediately,
 +        ie. it was put to queue.  The order of packets in the router
 +        was that Channel Key packet was first and Remove Channel User
 +        packet was second, even though they were encrypted in the
 +        reverse order.  For this reason, MAC check failed.  Now, this
 +        is fixed by not sending the Channel Key packet immediately but
 +        putting it to queue.  However, this is more fundamental problem:
 +        packets that are in queue should actually not be encrypted 
 +        because packets that are sent immediately gets encrypted
 +        actually with wrong IV (and thus MAC check fails).  So, packets
 +        that are in queue should be encrypted when they are sent to
 +        the wire and not when they put to the queue.
 +
 +        However, the problem is that the current system has not been
 +        designed to work that way.  Instead, the packet is encrypted
 +        as soon as possible and left to the queue.  The queue is then
 +        just purged into wire.  There won't be any fixes for this
 +        any time soon.  So, the current semantic for packet sending
 +        is as follows:
 +
 +        o If you send packet to remote host and do not force the send
 +        (the packet will be in queue) then all subsequent packets to the
 +        same remote host must also be put to the queue.  Only after the
 +        queue has been purged is it safe again to force the packet
 +        send immediately.
 +
 +        o If you send all packets immediately then it safe to send
 +        any of subsequent packets through the queue, however, after
 +        the first packet is put to queue then any subsequent packets
 +        must also be put to the queue.
 +
 +        Follow these rules and everything works fine.
 +
 +Thu Feb 15 14:24:32 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new function silc_server_remove_clients_by_server to
 +        remove all client entries from ID list when the server connection
 +        is lost.  In this case it is also important to invalidate all
 +        client entires as they hold the invalid server entry.  This
 +        fixes fatal bug when server has lost connection and will reconnect
 +        again.
 +
 +Wed Feb 14 16:03:25 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Made some sanity checks to silc_server_daemonise like to check
 +        whether the requested user and group actually exists.
 +
 +      * Added sanity check to SKE's silc_ske_responder_finish to check
 +        that the public and private key actually is valid. 
 +
 +      * Invalidate the client's nickname when receiving Replace ID
 +        packet and the Client ID is being replaced.  This means that the
 +        server will query the nickname if someone needs it (client)
 +        a bit later.
 +
 +      * Sort the ID Cache in client library when the ID Cache data
 +        has changed (needs sorting).
 +
 +      * Do not allow for SILC client to create several connections to
 +        several servers.  The client does not support windows right now
 +        and generating multiple connections causes weird behaviour.
 +
 +        Irssi-silc client does support windows and can handle several
 +        connections without problems, see: www.irssi.org and SILC plugin.
 +
 +      * Fixed some places where client was added to the IDList.  The
 +        rule of thumb is following (in order to get everything right):
 +        If the client is directly connected local client then the 
 +        `connection' argument must be set and `router' argument must be 
 +        NULL to silc_idlist_add_client function.  If the client is not
 +        directly connected client then the `router' argument must
 +        bet set and the `connection' argument must be NULL to the
 +        silc_idlist_add_client function.
 +
 +      * The funtion silc_server_packet_send_local_channel actually did
 +        not check whether the client was locally connected or not.  It
 +        does that now.  Fixed a bug related to LEAVE command.
 +
 +      * Fixed Remove Channel User payload parsing bug in server's
 +        silcd/packet_receive.c.  Fixed a bug related to LEAVE command.
 +
 +      * The server's silc_server_save_channel_key now checks also the
 +        global ID list for the channel as it might not be in the local
 +        list.  Fixed a bug related to LEAVE command.
 +
 +      * Is this the end of the [<unknown>] buglet that has been lurking
 +        around for a long time?  A little for loop fix in server's
 +        silc_server_command_whois_parse that is used by both IDENTIFY
 +        and WHOIS command.  At least, this was a clear bug and a cause
 +        of one type of [<unknown>] buglet.
 +
 +      * WHOIS and IDENTIFY commands call the function
 +        silc_server_command_[whois/identify]_check function even if
 +        we are not router server.
 +
 +Tue Feb 13 19:55:59 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added --with-gmp configuration option.  If set the GMP
 +        is always compiled in the SILC source tree.  If not set then
 +        it is checked whether the system has the GMP3 installed.  If
 +        it has then the GMP won't be compiled (the system's headers
 +        and library is used), if it doesn't have it then the GMP is
 +        compiled in the SILC source tree.
 +
 +Mon Feb 12 11:20:32 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed RSA private exponent generation to what PKCS #1
 +        suggests.  We try to find the smallest possible d by doing
 +        modinv(e, lcm(phi)) instead of modinv(e, phi).  Note: this is
 +        not security fix but optimization.
 +
 +Sun Feb 11 18:19:51 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new config entry [Identity] to fork the server and run
 +        it as specific user and group.  A patch from Bostik.
 +
 +      * Imported Dotconf configuration library into lib/dotconf.
 +        This will be used to create the SILC configuration files later.
 +        It will appear in the distsribution after this commit.
 +
 +Sat Feb 10 21:13:45 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * A big code auditing weekend happening.  Auditing code for 
 +        obvious mistakes, bugs and errors.  Also, removing any code
 +        that is obsolete.
 +
 +        Removed files for being obsolete:
 +
 +        o lib/silcutil/silcbuffer.c (the buffer interface is entirely in
 +        inline in the file lib/silcutil/silcbuffer.h)
 +
 +        o lib/silcutil/silcbufutil.c (the header has inline versions)
 +
 +        Changed code to fix possible error conditions:
 +
 +        o The buffer formatting routines now check that the destination
 +        buffer really has enough space to add the data.  This applies for
 +        both buffer formatting and unformatting 
 +        (lib/silcutil/silcbuffmt.[ch]).  Also, the entire buffer
 +        unformatting was changed to accomodate following rules: 
 +        XXX_*STRING_ALLOC will allocate space for the data into the pointer
 +        sent to the function while XXX_*STRING will not allocate or copy 
 +        the data into the buffer.  Instead it sets the pointer from the
 +        buffer into the pointer sent as argument (XXX_*STRING used to
 +        require that the pointer must be allocated already).  This change
 +        makes this whole thing a bit more consistent and more optimized
 +        (note that the data returned in the unformatting with XXX_*STRING
 +        must not be freed now).  The routines return now -1 on error.
 +
 +        o Tried to find all code that use buffer_format and buffer_unformat
 +        and added return value checking to prevent formatting and
 +        especially unformatting errors and possible subsequent fatal
 +        errors.
 +
 +        o Changed ske->x and ske->KEY to mallocated pointers in
 +        lib/silcske/silcske.h.  Fixed possible data and memory leak.
 +
 +        o Added return value checking to all *_parse* functions.  Fixed
 +        many memory leaks as well.
 +
 +        o Added length argument to silc_id_str2id in lib/silccore/id.[ch]
 +        so that buffer overflows would not happen.  All code now also
 +        checks the return value as it can fail.
 +
 +Mon Feb  5 20:08:30 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added reconnection support to server if the normal server looses
 +        its connection to the router (for example if router is rebooted).
 +        The server performs normal reconnection strategy implemented
 +        to the server.  Affected file silcd/server.c.
 +
 +Sun Feb  4 13:18:32 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new packet type SILC_PACKET_SET_MODE that is used to
 +        distribute the information about changed modes (for clients,
 +        channels and clients channel modes) to all routers in the
 +        network.  Updated the protocol specification accordingly.
 +
 +        Added functions into silcd/packet_send.c and 
 +        silcd/packet_receive.c: silc_server_send_set_mode, 
 +        silc_server_set_mode.
 +
 +        Added new files silcmode.[ch] into lib/silccore that implements
 +        the encoding and decoding of Set Mode Payload.  Added new type
 +        SilcSetModePayload.  Moved the definitions of different modes
 +        from lib/silccore/silcchannel.h into lib/silccore/silcmode.h.
 +
 +Sat Feb  3 15:44:54 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Oops, a little mistake in server's connection authentication 
 +        protocol.  The protocol is not ended with FAILURE but with
 +        SUCCESS if the authentication is Ok. :)  Affected file is
 +        silcd/protocol.c.
 +
 +      * Implemented NICK_CHANGE notify handling in server in the file
 +        silcd/packet_receive.c  The NICK_CHANGE notify is distributed to
 +        the local clients on the channel.  After the changing nickname
 +        in router environment snhould work and the [<unknown>] nickname
 +        should appear no more.
 + 
 +        The silc_server_replace_id function that receives the Replace ID
 +        payload now sends the NICK_CHANGE notify type also in the file
 +        silcd/packet_receive.c
 +
 +      * Changed WHOIS and IDENTIFY command to support the maximum amount
 +        of arguments defined in protocol specs (3328 arguments).  This 
 +        fixed a bug that caused problems when there were more than three
 +        users on a channel.
 +
 +Fri Feb  2 11:42:56 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added extra parameter, command identifier, to the
 +        silc_client_send_command so that explicit command identifier
 +        can be defined.
 +
 +        Changed that ID list routines uses specific command identifier
 +        when sending WHOIS/IDENTIFY requests to the server so that they
 +        can be identified when the reply comes back.
 +
 +        Affected files lib/silcclient/command.[ch],
 +        lib/silcclient/client.c and lib/silcclient/idlist.[ch].
 +
 +      * Added `sender' argument to silc_server_packet_send_to_channel
 +        to indicaet the sender who originally sent the packet to us
 +        that we are now re-sending.  Ignored if NULL.  Affected file
 +        silcd/packet_send.[ch].
 +
 +      * Added some server statistics support in silcd/server_internal.h
 +        SilcServerStatistics structure and around the server code.  Also
 +        send some nice statistics information when client is connecting
 +        to the client.
 +
 +Thu Feb  1 23:31:21 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed channel ID decoding in server's JOIN command reply in
 +        silcd/command_reply.c
 +
 +      * Fixed braodcasting of replace ID payload to not to send it if
 +        we are standalone server in silcd/packet_receive.c.
 +
 +      * Fixed all channel message sending routines to not to send
 +        packets to clients that has router set, since they are routed
 +        separately in the same function earlier.  Affects file
 +        silcd/packet_send.c and all channel packet sending functions.
 +
 +        * In USERS reply, res_argv[i] are not allocated, the table
 +          is allocated.  Thus changed that free the table, not its
 +          internals.
 +
 +      * In server's whois_check and identify_check if the client is
 +        locally connected do not send any WHOIS commands - they are not
 +        needed.
 +
 +Thu Feb  1 21:32:27 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed some minor bugs in client when sending WHOIS command.  The
 +        arguments was in wrong order.
 +
 +      * Removed statis function add_to_channel from server in 
 +        silcd/command.c that was previously used with the joining but
 +        is obsolete now.
 +
 +      * Tested USERS command in router environment successfully with two
 +        routers, two servers and two clients.
 +
 +Thu Feb  1 00:54:26 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Reorganized the USERS command and command reply in client library
 +        in lib/silcclient/command.c and lib/silcclient/command_reply.c.
 +        When the command is given by user we register a pending command
 +        callback that will reprocess the command after the reply has been
 +        received from the server.  When reprocessing the packet we then
 +        display the information.  Thus, the USERS information is displayed
 +        now in the command callback instead of in the command reply
 +        callback.  The processing of the command is same as previously
 +        when server has sent the command reply in the JOINing process.
 +
 +      * Added to USERS command in silcd/command_reply.c to join the client,
 +        we didn't use to know about, to the channel after we've created
 +        a client entry for it.  Also, for clienet we did know already still
 +        check whether it is on the channel or not and add it if not.
 +
 +      * Removed silc_server_command_join_notify as the function and its
 +        use was obsolete.
 +
 +Tue Jan 30 22:39:15 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed the client's pending command handling to the same as the
 +        server's pending command handling.  It is also now possible to
 +        execute command reply functions from other command reply
 +        function as the function callbacks for commands and command
 +        replies are one and same.  The pending commands are not static
 +        list anymore, it is mallocated SilcDList in lib/silcclient/client.h
 +        in client connection context.  Thus, pending commands are server
 +        connection specific as it is convenient.
 +
 +        Changed the function silc_client_command_pending and
 +        silc_client_command_pending_del and added new function
 +        silc_client_command_pending_check.  Removed the 
 +        SILC_CLIENT_CMD_REPLY_EXEC, and SILC_CLIENT_PENDING_COMMAND_CHECK
 +        macros.
 +
 +      * Added cmd_ident, current command identifier, to the client
 +        connection context in lib/silcclient/client.h to keep track on
 +        command identifiers used in command sending.  Client's command reply
 +        function handling now supports the mandatory command identifiers.
 +
 +      * Added SILC_CLIENT_COMMAND_EXEC_PENDING macros to all command reply
 +        funtions in client to fully support pending command callbacks.
 +
 +      * NOTE: the name_list in USERS (old NAMES) command is NOT sent anymore
 +        as one of the arguments to the application in the command reply
 +        client operation.
 +
 +      * NOTE: The FORWARDED flag is depracated.  It used to be depracated
 +        before first releasing SILC but came back.  Now it is removed again
 +        and should come back nomore.  The FORWARDED flag was used only
 +        by the JOINing procedure by forwarding the command packet to router.
 +        Now, the JOINing procedure has been changed to more generic (due
 +        to various router environment issues) and FORWARDED is not needed
 +        anymore for anything.  The protocol specification is yet to be
 +        updated.
 +
 +        Now, removed silc_server_packet_forward from server and the flag
 +        SILC_PACKET_FORWARDED from lib/silccore/silcpacket.h.
 +
 +Tue Jan 30 00:05:05 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Renamed NAMES command to USERS command.  The NAMES was named that
 +        due to historical reasons.  Now it is renamed.  Also, rewrote
 +        parts of the USERS command.  The nickname list is not sent anymore
 +        by the server.  Only Client ID and mode lists are sent in the USERS
 +        command.  Changed this also to the protocol specification.
 +
 +        The client now resolves the names and stuff after it receives
 +        the USERS list from the server when joining to the channel.
 +
 +      * WHOIS and IDENTIFY commands has been changed to support multiple
 +        Client ID's per command.  One can now search for multiple users
 +        in the network by sending only one WHOIS or IDENTIFY command.
 +        Changed the code and the protocol specifications.
 +
 +      * Removed silc_server_command_identify_parse and changed that IDENTIFY
 +        uses silc_server_command_whois_parse to parse the request. */
 +
 +      * If normal server, do not parse the WHOIS and IDENTIFY requests
 +        before sending it to the router.  Saves some time.
 +
 +Sun Jan 28 16:19:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed JOIN command on client library.  Wrong number of arguments
 +        used to crash the client.
 +
 +      * Added silc_server_channel_has_global function to check whether
 +        channel has global users or not.
 +
 +      * Added silc_server_channel_has_local function to check whether channel
 +        has locally connected clients on the channel.
 +
 +      * The silc_server_remove_from_one_channel now checks whether the
 +        channel has global users or not after given client was removed from
 +        the channel.  It also checks whether the channel has local clients
 +        on the channel anymore.  If it does not have then the channel entry
 +        is removed as it is not needed anymore.
 +
 +      * The silc_server_notify now checks on JOIN notify whether the joining
 +        client is one of locally connected or global.  If it is global then
 +        the channel has now global users on the channel and that is marked
 +        to the channel entry.  Also, it now saves the global client to
 +        global list who is joining and JOINs it to the channel.  This is
 +        for normal server, that is.
 +
 +        Changed silc_server_send_notify_on_channel, 
 +        silc_server_packet_relay_to_channel and 
 +        silc_server_packet_send_to_channel check if we are normal server
 +        and client has router set (ie. global client) do not send the
 +        message to that client, as it is already routed to our router.
 +
 +      * Implemented LEAVE notify type handling in silc_server_notify 
 +        function.
 +
 +      * Tested LEAVE command in router environment successfully.  Tested
 +        with two routers, two servers and two clients.
 +
 +      * Updated TODO.
 +
 +      * idlist_find_xxx_by_id routines now dumps the ID on the debug mode.
 +
 +      * Implemented SIGNOFF notify type handling in silc_server_notify
 +        function.
 +
 +      * silc_server_remove_id now removes the client entry from all channels
 +        it has joined and thusly sends SIGNOFF notify type.
 +
 +      * Rewrote the NAMES list generation in server by removing two excess
 +        loops.  The lists are created now inside one loop.
 +
 +Sat Jan 27 22:34:56 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * silc_server_remove_channel_user checks now also global list
 +        for channel and client.
 +
 +      * silc_server_new_channel_user checks now both local and global
 +        list for channel and client.  Fixed a bug in client id decoding.
 +        Used to decode wrong buffer.
 +
 +      * silc_server_channel_message checks now both local and global
 +        list for channel entry.
 +
 +      * Tested channel joining (hence JOIN) in router environment
 +        successfully.  Tested with two routers, two servers and two
 +        clients.
 +
 +      * Tested channel message sending in router environment successfully.
 +
 +Thu Jan 11 03:22:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_server_save_channel_key into server.[ch] to save the
 +        received channel key in Channel Key payload processing. It is
 +        also used in JOIN command reply handling.
 +
 +        Equivalent function silc_client_save_channel_key added into
 +        client.[ch] into client library.
 +
 +      * Changed JOIN command reply to send information whether the channel
 +        was created or not (is existing already) and the channel key 
 +        payload.  Changed protocol specs accordingly.
 +
 +      * Fixed bugs in WHOIS and IDENTIFY command reply sending when
 +        the request was sent by ID and not by nickname.  Crashed on
 +        NULL dereference.
 +
 +Sat Dec 23 21:55:07 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a bug in Client library.  IDENTIFY and WHOIS reply functions
 +        now correctly save the received data.
 +
 +      * silc_server_free_sock_user_data now notifies routers in the 
 +        network about entities leaving the network.
 +
 +        At the same time implemented functions silc_server_remove_id
 +        and silc_server_send_remove_id to receive and send REMOVE_ID
 +        packets.  The packet is used to notify routers in the network
 +        about leaving entities.  The ID removed will become invalid in
 +        the network.
 +
 +      * Added function silc_idlist_del_server into server. Removes and
 +        free's server entry from ID list.
 +
 +      * silc_server_private_message function now checks, if we are router,
 +        that the destination ID really is valid ID, naturally.
 +
 +      * In router when NEW_ID packet is received (for new client) the
 +        hash of the Client ID is saved in the ID Cache but the
 +        client->nickname is set to NULL, instead of putting the hash
 +        to it as well.
 +
 +        IDENTIFY command now also checks that client->nickname must be
 +        valid. If it is not if will request the data from the server who
 +        owns the client.  Added new function 
 +        silc_server_command_identify_check.
 +
 +      * Added silc_command_set_command into lib/silccore/silcommand.[ch]
 +        to set the command to already allocated Command Payload.
 +
 +      * Tested private message sending in router environment with two
 +        routers, two servers and two clients.  Fixed minor bugs and now
 +        it works fine.
 +
 +      * Fixed segfault from client's NAMES command. Used to crash if
 +        not on any channel.
 +
 +      * Forwarded packets must not be routed even if it is not destined
 +        to the receiver.  Changed server code comply with this.
 +
 +Sun Dec 17 14:40:08 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added `require_reverse_mapping' boolean value to ServerParams
 +        structure. If TRUE (not default) the server will require that
 +        the connecting host has fully qualified domain name.
 +
 +        If the reverse mapping is not required and hostname could not be
 +        found the IP address is used as hostname.
 +
 +Sat Dec 16 17:39:54 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented version string checking to both client and server.
 +        The check is incomplete currently due to the abnormal version 
 +        strings used in development version of SILC.
 +
 +      * Changed all command functions in server to use the new
 +        CHECK_ARGS macro.
 +
 +Fri Dec 15 15:55:12 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed char *data to unsigned char *data in ID Cache system to
 +        support binary data as ID Cache data. Changed code to support
 +        binary data in lib/silccore/idcache.c.
 +
 +      * Renamed silc_server_packet_relay_command_reply to 
 +        silc_server_command_reply as it is normal packet receiving
 +        function. Rewrote the function to accept command replys for
 +        servers and not only for clients.
 +
 +      * Mark remote router always as registered server if we are connecting
 +        to it.  Otherwise, commands sent by the router to us are ignored.
 +
 +      * All ID List find routines now returns the ID Cache Entry pointer
 +        as well if requested.
 +
 +      * WHOIS command works now in router environment, tested with two
 +        routers, two servers and two clients.
 +
 +      * Cleaned up and rewrote IDENTIFY command. IDENTIFY should work now
 +        in router environment (as it is almost equivalent to WHOIS) but
 +        hasn't been tested thoroughly.  Added new functions:
 +
 +        silc_server_command_identify_parse
 +        silc_server_command_identify_send_reply
 +        silc_server_command_identify_from_client
 +        silc_server_command_identify_from_server
 +
 +      * Disabled route cache adding because adding two different ID's with
 +        same IP replaces the old cache entry thus giving wrong route.
 +        The entry->router->connection is always the fastest route anyway
 +        so route cache may not be needed.  Of course, new routes maybe
 +        established after receiving the ID when the entry->router->connection
 +        might not be anymore the most optimal.
 +
 +Thu Dec 14 15:55:35 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Add route cache for received ID for fast routing.
 +
 +      * Added silc_server_packet_route to route received packet on router
 +        that is not destined to us.
 +
 +      * Renamed silc_server_get_route to silc_server_route_get.
 +
 +      * Added id_string and id_string_len fields into SilcServer to
 +        include encoded ServerID for fast comparing without excess
 +        encoding of the ID's.
 +
 +      * Cleaned up WHOIS command on server side. Added following static
 +        functions:
 +
 +        silc_server_command_whois_parse
 +        silc_server_command_whois_check
 +        silc_server_command_whois_send_reply
 +        silc_server_command_whois_from_client
 +        silc_server_command_whois_from_server
 +
 +      * Added macro SILC_SERVER_COMMAND_CHECK_ARGC to check mandatory
 +        arguments in command replies. All command functions should be
 +        updated to use this macro.
 +
 +Sun Dec 10 23:52:00 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Minor typo fixes on command reply handling on server.
 +
 +Tue Nov 28 11:05:39 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_server_command_add_to_channel internal routine to add
 +        the client to the channel after router has created the channel and
 +        sent command reply to the server.
 +
 +      * Added generic silc_server_send_command to send any command from
 +        server.
 +
 +      * Use static buffer with ID rendering instead of duplicating data.
 +
 +Mon Nov 27 21:39:40 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed a channel user mode bug when joining to a channel server gave
 +        everybody channel founder rights, oops.
 +
 +      * We mark ourselves as the router of the incoming server connection
 +        if we are router ourselves.  This way we can check in some packet
 +        sending functions whether it is locally connected server.  For
 +        incoming router connections we put NULL.
 +
 +      * For router sending packets locally means now always sending the
 +        packet cell wide; to local clients and local servers.  For normal
 +        server sending packet locally means sending it to only local
 +        clients.
 +
 +      * Fixed the JOIN command to really work in router environment.  If the
 +        channel is created it is always created by the router.  Router is
 +        also responsible of making the initial joining to the channel,
 +        sending JOIN notify to the sending server and distributing 
 +        NEW_CHANNEL and NEW_CHANNEL_USER packets.  Hence, if the channel
 +        does not exist server doesn't do anything else but forward the
 +        command to the router which performs everything.
 +
 +      * Added silc_server_send_channel_key function to send the Channel Key
 +        payload.
 +
 +      * Added silc_server_create_channel_key to create new channel key.  The
 +        channel key is now re-generated everytime someone joins or leaves
 +        a channel, as protocol dictates.  Note: channel->key_len is the
 +        key length in bits.
 +
 +Wed Nov 22 22:14:19 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Splitted server.[ch] finally.  Created now packet_send.[ch] and
 +        packet_receive.[ch] to separate packet sending and receiving
 +        routines.  The server.[ch] now includes everything else including
 +        actual packet processing (writing and reading data) and other
 +        server issues.
 +
 +        Renamed silc_server_private_message_send_internal to
 +        silc_server_send_private_message.  The routine is still though
 +        used only to relay private messages as server does not send
 +        private messages itself.
 +
 +        Renamed silc_server_new_channel to silc_server_create_new_channel
 +        and added new function sicl_server_new_channel that handles the
 +        incoming New Channel packet.  Added also new sending function
 +        silc_server_send_new_channel to send New Channel Payload.
 +
 +      * Added new function silc_server_notify to process incoming notify
 +        packet to the server/router. Server may then relay the notify
 +        to clients if needed.
 +
 +      * Added new function silc_server_new_channel_user to process incoming
 +        New Channel User packet.  Router will redistribute the packet and
 +        send JOIN notify to its local clients and locally connected servers
 +        if needed.  Normal server will send JOIN notify to its local client
 +        on same channel when received this packet.  Added also corresponding
 +        sending function silc_server_send_new_channel_user to sent the
 +        payload.
 +
 +      * Added boolean route argument to send_notif_to_channel and
 +        packet_send_to_channel functions to attempt to route the packet
 +        if it is TRUE and send only locally if it is FALSE.
 +
 +Tue Nov 21 19:49:31 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * silc_server_replace_id now broadcasts the received replace ID
 +        packet if it is not broadcast packet already. The router must
 +        broadcast to inform other routers about changed ID.
 +
 +      * Added backpointer to server's router into SilcServer context in
 +        silcd/server_internal.h.
 +
 +      * Fixed silc_server_packet_broadcast to send correct broadcast
 +        packets.
 +
 +      * The channel key is now distributed to the local client as soon
 +        as it is received from the router (in router environment) so that
 +        no other packet may be sent for the channel until client has 
 +        received the key.
 +
 +      * silc_server_remove_channel_user now broadcasts the received
 +        Remove Channel User packet if it is not broadcast packet already.
 +        The router must broadcast to inform other routers about removed
 +        channel user.
 +
 +      * Added users field into SilcPacketContext that is a reference count
 +        of the context.  One can increase the reference count by calling
 +        silc_packet_context_dup which is now changed to just increase the
 +        reference count instead of duplicating the data.  The reference
 +        count is decresed by calling silc_packet_context_free that will
 +        free the data after the reference count hits zero.
 +
 +        For now on the packet context and everything allocated into it
 +        (including the raw packet from network) must be freed by calling
 +        the new silc_packet_context_free function.  Added also new function
 +        silc_packet_context_alloc that must be used now to allocate the
 +        context.  This also means that if a routine is asynchronous from
 +        silc_[client/server]_packet_parse_type the packet context must
 +        be duplicated by calling silc_packet_context_dup.  Otherwise it
 +        gets free'd after silc_[client/server]_packet_parse_type returns.
 +        Also, one must remember that if packet is duplicated then its 
 +        reference count must be decresed by calling the free function as
 +        many times as it was duplicated.
 +
 +      * Changed SilcBuffer field from protocol contexts to SilcPacketContext
 +        from both client and server.
 +
 +Mon Nov 20 23:47:03 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Made joining to a channel working in router environment.
 +
 +      * Cleaned up JOIN command on server side and create function
 +        silc_server_command_join_channel internal routine to make the
 +        joining happen.
 +
 +Thu Nov  9 21:12:39 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed silc_command_pending list to SilcDList.  Also, added
 +        `ident' field to SilcServerCommandPending structure to identify
 +        the reply and to call correct callback.
 +
 +        Added silc_server_command_pending_check function to replace the
 +        corresnponding macro.  The silc_command_pending list is not
 +        extern anymore.
 +
 +      * Added silc_command_set_ident into lib/silccore/silccommand.[ch]
 +        to set identifier to previously allocated Command Payload.  It
 +        is used to set identifier for command when resending Command
 +        Payload.
 +
 +      * Added silc_command_payload_encode_payload to encode Command
 +        Payload buffer from SilcCommandPayload structure.
 +
 +      * Added silc_argument_payload_encode_payload to encode Argument
 +        payload buffer from SilcArgumentPayload structure.
 +
 +Wed Nov  8 21:03:28 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed WHOIS command to support router connection on server side.
 +        The whois request is always sent to router unless the server is
 +        standalone server.  After server has received the reply from the
 +        router will it send the reply to the client.
 +
 +      * Added silc_server_packet_broadcast into silcd/server.[ch] to
 +        broadcast received broadcast packet.  The function is used only
 +        by router.  The broadcast packet is always sent to the router's
 +        primary route.
 +
 +      * Added silc_id_render function in lib/silcutil/silcutil.[ch] to
 +        render given ID to printable string, for log files for example.
 +
 +Tue Nov  7 22:14:19 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Made basic router to router connections working.  At least they
 +        can now connect to each other but nothing really works the way
 +        they are supposed - yet.
 +
 +      * Added new initiator token to RouterConnection configuration
 +        file in silcd/serverconfig.[ch].  It is used to tell whether we
 +        are the initiator to the remote router or whether we'll expect
 +        the other end to connect.
 +
 +      * Moved registering of listener task to silc_server_init, hence
 +        the server starts listenning as soon as it is run, even if it
 +        does not have connections to other routers.  Let's see how well
 +        this will work.
 +
 +      * Changed default connection retry timeouts for more suitable in
 +        silcd/server.h.
 +
 +      * Removed cipher and such arguments from silc_idlist_add_client
 +        and silc_idlist_add_server prototypes from silcd/idlist.[ch].
 +        Added new function silc_idlist_add_data to add the keys and stuff
 +        to any ID entry.
 +
 +      * Added SilcIDListData structure and added it to SilcClientEntry
 +        and SilcServerEntry as their first field in the structure.  This
 +        way we can explicitly cast the ID entries to the SilcIDListData
 +        structure and get common data for the entries.  In past, we had
 +        to first check what type of connection it is and then cast it to
 +        correct ID entry type.  Now, we can directly cast the opaque
 +        pointer to the SilcIDListData (no matter what ID entry it actually
 +        is) and get the data needed.
 +
 +Mon Nov  6 21:56:12 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Wow, found a bug in scheduler.  The scheduler uninitialized itself
 +        in some circumstances even if threre were timeout tasks, though not
 +        IO tasks, but tasks anyway.  Now fixed.
 +
 +      * Defined SilcServerConnection structure to hold connection specific
 +        stuff about directly connected servers and routers.  The definition
 +        is currently in silcd/server_internal.h.  I thought about having
 +        a bit more important role fro this struct but for now it is used
 +        only when connecting to other server (or router actually).
 +
 +      * Added connecting retry support in server when connecting to
 +        router(s).  The retry feature implement exponential backoff
 +        algorithm.  Also, added SilcServerParams structure to hold default
 +        parameters for server.  For now, it include these retry settings
 +        and are hard coded.  After server is moded to be as Silc Server
 +        Library this structure will be more important.
 +
 +Sun Nov  5 22:28:44 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed client librarys channel->clients table to SilcList and
 +        changed code accordingly.
 +
 +Thu Nov  2 16:28:01 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed client's channel table to SilcList and changed code 
 +        accordingly.  Also changed SilcChannelClientEntry to include back-
 +        pointer to the channel so that client entry can use that structure
 +        as list as well and we have fast cross-reference to the channel.
 +        This change dramatically decreased the complexity of channel
 +        handling with client entry and vice versa (removed one extra
 +        loop when searching for channel entry from many functions).
 +
 +      * Changed server->sim from table to SilcDList and changed code
 +        accordingly.
 +
 +      * NAMES command can now be used from user interface.  It will show
 +        the user list on the channel, neatly.
 +
 +      * Added realname pointer to SilcClientEntry in lib/silcclient/idlist.h.
 +        Code now saves realname of the user if it becomes available.
 +
 +      * Renamed configure.in to configure.in.pre and made ./prepare
 +        script to automatically add correct version string to
 +        configure.in which it creates from configure.in.pre.
 +
 +Wed Nov  1 17:21:26 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * NAMES command reply now shows users mode with the nickname when
 +        joining to channel.
 +
 +      * Moved silc_client_ch[u]mode[_char] functions from 
 +        silc/clientutil.[ch] to lib/silcclient/client.[ch].  Though, that
 +        place sucks, they are utility functions and should be in some
 +        other file.
 +
 +      * Fixed some unsigned int's to unsigned short's.  Patch by cras.
 +
 +      * Fixed contrib/getopt*.[ch] to not require config.h.  Patch by
 +        cras.
 +
 +Tue Oct 31 20:10:37 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Updated README.
 +
 +      * Added TRQ (efficient deque and list library) into lib/trq.  This is
 +        a very good list library that is currently used in the SILC.  Defined
 +        SilcList API over the library because I didn't like the API very
 +        much.  See lib/trq/silclist.h for the API and examples of how to
 +        use the API.  Fixed various places in the code to use the new
 +        SilcList API. The SilcList is meant for lists that has a structure
 +        already defined as a list.  It is not suitable to add just some
 +        context to the list (in TRQ, the context is the list actually).
 +
 +        So, I defined SilcDList that can be used for the purpose where 
 +        predefined list structure does not exit.  This can be used as
 +        such list.  Now some context just can be added to the SilcDList.
 +        Currently this list is not used in the SILC just yet, though there
 +        are a lot places where this can replace dynamically allocated
 +        tables and I will fix these places, later, to use SilcDList.
 +        See lib/trq/silcdlist.h for SilcDList (they are all inline functions,
 +        and use TRQ internally).
 +
 +        Also fixed some annoying warning messages that the original TRQ
 +        code generated.  Also minor changes to TRQ's Makefile.in.
 +
 +      * Added support for querying by Client ID to both WHOIS and 
 +        IDENTIFY commands into server, as required by the protocol.
 +
 +      * Removed method function pointers from SilcBuffer structure. They
 +        weren't used to anything and just increased the context size for
 +        no good reason.  This change also made silc_buffer_alloc and
 +        silc_buffer_free functions inline functions.
 +
 +      * Disabled command flooding detection support until it's fixed so 
 +        that it accepts commands in but does not execute them more than once
 +        in two seconds.
 +
 +      * Added silc_net_localhost(), to return local hostname, into
 +        lib/silcutil/silcnet.[ch].  Also added client->hostname pointer
 +        that must be initialized before calling silc_client_init.
 +
 +      * Added new function: silc_server_send_notify_on_channels to send
 +        notify messages to all channels client has joined.  It is assured
 +        that the message is sent only once per client.
 +
 +      * Moved silc_log_format (from lib/silcutil/silclog.[ch] into
 +        lib/silcutil/silcutil.[ch] as silc_format function.  The new 
 +        function is generic and is used by server as well, not only by
 +        the logging routines.
 +
 +      * Added new SKE status type: SILC_SKE_STATUS_BAD_VERSION to indicate
 +        the provided version string was not acceptable.  Added new function:
 +        silc_ske_check_version into lib/silcske/silcske.h.  The function
 +        must be implemented by the application (client or server) and it
 +        does not reside in the SKE library.  The function checks the version
 +        string remote end sent.
 +
 +      * Added back pointers (to opaque context and to SilcSocketConnection) 
 +        into SilcPacketContext structure into lib/silccore/silcpacket.h.
 +
 +      * Added silc_packet_context_dup into lib/silccore/silcpacket.[ch] to
 +        duplicate packet context structure.
 +
 +      * Changed `notify' client operation to send same arguments as client
 +        receives from server except for ID's.  ID's are mapped to correct
 +        ID entry and that is returned.  Also, if channel entry is not sent
 +        by server but the notify is for channel the channel entry is sent
 +        to application (otherwise application doesn't know that it is for
 +        channel (library gets it from packet's Destination ID)).
 +
 +      * Added silc_client_remove_from_channels into client library to 
 +        remove a client from all channels it has joined to.  Used when 
 +        received SIGNOFF notify from server.  Added also new function
 +        silc_client_replace_from_channels to replace old ID entry with
 +        new ID entry on all channels.  Used when received NICK_CHANGE
 +        notify from server.
 +
 +      * Fixed ID Cache list handling in silc_idlist_get_client in 
 +        lib/silcclient/idlist.c.  Also, added silc_idlist_get_client_by_id
 +        to get (or query) client by ID.
 +
 +      * Updated TODO list.
 +
 +      * Added connection authentication status message defined by the
 +        protocol: SILC_CONN_AUTH_OK and SILC_CONN_AUTH_FAILED and added the
 +        support for these into the code in client and server side.
 +
 +      * Added generic function silc_client_send_command to send any command
 +        with variable argument list.  Application should use this function
 +        to send commands if the command functions provided by the library
 +        does not suite for the application's user interface needs.
 +
 +      * Added new `failure' client operation.  Application is notified about
 +        received failure packet if client is executing a protocol.  In this
 +        case the protocol's execution has failed.
 +
 +      * Added SKE's end notify to send the SKE_SUCCESS notify message that
 +        is required by the protocol.
 +
 +      * Added SILC_PROTOCOL_STATE_FAILURE to indicate received failure
 +        packet from remote.  SILC_PROTOCOL_STATE_ERROR indicates local
 +        error at our end.
 +
 +      * Added status flag to SilcSKE object to indicate realtime status
 +        of the SKE protocol.
 +
 +      * Application receives now exactly same command reply arguments as
 +        the library receives from server.  However, if ID is received the
 +        corresponding ID entry is returned to the application (eg. Client
 +        ID is mapped to correct SilcClientEntry entry and that is returned).
 +        Changed command_reply client operation due to this change.
 +
 +      * Changed all ID's in commands and in command replys as ID Payloads.
 +        Change affected both client and server side codes.
 +
 +        All ID's sent in SILC network (with execption of ID's in SILC
 +        Packet header) are sent in ID Payload to support variable length
 +        ID's.
 +
 +      * Server now notifies nick changes and notifies all clients on
 +        the channels about the new nickname (about the new Client ID,
 +        actually).
 +
 +      * Implemented CMODE command to change channel modes. Supports all
 +        channel modes defined by the protocol specs except ban and invite
 +        lists. (Also, private channel key mode is supported but support for
 +        setting private channel key in client is missing, thus, this mode
 +        has no effect on client side (except that server requires that the
 +        client uses private channel key and normal channel traffic does not
 +        work anymore)).
 +
 +        Also, invite mode works per se, but INVITE command does not work
 +        yet correctly, so you can set channel as invite only channel but
 +        inviting clients to the channel does not work (it is yet to be
 +        thought what's the best way to do it).
 +
 +      * Added new command SILC_COMMAND_CUMODE to change user mode on the
 +        channel.  Defined user modes: CHANNEL_FOUNDER and CHANNEL_OPERATOR.
 +        Implemented CUMODE command to change user's mode on the channel.
 +        Supports all modes defined by the protocol specs.
 +
 +      * Added NAMES command reply to return users modes on the channel.
 +
 +      * Removed unnecessary and slow ciphers from lib/silccrypt.
 +
 +      * Set SO_KEEPALIVE option to connection sockets by default.
 +
 +      * Added new command reply status: SILC_STATUS_USER_NOT_ON_CHANNEL.
 +
 +      * Added notify types: MOTD, CMODE_CHANGE and CUMODE_CHANGE.  Also,
 +        redefined the Notify Payload into protocol specs.
 +
 +      * Added silc_id_payload_parse_id to get ID directly from raw
 +        ID payload data.
 +
 +Mon Oct  9 20:57:02 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Changed SILC_COMMAND_IDENTIFY in protocol specification to 
 +        accept searching by Client ID as well.
 +
 +      * Added support for LEAVE and SIGNOFF notify types in client library.
 +
 +      * Added silc_id_payload_parse_data into lib/silccore/silcpayload.[ch]
 +        to parse ID Payload from raw data.
 +
 +Sun Oct  8 19:33:08 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added flags parameter into silc_ske_assemble_security_properties
 +        function in lib/silcske/silcske.[ch].
 +
 +      * Changed notify client operation to fit better for notify messages
 +        sent by server.  The notify payload received from server is now
 +        passed to the application (after parsing it to SilcNotifyPayload).
 +        It is application's responsibility to retrieve the arguments
 +        from the payload and show the message the way it wants.  The message
 +        sent by server is implementation specific.
 +
 +      * Changed public keys to comply with the protocol specification.
 +        Old public keys are not supported anymore and are not compatible.
 +
 +      * Removed nickname from Channel Payload as the latest draft removed
 +        it.  The client must resolve the nickname from the NAMES command
 +        reply received when it joined the channel.
 +
 +        Also, changed all channel_xxxx_payload to channel_payload_xxxx.
 +
 +Sat Oct  7 21:55:01 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed some errors in protocol specification drafts.
 +
 +      * Created lib/silccore/silcnotify.c to implement Notify Payload
 +        encoding and decoding, lib/silccore/silcpayload.[ch] to implement
 +        generic payloads described by protocol specifications.  The file
 +        includes implementations for ID Payload and Argument Payload.
 +
 +      * Changed Command Payload implementation to use the new Argument
 +        Payload.  Changed command_xxxx_payload to command_payload_xxxx
 +        to comply with SILC coding conventions.
 +
 +      * Added suppport for Argument Payload handling in Notify Payload
 +        implementation as protocol requires it.  Added the new support
 +        into server and client lib as well.
 +
 +Thu Oct  5 21:16:28 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added support for multiple nicknames on same channel.  [n] is
 +        added locally to the nickname if there are more than one same
 +        nicknames on the channel.
 +
 +      * Server now sends all nicknames that matched WHOIS request.
 +        Client also shows the list received from server.
 +
 +      * Added TOPIC command to client side.  User can now set and show
 +        current topic on channel.
 +
 +      * Added MOTD command to client and server.  Also, server sends the
 +        motd when client connects to the server.
 +
 +      * Changed version strings to comply ISO 8601.
 +
 +Wed Oct  4 23:29:06 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed protocol error handling in client library.  It should now
 +        cope even if the SKE fails for some reason.
 +
 +      * Made new protocol specification drafts for submitting to IETF.
 +
 +      * Implemented TOPIC command to server in silcd/command.c.
 +
 +      * Added two new notify types into lib/silccore/silcnotify.h:
 +        SILC_NOTIFY_TYPE_NICK_CHANGE and SILC_NOTIFY_TYPE_TOPIC_SET to
 +        notify nickname change and topic setting/change on a channel.
 +
 +      * API change of command_reply operation in client library.  The
 +        application gets now the status type received from server as well.
 +
 +Sat Sep 30 16:57:42 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Removed the function just added to lib/silcutil/silcschedule.[ch].
 +
 +      * Cras fixed and optimized the packet handling even further and
 +        it should work now.  Minor change to the prototype of function
 +        silc_packet_receive_process in lib/silccore/silcpacket.[ch].
 +
 +Sat Sep 30 08:48:47 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added new function into lib/silcutil/silcschedule.[ch]:
 +        silc_schedule_with_fd to select() a specified fd.  The function
 +        returns after timeout expires or data arrives or goes.  The
 +        function is used by packet routines to wait that all data is
 +        received from network.
 +
 +      * Fixed data reading from network in lib/silccore/silcpacket.c.
 +        The code now assures that all data is read from the fd and then
 +        continues packet processing.  This was a bug fix since the code
 +        used to drop some data in some circumstances.
 +
 +      * Added new function into lib/silcclient/client.[ch]:
 +        silc_client_start_key_exchange to start key exchange after
 +        connection has been established to server.  The code internally
 +        now uses this funtion but its main purpose was to provide it
 +        for applications that perform their own connecting.  After
 +        application has created a connection it merely calls this
 +        function to start the key exchange between client and server.
 +        The library takes care of everything else after that.
 +
 +        Updated also lib/silcclient/README to explain the usage of
 +        this new function.
 +
 +      * Do not send to application information that connection has
 +        been established.  Application gets notified it by connect
 +        operation anyway.
 +
 +Thu Sep 28 23:40:19 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Applied cras's patch to add silc_schedule_one function.  The
 +        function runs scheduler once and returns.
 +
 +      * Fixed the scheduler after cras messed it up.  The timeout
 +        handling works now as it's supposed to work.
 +
 +      * Added into lib/silccore/ silcnotify.h to include notify
 +        message types support.  Changed silc_server_send_notify*
 +        functions, in server.[ch], to support those new notify types.
 +        Added the support for the notify types into client library,
 +        as well.  Added new notify client operation into ops.h in
 +        lib/silcclient/.
 +
 +      * Changed silc_server_packet_send_to_channel to send normal
 +        packets instead of just channel message packets.  The function
 +        is now used to send the notify packets to channels.  It is not
 +        used to send channel message packets anymore, as server never
 +        sends them anymore.
 +
 +      * Added explicit casting into lib/silcutil/silcbuffmt.c to few
 +        va_arg()s as it seems to require it nowadays.  I guess, if SILC
 +        is compiled with older va_arg() the new code should work anyway.
 +
 +Wed Sep 13 18:10:14 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Splitted core library.  Core library (lib/silccore) includes
 +        now only SILC protocol specific core (and common) components.
 +        Created new utility library (lib/silcutil) that includes more
 +        generic purpose stuff.  The stuff for util library was taken
 +        from the old core library.  This was minor and easy split.
 +
 +      * Created SILC Client Library (lib/silcclient) that includes
 +        implementation of the SILC client without user interface.  This
 +        was major move from silc/ directory.  The code has been changed
 +        so that it is transparent towards the user interface.  The
 +        silc/ directory includes now the same user interface as before
 +        and it uses the new client library.  Read lib/silcclient/README.
 +        Basicly, the client library performs everything else related
 +        to SILC except user interface handling.  Also, configuration
 +        files are considered to be part of user interface and library
 +        does not handle them.
 +
 +        This change also changed a lot of structures, function naming etc.
 +        Most important change was that SilcClientWindow object was
 +        renamed to SilcClientConnection in the client library.  Created
 +        also new file lib/silcclient/ops.h.  Also added new files
 +        silc/local_command.[ch] and silc/client_ops.[ch].
 +
 +        All these changes were made to make it easier for user interface
 +        designers to create what ever user interface for the SILC client
 +        they want.
 +
 +        It is also expected that the server will be moved to lib
 +        directory as well and SILC Server Library will be created;
 +        sometimes in the future.
 +
 +      * Removed Local commands from lib/silccore/silccommand.h as
 +        they are application specific and new client library does not
 +        handle any of those anymore.
 +
 +      * Several functions moved to lib/silcutil/silcutilc.[ch] from
 +        old client implementation in silc/.
 +
 +      * Added support for callback functions in SILC_LOG_* macros.
 +        Application can now set its own callbacks that will be called
 +        instead of using the default functions that will always print
 +        the debug messages to stderr (or stdout).  Also, debugging can
 +        now be disabled by setting silc_debug to FALSE and re-enabled by
 +        setting it to TRUE.  Note, that logging will still work even
 +        if debugging is disabled.
 +
 +        New functions in lib/silcutil/silclog.[ch]: silc_log_set_callbacks,
 +        silc_log_reset_callbacks, silc_log_set_debug_callbacks and
 +        silc_log_reset_debug_callbacks.
 +
 +      * To enable debugging in silc client one must give now -d
 +        option on command line.
 +
 +      * Changed silc_schedule_init to automatically allocate task queues
 +        if they are not allocated before calling it.
 +
 +Thu Sep  7 10:49:33 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added GMP 3.1 into math library.
 +
 +Sun Aug 20 21:27:26 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added SILC_PACKET_REMOVE_CHANNEL_USER to remove a client from
 +        a channel in SILC network.  The packet is used by servers and
 +        routers to notify other routers that user has left a channel.
 +        This little feature was missing until now.  Added the feature
 +        to protocol specification as well.
 +
 +        Added functions: silc_server_send_remove_channel_user and
 +        silc_server_remove_channel_user into server.[ch].
 +
 +      * Added SILC_PACKET_REKEY and SILC_PACKET_REKEY_DONE into
 +        lib/silccore/silcpacket.h.  However, they are not implemented
 +        yet.
 +
 +Sat Aug 19 23:04:16 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed joining to a channel and sending channel messages
 +        between server and router.  The channel message sending should
 +        now work inside a cell.
 +
 +Tue Jul 25 20:46:13 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the private message sending between server and router.
 +        The private message sending should now work inside a cell.
 +
 +      * Added silc_server_replace_id into server.[ch] to replace
 +        existing ID in the SILC network.
 +
 +      * Added silc_idlist_find_server_by, silc_idlist_replace_client_id
 +        and silc_idlist_replace_server_id into idlist.[ch] in server.
 +
 +Mon Jul 24 18:33:31 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed the server to server connections.  Server can again now
 +        connect to router.  Router to router connections probably does
 +        not work just yet.
 +
 +Thu Jul 20 13:15:01 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added dynamic protocol registering support.  Now protocols can
 +        registered and unregistered on the fly.  Patch by cras.
 +
 +Wed Jul 19 19:08:46 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added lib/contrib directory to hold routines that some platforms
 +        don't have but are needed by SILC.
 +
 +      * Added getopt.c, getopt1.c and getopt.h from GNU C library
 +        into lin/contrib to provide getopt() and getopt_long() for
 +        those who don't have it.
 +
 +Tue Jul 18 20:41:20 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added AWAY command to client.  When away message is set and
 +        client receives a private message packet the client automatically
 +        replies to the sender with the away message.
 +
 +      * Fixed a bug in lib/silcmath/mpbin.c: silc_mp_mp2bin.  This
 +        bug seemed to be the cause of recent problems when compiling
 +        with gcc-2.95.
 +
 +      * Added version detection support to SKE protocol specification
 +        and added the new changes to the SKE implementation as well.
 +        There were other minor changes in the SKE protocol as well.
 +
 +        Many changes in lib/silcske/silcske.[ch] and in
 +        lib/silcske/payload.[ch].
 +
 +      * Added ^U functionality, clear input line.  Patch from cras.
 +
 +Mon Jul 17 23:33:26 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Mainly small bugfixes on core library.  Fixed some debugging
 +        logging and buffer overflow in silclog.c.
 +
 +      * Updated config.sub and config.guess on the distribution tree.
 +
 +Sat Jul 15 15:33:48 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added command lagging support in server. Client may execute
 +        commands now only once in two seconds.
 +
 +Thu Jul 13 22:10:21 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Optimized packet reception. MAC computation and checking is now
 +        also more optimized.  A lot previously duplicated code is now
 +        used as generic by both client and server.
 +
 +      * Fixed key pair generation in clientutil.c
 +
 +Wed Jul 12 18:28:07 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added into lib/silccore/silcbufutil.[ch] new function;
 +        silc_buffer_realloc.
 +
 +      * Moved generic packet sending/encryption functions to 
 +        lib/silccore/silcpacket.[ch] from client and server.  Some
 +        rewriting of the functions.
 +
 +      * Moved all generic packet reception/decryption functions to
 +        lib/silccore/silcpacket.[ch] from client and server.  The
 +        packet processing is now much cleaner in both client and server.
 +        These were major changes in both client and server.
 +
 +      * Created many common functions in server to do packet sending.
 +        Previously code were duplicated a lot, this has been removed
 +        with these changes.
 +
 +Tue Jul 11 20:27:26 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Rewrote major parts of the ID cache system.  Don't know 
 +        whether it is better now or not but at least the API is more
 +        cleaner now.
 +
 +      * Major rewrite on ID cache stuff on client because of the ID
 +        cache API changes.  Added idlist.c to client.
 +
 +      * Also major rewrite on ID cache stuff on server as well.
 +        Major rewrite of idlist.[ch]. SilcXXXList's are now named
 +        SilcXXXEntry's.  We won't keep anymore idlist specific pointers
 +        in hand, instead they are all put into the ID cache system now.
 +        All server_idlist_* routines uses ID cache now instead of
 +        traversing its own lists (those lists does not exist anymore).
 +        SilcIDList though still exists.  Also, SilcXXXEntry's are
 +        now pointers.
 +
 +Sun Jul  9 15:19:24 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Finally made the SKE implementation compliant to the protocol
 +        specification.  All mp integers are now binary encoded as
 +        opposed being HEX encoded.
 +
 +      * Added lib/silcmath/mpbin.[ch].  Encoding mp intergers to and
 +        from binary data.
 +
 +      * Added into lib/silccore/silcutil.[ch] PEM encoding/decoding
 +        functions: silc_[encode/decode]_pem.  Also added function
 +        silc_encode_pem_file to PEM encode with newlines ('\n') for
 +        saving into a file.
 +
 +      * SILC public keys are now encoded either PEM or binary.  Same
 +        option is for private keys as well.  By default private keys
 +        are binary encoded and public keys PEM encoded.  Silly HEX
 +        encoding were removed.
 +
 +      * Added into lib/silccrypt/silchash.[ch] silc_hash_fingerprint
 +        function to create fingerprints.
 +
 +      * Fixed a bug in SHA1; does not change the original data anymore.
 +
 +      * Partly implemented INFO command on client and server side.
 +        Fixed CLEAR command.  Changes to SERVER command; show current
 +        server(s) when giving command without arguments.  Added
 +        VERSION command to client.
 +
 +      * Added check to server that unregistered connections cannot
 +        execute commands (unless it is specificly allowed).
 +
 +Thu Jul  6 18:12:24 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Fixed screen refresh.
 +
 +      * Fixed channel joining bug from client.  On some circumstances
 +        client tried to join to a channel it had already joined.
 +
 +      * Added public key verification process into client's protocol.c.
 +        The client now verifies the public key from user and saves
 +        it into ~./silc/serverkeys/ directory. 
 +
 +        Added into: clientutil.[ch]: silc_client_verify_server_key.
 +
 +      * Changed SKE protocol's silc_ske_initiator_finish function
 +        to accept callback function that verifies the received public
 +        key.  Removed old silc_ske_verify_public_key function.
 +
 +Wed Jul  5 19:19:02 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added into silcpkcs[ch]: silc_pkcs_public_key[_data]_set and
 +        silc_pkcs_private_key[_data]_set.
 +
 +      * Made the password and public authentication more cleaner in
 +        server's protocol.c.
 +
 +      * Removed historic and obsolete protocol `channel_auth' from
 +        both client and server.
 +
 +      * Removed wrong way of sending command status messages from
 +        server to client in server's command.c.  The old way violated
 +        protocol specification.  
 +
 +        Changes to silccore/silccommand.[ch]: removed
 +        silc_command_encode_status_payload -> not needed anymore,
 +        changed silc_command_encode_payload_va to accept extra
 +        argument on variable argument list.  The argument type must
 +        now be provided to the function.  Also, added new function:
 +        silc_command_encode_reply_payload_va which is same as
 +        normal command_encode_payload_va except command status type
 +        is provided as extra argument.
 +
 +Tue Jul  4 18:26:39 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added ~./silc directory handling.  The directory includes the
 +        public and private keys for the client.
 +
 +        Added silc_client_check_silc_dir, silc_client_create_identifier
 +        and silc_client_load_keys.
 +
 +      * Implemented SILC protocol compliant public key.  Added public
 +        and private key saving to and loading from files.
 +
 +        Added into silcpkcs.[ch]: silc_pkcs_encode_identifier,
 +        silc_pkcs_public_key_encode[_data], silc_pkcs_public_key_decode,
 +        silc_pkcs_private_key_encode[_data], silc_pkcs_private_key_decode,
 +        silc_pkcs_public_key_alloc, silc_pkcs_public_key_free,
 +        silc_pkcs_private_key_alloc and silc_pkcs_private_key_free.
 +
 +        Implemented: silc_pkcs_save_[public/private]_key[_data] and
 +        silc_pkcs_load_[public/private]_key.
 +
 +Mon Jul  3 18:51:27 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added silc_server_get_route (route.[ch]) to get connection
 +        data for the fastest route for given ID.
 +
 +      * Implemented INVITE command on client and server.  The command
 +        were re-defined in the SILC Protocol Specification and the
 +        implementation now complies with the specification.
 +
 +      * Implemented PING command on client and server.
 +
 +      * Implemented NAMES command on client and server.  The server side
 +        supports currently only normal server not router server yet.
 +        Some changes to NAMES definition in SILC protocol specification.
 +
 +Sun Jul  2 18:23:01 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Implemented LEAVE command on client and server.
 +
 +      * Previously deprecated SILC_PACKET_FORWARDED flag is now in use 
 +        again.  This change was made to the protocol as well.  Server
 +        should not violate the protocol specification anymore.
 +
 +Fri Jun 30 14:03:26 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +      * Added SOCKS4 and SOCKS5 support to SILC client.  SOCKS5
 +        was tested.  SOCKS4 was not but should work anyway.
diff --combined apps/silcd/command.c
index 3b3a8b5cde781038304a90332b5747b2c7c69bf9,b9a0cc63b90f069216bf6bc42f1a58079f75f260..b4d5bcee37adf53669b7e5e77dde3ac515eb6dae
@@@ -4,7 -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
    GNU General Public License for more details.
  
  */
 -/*
 - * $Id$
 - * $Log$
 - * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
 - *    Importet from internal CVS/Added Log headers.
 - *
 - *
 - */
 +/* $Id$ */
  
  #include "serverincludes.h"
  #include "server_internal.h"
  
 +static int silc_server_is_registered(SilcServer server,
 +                                   SilcSocketConnection sock,
 +                                   SilcServerCommandContext cmd,
 +                                   SilcCommand command);
 +static void 
 +silc_server_command_send_status_reply(SilcServerCommandContext cmd,
 +                                    SilcCommand command,
 +                                    SilcCommandStatus status);
 +static void 
 +silc_server_command_send_status_data(SilcServerCommandContext cmd,
 +                                   SilcCommand command,
 +                                   SilcCommandStatus status,
 +                                   uint32 arg_type,
 +                                   const unsigned char *arg,
 +                                   uint32 arg_len);
 +static bool
 +silc_server_command_pending_error_check(SilcServerCommandContext cmd,
 +                                      SilcServerCommandReplyContext cmdr,
 +                                      SilcCommand command);
 +SILC_TASK_CALLBACK(silc_server_command_process_timeout);
 +
  /* Server command list. */
  SilcServerCommand silc_command_list[] =
  {
    SILC_SERVER_CMD(whois, WHOIS, SILC_CF_LAG | SILC_CF_REG),
    SILC_SERVER_CMD(whowas, WHOWAS, SILC_CF_LAG | SILC_CF_REG),
    SILC_SERVER_CMD(identify, IDENTIFY, SILC_CF_LAG | SILC_CF_REG),
 -  SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG | SILC_CF_REG),
 -  SILC_SERVER_CMD(list, LIST, SILC_CF_LAG | SILC_CF_REG),
 +  SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
 +  SILC_SERVER_CMD(list, LIST, SILC_CF_LAG_STRICT | SILC_CF_REG),
    SILC_SERVER_CMD(topic, TOPIC, SILC_CF_LAG | SILC_CF_REG),
    SILC_SERVER_CMD(invite, INVITE, SILC_CF_LAG | SILC_CF_REG),
    SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
 -  SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
 +  SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG_STRICT | SILC_CF_REG | SILC_CF_OPER),
    SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
    SILC_SERVER_CMD(connect, CONNECT, 
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
    SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
    SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
 -  SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG | SILC_CF_REG),
 +  SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
    SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG),
    SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG),
 -  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG | SILC_CF_REG),
 -  SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG | SILC_CF_REG),
 -  SILC_SERVER_CMD(restart, RESTART, 
 -                SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
 +  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG_STRICT | SILC_CF_REG),
 +  SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
 +  SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
 +  SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
    SILC_SERVER_CMD(close, CLOSE,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
 -  SILC_SERVER_CMD(die, DIE, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
 +  SILC_SERVER_CMD(shutdown, SHUTDOWN, SILC_CF_LAG | SILC_CF_REG | 
 +                SILC_CF_OPER),
    SILC_SERVER_CMD(silcoper, SILCOPER,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
 -  SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG | SILC_CF_REG),
 -  SILC_SERVER_CMD(names, NAMES, SILC_CF_LAG | SILC_CF_REG),
 +  SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
 +  SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
 +  SILC_SERVER_CMD(getkey, GETKEY, SILC_CF_LAG | SILC_CF_REG),
  
    { NULL, 0 },
  };
  
 -/* List of pending commands. */
 -SilcServerCommandPending *silc_command_pending = NULL;
 +/* Performs several checks to the command. It first checks whether this
 +   command was called as pending command callback. If it was then it checks
 +   whether error occurred in the command reply where the pending command
 +   callback was called. 
 +
 +   It also checks that the requested command includes correct amount
 +   of arguments. */
 +#define SILC_SERVER_COMMAND_CHECK(command, context, min, max)               \
 +do {                                                                        \
 +  uint32 _argc;                                                                     \
 +                                                                            \
 +  SILC_LOG_DEBUG(("Start"));                                                \
 +                                                                            \
 +  if (silc_server_command_pending_error_check(cmd, context2, command)) {      \
 +    silc_server_command_free(cmd);                                          \
 +    return;                                                                 \
 +  }                                                                         \
 +                                                                            \
 +  _argc = silc_argument_get_arg_num(cmd->args);                                     \
 +  if (_argc < min) {                                                        \
 +    silc_server_command_send_status_reply(cmd, command,                             \
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); \
 +    silc_server_command_free(cmd);                                          \
 +    return;                                                                 \
 +  }                                                                         \
 +  if (_argc > max) {                                                        \
 +    silc_server_command_send_status_reply(cmd, command,                             \
 +                                        SILC_STATUS_ERR_TOO_MANY_PARAMS);   \
 +    silc_server_command_free(cmd);                                          \
 +    return;                                                                 \
 +  }                                                                         \
 +} while(0)
 +
 +/* Returns TRUE if the connection is registered. Unregistered connections
 +   usually cannot send commands hence the check. */
 +
 +static int silc_server_is_registered(SilcServer server,
 +                                   SilcSocketConnection sock,
 +                                   SilcServerCommandContext cmd,
 +                                   SilcCommand command)
 +{
 +  SilcIDListData idata = (SilcIDListData)sock->user_data;
 +
 +  if (!idata)
 +    return FALSE;
  
 -/* Add new pending command to the list of pending commands. Currently
 -   pending commands are executed from command replies, thus we can
 -   execute any command after receiving some specific command reply.
 +  if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
 +    return TRUE;
  
 -   The argument `reply_cmd' is the command reply from where the callback
 -   function is to be called, thus, it IS NOT the command to be executed. */
 +  silc_server_command_send_status_reply(cmd, command,
 +                                      SILC_STATUS_ERR_NOT_REGISTERED);
 +  return FALSE;
 +}
  
 -void silc_server_command_pending(SilcCommand reply_cmd,
 -                               SilcCommandCb callback,
 -                               void *context)
 +/* Internal context to hold data when executed command with timeout. */
 +typedef struct {
 +  SilcServerCommandContext ctx;
 +  SilcServerCommand *cmd;
 +} *SilcServerCommandTimeout;
 +
 +/* Timeout callback to process commands with timeout for client. Client's
 +   commands are always executed with timeout. */
 +
 +SILC_TASK_CALLBACK(silc_server_command_process_timeout)
  {
 -  SilcServerCommandPending *reply, *r;
 +  SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
 +  SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
  
 -  reply = silc_calloc(1, sizeof(*reply));
 -  reply->reply_cmd = reply_cmd;
 -  reply->context = context;
 -  reply->callback = callback;
 +  if (!client) {
 +    silc_server_command_free(timeout->ctx);
 +    silc_free(timeout);
 +  }
 +
 +  /* Update access time */
 +  client->last_command = time(NULL);
 +
 +  if (!(timeout->cmd->flags & SILC_CF_REG))
 +    timeout->cmd->cb(timeout->ctx, NULL);
 +  else if (silc_server_is_registered(timeout->ctx->server, 
 +                                   timeout->ctx->sock, 
 +                                   timeout->ctx, 
 +                                   timeout->cmd->cmd))
 +    timeout->cmd->cb(timeout->ctx, NULL);
 +  else
 +    silc_server_command_free(timeout->ctx);
 +
 +  silc_free(timeout);
 +}
 +
 +/* Processes received command packet. */
  
 -  if (silc_command_pending == NULL) {
 -    silc_command_pending = reply;
 +void silc_server_command_process(SilcServer server,
 +                               SilcSocketConnection sock,
 +                               SilcPacketContext *packet)
 +{
 +  SilcServerCommandContext ctx;
 +  SilcServerCommand *cmd;
 +  SilcCommand command;
 +
 +  /* Allocate command context. This must be free'd by the
 +     command routine receiving it. */
 +  ctx = silc_server_command_alloc();
 +  ctx->server = server;
 +  ctx->sock = silc_socket_dup(sock);
 +  ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
 +  
 +  /* Parse the command payload in the packet */
 +  ctx->payload = silc_command_payload_parse(packet->buffer->data,
 +                                          packet->buffer->len);
 +  if (!ctx->payload) {
 +    SILC_LOG_ERROR(("Bad command payload, packet dropped"));
 +    silc_buffer_free(packet->buffer);
 +    silc_packet_context_free(packet);
 +    silc_socket_free(ctx->sock);
 +    silc_free(ctx);
      return;
    }
 +  ctx->args = silc_command_get_args(ctx->payload);
  
 -  for (r = silc_command_pending; r; r = r->next) {
 -    if (r->next == NULL) {
 -      r->next = reply;
 +  /* Get the command */
 +  command = silc_command_get(ctx->payload);
 +  for (cmd = silc_command_list; cmd->cb; cmd++)
 +    if (cmd->cmd == command)
        break;
 +
 +  if (cmd == NULL) {
 +    silc_server_command_send_status_reply(ctx, command,
 +                                        SILC_STATUS_ERR_UNKNOWN_COMMAND);
 +    silc_server_command_free(ctx);
 +    return;
 +  }
 +
 +  /* Execute client's commands always with timeout.  Normally they are
 +     executed with zero (0) timeout but if client is sending command more
 +     frequently than once in 2 seconds, then the timeout may be 0 to 2
 +     seconds. */
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
 +    SilcClientEntry client = (SilcClientEntry)sock->user_data;
 +    SilcServerCommandTimeout timeout = silc_calloc(1, sizeof(*timeout));
 +    int fast;
 +
 +    timeout->ctx = ctx;
 +    timeout->cmd = cmd;
 +
 +    if (client->last_command && (time(NULL) - client->last_command) < 2) {
 +      client->fast_command++;
 +      fast = FALSE;
 +    } else {
 +      client->fast_command = ((client->fast_command - 1) <= 0 ? 0 : 
 +                            client->fast_command--);
 +      fast = TRUE;
      }
 +
 +    if (!fast && ((cmd->flags & SILC_CF_LAG_STRICT) ||
 +                (client->fast_command > 5 && cmd->flags & SILC_CF_LAG)))
 +      silc_schedule_task_add(server->schedule, sock->sock, 
 +                       silc_server_command_process_timeout,
 +                       (void *)timeout, 
 +                       2 - (time(NULL) - client->last_command), 0,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 +    else
 +      silc_schedule_task_add(server->schedule, sock->sock, 
 +                       silc_server_command_process_timeout,
 +                       (void *)timeout, 0, 1,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 +    return;
 +  }
 +
 +  /* Execute for server */
 +
 +  if (!(cmd->flags & SILC_CF_REG))
 +    cmd->cb(ctx, NULL);
 +  else if (silc_server_is_registered(server, sock, ctx, cmd->cmd))
 +    cmd->cb(ctx, NULL);
 +  else
 +    silc_server_command_free(ctx);
 +}
 +
 +/* Allocate Command Context */
 +
 +SilcServerCommandContext silc_server_command_alloc()
 +{
 +  SilcServerCommandContext ctx = silc_calloc(1, sizeof(*ctx));
 +  ctx->users++;
 +  return ctx;
 +}
 +
 +/* Free's the command context allocated before executing the command */
 +
 +void silc_server_command_free(SilcServerCommandContext ctx)
 +{
 +  ctx->users--;
 +  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
 +                ctx->users));
 +  if (ctx->users < 1) {
 +    if (ctx->payload)
 +      silc_command_payload_free(ctx->payload);
 +    if (ctx->packet)
 +      silc_packet_context_free(ctx->packet);
 +    if (ctx->sock)
 +      silc_socket_free(ctx->sock); /* Decrease reference counter */
 +    silc_free(ctx);
 +  }
 +}
 +
 +/* Duplicate Command Context by adding reference counter. The context won't
 +   be free'd untill it hits zero. */
 +
 +SilcServerCommandContext 
 +silc_server_command_dup(SilcServerCommandContext ctx)
 +{
 +  ctx->users++;
 +  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
 +                ctx->users));
 +  return ctx;
 +}
 +
 +/* Add new pending command to be executed when reply to a command has been
 +   received. The `reply_cmd' is the command that will call the `callback'
 +   with `context' when reply has been received.  It can be SILC_COMMAND_NONE
 +   to match any command with the `ident'.  If `ident' is non-zero
 +   the `callback' will be executed when received reply with command
 +   identifier `ident'. If there already exists pending command for the
 +   specified command, ident, callback and context this function has no
 +   effect. */
 +
 +bool silc_server_command_pending(SilcServer server,
 +                               SilcCommand reply_cmd,
 +                               uint16 ident,
 +                               SilcCommandCb callback,
 +                               void *context)
 +{
 +  SilcServerCommandPending *reply;
 +
 +  /* Check whether identical pending already exists for same command,
 +     ident, callback and callback context. If it does then it would be
 +     error to register it again. */
 +  silc_dlist_start(server->pending_commands);
 +  while ((reply = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
 +    if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
 +      reply->callback == callback && reply->context == context)
 +      return FALSE;
    }
 +
 +  reply = silc_calloc(1, sizeof(*reply));
 +  reply->reply_cmd = reply_cmd;
 +  reply->ident = ident;
 +  reply->context = context;
 +  reply->callback = callback;
 +  silc_dlist_add(server->pending_commands, reply);
 +
 +  return TRUE;
  }
  
  /* Deletes pending command by reply command type. */
  
 -void silc_server_command_pending_del(SilcCommand reply_cmd)
 +void silc_server_command_pending_del(SilcServer server,
 +                                   SilcCommand reply_cmd,
 +                                   uint16 ident)
  {
 -  SilcServerCommandPending *r, *tmp;
 -  
 -  if (silc_command_pending) {
 -    if (silc_command_pending->reply_cmd == reply_cmd) {
 -      silc_free(silc_command_pending);
 -      silc_command_pending = NULL;
 -      return;
 -    }
 +  SilcServerCommandPending *r;
  
 -    for (r = silc_command_pending; r; r = r->next) {
 -      if (r->next && r->next->reply_cmd == reply_cmd) {
 -      tmp = r->next;
 -      r->next = r->next->next;
 -      silc_free(tmp);
 -      break;
 -      }
 +  silc_dlist_start(server->pending_commands);
 +  while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
 +    if (r->reply_cmd == reply_cmd && r->ident == ident) {
 +      silc_dlist_del(server->pending_commands, r);
 +      break;
      }
    }
  }
  
 -/* Free's the command context allocated before executing the command */
 +/* Checks for pending commands and marks callbacks to be called from
 +   the command reply function. Returns TRUE if there were pending command. */
  
 -static void silc_server_command_free(SilcServerCommandContext cmd)
 +SilcServerCommandPendingCallbacks
 +silc_server_command_pending_check(SilcServer server,
 +                                SilcServerCommandReplyContext ctx,
 +                                SilcCommand command, 
 +                                uint16 ident,
 +                                uint32 *callbacks_count)
  {
 -  if (cmd) {
 -    silc_command_free_payload(cmd->payload);
 -    silc_free(cmd);
 +  SilcServerCommandPending *r;
 +  SilcServerCommandPendingCallbacks callbacks = NULL;
 +  int i = 0;
 +
 +  silc_dlist_start(server->pending_commands);
 +  while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
 +    if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
 +      && r->ident == ident) {
 +      callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
 +      callbacks[i].context = r->context;
 +      callbacks[i].callback = r->callback;
 +      ctx->ident = ident;
 +      i++;
 +    }
    }
 +
 +  *callbacks_count = i;
 +  return callbacks;
  }
  
 -/* Sends command status message as command reply packet. */
 +/* Sends simple status message as command reply packet */
  
  static void 
 -silc_server_command_send_status_msg(SilcServerCommandContext cmd,
 -                                  SilcCommand command,
 -                                  SilcCommandStatus status,
 -                                  unsigned char *msg,
 -                                  unsigned int msg_len)
 +silc_server_command_send_status_reply(SilcServerCommandContext cmd,
 +                                    SilcCommand command,
 +                                    SilcCommandStatus status)
  {
 -  SilcBuffer sp_buf, buffer;
 +  SilcBuffer buffer;
  
    SILC_LOG_DEBUG(("Sending command status %d", status));
  
 -  sp_buf = silc_command_encode_status_payload(status, msg, msg_len);
 -  buffer = silc_command_encode_payload_va(command, 1, 
 -                                        sp_buf->data, sp_buf->len);
 +  buffer = 
 +    silc_command_reply_payload_encode_va(command, status, 
 +                                       silc_command_get_ident(cmd->payload),
 +                                       0);
    silc_server_packet_send(cmd->server, cmd->sock,
                          SILC_PACKET_COMMAND_REPLY, 0, 
                          buffer->data, buffer->len, FALSE);
    silc_buffer_free(buffer);
 -  silc_buffer_free(sp_buf);
  }
  
 -/* Sends simple status message as command reply packet */
 +/* Sends command status reply with one extra argument. The argument
 +   type must be sent as argument. */
  
  static void 
 -silc_server_command_send_status_reply(SilcServerCommandContext cmd,
 -                                    SilcCommand command,
 -                                    SilcCommandStatus status)
 +silc_server_command_send_status_data(SilcServerCommandContext cmd,
 +                                   SilcCommand command,
 +                                   SilcCommandStatus status,
 +                                   uint32 arg_type,
 +                                   const unsigned char *arg,
 +                                   uint32 arg_len)
  {
 -  SilcBuffer sp_buf, buffer;
 +  SilcBuffer buffer;
  
    SILC_LOG_DEBUG(("Sending command status %d", status));
  
 -  sp_buf = silc_command_encode_status_payload(status, NULL, 0);
 -  buffer = silc_command_encode_payload_va(command, 1, 
 -                                        sp_buf->data, sp_buf->len);
 +  buffer = 
 +    silc_command_reply_payload_encode_va(command, status, 
 +                                       silc_command_get_ident(cmd->payload),
 +                                       1, arg_type, arg, arg_len);
    silc_server_packet_send(cmd->server, cmd->sock,
                          SILC_PACKET_COMMAND_REPLY, 0, 
                          buffer->data, buffer->len, FALSE);
    silc_buffer_free(buffer);
 -  silc_buffer_free(sp_buf);
  }
  
 -/* Server side of command WHOIS. Processes user's query and sends found 
 -   results as command replies back to the client. */
 +/* This function can be called to check whether in the command reply
 +   an error occurred. This function has no effect if this is called
 +   when the command function was not called as pending command callback. 
 +   This returns TRUE if error had occurred. */
  
 -SILC_SERVER_CMD_FUNC(whois)
 +static bool
 +silc_server_command_pending_error_check(SilcServerCommandContext cmd,
 +                                      SilcServerCommandReplyContext cmdr,
 +                                      SilcCommand command)
  {
 -  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 -  char *tmp, *nick = NULL, *server = NULL;
 -  unsigned int argc, count = 0, len;
 -  SilcClientList *entry;
 -  SilcBuffer sp_buf, packet;
 -  unsigned char *id_string;
 +  SilcCommandStatus status;
 +
 +  if (!cmd->pending || !cmdr)
 +    return FALSE;
 +
 +  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmdr->args, 1, NULL));
 +  if (status != SILC_STATUS_OK &&
 +      status != SILC_STATUS_LIST_START &&
 +      status != SILC_STATUS_LIST_ITEM &&
 +      status != SILC_STATUS_LIST_END) {
 +    SilcBuffer buffer;
 +
 +    /* Send the same command reply payload */
 +    silc_command_set_ident(cmdr->payload, 
 +                         silc_command_get_ident(cmd->payload));
 +    buffer = silc_command_payload_encode_payload(cmdr->payload);
 +    silc_server_packet_send(cmd->server, cmd->sock,
 +                          SILC_PACKET_COMMAND_REPLY, 0, 
 +                          buffer->data, buffer->len, FALSE);
 +    silc_buffer_free(buffer);
 +    return TRUE;
 +  }
  
 -  SILC_LOG_DEBUG(("Start"));
 +  return FALSE;
 +}
  
 -  argc = silc_command_get_arg_num(cmd->payload);
 -  if (argc < 1) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
 -                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 -    goto out;
 -  }
 -  if (argc > 2) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
 -                                        SILC_STATUS_ERR_TOO_MANY_PARAMS);
 -    goto out;
 -  }
 +/******************************************************************************
  
 -  /* Get the nickname@server string and parse it. */
 -  tmp = silc_command_get_first_arg(cmd->payload, NULL);
 -  if (tmp) {
 -    if (strchr(tmp, '@')) {
 -      len = strcspn(tmp, "@");
 -      nick = silc_calloc(len + 1, sizeof(char));
 -      memcpy(nick, tmp, len);
 -      server = silc_calloc(strlen(tmp) - len, sizeof(char));
 -      memcpy(server, tmp + len + 1, strlen(tmp) - len - 1);
 +                              WHOIS Functions
 +
 +******************************************************************************/
 +
 +static int
 +silc_server_command_whois_parse(SilcServerCommandContext cmd,
 +                              SilcClientID ***client_id,
 +                              uint32 *client_id_count,
 +                              char **nickname,
 +                              char **server_name,
 +                              int *count,
 +                              SilcCommand command)
 +{
 +  unsigned char *tmp;
 +  uint32 len;
 +  uint32 argc = silc_argument_get_arg_num(cmd->args);
 +  int i, k;
 +
 +  /* If client ID is in the command it must be used instead of nickname */
 +  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
 +  if (!tmp) {
 +    /* No ID, get the nickname@server string and parse it. */
 +    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +    if (tmp) {
 +      silc_parse_userfqdn(tmp, nickname, server_name);
      } else {
 -      nick = strdup(tmp);
 +      silc_server_command_send_status_reply(cmd, command,
 +                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      return FALSE;
      }
    } else {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
 -                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 -    goto out;
 +    /* Command includes ID, we must use that.  Also check whether the command
 +       has more than one ID set - take them all. */
 +
 +    *client_id = silc_calloc(1, sizeof(**client_id));
 +    (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
 +    if ((*client_id)[0] == NULL) {
 +      silc_free(*client_id);
 +      silc_server_command_send_status_reply(cmd, command,
 +                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      return FALSE;
 +    }
 +    *client_id_count = 1;
 +
 +    /* Take all ID's from the command packet */
 +    if (argc > 1) {
 +      for (k = 1, i = 1; i < argc; i++) {
 +      tmp = silc_argument_get_arg_type(cmd->args, i + 3, &len);
 +      if (tmp) {
 +        *client_id = silc_realloc(*client_id, sizeof(**client_id) *
 +                                  (*client_id_count + 1));
 +        (*client_id)[k] = silc_id_payload_parse_id(tmp, len);
 +        if ((*client_id)[k] == NULL) {
 +          /* Cleanup all and fail */
 +          for (i = 0; i < *client_id_count; i++)
 +            silc_free((*client_id)[i]);
 +          silc_free(*client_id);
 +          silc_server_command_send_status_reply(
 +                                       cmd, command,
 +                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +          return FALSE;
 +        }
 +        (*client_id_count)++;
 +        k++;
 +      }
 +      }
 +    }
    }
  
    /* Get the max count of reply messages allowed */
 -  if (argc == 2) {
 -    tmp = silc_command_get_next_arg(cmd->payload, NULL);
 -    if (!tmp) {
 -      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
 -                                          SILC_STATUS_ERR_TOO_MANY_PARAMS);
 -      if (nick)
 -      silc_free(nick);
 -      if (server)
 -      silc_free(server);
 -      goto out;
 -    }
 -    count = atoi(tmp);
 -  }
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +  if (tmp)
 +    *count = atoi(tmp);
 +  else
 +    *count = 0;
  
 -  /* Then, make the query from our local client list */
 -  entry = silc_idlist_find_client_by_nickname(cmd->server->local_list->clients,
 -                                            nick, server);
 -  if (!entry) {
 +  return TRUE;
 +}
 +
 +/* Resolve context used by both WHOIS and IDENTIFY commands */
 +typedef struct {
 +  SilcServerEntry router;
 +  uint16 ident;
 +  unsigned char **res_argv;
 +  uint32 *res_argv_lens;
 +  uint32 *res_argv_types;
 +  uint32 res_argc;
 +} *SilcServerResolveContext;
 +
 +static bool
 +silc_server_command_whois_check(SilcServerCommandContext cmd,
 +                              SilcClientEntry *clients,
 +                              uint32 clients_count)
 +{
 +  SilcServer server = cmd->server;
 +  SilcClientEntry entry;
 +  SilcServerResolveContext resolve = NULL, r = NULL;
 +  uint32 resolve_count = 0;
 +  int i, k;
 +  bool no_res = TRUE;
  
 -    /* If we are normal server and are connected to a router we will
 -       make global query from the router. */
 -    if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) {
 +  SILC_LOG_DEBUG(("Start"));
  
 -      goto ok;
 +  for (i = 0; i < clients_count; i++) {
 +    entry = clients[i];
 +    if (!entry)
 +      continue;
 +
 +    if ((entry->nickname && entry->username && entry->userinfo) ||
 +      !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
 +      if (!entry->router)
 +      continue;
 +
 +      /* If we are normal server, and we've not resolved this client from
 +       router and it is global client, we'll check whether it is on some
 +       channel.  If not then we cannot be sure about its validity, and
 +       we'll resolve it from router. */
 +      if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
 +        entry->connection || silc_hash_table_count(entry->channels))
 +      continue;
      }
 -    
 -    /* If we are router then we will check our global list as well. */
 -    if (cmd->server->server_type == SILC_ROUTER) {
 -      entry =
 -      silc_idlist_find_client_by_nickname(cmd->server->global_list->clients,
 -                                          nick, server);
 -      if (!entry) {
 -      silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS,
 -                                          SILC_STATUS_ERR_NO_SUCH_NICK,
 -                                          tmp, strlen(tmp));
 -      goto out;
 +
 +    /* We need to resolve this entry since it is not complete */
 +
 +    if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
 +      /* The entry is being resolved (and we are not the resolver) so attach
 +       to the command reply and we're done with this one. */
 +      silc_server_command_pending(server, SILC_COMMAND_NONE, 
 +                                entry->resolve_cmd_ident,
 +                                silc_server_command_whois,
 +                                silc_server_command_dup(cmd));
 +      no_res = FALSE;
 +    } else {
 +      if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
 +      /* We've resolved this and it still is not ready.  We'll return
 +         and are that this will be handled again after it is resolved. */
 +      for (i = 0; i < resolve_count; i++) {
 +        for (k = 0; k < r->res_argc; k++)
 +          silc_free(r->res_argv[k]);
 +        silc_free(r->res_argv);
 +        silc_free(r->res_argv_lens);
 +        silc_free(r->res_argv_types);
 +      }
 +      silc_free(resolve);
 +      return FALSE;
 +      } else {
 +      /* We'll resolve this client */
 +      SilcBuffer idp;
 +
 +      r = NULL;
 +      for (k = 0; k < resolve_count; k++) {
 +        if (resolve[k].router == entry->router) {
 +          r = &resolve[k];
 +          break;
 +        }
 +      }
 +
 +      if (!r) {
 +        resolve = silc_realloc(resolve, sizeof(*resolve) * 
 +                               (resolve_count + 1));
 +        r = &resolve[resolve_count];
 +        memset(r, 0, sizeof(*r));
 +        r->router = entry->router;
 +        r->ident = ++server->cmd_ident;
 +        resolve_count++;
 +      }
 +
 +      r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
 +                                 (r->res_argc + 1));
 +      r->res_argv_lens = silc_realloc(r->res_argv_lens, 
 +                                      sizeof(*r->res_argv_lens) *
 +                                      (r->res_argc + 1));
 +      r->res_argv_types = silc_realloc(r->res_argv_types, 
 +                                       sizeof(*r->res_argv_types) *
 +                                       (r->res_argc + 1));
 +      idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
 +      r->res_argv[r->res_argc] = silc_calloc(idp->len, 
 +                                             sizeof(**r->res_argv));
 +      memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
 +      r->res_argv_lens[r->res_argc] = idp->len;
 +      r->res_argv_types[r->res_argc] = r->res_argc + 3;
 +      r->res_argc++;
 +      silc_buffer_free(idp);
 +
 +      entry->resolve_cmd_ident = r->ident;
 +      entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
 +      entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
        }
 -      goto ok;
      }
 +  }
  
 -    silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS,
 -                                      SILC_STATUS_ERR_NO_SUCH_NICK,
 -                                      tmp, strlen(tmp));
 -    goto out;
 +  /* Do the resolving */
 +  for (i = 0; i < resolve_count; i++) {
 +    SilcBuffer res_cmd;
 +
 +    r = &resolve[i];
 +
 +    /* Send WHOIS request. We send WHOIS since we're doing the requesting
 +       now anyway so make it a good one. */
 +    res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
 +                                        r->res_argc, r->res_argv, 
 +                                        r->res_argv_lens,
 +                                        r->res_argv_types, 
 +                                        r->ident);
 +    silc_server_packet_send(server, r->router->connection,
 +                          SILC_PACKET_COMMAND, cmd->packet->flags,
 +                          res_cmd->data, res_cmd->len, FALSE);
 +
 +    /* Reprocess this packet after received reply */
 +    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
 +                              r->ident,
 +                              silc_server_command_whois,
 +                              silc_server_command_dup(cmd));
 +    cmd->pending = TRUE;
 +
 +    silc_buffer_free(res_cmd);
 +    for (k = 0; k < r->res_argc; k++)
 +      silc_free(r->res_argv[k]);
 +    silc_free(r->res_argv);
 +    silc_free(r->res_argv_lens);
 +    silc_free(r->res_argv_types);
 +    no_res = FALSE;
    }
 +  silc_free(resolve);
  
 - ok:
 -  /* XXX, works only for local server info */
 +  return no_res;
 +}
  
 -  /* Send WHOIS reply */
 -  id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT);
 -  tmp = silc_command_get_first_arg(cmd->payload, NULL),
 -  sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
 +static void
 +silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
 +                                   SilcClientEntry *clients,
 +                                   uint32 clients_count,
 +                                   int count,
 +                                   const char *nickname,
 +                                   SilcClientID **client_ids)
 +{
 +  SilcServer server = cmd->server;
 +  char *tmp;
 +  int i, k, len, valid_count;
 +  SilcBuffer packet, idp, channels;
 +  SilcClientEntry entry;
 +  SilcCommandStatus status;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  char nh[256], uh[256];
 +  unsigned char idle[4], mode[4];
 +  unsigned char *fingerprint;
 +  SilcSocketConnection hsock;
 +
 +  /* Process only valid clients and ignore those that are not registered. */
 +  valid_count = 0;
 +  for (i = 0; i < clients_count; i++) {
 +    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
 +      valid_count++;
 +    else
 +      clients[i] = NULL;
 +  }
  
 -  /* XXX */
 -  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
 -    char nh[256], uh[256];
 -    SilcSocketConnection hsock;
 +  if (!valid_count) {
 +    /* No valid clients found, send error reply */
 +    if (nickname) {
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
 +                                         SILC_STATUS_ERR_NO_SUCH_NICK,
 +                                         3, nickname, strlen(nickname));
 +    } else if (client_ids && client_ids[0]) {
 +      SilcBuffer idp = silc_id_payload_encode(client_ids[0], SILC_ID_CLIENT);
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
 +                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
 +                                         2, idp->data, idp->len);
 +      silc_buffer_free(idp);
 +    }
 +    return;
 +  }
  
 +  /* Start processing found clients. */
 +  if (valid_count > 1)
 +    status = SILC_STATUS_LIST_START;
 +  else
 +    status = SILC_STATUS_OK;
 +
 +  for (i = 0, k = 0; i < clients_count; i++) {
 +    entry = clients[i];
 +    if (!entry)
 +      continue;
 +
 +    if (k >= 1)
 +      status = SILC_STATUS_LIST_ITEM;
 +    if (valid_count > 1 && k == valid_count - 1)
 +      status = SILC_STATUS_LIST_END;
 +    if (count && k - 1 == count)
 +      status = SILC_STATUS_LIST_END;
 +
 +    /* Send WHOIS reply */
 +    idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
 +    tmp = silc_argument_get_first_arg(cmd->args, NULL);
 +    
      memset(uh, 0, sizeof(uh));
      memset(nh, 0, sizeof(nh));
 -
 +    memset(idle, 0, sizeof(idle));
 +    
      strncat(nh, entry->nickname, strlen(entry->nickname));
 -    strncat(nh, "@", 1);
 -    len = entry->router ? strlen(entry->router->server_name) :
 -      strlen(cmd->server->server_name);
 -    strncat(nh, entry->router ? entry->router->server_name :
 -          cmd->server->server_name, len);
 -
 +    if (!strchr(entry->nickname, '@')) {
 +      strncat(nh, "@", 1);
 +      if (entry->servername) {
 +      strncat(nh, entry->servername, strlen(entry->servername));
 +      } else {
 +      len = entry->router ? strlen(entry->router->server_name) :
 +        strlen(server->server_name);
 +      strncat(nh, entry->router ? entry->router->server_name :
 +              server->server_name, len);
 +      }
 +    }
 +      
      strncat(uh, entry->username, strlen(entry->username));
 -    strncat(uh, "@", 1);
 -    hsock = (SilcSocketConnection)entry->connection;
 -    len = hsock->hostname ? strlen(hsock->hostname) : strlen(hsock->ip);
 -    strncat(uh, hsock->hostname ? hsock->hostname : hsock->ip, len);
 +    if (!strchr(entry->username, '@')) {
 +      strncat(uh, "@", 1);
 +      hsock = (SilcSocketConnection)entry->connection;
 +      len = strlen(hsock->hostname);
 +      strncat(uh, hsock->hostname, len);
 +    }
  
 -    /* XXX */
 -    if (entry->userinfo)
 -      packet = 
 -        silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 5, 
 -                                     sp_buf->data, sp_buf->len,
 -                                     id_string, SILC_ID_CLIENT_LEN,
 -                                     nh, strlen(nh),
 -                                     uh, strlen(uh),
 -                                     entry->userinfo, 
 -                                     strlen(entry->userinfo));
 +    channels = silc_server_get_client_channel_list(server, entry);
 +
 +    if (entry->data.fingerprint[0] != 0 && entry->data.fingerprint[1] != 0)
 +      fingerprint = entry->data.fingerprint;
      else
 -      packet = 
 -        silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 4,
 -                                     sp_buf->data, sp_buf->len,
 -                                     id_string, SILC_ID_CLIENT_LEN,
 -                                     nh, strlen(nh),
 -                                     uh, strlen(uh));
 +      fingerprint = NULL;
 +      
 +    SILC_PUT32_MSB(entry->mode, mode);
  
 -  } else {
 -    /* XXX */
 -    packet = 
 -      silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 4, 
 -                                   sp_buf->data, sp_buf->len,
 -                                   id_string, SILC_ID_CLIENT_LEN,
 -                                   entry->nickname, strlen(entry->nickname),
 -                                   tmp, strlen(tmp)); /* XXX */
 -  }
 -  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 -                        0, packet->data, packet->len, FALSE);
 +    if (entry->connection) {
 +      SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
 +    }
  
 -  silc_free(id_string);
 -  silc_buffer_free(packet);
 -  silc_free(sp_buf);
 +    packet = 
 +      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
 +                                         status, ident, 8, 
 +                                         2, idp->data, idp->len,
 +                                         3, nh, strlen(nh),
 +                                         4, uh, strlen(uh),
 +                                         5, entry->userinfo, 
 +                                         strlen(entry->userinfo),
 +                                         6, channels ? channels->data : NULL,
 +                                         channels ? channels->len : 0,
 +                                         7, mode, 4,
 +                                         8, idle, 4,
 +                                         9, fingerprint,
 +                                         fingerprint ? 20 : 0);
 +
 +    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 +                          0, packet->data, packet->len, FALSE);
 +    
 +    silc_buffer_free(packet);
 +    silc_buffer_free(idp);
 +    if (channels)
 +      silc_buffer_free(channels);
  
 - out:
 -  silc_server_command_free(cmd);
 +    k++;
 +  }
  }
  
 -SILC_SERVER_CMD_FUNC(whowas)
 +static void 
 +silc_server_command_whois_send_router(SilcServerCommandContext cmd)
  {
 +  SilcServer server = cmd->server;
 +  SilcBuffer tmpbuf;
 +  uint16 old_ident;
 +
 +  old_ident = silc_command_get_ident(cmd->payload);
 +  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +
 +  /* Send WHOIS command to our router */
 +  silc_server_packet_send(server, (SilcSocketConnection)
 +                        server->router->connection,
 +                        SILC_PACKET_COMMAND, cmd->packet->flags,
 +                        tmpbuf->data, tmpbuf->len, TRUE);
 +
 +  /* Reprocess this packet after received reply from router */
 +  silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
 +                            silc_command_get_ident(cmd->payload),
 +                            silc_server_command_whois,
 +                            silc_server_command_dup(cmd));
 +  cmd->pending = TRUE;
 +  silc_command_set_ident(cmd->payload, old_ident);
 +  silc_buffer_free(tmpbuf);
  }
  
 -SILC_SERVER_CMD_FUNC(identify)
 +static int
 +silc_server_command_whois_process(SilcServerCommandContext cmd)
  {
 -  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 -  char *tmp, *nick = NULL, *server = NULL;
 -  unsigned int argc, count = 0, len;
 -  SilcClientList *entry;
 -  SilcBuffer sp_buf, packet;
 -  unsigned char *id_string;
 -
 -  SILC_LOG_DEBUG(("Start"));
 -
 -  argc = silc_command_get_arg_num(cmd->payload);
 -  if (argc < 1) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
 -                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 -    goto out;
 -  }
 -  if (argc > 2) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
 -                                        SILC_STATUS_ERR_TOO_MANY_PARAMS);
 +  SilcServer server = cmd->server;
 +  char *nick = NULL, *server_name = NULL;
 +  int count = 0;
 +  SilcClientEntry *clients = NULL, entry;
 +  SilcClientID **client_id = NULL;
 +  uint32 client_id_count = 0, clients_count = 0;
 +  int i, ret = 0;
 +  bool check_global = FALSE;
 +
 +  /* Parse the whois request */
 +  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
 +                                     &nick, &server_name, &count,
 +                                     SILC_COMMAND_WHOIS))
 +    return 0;
 +
 +  /* Send the WHOIS request to the router only if it included nickname.
 +     Since nicknames can be expanded into many clients we need to send it
 +     to router.  If the WHOIS included only client ID's we will check them
 +     first locally since we just might have them. */
 +  if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
 +      server->server_type == SILC_SERVER && !cmd->pending &&
 +      !server->standalone) {
 +    silc_server_command_whois_send_router(cmd);
 +    ret = -1;
      goto out;
    }
  
 -  /* Get the nickname@server string and parse it. */
 -  tmp = silc_command_get_first_arg(cmd->payload, NULL);
 -  if (tmp) {
 -    if (strchr(tmp, '@')) {
 -      len = strcspn(tmp, "@");
 -      nick = silc_calloc(len + 1, sizeof(char));
 -      memcpy(nick, tmp, len);
 -      server = silc_calloc(strlen(tmp) - len, sizeof(char));
 -      memcpy(server, tmp + len + 1, strlen(tmp) - len - 1);
 -    } else {
 -      nick = strdup(tmp);
 +  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
 +    check_global = TRUE;
 +  else if (server->server_type != SILC_SERVER)
 +    check_global = TRUE;
 +
 +  /* Get all clients matching that ID or nickname from local list */
 +  if (client_id_count) {
 +    /* Check all Client ID's received in the command packet */
 +    for (i = 0; i < client_id_count; i++) {
 +      entry = silc_idlist_find_client_by_id(server->local_list, 
 +                                          client_id[i], TRUE, NULL);
 +      if (!entry && check_global)
 +      entry = silc_idlist_find_client_by_id(server->global_list, 
 +                                            client_id[i], TRUE, NULL);
 +      if (entry) {
 +      clients = silc_realloc(clients, sizeof(*clients) * 
 +                             (clients_count + 1));
 +      clients[clients_count++] = entry;
 +      } else {
 +      /* If we are normal server and did not send the request first to router
 +         do it now, since we do not have the Client ID information. */
 +      if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
 +          server->server_type == SILC_SERVER && !cmd->pending && 
 +          !server->standalone) {
 +        silc_server_command_whois_send_router(cmd);
 +        ret = -1;
 +        goto out;
 +      }
 +      }
      }
    } else {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
 -                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 -    goto out;
 -  }
 -
 -  /* Get the max count of reply messages allowed */
 -  if (argc == 2) {
 -    tmp = silc_command_get_next_arg(cmd->payload, NULL);
 -    if (!tmp) {
 -      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
 -                                          SILC_STATUS_ERR_TOO_MANY_PARAMS);
 -      goto out;
 +    /* Find by nickname */
 +    if (!silc_idlist_get_clients_by_hash(server->local_list, 
 +                                       nick, server->md5hash,
 +                                       &clients, &clients_count))
 +      silc_idlist_get_clients_by_nickname(server->local_list, 
 +                                        nick, server_name,
 +                                        &clients, &clients_count);
 +    if (check_global) {
 +      if (!silc_idlist_get_clients_by_hash(server->global_list, 
 +                                         nick, server->md5hash,
 +                                         &clients, &clients_count))
 +      silc_idlist_get_clients_by_nickname(server->global_list, 
 +                                          nick, server_name,
 +                                          &clients, &clients_count);
      }
 -    count = atoi(tmp);
    }
 -
 -  /* Then, make the query from our local client list */
 -  entry = silc_idlist_find_client_by_hash(cmd->server->local_list->clients,
 -                                        nick, cmd->server->md5hash);
 -  if (!entry) {
 -
 -    /* If we are normal server and are connected to a router we will
 -       make global query from the router. */
 -    if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) {
 -      SilcBuffer buffer = cmd->packet->buffer;
 -
 -      /* Forward the received IDENTIFY command to our router */
 -      silc_buffer_push(buffer, buffer->data - buffer->head);
 -      silc_server_packet_forward(cmd->server, (SilcSocketConnection)
 -                               cmd->server->id_entry->router->connection,
 -                               buffer->data, buffer->len,
 -                               TRUE);
 +  
 +  if (!clients) {
 +    /* If we are normal server and did not send the request first to router
 +       do it now, since we do not have the information. */
 +    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
 +      server->server_type == SILC_SERVER && !cmd->pending && 
 +      !server->standalone) {
 +      silc_server_command_whois_send_router(cmd);
 +      ret = -1;
        goto out;
      }
 -    
 -    /* If we are router then we will check our global list as well. */
 -    if (cmd->server->server_type == SILC_ROUTER) {
 -      entry = 
 -      silc_idlist_find_client_by_hash(cmd->server->global_list->clients,
 -                                      nick, cmd->server->md5hash);
 -      if (!entry) {
 -      silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY,
 -                                          SILC_STATUS_ERR_NO_SUCH_NICK,
 -                                          tmp, strlen(tmp));
 -      goto out;
 -      }
 -      goto ok;
 -    }
  
 -    silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY,
 -                                      SILC_STATUS_ERR_NO_SUCH_NICK,
 -                                      tmp, strlen(tmp));
 +    /* Such client(s) really does not exist in the SILC network. */
 +    if (!client_id_count) {
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
 +                                         SILC_STATUS_ERR_NO_SUCH_NICK,
 +                                         3, nick, strlen(nick));
 +    } else {
 +      SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
 +                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
 +                                         2, idp->data, idp->len);
 +      silc_buffer_free(idp);
 +    }
      goto out;
    }
  
 - ok:
 -  /* Send IDENTIFY reply */
 -  id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT);
 -  tmp = silc_command_get_first_arg(cmd->payload, NULL);
 -  sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
 -  packet = silc_command_encode_payload_va(SILC_COMMAND_IDENTIFY, 3,
 -                                        sp_buf->data, sp_buf->len,
 -                                        id_string, SILC_ID_CLIENT_LEN,
 -                                        nick, strlen(nick));
 -  if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
 -    void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
 -    silc_server_packet_send_dest(cmd->server, cmd->sock, 
 -                               SILC_PACKET_COMMAND_REPLY, 0,
 -                               id, cmd->packet->src_id_type,
 -                               packet->data, packet->len, FALSE);
 -    silc_free(id);
 -  } else
 -    silc_server_packet_send(cmd->server, cmd->sock, 
 -                          SILC_PACKET_COMMAND_REPLY, 0, 
 -                          packet->data, packet->len, FALSE);
 +  /* Router always finds the client entry if it exists in the SILC network.
 +     However, it might be incomplete entry and does not include all the
 +     mandatory fields that WHOIS command reply requires. Check for these and
 +     make query from the server who owns the client if some fields are 
 +     missing. */
 +  if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
 +    ret = -1;
 +    goto out;
 +  }
  
 -  silc_free(id_string);
 -  silc_buffer_free(packet);
 -  silc_free(sp_buf);
 +  /* Send the command reply */
 +  silc_server_command_whois_send_reply(cmd, clients, clients_count,
 +                                     count, nick, client_id);
  
   out:
 -  if (nick)
 -    silc_free(nick);
 -  if (server)
 -    silc_free(server);
 -  silc_server_command_free(cmd);
 +  if (client_id_count) {
 +    for (i = 0; i < client_id_count; i++)
 +      silc_free(client_id[i]);
 +    silc_free(client_id);
 +  }
 +  silc_free(clients);
 +  silc_free(nick);
 +  silc_free(server_name);
 +
 +  return ret;
  }
  
 -/* Checks string for bad characters and returns TRUE if they are found. */
 +/* Server side of command WHOIS. Processes user's query and sends found 
 +   results as command replies back to the client. */
  
 -static int silc_server_command_bad_chars(char *nick)
 +SILC_SERVER_CMD_FUNC(whois)
  {
 -  if (strchr(nick, '\\')) return TRUE;
 -  if (strchr(nick, '\"')) return TRUE;
 -  if (strchr(nick, '´')) return TRUE;
 -  if (strchr(nick, '`')) return TRUE;
 -  if (strchr(nick, '\'')) return TRUE;
 -  if (strchr(nick, '*')) return TRUE;
 -  if (strchr(nick, '/')) return TRUE;
 -  if (strchr(nick, '@')) return TRUE;
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  int ret = 0;
  
 -  return FALSE;
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 3328);
 +
 +  ret = silc_server_command_whois_process(cmd);
 +  silc_server_command_free(cmd);
  }
  
 -/* Server side of command NICK. Sets nickname for user. Setting
 -   nickname causes generation of a new client ID for the client. The
 -   new client ID is sent to the client after changing the nickname. */
 +/******************************************************************************
  
 -SILC_SERVER_CMD_FUNC(nick)
 -{
 -  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 -  SilcClientList *id_entry = (SilcClientList *)cmd->sock->user_data;
 -  SilcServer server = cmd->server;
 -  SilcBuffer packet, sp_buf;
 -  SilcClientID *new_id;
 -  char *id_string;
 -  char *nick;
 +                              WHOWAS Functions
  
 -  SILC_LOG_DEBUG(("Start"));
 +******************************************************************************/
  
 -#define LCC(x) server->local_list->client_cache[(x) - 32]
 -#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
 +static int
 +silc_server_command_whowas_parse(SilcServerCommandContext cmd,
 +                               char **nickname,
 +                               char **server_name,
 +                               int *count)
 +{
 +  unsigned char *tmp;
 +  uint32 len;
  
 -  /* Check number of arguments */
 -  if (silc_command_get_arg_num(cmd->payload) < 1) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS,
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 -    goto out;
 -  }
 -
 -  /* Check nickname */
 -  nick = silc_command_get_arg_type(cmd->payload, 1, NULL);
 -  if (silc_server_command_bad_chars(nick) == TRUE) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
 -                                        SILC_STATUS_ERR_BAD_NICKNAME);
 -    goto out;
 +    return FALSE;
    }
  
 -  /* Create new Client ID */
 -  silc_id_create_client_id(cmd->server->id, cmd->server->rng, 
 -                         cmd->server->md5hash, nick,
 -                         &new_id);
 +  /* Get the nickname@server string and parse it. */
 +  silc_parse_userfqdn(tmp, nickname, server_name);
  
 -  /* Send notify about nickname change to our router. We send the new
 -     ID and ask to replace it with the old one. */
 -  if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone)
 -    silc_server_send_replace_id(server, server->id_entry->router->connection, 
 -                              FALSE, id_entry->id,
 -                              SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
 -                              new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
 -
 -  /* If we are router we have to distribute the new Client ID to all 
 -     routers in SILC. */
 -  if (cmd->server->server_type == SILC_ROUTER && !cmd->server->standalone)
 -    silc_server_send_replace_id(server, server->id_entry->router->connection,  
 -                              TRUE, id_entry->id,
 -                              SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
 -                              new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
 +  /* Get the max count of reply messages allowed */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +  if (tmp)
 +    *count = atoi(tmp);
 +  else
 +    *count = 0;
  
 -  /* Remove old cache entry */
 -  silc_idcache_del_by_id(LCC(id_entry->nickname[0]),
 -                       LCCC(id_entry->nickname[0]), 
 -                       SILC_ID_CLIENT, id_entry->id); 
 -  
 -  /* Free old ID */
 -  if (id_entry->id) {
 -    memset(id_entry->id, 0, SILC_ID_CLIENT_LEN);
 -    silc_free(id_entry->id);
 -  }
 +  return TRUE;
 +}
  
 -  /* Save the nickname as this client is our local client */
 -  if (id_entry->nickname)
 -    silc_free(id_entry->nickname);
 +static char
 +silc_server_command_whowas_check(SilcServerCommandContext cmd,
 +                               SilcClientEntry *clients,
 +                               uint32 clients_count)
 +{
 +  SilcServer server = cmd->server;
 +  int i;
 +  SilcClientEntry entry;
  
 -  id_entry->nickname = strdup(nick);
 -  id_entry->id = new_id;
 +  for (i = 0; i < clients_count; i++) {
 +    entry = clients[i];
  
 -  /* Update client cache */
 -  LCCC(nick[0]) = silc_idcache_add(&LCC(nick[0]), LCCC(nick[0]),
 -                                 id_entry->nickname, SILC_ID_CLIENT, 
 -                                 id_entry->id, (void *)id_entry);
 +    if (!entry->nickname || !entry->username) {
 +      SilcBuffer tmpbuf;
 +      uint16 old_ident;
  
 -  /* Send the new Client ID as reply command back to client */
 -  id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT);
 -  sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
 -  packet = silc_command_encode_payload_va(SILC_COMMAND_NICK, 2, 
 -                                        sp_buf->data, sp_buf->len,
 -                                        id_string, SILC_ID_CLIENT_LEN);
 -  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 -                        0, packet->data, packet->len, FALSE);
 +      if (!entry->router)
 +      continue;
 +      
 +      old_ident = silc_command_get_ident(cmd->payload);
 +      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +
 +      /* Send WHOWAS command */
 +      silc_server_packet_send(server, entry->router->connection,
 +                            SILC_PACKET_COMMAND, cmd->packet->flags,
 +                            tmpbuf->data, tmpbuf->len, TRUE);
 +      
 +      /* Reprocess this packet after received reply */
 +      silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
 +                                silc_command_get_ident(cmd->payload),
 +                                silc_server_command_whowas, 
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_command_set_ident(cmd->payload, old_ident);
  
 -  silc_free(id_string);
 -  silc_buffer_free(packet);
 -  silc_free(sp_buf);
 +      silc_buffer_free(tmpbuf);
 +      return FALSE;
 +    }
 +  }
  
 - out:
 -  silc_server_command_free(cmd);
 -#undef LCC
 -#undef LCCC
 +  return TRUE;
  }
  
 +static void
 +silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
 +                                    SilcClientEntry *clients,
 +                                    uint32 clients_count)
 +{
 +  SilcServer server = cmd->server;
 +  char *tmp;
 +  int i, k, count = 0, len;
 +  SilcBuffer packet, idp;
 +  SilcClientEntry entry = NULL;
 +  SilcCommandStatus status;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  char nh[256], uh[256];
 +  int valid_count;
 +
 +  status = SILC_STATUS_OK;
 +
 +  /* Process only entries that are not registered anymore. */
 +  valid_count = 0;
 +  for (i = 0; i < clients_count; i++) {
 +    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
 +      clients[i] = NULL;
 +    else
 +      valid_count++;
 +  }
 +
 +  if (!valid_count) {
 +    /* No valid entries found at all, just send error */
 +    unsigned char *tmp;
 +    
 +    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +    if (tmp)
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
 +                                         SILC_STATUS_ERR_NO_SUCH_NICK,
 +                                         3, tmp, strlen(tmp));
 +    return;
 +  }
 +
 +  if (valid_count > 1)
 +    status = SILC_STATUS_LIST_START;
 +
 +  for (i = 0, k = 0; i < clients_count; i++) {
 +    entry = clients[i];
 +    if (!entry)
 +      continue;
 +
 +    if (k >= 1)
 +      status = SILC_STATUS_LIST_ITEM;
 +    if (valid_count > 1 && k == valid_count - 1)
 +      status = SILC_STATUS_LIST_END;
 +    if (count && k - 1 == count)
 +      status = SILC_STATUS_LIST_END;
 +    if (count && k - 1 > count)
 +      break;
 +
 +    /* Send WHOWAS reply */
 +    idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
 +    tmp = silc_argument_get_first_arg(cmd->args, NULL);
 +    memset(uh, 0, sizeof(uh));
 +    memset(nh, 0, sizeof(nh));
 +
 +    strncat(nh, entry->nickname, strlen(entry->nickname));
 +    if (!strchr(entry->nickname, '@')) {
 +      strncat(nh, "@", 1);
 +      if (entry->servername) {
 +      strncat(nh, entry->servername, strlen(entry->servername));
 +      } else {
 +      len = entry->router ? strlen(entry->router->server_name) :
 +        strlen(server->server_name);
 +      strncat(nh, entry->router ? entry->router->server_name :
 +              server->server_name, len);
 +      }
 +    }
 +      
 +    strncat(uh, entry->username, strlen(entry->username));
 +    if (!strchr(entry->username, '@')) {
 +      strncat(uh, "@", 1);
 +      strcat(uh, "*private*");
 +    }
 +      
 +    packet = 
 +      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
 +                                         status, ident, 4, 
 +                                         2, idp->data, idp->len,
 +                                         3, nh, strlen(nh),
 +                                         4, uh, strlen(uh),
 +                                         5, entry->userinfo, 
 +                                         entry->userinfo ? 
 +                                         strlen(entry->userinfo) : 0);
 +    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 +                          0, packet->data, packet->len, FALSE);
 +    
 +    silc_buffer_free(packet);
 +    silc_buffer_free(idp);
 +
 +    k++;
 +  }
 +}
 +
 +static int
 +silc_server_command_whowas_process(SilcServerCommandContext cmd)
 +{
 +  SilcServer server = cmd->server;
 +  char *nick = NULL, *server_name = NULL;
 +  int count = 0;
 +  SilcClientEntry *clients = NULL;
 +  uint32 clients_count = 0;
 +  int ret = 0;
 +  bool check_global = FALSE;
 +
 +  /* Protocol dictates that we must always send the received WHOWAS request
 +     to our router if we are normal server, so let's do it now unless we
 +     are standalone. We will not send any replies to the client until we
 +     have received reply from the router. */
 +  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
 +      server->server_type == SILC_SERVER && !cmd->pending && 
 +      !server->standalone) {
 +    SilcBuffer tmpbuf;
 +    uint16 old_ident;
 +
 +    old_ident = silc_command_get_ident(cmd->payload);
 +    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +
 +    /* Send WHOWAS command to our router */
 +    silc_server_packet_send(server, (SilcSocketConnection)
 +                          server->router->connection,
 +                          SILC_PACKET_COMMAND, cmd->packet->flags,
 +                          tmpbuf->data, tmpbuf->len, TRUE);
 +
 +    /* Reprocess this packet after received reply from router */
 +    silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
 +                              silc_command_get_ident(cmd->payload),
 +                              silc_server_command_whowas,
 +                              silc_server_command_dup(cmd));
 +    cmd->pending = TRUE;
 +    silc_command_set_ident(cmd->payload, old_ident);
 +
 +    silc_buffer_free(tmpbuf);
 +    ret = -1;
 +    goto out;
 +  }
 +
 +  /* We are ready to process the command request. Let's search for the
 +     requested client and send reply to the requesting client. */
 +
 +  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
 +    check_global = TRUE;
 +  else if (server->server_type != SILC_SERVER)
 +    check_global = TRUE;
 +
 +  /* Parse the whowas request */
 +  if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
 +    return 0;
 +
 +  /* Get all clients matching that nickname from local list */
 +  if (!silc_idlist_get_clients_by_nickname(server->local_list, 
 +                                         nick, server_name,
 +                                         &clients, &clients_count))
 +    silc_idlist_get_clients_by_hash(server->local_list, 
 +                                  nick, server->md5hash,
 +                                  &clients, &clients_count);
 +  
 +  /* Check global list as well */
 +  if (check_global) {
 +    if (!silc_idlist_get_clients_by_nickname(server->global_list, 
 +                                           nick, server_name,
 +                                           &clients, &clients_count))
 +      silc_idlist_get_clients_by_hash(server->global_list, 
 +                                    nick, server->md5hash,
 +                                    &clients, &clients_count);
 +  }
 +  
 +  if (!clients) {
 +    /* Such a client really does not exist in the SILC network. */
 +    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
 +                                       SILC_STATUS_ERR_NO_SUCH_NICK,
 +                                       3, nick, strlen(nick));
 +    goto out;
 +  }
 +
 +  if (!silc_server_command_whowas_check(cmd, clients, clients_count)) {
 +    ret = -1;
 +    goto out;
 +  }
 +
 +  /* Send the command reply to the client */
 +  silc_server_command_whowas_send_reply(cmd, clients, clients_count);
 +
 + out:
 +  silc_free(clients);
 +  silc_free(nick);
 +  silc_free(server_name);
 +  return ret;
 +}
 +
 +/* Server side of command WHOWAS. */
 +
 +SILC_SERVER_CMD_FUNC(whowas)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  int ret = 0;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
 +
 +  ret = silc_server_command_whowas_process(cmd);
 +  silc_server_command_free(cmd);
 +}
 +
 +/******************************************************************************
 +
 +                              IDENTIFY Functions
 +
 +******************************************************************************/
 +
 +static void 
 +silc_server_command_identify_send_router(SilcServerCommandContext cmd)
 +{
 +  SilcServer server = cmd->server;
 +  SilcBuffer tmpbuf;
 +  uint16 old_ident;
 +
 +  old_ident = silc_command_get_ident(cmd->payload);
 +  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +
 +  /* Send IDENTIFY command to our router */
 +  silc_server_packet_send(server, (SilcSocketConnection)
 +                        server->router->connection,
 +                        SILC_PACKET_COMMAND, cmd->packet->flags,
 +                        tmpbuf->data, tmpbuf->len, TRUE);
 +
 +  /* Reprocess this packet after received reply from router */
 +  silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
 +                            silc_command_get_ident(cmd->payload),
 +                            silc_server_command_identify,
 +                            silc_server_command_dup(cmd));
 +  cmd->pending = TRUE;
 +  silc_command_set_ident(cmd->payload, old_ident);
 +  silc_buffer_free(tmpbuf);
 +}
 +
 +static int
 +silc_server_command_identify_parse(SilcServerCommandContext cmd,
 +                                 SilcClientEntry **clients,
 +                                 uint32 *clients_count,
 +                                 SilcServerEntry **servers,
 +                                 uint32 *servers_count,
 +                                 SilcChannelEntry **channels,
 +                                 uint32 *channels_count,
 +                                 uint32 *count)
 +{
 +  SilcServer server = cmd->server;
 +  unsigned char *tmp;
 +  uint32 len;
 +  uint32 argc = silc_argument_get_arg_num(cmd->args);
 +  SilcIDPayload idp;
 +  bool check_global = FALSE;
 +  void *entry;
 +  int i;
 +  bool error = FALSE;
 +
 +  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
 +    check_global = TRUE;
 +  else if (server->server_type != SILC_SERVER)
 +    check_global = TRUE;
 +
 +  /* If ID Payload is in the command it must be used instead of names */
 +  tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
 +  if (!tmp) {
 +    /* No ID, get the names. */
 +
 +    /* If we are normal server and have not resolved information from
 +       router yet, do so now. */
 +    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
 +      server->server_type == SILC_SERVER && !cmd->pending && 
 +      !server->standalone) {
 +      silc_server_command_identify_send_router(cmd);
 +      return -1;
 +    }
 +
 +    /* Try to get nickname@server. */
 +    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +    if (tmp) {
 +      char *nick = NULL;
 +      char *nick_server = NULL;
 +
 +      silc_parse_userfqdn(tmp, &nick, &nick_server);
 +
 +      if (!silc_idlist_get_clients_by_hash(server->local_list, 
 +                                         nick, server->md5hash,
 +                                         clients, clients_count))
 +      silc_idlist_get_clients_by_nickname(server->local_list, 
 +                                          nick, nick_server,
 +                                          clients, clients_count);
 +      if (check_global) {
 +      if (!silc_idlist_get_clients_by_hash(server->global_list, 
 +                                           nick, server->md5hash,
 +                                           clients, clients_count))
 +        silc_idlist_get_clients_by_nickname(server->global_list, 
 +                                            nick, nick_server,
 +                                            clients, clients_count);
 +      }
 +
 +      silc_free(nick);
 +      silc_free(nick_server);
 +
 +      if (!(*clients)) {
 +      /* the nickname does not exist, send error reply */
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
 +                                           SILC_STATUS_ERR_NO_SUCH_NICK,
 +                                           3, tmp, strlen(tmp));
 +      return 0;
 +      }
 +    }
 +
 +    /* Try to get server name */
 +    tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +    if (tmp) {
 +      entry = silc_idlist_find_server_by_name(server->local_list,
 +                                            tmp, TRUE, NULL);
 +      if (!entry && check_global)
 +      entry = silc_idlist_find_server_by_name(server->global_list,
 +                                              tmp, TRUE, NULL);
 +      if (entry) {
 +      *servers = silc_realloc(*servers, sizeof(**servers) * 
 +                              (*servers_count + 1));
 +      (*servers)[(*servers_count)++] = entry;
 +      }
 +
 +      if (!(*servers)) {
 +      /* the server does not exist, send error reply */
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
 +                                           SILC_STATUS_ERR_NO_SUCH_SERVER,
 +                                           3, tmp, strlen(tmp));
 +      return 0;
 +      }
 +    }
 +
 +    /* Try to get channel name */
 +    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
 +    if (tmp) {
 +      entry = silc_idlist_find_channel_by_name(server->local_list,
 +                                             tmp, NULL);
 +      if (!entry && check_global)
 +      entry = silc_idlist_find_channel_by_name(server->global_list,
 +                                               tmp, NULL);
 +      if (entry) {
 +      *channels = silc_realloc(*channels, sizeof(**channels) * 
 +                               (*channels_count + 1));
 +      (*channels)[(*channels_count)++] = entry;
 +      }
 +
 +      if (!(*channels)) {
 +      /* The channel does not exist, send error reply */
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
 +                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
 +                                           3, tmp, strlen(tmp));
 +      return 0;
 +      }
 +    }
 +
 +    if (!(*clients) && !(*servers) && !(*channels)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
 +                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      return 0;
 +    }
 +  } else {
 +    /* Command includes ID, we must use that.  Also check whether the command
 +       has more than one ID set - take them all. */
 +
 +    /* Take all ID's from the command packet */
 +    for (i = 0; i < argc; i++) {
 +      void *id;
 +
 +      tmp = silc_argument_get_arg_type(cmd->args, i + 5, &len);
 +      if (!tmp)
 +      continue;
 +      
 +      idp = silc_id_payload_parse(tmp, len);
 +      if (!idp) {
 +      silc_free(*clients);
 +      silc_free(*servers);
 +      silc_free(*channels);
 +      silc_server_command_send_status_reply(
 +                                     cmd, SILC_COMMAND_IDENTIFY,
 +                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      return 0;
 +      }
 +
 +      id = silc_id_payload_get_id(idp);
 +      
 +      switch (silc_id_payload_get_type(idp)) {
 +      
 +      case SILC_ID_CLIENT:
 +      entry = (void *)silc_idlist_find_client_by_id(server->local_list, 
 +                                                    id, TRUE, NULL);
 +      if (!entry && check_global)
 +        entry = (void *)silc_idlist_find_client_by_id(server->global_list, 
 +                                                      id, TRUE, NULL);
 +      if (entry) {
 +        *clients = silc_realloc(*clients, sizeof(**clients) * 
 +                                (*clients_count + 1));
 +        (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
 +      } else {
 +        /* If we are normal server and have not resolved information from
 +           router yet, do so now. */
 +        if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
 +            server->server_type == SILC_SERVER && !cmd->pending && 
 +            !server->standalone) {
 +          silc_server_command_identify_send_router(cmd);
 +          silc_free(*clients);
 +          silc_free(*servers);
 +          silc_free(*channels);
 +          return -1;
 +        } else {
 +          silc_server_command_send_status_data(
 +                                      cmd, SILC_COMMAND_IDENTIFY,
 +                                      SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
 +                                      2, tmp, len);
 +          error = TRUE;
 +        }
 +      }
 +
 +      break;
 +      
 +      case SILC_ID_SERVER:
 +      entry = (void *)silc_idlist_find_server_by_id(server->local_list, 
 +                                                    id, TRUE, NULL);
 +      if (!entry && check_global)
 +        entry = (void *)silc_idlist_find_server_by_id(server->global_list, 
 +                                                      id, TRUE, NULL);
 +      if (entry) {
 +        *servers = silc_realloc(*servers, sizeof(**servers) * 
 +                                (*servers_count + 1));
 +        (*servers)[(*servers_count)++] = (SilcServerEntry)entry;
 +      } else {
 +        /* If we are normal server and have not resolved information from
 +           router yet, do so now. */
 +        if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
 +            server->server_type == SILC_SERVER && !cmd->pending && 
 +            !server->standalone) {
 +          silc_server_command_identify_send_router(cmd);
 +          silc_free(*clients);
 +          silc_free(*servers);
 +          silc_free(*channels);
 +          return -1;
 +        } else {
 +          silc_server_command_send_status_data(
 +                                       cmd, SILC_COMMAND_IDENTIFY,
 +                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
 +                                       2, tmp, len);
 +          error = TRUE;
 +        }
 +      }
 +      break;
 +      
 +      case SILC_ID_CHANNEL:
 +      entry = (void *)silc_idlist_find_channel_by_id(server->local_list, 
 +                                                     id, NULL);
 +      if (!entry && check_global)
 +        entry = (void *)silc_idlist_find_channel_by_id(server->global_list, 
 +                                                       id, NULL);
 +      if (entry) {
 +        *channels = silc_realloc(*channels, sizeof(**channels) * 
 +                                 (*channels_count + 1));
 +        (*channels)[(*channels_count)++] = (SilcChannelEntry)entry;
 +      } else {
 +        /* If we are normal server and have not resolved information from
 +           router yet, do so now. */
 +        if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
 +            server->server_type == SILC_SERVER && !cmd->pending && 
 +            !server->standalone) {
 +          silc_server_command_identify_send_router(cmd);
 +          silc_free(*clients);
 +          silc_free(*servers);
 +          silc_free(*channels);
 +          return -1;
 +        } else {
 +          silc_server_command_send_status_data(
 +                                       cmd, SILC_COMMAND_IDENTIFY,
 +                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
 +                                       2, tmp, len);
 +          error = TRUE;
 +        }
 +      }
 +      break;
 +      }
 +
 +      silc_free(id);
 +    }
 +  }
 +
 +  if (error) {
 +    silc_free(*clients);
 +    silc_free(*servers);
 +    silc_free(*channels);
 +    return FALSE;
 +  }
 +  
 +  /* Get the max count of reply messages allowed */
 +  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
 +  if (tmp)
 +    *count = atoi(tmp);
 +  else
 +    *count = 0;
 +
 +  return 1;
 +}
 +
 +/* Checks that all mandatory fields in client entry are present. If not
 +   then send WHOIS request to the server who owns the client. We use
 +   WHOIS because we want to get as much information as possible at once. */
 +
 +static bool
 +silc_server_command_identify_check_client(SilcServerCommandContext cmd,
 +                                        SilcClientEntry *clients,
 +                                        uint32 clients_count)
 +{
 +  SilcServer server = cmd->server;
 +  SilcClientEntry entry;
 +  SilcServerResolveContext resolve = NULL, r = NULL;
 +  uint32 resolve_count = 0;
 +  int i, k;
 +  bool no_res = TRUE;
 +
 +  for (i = 0; i < clients_count; i++) {
 +    entry = clients[i];
 +    if (!entry)
 +      continue;
 +
 +    if (entry->nickname || 
 +      !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
 +      if (!entry->router)
 +      continue;
 +
 +      /* If we are normal server, and we've not resolved this client from
 +       router and it is global client, we'll check whether it is on some
 +       channel.  If not then we cannot be sure about its validity, and
 +       we'll resolve it from router. */
 +      if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
 +        entry->connection || silc_hash_table_count(entry->channels))
 +      continue;
 +    }
 +
 +    /* We need to resolve this entry since it is not complete */
 +
 +    if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
 +      /* The entry is being resolved (and we are not the resolver) so attach
 +       to the command reply and we're done with this one. */
 +      silc_server_command_pending(server, SILC_COMMAND_NONE, 
 +                                entry->resolve_cmd_ident,
 +                                silc_server_command_identify,
 +                                silc_server_command_dup(cmd));
 +      no_res = FALSE;
 +    } else {
 +      if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
 +      /* We've resolved this and it still is not ready.  We'll return
 +         and are that this will be handled again after it is resolved. */
 +      for (i = 0; i < resolve_count; i++) {
 +        for (k = 0; k < r->res_argc; k++)
 +          silc_free(r->res_argv[k]);
 +        silc_free(r->res_argv);
 +        silc_free(r->res_argv_lens);
 +        silc_free(r->res_argv_types);
 +      }
 +      silc_free(resolve);
 +      return FALSE;
 +      } else {
 +      /* We'll resolve this client */
 +      SilcBuffer idp;
 +
 +      r = NULL;
 +      for (k = 0; k < resolve_count; k++) {
 +        if (resolve[k].router == entry->router) {
 +          r = &resolve[k];
 +          break;
 +        }
 +      }
 +
 +      if (!r) {
 +        resolve = silc_realloc(resolve, sizeof(*resolve) * 
 +                               (resolve_count + 1));
 +        r = &resolve[resolve_count];
 +        memset(r, 0, sizeof(*r));
 +        r->router = entry->router;
 +        r->ident = ++server->cmd_ident;
 +        resolve_count++;
 +      }
 +
 +      r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
 +                                 (r->res_argc + 1));
 +      r->res_argv_lens = silc_realloc(r->res_argv_lens, 
 +                                      sizeof(*r->res_argv_lens) *
 +                                      (r->res_argc + 1));
 +      r->res_argv_types = silc_realloc(r->res_argv_types, 
 +                                       sizeof(*r->res_argv_types) *
 +                                       (r->res_argc + 1));
 +      idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
 +      r->res_argv[r->res_argc] = silc_calloc(idp->len, 
 +                                             sizeof(**r->res_argv));
 +      memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
 +      r->res_argv_lens[r->res_argc] = idp->len;
 +      r->res_argv_types[r->res_argc] = r->res_argc + 3;
 +      r->res_argc++;
 +      silc_buffer_free(idp);
 +
 +      entry->resolve_cmd_ident = r->ident;
 +      entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
 +      entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
 +      }
 +    }
 +  }
 +
 +  /* Do the resolving */
 +  for (i = 0; i < resolve_count; i++) {
 +    SilcBuffer res_cmd;
 +
 +    r = &resolve[i];
 +
 +    /* Send WHOIS request. We send WHOIS since we're doing the requesting
 +       now anyway so make it a good one. */
 +    res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
 +                                        r->res_argc, r->res_argv, 
 +                                        r->res_argv_lens,
 +                                        r->res_argv_types, 
 +                                        r->ident);
 +    silc_server_packet_send(server, r->router->connection,
 +                          SILC_PACKET_COMMAND, cmd->packet->flags,
 +                          res_cmd->data, res_cmd->len, FALSE);
 +
 +    /* Reprocess this packet after received reply */
 +    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
 +                              r->ident,
 +                              silc_server_command_identify,
 +                              silc_server_command_dup(cmd));
 +    cmd->pending = TRUE;
 +
 +    silc_buffer_free(res_cmd);
 +    for (k = 0; k < r->res_argc; k++)
 +      silc_free(r->res_argv[k]);
 +    silc_free(r->res_argv);
 +    silc_free(r->res_argv_lens);
 +    silc_free(r->res_argv_types);
 +    no_res = FALSE;
 +  }
 +  silc_free(resolve);
 +
 +  return no_res;
 +}
 +
 +static void
 +silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
 +                                      SilcClientEntry *clients,
 +                                      uint32 clients_count,
 +                                      SilcServerEntry *servers,
 +                                      uint32 servers_count,
 +                                      SilcChannelEntry *channels,
 +                                      uint32 channels_count,
 +                                      int count)
 +{
 +  SilcServer server = cmd->server;
 +  int i, k, len, valid_count;
 +  SilcBuffer packet, idp;
 +  SilcCommandStatus status;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  char nh[256], uh[256];
 +  SilcSocketConnection hsock;
 +
 +  status = SILC_STATUS_OK;
 +
 +  if (clients) {
 +    SilcClientEntry entry;
 +
 +    /* Process only valid entries. */
 +    valid_count = 0;
 +    for (i = 0; i < clients_count; i++) {
 +      if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
 +      valid_count++;
 +      else
 +      clients[i] = NULL;
 +    }
 +
 +    if (!valid_count) {
 +      /* No valid entries found at all, just send error */
 +      unsigned char *tmp;
 +
 +      tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +      if (tmp) {
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
 +                                           SILC_STATUS_ERR_NO_SUCH_NICK,
 +                                           3, tmp, strlen(tmp));
 +      } else {
 +      tmp = silc_argument_get_arg_type(cmd->args, 5, (uint32 *)&len);
 +      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
 +                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
 +                                           2, tmp, len);
 +      }
 +      return;
 +    }
 +
 +    /* Process all valid client entries and send command replies */
 +
 +    if (valid_count > 1)
 +      status = SILC_STATUS_LIST_START;
 +
 +    for (i = 0, k = 0; i < clients_count; i++) {
 +      entry = clients[i];
 +      if (!entry)
 +      continue;
 +
 +      if (k >= 1)
 +      status = SILC_STATUS_LIST_ITEM;
 +      if (valid_count > 1 && k == valid_count - 1 
 +        && !servers_count && !channels_count)
 +      status = SILC_STATUS_LIST_END;
 +      if (count && k - 1 == count)
 +      status = SILC_STATUS_LIST_END;
 +      if (count && k - 1 > count)
 +      break;
 +
 +      /* Send IDENTIFY reply */
 +
 +      idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
 +      memset(uh, 0, sizeof(uh));
 +      memset(nh, 0, sizeof(nh));
 +      strncat(nh, entry->nickname, strlen(entry->nickname));
 +      if (!strchr(entry->nickname, '@')) {
 +      strncat(nh, "@", 1);
 +      if (entry->servername) {
 +        strncat(nh, entry->servername, strlen(entry->servername));
 +      } else {
 +        len = entry->router ? strlen(entry->router->server_name) :
 +          strlen(server->server_name);
 +        strncat(nh, entry->router ? entry->router->server_name :
 +                server->server_name, len);
 +      }
 +      }
 +
 +      if (!entry->username) {
 +      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
 +                                                    status, ident, 2,
 +                                                    2, idp->data, idp->len, 
 +                                                    3, nh, strlen(nh));
 +      } else {
 +      strncat(uh, entry->username, strlen(entry->username));
 +      if (!strchr(entry->username, '@')) {
 +        strncat(uh, "@", 1);
 +        hsock = (SilcSocketConnection)entry->connection;
 +        len = strlen(hsock->hostname);
 +        strncat(uh, hsock->hostname, len);
 +      }
 +      
 +      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
 +                                                    status, ident, 3,
 +                                                    2, idp->data, idp->len, 
 +                                                    3, nh, strlen(nh),
 +                                                    4, uh, strlen(uh));
 +      }
 +      
 +      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 +                            0, packet->data, packet->len, FALSE);
 +      
 +      silc_buffer_free(packet);
 +      silc_buffer_free(idp);
 +      
 +      k++;
 +    }
 +  }
 +
 +  if (servers) {
 +    SilcServerEntry entry;
 +
 +    if (status == SILC_STATUS_OK && servers_count > 1)
 +      status = SILC_STATUS_LIST_START;
 +
 +    for (i = 0, k = 0; i < servers_count; i++) {
 +      entry = servers[i];
 +      
 +      if (k >= 1)
 +      status = SILC_STATUS_LIST_ITEM;
 +      if (servers_count > 1 && k == servers_count - 1 && !channels_count)
 +      status = SILC_STATUS_LIST_END;
 +      if (count && k - 1 == count)
 +      status = SILC_STATUS_LIST_END;
 +      if (count && k - 1 > count)
 +      break;
 +      
 +      /* Send IDENTIFY reply */
 +      idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
 +      packet = 
 +      silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
 +                                           status, ident, 2,
 +                                           2, idp->data, idp->len, 
 +                                           3, entry->server_name, 
 +                                           entry->server_name ? 
 +                                           strlen(entry->server_name) : 0);
 +      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 +                            0, packet->data, packet->len, FALSE);
 +      
 +      silc_buffer_free(packet);
 +      silc_buffer_free(idp);
 +      
 +      k++;
 +    }
 +  }
 +
 +  if (channels) {
 +    SilcChannelEntry entry;
 +
 +    if (status == SILC_STATUS_OK && channels_count > 1)
 +      status = SILC_STATUS_LIST_START;
 +
 +    for (i = 0, k = 0; i < channels_count; i++) {
 +      entry = channels[i];
 +      
 +      if (k >= 1)
 +      status = SILC_STATUS_LIST_ITEM;
 +      if (channels_count > 1 && k == channels_count - 1)
 +      status = SILC_STATUS_LIST_END;
 +      if (count && k - 1 == count)
 +      status = SILC_STATUS_LIST_END;
 +      if (count && k - 1 > count)
 +      break;
 +      
 +      /* Send IDENTIFY reply */
 +      idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
 +      packet = 
 +      silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
 +                                           status, ident, 2,
 +                                           2, idp->data, idp->len, 
 +                                           3, entry->channel_name, 
 +                                           entry->channel_name ? 
 +                                           strlen(entry->channel_name): 0);
 +      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 +                            0, packet->data, packet->len, FALSE);
 +      
 +      silc_buffer_free(packet);
 +      silc_buffer_free(idp);
 +      
 +      k++;
 +    }
 +  }
 +}
 +
 +static int
 +silc_server_command_identify_process(SilcServerCommandContext cmd)
 +{
 +  uint32 count = 0;
 +  int ret = 0;
 +  SilcClientEntry *clients = NULL;
 +  SilcServerEntry *servers = NULL;
 +  SilcChannelEntry *channels = NULL;
 +  uint32 clients_count = 0, servers_count = 0, channels_count = 0;
 +
 +  /* Parse the IDENTIFY request */
 +  ret = silc_server_command_identify_parse(cmd,
 +                                         &clients, &clients_count,
 +                                         &servers, &servers_count,
 +                                         &channels, &channels_count,
 +                                         &count);
 +  if (ret < 1)
 +    return ret;
 +  ret = 0;
 +
 +  /* Check that all mandatory fields are present and request those data
 +     from the server who owns the client if necessary. */
 +  if (clients && !silc_server_command_identify_check_client(cmd, clients, 
 +                                                          clients_count)) {
 +    ret = -1;
 +    goto out;
 +  }
 +
 +  /* Send the command reply to the client */
 +  silc_server_command_identify_send_reply(cmd, 
 +                                        clients, clients_count,
 +                                        servers, servers_count,
 +                                        channels, channels_count, 
 +                                        count);
 +
 + out:
 +  silc_free(clients);
 +  silc_free(servers);
 +  silc_free(channels);
 +  return ret;
 +}
 +
 +SILC_SERVER_CMD_FUNC(identify)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  int ret = 0;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
 +
 +  ret = silc_server_command_identify_process(cmd);
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of command NICK. Sets nickname for user. Setting
 +   nickname causes generation of a new client ID for the client. The
 +   new client ID is sent to the client after changing the nickname. */
 +
 +SILC_SERVER_CMD_FUNC(nick)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcServer server = cmd->server;
 +  SilcBuffer packet, nidp, oidp = NULL;
 +  SilcClientID *new_id;
 +  uint32 nick_len;
 +  char *nick;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  int nickfail = 0;
 +
 +  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
 +
 +  /* Check nickname */
 +  nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
 +  if (nick_len > 128)
 +    nick[128] = '\0';
 +  if (silc_server_name_bad_chars(nick, nick_len) == TRUE) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
 +                                        SILC_STATUS_ERR_BAD_NICKNAME);
 +    goto out;
 +  }
 +
 +  /* Check for same nickname */
 +  if (!strcmp(client->nickname, nick)) {
 +    nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +    goto send_reply;
 +  }
 +
 +  /* Create new Client ID */
 +  while (!silc_id_create_client_id(cmd->server, cmd->server->id, 
 +                                 cmd->server->rng, 
 +                                 cmd->server->md5hash, nick,
 +                                 &new_id)) {
 +    nickfail++;
 +    snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
 +  }
 +
 +  /* Send notify about nickname change to our router. We send the new
 +     ID and ask to replace it with the old one. If we are router the
 +     packet is broadcasted. Send NICK_CHANGE notify. */
 +  if (!server->standalone)
 +    silc_server_send_notify_nick_change(server, server->router->connection, 
 +                                      server->server_type == SILC_SERVER ? 
 +                                      FALSE : TRUE, client->id,
 +                                      new_id);
 +
 +  oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +
 +  /* Remove old cache entry */
 +  silc_idcache_del_by_context(server->local_list->clients, client);
 +
 +  /* Free old ID */
 +  silc_free(client->id);
 +
 +  /* Save the nickname as this client is our local client */
 +  silc_free(client->nickname);
 +
 +  client->nickname = strdup(nick);
 +  client->id = new_id;
 +
 +  /* Update client cache */
 +  silc_idcache_add(server->local_list->clients, client->nickname, 
 +                 client->id, (void *)client, 0, NULL);
 +
 +  nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +
 +  /* Send NICK_CHANGE notify to the client's channels */
 +  silc_server_send_notify_on_channels(server, NULL, client, 
 +                                    SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
 +                                    oidp->data, oidp->len, 
 +                                    nidp->data, nidp->len);
 +
 + send_reply:
 +  /* Send the new Client ID as reply command back to client */
 +  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, 
 +                                              SILC_STATUS_OK, ident, 1, 
 +                                              2, nidp->data, nidp->len);
 +  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 +                        0, packet->data, packet->len, FALSE);
 +
 +  silc_buffer_free(packet);
 +  silc_buffer_free(nidp);
 +  if (oidp)
 +    silc_buffer_free(oidp);
 +  
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Sends the LIST command reply */
 +
 +static void
 +silc_server_command_list_send_reply(SilcServerCommandContext cmd,
 +                                  SilcChannelEntry *lch, 
 +                                  uint32 lch_count,
 +                                  SilcChannelEntry *gch,
 +                                  uint32 gch_count)
 +{
 +  int i, k;
 +  SilcBuffer packet, idp;
 +  SilcChannelEntry entry;
 +  SilcCommandStatus status;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  char *topic;
 +  unsigned char usercount[4];
 +  uint32 users;
 +  int valid_lcount = 0, valid_rcount = 0;
 +
 +  for (i = 0; i < lch_count; i++) {
 +    if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
 +      lch[i] = NULL;
 +    else
 +      valid_lcount++;
 +  }
 +  for (i = 0; i < gch_count; i++) {
 +    if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
 +      gch[i] = NULL;
 +    else
 +      valid_rcount++;
 +  }
 +
 +  status = SILC_STATUS_OK;
 +  if ((lch_count + gch_count) > 1)
 +    status = SILC_STATUS_LIST_START;
 +
 +  /* Local list */
 +  for (i = 0, k = 0; i < lch_count; i++) {
 +    entry = lch[i];
 +    if (!entry)
 +      continue;
 +
 +    if (k >= 1)
 +      status = SILC_STATUS_LIST_ITEM;
 +    if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
 +      status = SILC_STATUS_LIST_END;
 +
 +    idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
 +
 +    if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
 +      topic = "*private*";
 +      memset(usercount, 0, sizeof(usercount));
 +    } else {
 +      topic = entry->topic;
 +      users = silc_hash_table_count(entry->user_list);
 +      SILC_PUT32_MSB(users, usercount);
 +    }
 +
 +    /* Send the reply */
 +    packet = 
 +      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
 +                                         status, ident, 4,
 +                                         2, idp->data, idp->len,
 +                                         3, entry->channel_name, 
 +                                         strlen(entry->channel_name),
 +                                         4, topic, topic ? strlen(topic) : 0,
 +                                         5, usercount, 4);
 +    silc_server_packet_send(cmd->server, cmd->sock, 
 +                          SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
 +                          packet->len, FALSE);
 +    silc_buffer_free(packet);
 +    silc_buffer_free(idp);
 +    k++;
 +  }
 +
 +  /* Global list */
 +  for (i = 0, k = 0; i < gch_count; i++) {
 +    entry = gch[i];
 +    if (!entry)
 +      continue;
 +
 +    if (k >= 1)
 +      status = SILC_STATUS_LIST_ITEM;
 +    if (valid_rcount > 1 && k == valid_rcount - 1)
 +      status = SILC_STATUS_LIST_END;
 +
 +    idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
 +
 +    if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
 +      topic = "*private*";
 +      memset(usercount, 0, sizeof(usercount));
 +    } else {
 +      topic = entry->topic;
 +      users = silc_hash_table_count(entry->user_list);
 +      SILC_PUT32_MSB(users, usercount);
 +    }
 +
 +    /* Send the reply */
 +    packet = 
 +      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
 +                                         status, ident, 4,
 +                                         2, idp->data, idp->len,
 +                                         3, entry->channel_name, 
 +                                         strlen(entry->channel_name),
 +                                         4, topic, topic ? strlen(topic) : 0,
 +                                         5, usercount, 4);
 +    silc_server_packet_send(cmd->server, cmd->sock, 
 +                          SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
 +                          packet->len, FALSE);
 +    silc_buffer_free(packet);
 +    silc_buffer_free(idp);
 +    k++;
 +  }
 +}
 +
 +/* Server side of LIST command. This lists the channel of the requested
 +   server. Secret channels are not listed. */
 +
  SILC_SERVER_CMD_FUNC(list)
  {
-            server->config->admin_info->location,
-            server->config->admin_info->server_type,
-            server->config->admin_info->admin_name,
-            server->config->admin_info->admin_email);
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcChannelID *channel_id = NULL;
 +  unsigned char *tmp;
 +  uint32 tmp_len;
 +  SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
 +  uint32 lch_count = 0, gch_count = 0;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
 +
 +  /* If we are normal server, send the command to router, since we
 +     want to know all channels in the network. */
 +  if (!cmd->pending && server->server_type == SILC_SERVER && 
 +      !server->standalone) {
 +    SilcBuffer tmpbuf;
 +    uint16 old_ident;
 +    
 +    old_ident = silc_command_get_ident(cmd->payload);
 +    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +    silc_server_packet_send(server, server->router->connection,
 +                          SILC_PACKET_COMMAND, cmd->packet->flags,
 +                          tmpbuf->data, tmpbuf->len, TRUE);
 +
 +    /* Reprocess this packet after received reply from router */
 +    silc_server_command_pending(server, SILC_COMMAND_LIST, 
 +                              silc_command_get_ident(cmd->payload),
 +                              silc_server_command_list, 
 +                              silc_server_command_dup(cmd));
 +    cmd->pending = TRUE;
 +    silc_command_set_ident(cmd->payload, old_ident);
 +    silc_buffer_free(tmpbuf);
 +    goto out;
 +  }
 +
 +  /* Get Channel ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (tmp) {
 +    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!channel_id) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
 +                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
 +      goto out;
 +    }
 +  }
 +
 +  /* Get the channels from local list */
 +  lchannels = silc_idlist_get_channels(server->local_list, channel_id,
 +                                     &lch_count);
 +  
 +  /* Get the channels from global list */
 +  gchannels = silc_idlist_get_channels(server->global_list, channel_id,
 +                                     &gch_count);
 +
 +  /* Send the reply */
 +  silc_server_command_list_send_reply(cmd, lchannels, lch_count, 
 +                                    gchannels, gch_count);
 +
 +  silc_free(lchannels);
 +  silc_free(gchannels);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of TOPIC command. Sets topic for channel and/or returns
 +   current topic to client. */
 +
 +SILC_SERVER_CMD_FUNC(topic)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcChannelID *channel_id;
 +  SilcChannelEntry channel;
 +  SilcChannelClientEntry chl;
 +  SilcBuffer packet, idp;
 +  unsigned char *tmp;
 +  uint32 argc, tmp_len;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
 +
 +  argc = silc_argument_get_arg_num(cmd->args);
 +
 +  /* Get Channel ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
 +  if (!channel_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +
 +  /* Check whether the channel exists */
 +  channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                         channel_id, NULL);
 +  if (!channel) {
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
 +    }
 +  }
 +
 +  if (argc > 1) {
 +    /* Get the topic */
 +    tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +    if (!tmp) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
 +                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      goto out;
 +    }
 +
 +    if (strlen(tmp) > 256) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
 +                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      goto out;
 +    }
 +
 +    /* See whether the client is on channel and has rights to change topic */
 +    if (!silc_hash_table_find(channel->user_list, client, NULL, 
 +                            (void *)&chl)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
 +                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
 +      goto out;
 +    }
 +
 +    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
 +      if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
 +                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +      goto out;
 +      }
 +    }
 +
 +    /* Set the topic for channel */
 +    silc_free(channel->topic);
 +    channel->topic = strdup(tmp);
 +
 +    /* Send TOPIC_SET notify type to the network */
 +    if (!server->standalone)
 +      silc_server_send_notify_topic_set(server, server->router->connection,
 +                                      server->server_type == SILC_ROUTER ?
 +                                      TRUE : FALSE, channel, 
 +                                      client->id, SILC_ID_CLIENT,
 +                                      channel->topic);
 +
 +    idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +
 +    /* Send notify about topic change to all clients on the channel */
 +    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
 +                                     SILC_NOTIFY_TYPE_TOPIC_SET, 2,
 +                                     idp->data, idp->len,
 +                                     channel->topic, strlen(channel->topic));
 +    silc_buffer_free(idp);
 +  }
 +
 +  /* Send the topic to client as reply packet */
 +  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
 +  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
 +                                              SILC_STATUS_OK, ident, 2, 
 +                                              2, idp->data, idp->len,
 +                                              3, channel->topic, 
 +                                              channel->topic ? 
 +                                              strlen(channel->topic) : 0);
 +  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
 +                        0, packet->data, packet->len, FALSE);
 +
 +  silc_buffer_free(packet);
 +  silc_buffer_free(idp);
 +  silc_free(channel_id);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of INVITE command. Invites some client to join some channel. 
 +   This command is also used to manage the invite list of the channel. */
 +
 +SILC_SERVER_CMD_FUNC(invite)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcSocketConnection sock = cmd->sock, dest_sock;
 +  SilcChannelClientEntry chl;
 +  SilcClientEntry sender, dest;
 +  SilcClientID *dest_id = NULL;
 +  SilcChannelEntry channel;
 +  SilcChannelID *channel_id = NULL;
 +  SilcIDListData idata;
 +  SilcBuffer idp, idp2, packet;
 +  unsigned char *tmp, *add, *del;
 +  uint32 len;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
 +
 +  /* Get Channel ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +  channel_id = silc_id_payload_parse_id(tmp, len);
 +  if (!channel_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +
 +  /* Get the channel entry */
 +  channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                         channel_id, NULL);
 +  if (!channel) {
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
 +    }
 +  }
 +
 +  /* Check whether the sender of this command is on the channel. */
 +  sender = (SilcClientEntry)sock->user_data;
 +  if (!silc_server_client_on_channel(sender, channel)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
 +                                        SILC_STATUS_ERR_NOT_ON_CHANNEL);
 +    goto out;
 +  }
 +
 +  /* Check whether the channel is invite-only channel. If yes then the
 +     sender of this command must be at least channel operator. */
 +  if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
 +    silc_hash_table_find(channel->user_list, sender, NULL, (void *)&chl);
 +    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
 +                                          SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +      goto out;
 +    }
 +  }
 +
 +  /* Get destination client ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
 +  if (tmp) {
 +    char invite[512];
 +    bool resolve;
 +
 +    dest_id = silc_id_payload_parse_id(tmp, len);
 +    if (!dest_id) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
 +                                          SILC_STATUS_ERR_NO_CLIENT_ID);
 +      goto out;
 +    }
 +
 +    /* Get the client entry */
 +    dest = silc_server_get_client_resolve(server, dest_id, &resolve);
 +    if (!dest) {
 +      if (server->server_type != SILC_SERVER || !resolve) {
 +      silc_server_command_send_status_reply(
 +                                      cmd, SILC_COMMAND_INVITE,
 +                                      SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
 +      goto out;
 +      }
 +      
 +      /* The client info is being resolved. Reprocess this packet after
 +       receiving the reply to the query. */
 +      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
 +                                server->cmd_ident,
 +                                silc_server_command_invite, 
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_free(channel_id);
 +      silc_free(dest_id);
 +      goto out;
 +    }
 +
 +    /* Check whether the requested client is already on the channel. */
 +    if (silc_server_client_on_channel(dest, channel)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
 +                                          SILC_STATUS_ERR_USER_ON_CHANNEL);
 +      goto out;
 +    }
 +    
 +    /* Get route to the client */
 +    dest_sock = silc_server_get_client_route(server, NULL, 0, dest_id, &idata);
 +    if (!dest_sock) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
 +                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
 +      goto out;
 +    }
 +
 +    memset(invite, 0, sizeof(invite));
 +    strncat(invite, dest->nickname, strlen(dest->nickname));
 +    strncat(invite, "!", 1);
 +    strncat(invite, dest->username, strlen(dest->username));
 +    if (!strchr(dest->username, '@')) {
 +      strncat(invite, "@", 1);
 +      strncat(invite, cmd->sock->hostname, strlen(cmd->sock->hostname));
 +    }
 +
 +    len = strlen(invite);
 +    if (!channel->invite_list)
 +      channel->invite_list = silc_calloc(len + 2, 
 +                                       sizeof(*channel->invite_list));
 +    else
 +      channel->invite_list = silc_realloc(channel->invite_list, 
 +                                        sizeof(*channel->invite_list) * 
 +                                        (len + 
 +                                         strlen(channel->invite_list) + 2));
 +    strncat(channel->invite_list, invite, len);
 +    strncat(channel->invite_list, ",", 1);
 +
 +    /* Send notify to the client that is invited to the channel */
 +    idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
 +    idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
 +    silc_server_send_notify_dest(server, dest_sock, FALSE, dest_id, 
 +                               SILC_ID_CLIENT,
 +                               SILC_NOTIFY_TYPE_INVITE, 3, 
 +                               idp->data, idp->len, 
 +                               channel->channel_name, 
 +                               strlen(channel->channel_name),
 +                               idp2->data, idp2->len);
 +    silc_buffer_free(idp);
 +    silc_buffer_free(idp2);
 +  }
 +
 +  /* Add the client to the invite list of the channel */
 +  add = silc_argument_get_arg_type(cmd->args, 3, &len);
 +  if (add) {
 +    if (!channel->invite_list)
 +      channel->invite_list = silc_calloc(len + 2, 
 +                                       sizeof(*channel->invite_list));
 +    else
 +      channel->invite_list = silc_realloc(channel->invite_list, 
 +                                        sizeof(*channel->invite_list) * 
 +                                        (len + 
 +                                         strlen(channel->invite_list) + 2));
 +    if (add[len - 1] == ',')
 +      add[len - 1] = '\0';
 +    
 +    strncat(channel->invite_list, add, len);
 +    strncat(channel->invite_list, ",", 1);
 +  }
 +
 +  /* Get the invite to be removed and remove it from the list */
 +  del = silc_argument_get_arg_type(cmd->args, 4, &len);
 +  if (del && channel->invite_list) {
 +    char *start, *end, *n;
 +
 +    if (!strncmp(channel->invite_list, del, 
 +               strlen(channel->invite_list) - 1)) {
 +      silc_free(channel->invite_list);
 +      channel->invite_list = NULL;
 +    } else {
 +      start = strstr(channel->invite_list, del);
 +      if (start && strlen(start) >= len) {
 +      end = start + len;
 +      n = silc_calloc(strlen(channel->invite_list) - 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;
 +      }
 +    }
 +  }
 +
 +  /* Send notify to the primary router */
 +  if (!server->standalone)
 +    silc_server_send_notify_invite(server, server->router->connection,
 +                                 server->server_type == SILC_ROUTER ?
 +                                 TRUE : FALSE, channel,
 +                                 sender->id, add, del);
 +
 +  /* Send command reply */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
 +
 +  if (add || del)
 +    packet = 
 +      silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
 +                                         SILC_STATUS_OK, ident, 2,
 +                                         2, tmp, len,
 +                                         3, channel->invite_list,
 +                                         channel->invite_list ?
 +                                         strlen(channel->invite_list) : 0);
 +  else
 +    packet = 
 +      silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
 +                                         SILC_STATUS_OK, ident, 1,
 +                                         2, tmp, len);
 +  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        packet->data, packet->len, FALSE);
 +  silc_buffer_free(packet);
 +
 + out:
 +  silc_free(dest_id);
 +  silc_free(channel_id);
 +  silc_server_command_free(cmd);
 +}
 +
 +typedef struct {
 +  SilcServer server;
 +  SilcSocketConnection sock;
 +  char *signoff;
 +} *QuitInternal;
 +
 +/* Quits connection to client. This gets called if client won't
 +   close the connection even when it has issued QUIT command. */
 +
 +SILC_TASK_CALLBACK(silc_server_command_quit_cb)
 +{
 +  QuitInternal q = (QuitInternal)context;
 +
 +  /* Free all client specific data, such as client entry and entires
 +     on channels this client may be on. */
 +  silc_server_free_client_data(q->server, q->sock, q->sock->user_data,
 +                             TRUE, q->signoff);
 +  q->sock->user_data = NULL;
 +
 +  /* Close the connection on our side */
 +  silc_server_close_connection(q->server, q->sock);
 +
 +  silc_free(q->signoff);
 +  silc_free(q);
 +}
 +
 +/* Quits SILC session. This is the normal way to disconnect client. */
 + 
 +SILC_SERVER_CMD_FUNC(quit)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcSocketConnection sock = cmd->sock;
 +  QuitInternal q;
 +  unsigned char *tmp = NULL;
 +  uint32 len = 0;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_QUIT, cmd, 0, 1);
 +
 +  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
 +
 +  /* Get destination ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
 +  if (len > 128)
 +    tmp = NULL;
 +
 +  q = silc_calloc(1, sizeof(*q));
 +  q->server = server;
 +  q->sock = sock;
 +  q->signoff = tmp ? strdup(tmp) : NULL;
 +
 +  /* We quit the connection with little timeout */
 +  silc_schedule_task_add(server->schedule, sock->sock,
 +                   silc_server_command_quit_cb, (void *)q,
 +                   0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of command KILL. This command is used by router operator
 +   to remove an client from the SILC Network temporarily. */
 +
 +SILC_SERVER_CMD_FUNC(kill)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcClientEntry remote_client;
 +  SilcClientID *client_id;
 +  unsigned char *tmp, *comment;
 +  uint32 tmp_len, tmp_len2;
 +  bool local;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
 +
 +  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
 +
 +  /* KILL command works only on router */
 +  if (server->server_type != SILC_ROUTER) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
 +                                        SILC_STATUS_ERR_NO_ROUTER_PRIV);
 +    goto out;
 +  }
 +
 +  /* Check whether client has the permissions. */
 +  if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
 +                                        SILC_STATUS_ERR_NO_ROUTER_PRIV);
 +    goto out;
 +  }
 +
 +  /* Get the client ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +  client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +  if (!client_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
 +                                        SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
 +    goto out;
 +  }
 +
 +  /* Get the client entry */
 +  remote_client = silc_idlist_find_client_by_id(server->local_list, 
 +                                              client_id, TRUE, NULL);
 +  local = TRUE;
 +  if (!remote_client) {
 +    remote_client = silc_idlist_find_client_by_id(server->global_list, 
 +                                                client_id, TRUE, NULL);
 +    local = FALSE;
 +    if (!remote_client) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
 +                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
 +      goto out;
 +    }
 +  }
 +
 +  /* Get comment */
 +  comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
 +  if (tmp_len2 > 128)
 +    comment = NULL;
 +
 +  /* Send reply to the sender */
 +  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
 +                                      SILC_STATUS_OK);
 +
 +  /* Send the KILL notify packets. First send it to the channel, then
 +     to our primary router and then directly to the client who is being
 +     killed right now. */
 +
 +  /* Send KILLED notify to the channels. It is not sent to the client
 +     as it will be sent differently destined directly to the client and not
 +     to the channel. */
 +  silc_server_send_notify_on_channels(server, remote_client, 
 +                                    remote_client, SILC_NOTIFY_TYPE_KILLED,
 +                                    comment ? 2 : 1,
 +                                    tmp, tmp_len,
 +                                    comment, comment ? tmp_len2 : 0);
 +
 +  /* Send KILLED notify to primary route */
 +  if (!server->standalone)
 +    silc_server_send_notify_killed(server, server->router->connection, TRUE,
 +                                 remote_client->id, comment);
 +
 +  /* Send KILLED notify to the client directly */
 +  silc_server_send_notify_killed(server, remote_client->connection ? 
 +                               remote_client->connection : 
 +                               remote_client->router->connection, FALSE,
 +                               remote_client->id, comment);
 +
 +  /* Remove the client from all channels. This generates new keys to the
 +     channels as well. */
 +  silc_server_remove_from_channels(server, NULL, remote_client, FALSE, 
 +                                 NULL, TRUE);
 +
 +  /* Remove the client entry, If it is locally connected then we will also
 +     disconnect the client here */
 +  if (remote_client->connection) {
 +    /* Remove locally conneted client */
 +    SilcSocketConnection sock = remote_client->connection;
 +    silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
 +    silc_server_close_connection(server, sock);
 +  } else {
 +    /* Update statistics */
 +    if (remote_client->connection)
 +      server->stat.my_clients--;
 +    if (server->server_type == SILC_ROUTER)
 +      server->stat.cell_clients--;
 +    SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
 +    SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
 +
 +    /* Remove remote client */
 +    silc_idlist_del_client(local ? server->local_list :
 +                         server->global_list, remote_client);
 +  }
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of command INFO. This sends information about us to 
 +   the client. If client requested specific server we will send the 
 +   command to that server. */
 +
 +SILC_SERVER_CMD_FUNC(info)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcBuffer packet, idp;
 +  unsigned char *tmp;
 +  uint32 tmp_len;
 +  char *dest_server, *server_info = NULL, *server_name;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  SilcServerEntry entry = NULL;
 +  SilcServerID *server_id = NULL;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 0, 2);
 +
 +  /* Get server name */
 +  dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +
 +  /* Get Server ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 +  if (tmp) {
 +    server_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!server_id) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
 +                                          SILC_STATUS_ERR_NO_SERVER_ID);
 +      goto out;
 +    }
 +  }
 +
 +  if (server_id) {
 +    /* Check whether we have this server cached */
 +    entry = silc_idlist_find_server_by_id(server->local_list,
 +                                        server_id, TRUE, NULL);
 +    if (!entry) {
 +      entry = silc_idlist_find_server_by_id(server->global_list,
 +                                          server_id, TRUE, NULL);
 +      if (!entry && server->server_type != SILC_SERVER) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
 +                                            SILC_STATUS_ERR_NO_SUCH_SERVER);
 +      goto out;
 +      }
 +    }
 +  }
 +
 +  /* Some buggy servers has sent request to router about themselves. */
 +  if (server->server_type != SILC_SERVER && cmd->sock->user_data == entry)
 +    goto out;
 +
 +  if ((!dest_server && !server_id && !entry) || (entry && 
 +                                               entry == server->id_entry) ||
 +      (dest_server && !cmd->pending && 
 +       !strncasecmp(dest_server, server->server_name, strlen(dest_server)))) {
 +    /* Send our reply */
 +    char info_string[256];
 +
 +    memset(info_string, 0, sizeof(info_string));
 +    snprintf(info_string, sizeof(info_string), 
 +           "location: %s server: %s admin: %s <%s>",
-     if (server->config && server->config->motd && 
-       server->config->motd->motd_file) {
++           server->config->server_info->location,
++           server->config->server_info->server_type,
++           server->config->server_info->admin,
++           server->config->server_info->email);
 +
 +    server_info = info_string;
 +    entry = server->id_entry;
 +  } else {
 +    /* Check whether we have this server cached */
 +    if (!entry && dest_server) {
 +      entry = silc_idlist_find_server_by_name(server->global_list,
 +                                            dest_server, TRUE, NULL);
 +      if (!entry) {
 +      entry = silc_idlist_find_server_by_name(server->local_list,
 +                                              dest_server, TRUE, NULL);
 +      }
 +    }
 +
 +    if (!cmd->pending &&
 +      server->server_type != SILC_SERVER && entry && !entry->server_info) {
 +      /* Send to the server */
 +      SilcBuffer tmpbuf;
 +      uint16 old_ident;
 +
 +      old_ident = silc_command_get_ident(cmd->payload);
 +      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +
 +      silc_server_packet_send(server, entry->connection,
 +                            SILC_PACKET_COMMAND, cmd->packet->flags,
 +                            tmpbuf->data, tmpbuf->len, TRUE);
 +
 +      /* Reprocess this packet after received reply from router */
 +      silc_server_command_pending(server, SILC_COMMAND_INFO, 
 +                                silc_command_get_ident(cmd->payload),
 +                                silc_server_command_info,
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_command_set_ident(cmd->payload, old_ident);
 +      silc_buffer_free(tmpbuf);
 +      goto out;
 +    }
 +
 +    if (!entry && !cmd->pending && !server->standalone) {
 +      /* Send to the primary router */
 +      SilcBuffer tmpbuf;
 +      uint16 old_ident;
 +
 +      old_ident = silc_command_get_ident(cmd->payload);
 +      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +
 +      silc_server_packet_send(server, server->router->connection,
 +                            SILC_PACKET_COMMAND, cmd->packet->flags,
 +                            tmpbuf->data, tmpbuf->len, TRUE);
 +
 +      /* Reprocess this packet after received reply from router */
 +      silc_server_command_pending(server, SILC_COMMAND_INFO, 
 +                                silc_command_get_ident(cmd->payload),
 +                                silc_server_command_info,
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_command_set_ident(cmd->payload, old_ident);
 +      silc_buffer_free(tmpbuf);
 +      goto out;
 +    }
 +  }
 +
 +  silc_free(server_id);
 +
 +  if (!entry) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
 +                                        SILC_STATUS_ERR_NO_SUCH_SERVER);
 +    goto out;
 +  }
 +
 +  idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
 +  if (!server_info)
 +    server_info = entry->server_info;
 +  server_name = entry->server_name;
 +
 +  /* Send the reply */
 +  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
 +                                              SILC_STATUS_OK, ident, 3,
 +                                              2, idp->data, idp->len,
 +                                              3, server_name, 
 +                                              strlen(server_name),
 +                                              4, server_info, 
 +                                              server_info ? 
 +                                              strlen(server_info) : 0);
 +  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        packet->data, packet->len, FALSE);
 +    
 +  silc_buffer_free(packet);
 +  silc_buffer_free(idp);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of command PING. This just replies to the ping. */
 +
 +SILC_SERVER_CMD_FUNC(ping)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcServerID *id;
 +  uint32 len;
 +  unsigned char *tmp;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 1, 2);
 +
 +  /* Get Server ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
 +                                        SILC_STATUS_ERR_NO_SERVER_ID);
 +    goto out;
 +  }
 +  id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
 +  if (!id)
 +    goto out;
 +
 +  if (SILC_ID_SERVER_COMPARE(id, server->id)) {
 +    /* Send our reply */
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
 +                                        SILC_STATUS_OK);
 +  } else {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
 +                                        SILC_STATUS_ERR_NO_SUCH_SERVER);
 +    goto out;
 +  }
 +
 +  silc_free(id);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Internal routine to join channel. The channel sent to this function
 +   has been either created or resolved from ID lists. This joins the sent
 +   client to the channel. */
 +
 +static void silc_server_command_join_channel(SilcServer server, 
 +                                           SilcServerCommandContext cmd,
 +                                           SilcChannelEntry channel,
 +                                           SilcClientID *client_id,
 +                                           bool created,
 +                                           bool create_key,
 +                                           uint32 umode,
 +                                           const unsigned char *auth,
 +                                           uint32 auth_len)
 +{
 +  SilcSocketConnection sock = cmd->sock;
 +  unsigned char *tmp;
 +  uint32 tmp_len, user_count;
 +  unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
 +  SilcClientEntry client;
 +  SilcChannelClientEntry chl;
 +  SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  char check[512], check2[512];
 +  bool founder = FALSE;
 +  bool resolve;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (!channel)
 +    return;
 +
 +  /* Get the client entry */
 +  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
 +    client = (SilcClientEntry)sock->user_data;
 +  } else {
 +    client = silc_server_get_client_resolve(server, client_id, &resolve);
 +    if (!client) {
 +      if (cmd->pending)
 +      goto out;
 +
 +      if (!resolve) {
 +      silc_server_command_send_status_reply(
 +                                       cmd, SILC_COMMAND_JOIN,
 +                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      goto out;
 +      }
 +
 +      /* The client info is being resolved. Reprocess this packet after
 +       receiving the reply to the query. */
 +      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
 +                                server->cmd_ident,
 +                                silc_server_command_join, 
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      goto out;
 +    }
 +
 +    cmd->pending = FALSE;
 +  }
 +
 +  /*
 +   * Check founder auth payload if provided.  If client can gain founder
 +   * privileges it can override various conditions on joining the channel,
 +   * and can have directly the founder mode set on the channel.
 +   */
 +  if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
 +    SilcIDListData idata = (SilcIDListData)client;
 +
 +    if (channel->founder_key && idata->public_key &&
 +      silc_pkcs_public_key_compare(channel->founder_key, 
 +                                   idata->public_key)) {
 +      void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ?
 +                       (void *)channel->founder_passwd : 
 +                       (void *)channel->founder_key);
 +      uint32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
 +                            channel->founder_passwd_len : 0);
 +
 +      /* Check whether the client is to become founder */
 +      if (silc_auth_verify_data(auth, auth_len, channel->founder_method, 
 +                              auth_data, auth_data_len,
 +                              idata->hash, client->id, SILC_ID_CLIENT)) {
 +      umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 +      founder = TRUE;
 +      }
 +    }
 +  }
 +
 +  /*
 +   * Check channel modes
 +   */
 +
 +  if (!umode) {
 +    memset(check, 0, sizeof(check));
 +    memset(check2, 0, sizeof(check2));
 +    strncat(check, client->nickname, strlen(client->nickname));
 +    strncat(check, "!", 1);
 +    strncat(check, client->username, strlen(client->username));
 +    if (!strchr(client->username, '@')) {
 +      strncat(check, "@", 1);
 +      strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname));
 +    }
 +
 +    strncat(check2, client->nickname, strlen(client->nickname));
 +    if (!strchr(client->nickname, '@')) {
 +      strncat(check2, "@", 1);
 +      strncat(check2, server->server_name, strlen(server->server_name));
 +    }
 +    strncat(check2, "!", 1);
 +    strncat(check2, client->username, strlen(client->username));
 +    if (!strchr(client->username, '@')) {
 +      strncat(check2, "@", 1);
 +      strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname));
 +    }
 +    
 +    /* Check invite list if channel is invite-only channel */
 +    if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
 +      if (!channel->invite_list ||
 +        (!silc_string_match(channel->invite_list, check) &&
 +         !silc_string_match(channel->invite_list, check2))) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                            SILC_STATUS_ERR_NOT_INVITED);
 +      goto out;
 +      }
 +    }
 +
 +    /* Check ban list if it exists. If the client's nickname, server,
 +       username and/or hostname is in the ban list the access to the
 +       channel is denied. */
 +    if (channel->ban_list) {
 +      if (silc_string_match(channel->ban_list, check) ||
 +        silc_string_match(channel->ban_list, check2)) {
 +      silc_server_command_send_status_reply(
 +                                    cmd, SILC_COMMAND_JOIN,
 +                                    SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
 +      goto out;
 +      }
 +    }
 +    
 +    /* Check user count limit if set. */
 +    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
 +      if (silc_hash_table_count(channel->user_list) + 1 > 
 +        channel->user_limit) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                            SILC_STATUS_ERR_CHANNEL_IS_FULL);
 +      goto out;
 +      }
 +    }
 +  }
 +
 +  /* Check the channel passphrase if set. */
 +  if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
 +    /* Get passphrase */
 +    tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
 +    if (tmp) {
 +      passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
 +      memcpy(passphrase, tmp, tmp_len);
 +    }
 +  
 +    if (!passphrase || !channel->passphrase ||
 +        memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                          SILC_STATUS_ERR_BAD_PASSWORD);
 +      goto out;
 +    }
 +  }
 +
 +  /*
 +   * Client is allowed to join to the channel. Make it happen.
 +   */
 +
 +  /* Check whether the client already is on the channel */
 +  if (silc_server_client_on_channel(client, channel)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                        SILC_STATUS_ERR_USER_ON_CHANNEL);
 +    goto out;
 +  }
 +
 +  /* Generate new channel key as protocol dictates */
 +  if (create_key) {
 +    if (!silc_server_create_channel_key(server, channel, 0))
 +      goto out;
 +
 +    /* Send the channel key. This is broadcasted to the channel but is not
 +       sent to the client who is joining to the channel. */
 +    if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
 +      silc_server_send_channel_key(server, NULL, channel, 
 +                                 server->server_type == SILC_ROUTER ? 
 +                                 FALSE : !server->standalone);
 +  }
 +
 +  /* Join the client to the channel by adding it to channel's user list.
 +     Add also the channel to client entry's channels list for fast cross-
 +     referencing. */
 +  chl = silc_calloc(1, sizeof(*chl));
 +  chl->mode = umode;
 +  chl->client = client;
 +  chl->channel = channel;
 +  silc_hash_table_add(channel->user_list, client, chl);
 +  silc_hash_table_add(client->channels, channel, chl);
 +
 +  /* Get users on the channel */
 +  silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
 +                                 &user_count);
 +
 +  /* Encode Client ID Payload of the original client who wants to join */
 +  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +
 +  /* Encode command reply packet */
 +  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 +  SILC_PUT32_MSB(channel->mode, mode);
 +  SILC_PUT32_MSB(created, tmp2);
 +  SILC_PUT32_MSB(user_count, tmp3);
 +
 +  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
 +    tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 +    keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, 
 +                                         strlen(channel->channel_key->
 +                                                cipher->name),
 +                                         channel->channel_key->cipher->name,
 +                                         channel->key_len / 8, channel->key);
 +    silc_free(tmp);
 +  }
 +
 +  reply = 
 +    silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
 +                                       SILC_STATUS_OK, ident, 13,
 +                                       2, channel->channel_name,
 +                                       strlen(channel->channel_name),
 +                                       3, chidp->data, chidp->len,
 +                                       4, clidp->data, clidp->len,
 +                                       5, mode, 4,
 +                                       6, tmp2, 4,
 +                                       7, keyp ? keyp->data : NULL, 
 +                                       keyp ? keyp->len : 0,
 +                                       8, channel->ban_list, 
 +                                       channel->ban_list ?
 +                                       strlen(channel->ban_list) : 0,
 +                                       9, channel->invite_list,
 +                                       channel->invite_list ?
 +                                       strlen(channel->invite_list) : 0,
 +                                       10, channel->topic,
 +                                       channel->topic ?
 +                                       strlen(channel->topic) : 0,
 +                                       11, silc_hmac_get_name(channel->hmac),
 +                                       strlen(silc_hmac_get_name(channel->
 +                                                                 hmac)),
 +                                       12, tmp3, 4,
 +                                       13, user_list->data, user_list->len,
 +                                       14, mode_list->data, 
 +                                       mode_list->len);
 +
 +  /* Send command reply */
 +  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        reply->data, reply->len, FALSE);
 +
 +  /* Send JOIN notify to locally connected clients on the channel. If
 +     we are normal server then router will send or have sent JOIN notify
 +     already. However since we've added the client already to our channel
 +     we'll ignore it (in packet_receive.c) so we must send it here. If
 +     we are router then this will send it to local clients and local
 +     servers. */
 +  silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
 +                                   SILC_NOTIFY_TYPE_JOIN, 2,
 +                                   clidp->data, clidp->len,
 +                                   chidp->data, chidp->len);
 +
 +  if (!cmd->pending) {
 +    /* Send JOIN notify packet to our primary router */
 +    if (!server->standalone)
 +      silc_server_send_notify_join(server, server->router->connection,
 +                                 server->server_type == SILC_ROUTER ?
 +                                 TRUE : FALSE, channel, client->id);
 +
 +    if (keyp)
 +      /* Distribute the channel key to all backup routers. */
 +      silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
 +                            keyp->data, keyp->len, FALSE, TRUE);
 +  }
 +
 +  /* If client became founder by providing correct founder auth data
 +     notify the mode change to the channel. */
 +  if (founder) {
 +    SILC_PUT32_MSB(chl->mode, mode);
 +    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
 +                                     SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
 +                                     clidp->data, clidp->len,
 +                                     mode, 4, clidp->data, clidp->len);
 +      
 +    /* Set CUMODE notify type to network */
 +    if (!server->standalone)
 +      silc_server_send_notify_cumode(server, server->router->connection,
 +                                   server->server_type == SILC_ROUTER ? 
 +                                   TRUE : FALSE, channel,
 +                                   chl->mode, client->id, SILC_ID_CLIENT,
 +                                   client->id);
 +  }
 +
 +  silc_buffer_free(reply);
 +  silc_buffer_free(clidp);
 +  silc_buffer_free(chidp);
 +  silc_buffer_free(keyp);
 +  silc_buffer_free(user_list);
 +  silc_buffer_free(mode_list);
 +
 + out:
 +  silc_free(passphrase);
 +}
 +
 +/* Server side of command JOIN. Joins client into requested channel. If 
 +   the channel does not exist it will be created. */
 +
 +SILC_SERVER_CMD_FUNC(join)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  unsigned char *auth;
 +  uint32 tmp_len, auth_len;
 +  char *tmp, *channel_name = NULL, *cipher, *hmac;
 +  SilcChannelEntry channel;
 +  uint32 umode = 0;
 +  bool created = FALSE, create_key = TRUE;
 +  SilcClientID *client_id;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 6);
 +
 +  /* Get channel name */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +  channel_name = tmp;
 +
 +  if (tmp_len > 256)
 +    channel_name[255] = '\0';
 +
 +  if (silc_server_name_bad_chars(channel_name, tmp_len) == TRUE) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                        SILC_STATUS_ERR_BAD_CHANNEL);
 +    goto out;
 +  }
 +
 +  /* Get Client ID of the client who is joining to the channel */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +  client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +  if (!client_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +
 +  /* Get cipher, hmac name and auth payload */
 +  cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
 +  hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
 +  auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len);
 +
 +  /* See if the channel exists */
 +  channel = silc_idlist_find_channel_by_name(server->local_list, 
 +                                           channel_name, NULL);
 +
 +  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
 +    SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
 +    client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
 +
 +    if (!channel || channel->disabled) {
 +      /* Channel not found */
 +
 +      /* If we are standalone server we don't have a router, we just create 
 +       the channel by ourselves. */
 +      if (server->standalone) {
 +      channel = silc_server_create_new_channel(server, server->id, cipher, 
 +                                               hmac, channel_name, TRUE);
 +      if (!channel) {
 +        silc_server_command_send_status_reply(
 +                                       cmd, SILC_COMMAND_JOIN,
 +                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
 +        goto out;
 +      }
 +      
 +      umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 +      created = TRUE;
 +      create_key = FALSE;
 +      
 +      } else {
 +
 +      /* The channel does not exist on our server. If we are normal server 
 +         we will send JOIN command to our router which will handle the
 +         joining procedure (either creates the channel if it doesn't exist 
 +         or joins the client to it). */
 +      if (server->server_type != SILC_ROUTER) {
 +        SilcBuffer tmpbuf;
 +        uint16 old_ident;
 +
 +        /* If this is pending command callback then we've resolved
 +           it and it didn't work, return since we've notified the
 +           client already in the command reply callback. */
 +        if (cmd->pending)
 +          goto out;
 +        
 +        old_ident = silc_command_get_ident(cmd->payload);
 +        silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +        tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +        
 +        /* Send JOIN command to our router */
 +        silc_server_packet_send(server, (SilcSocketConnection)
 +                                server->router->connection,
 +                                SILC_PACKET_COMMAND, cmd->packet->flags,
 +                                tmpbuf->data, tmpbuf->len, TRUE);
 +        
 +        /* Reprocess this packet after received reply from router */
 +        silc_server_command_pending(server, SILC_COMMAND_JOIN, 
 +                                    silc_command_get_ident(cmd->payload),
 +                                    silc_server_command_join,
 +                                    silc_server_command_dup(cmd));
 +        cmd->pending = TRUE;
 +          silc_command_set_ident(cmd->payload, old_ident);
 +        silc_buffer_free(tmpbuf);
 +        goto out;
 +      }
 +      
 +      /* We are router and the channel does not seem exist so we will check
 +         our global list as well for the channel. */
 +      channel = silc_idlist_find_channel_by_name(server->global_list, 
 +                                                 channel_name, NULL);
 +      if (!channel) {
 +        /* Channel really does not exist, create it */
 +        channel = silc_server_create_new_channel(server, server->id, cipher, 
 +                                                 hmac, channel_name, TRUE);
 +        if (!channel) {
 +          silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
 +          goto out;
 +        }
 +
 +        umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 +        created = TRUE;
 +        create_key = FALSE;
 +      }
 +      }
 +    }
 +  } else {
 +    if (!channel) {
 +      /* Channel not found */
 +
 +      /* If the command came from router and we are normal server then
 +       something went wrong with the joining as the channel was not found.
 +       We can't do anything else but ignore this. */
 +      if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
 +        server->server_type != SILC_ROUTER)
 +      goto out;
 +      
 +      /* We are router and the channel does not seem exist so we will check
 +       our global list as well for the channel. */
 +      channel = silc_idlist_find_channel_by_name(server->global_list, 
 +                                               channel_name, NULL);
 +      if (!channel) {
 +      /* Channel really does not exist, create it */
 +      channel = silc_server_create_new_channel(server, server->id, cipher, 
 +                                               hmac, channel_name, TRUE);
 +      if (!channel) {
 +        silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 +                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
 +        goto out;
 +      }
 +
 +      umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 +      created = TRUE;
 +      create_key = FALSE;
 +      }
 +    }
 +  }
 +
 +  /* Check whether the channel was created by our router */
 +  if (cmd->pending && context2) {
 +    SilcServerCommandReplyContext reply = 
 +      (SilcServerCommandReplyContext)context2;
 +
 +    if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
 +      tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
 +      SILC_GET32_MSB(created, tmp);
 +      if (silc_argument_get_arg_type(reply->args, 7, NULL))
 +      create_key = FALSE;     /* Router returned the key already */
 +    }
 +
 +    if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
 +      !silc_hash_table_count(channel->user_list))
 +      created = TRUE;
 +  }
 +
 +  /* If the channel does not have global users and is also empty the client
 +     will be the channel founder and operator. */
 +  if (!channel->global_users && !silc_hash_table_count(channel->user_list))
 +    umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 +
 +  /* Join to the channel */
 +  silc_server_command_join_channel(server, cmd, channel, client_id,
 +                                 created, create_key, umode,
 +                                 auth, auth_len);
 +
 +  silc_free(client_id);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of command MOTD. Sends server's current "message of the
 +   day" to the client. */
 +
 +SILC_SERVER_CMD_FUNC(motd)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcBuffer packet, idp;
 +  char *motd, *dest_server;
 +  uint32 motd_len;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_MOTD, cmd, 1, 1);
 +
 +  /* Get server name */
 +  dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +  if (!dest_server) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
 +                                        SILC_STATUS_ERR_NO_SUCH_SERVER);
 +    goto out;
 +  }
 +
 +  if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) {
 +    /* Send our MOTD */
 +
 +    idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
 +
-       motd = silc_file_readfile(server->config->motd->motd_file, &motd_len);
++    if (server->config && server->config->server_info &&
++      server->config->server_info->motd_file) {
 +      /* Send motd */
++      motd = silc_file_readfile(server->config->server_info->motd_file, &motd_len);
 +      if (!motd)
 +      goto out;
 +      
 +      motd[motd_len] = 0;
 +      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
 +                                                  SILC_STATUS_OK, ident, 2,
 +                                                  2, idp, idp->len,
 +                                                  3, motd, motd_len);
 +    } else {
 +      /* No motd */
 +      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
 +                                                  SILC_STATUS_OK, ident, 1,
 +                                                  2, idp, idp->len);
 +    }
 +
 +    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                          packet->data, packet->len, FALSE);
 +    silc_buffer_free(packet);
 +    silc_buffer_free(idp);
 +  } else {
 +    SilcServerEntry entry;
 +
 +    /* Check whether we have this server cached */
 +    entry = silc_idlist_find_server_by_name(server->global_list,
 +                                          dest_server, TRUE, NULL);
 +    if (!entry) {
 +      entry = silc_idlist_find_server_by_name(server->local_list,
 +                                            dest_server, TRUE, NULL);
 +    }
 +
 +    if (server->server_type != SILC_SERVER && !cmd->pending && 
 +      entry && !entry->motd) {
 +      /* Send to the server */
 +      SilcBuffer tmpbuf;
 +      uint16 old_ident;
 +
 +      old_ident = silc_command_get_ident(cmd->payload);
 +      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +
 +      silc_server_packet_send(server, entry->connection,
 +                            SILC_PACKET_COMMAND, cmd->packet->flags,
 +                            tmpbuf->data, tmpbuf->len, TRUE);
 +
 +      /* Reprocess this packet after received reply from router */
 +      silc_server_command_pending(server, SILC_COMMAND_MOTD, 
 +                                silc_command_get_ident(cmd->payload),
 +                                silc_server_command_motd,
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_command_set_ident(cmd->payload, old_ident);
 +      silc_buffer_free(tmpbuf);
 +      goto out;
 +    }
 +
 +    if (!entry && !cmd->pending && !server->standalone) {
 +      /* Send to the primary router */
 +      SilcBuffer tmpbuf;
 +      uint16 old_ident;
 +
 +      old_ident = silc_command_get_ident(cmd->payload);
 +      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +
 +      silc_server_packet_send(server, server->router->connection,
 +                            SILC_PACKET_COMMAND, cmd->packet->flags,
 +                            tmpbuf->data, tmpbuf->len, TRUE);
 +
 +      /* Reprocess this packet after received reply from router */
 +      silc_server_command_pending(server, SILC_COMMAND_MOTD, 
 +                                silc_command_get_ident(cmd->payload),
 +                                silc_server_command_motd,
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_command_set_ident(cmd->payload, old_ident);
 +      silc_buffer_free(tmpbuf);
 +      goto out;
 +    }
 +
 +    if (!entry) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
 +                                          SILC_STATUS_ERR_NO_SUCH_SERVER);
 +      goto out;
 +    }
 +
 +    idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
 +    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
 +                                                SILC_STATUS_OK, ident, 2,
 +                                                2, idp, idp->len,
 +                                                3, entry->motd,
 +                                                entry->motd ? 
 +                                                strlen(entry->motd) : 0);
 +    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                          packet->data, packet->len, FALSE);
 +    silc_buffer_free(packet);
 +    silc_buffer_free(idp);
 +  }
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of command UMODE. Client can use this command to set/unset
 +   user mode. Client actually cannot set itself to be as server/router
 +   operator so this can be used only to unset the modes. */
 +
 +SILC_SERVER_CMD_FUNC(umode)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcBuffer packet;
 +  unsigned char *tmp_mask;
 +  uint32 mask;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +
 +  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2);
 +
 +  /* Get the client's mode mask */
 +  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +  if (!tmp_mask) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +  SILC_GET32_MSB(mask, tmp_mask);
 +
 +  /* 
 +   * Change the mode 
 +   */
 +
 +  if (mask & SILC_UMODE_SERVER_OPERATOR) {
 +    if (!(client->mode & SILC_UMODE_SERVER_OPERATOR)) {
 +      /* Cannot operator mode */
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
 +                                          SILC_STATUS_ERR_PERM_DENIED);
 +      goto out;
 +    }
 +  } else {
 +    /* Remove the server operator rights */
 +    if (client->mode & SILC_UMODE_SERVER_OPERATOR) {
 +      client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
 +      if (client->connection)
 +      server->stat.my_server_ops--;
 +      if (server->server_type == SILC_ROUTER)
 +      server->stat.server_ops--;
 +    }
 +  }
 +
 +  if (mask & SILC_UMODE_ROUTER_OPERATOR) {
 +    if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
 +      /* Cannot operator mode */
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
 +                                          SILC_STATUS_ERR_PERM_DENIED);
 +      goto out;
 +    }
 +  } else {
 +    /* Remove the router operator rights */
 +    if (client->mode & SILC_UMODE_ROUTER_OPERATOR) {
 +      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
 +      if (client->connection)
 +      server->stat.my_router_ops--;
 +      if (server->server_type == SILC_ROUTER)
 +      server->stat.router_ops--;
 +    }
 +  }
 +
 +  if (mask & SILC_UMODE_GONE) {
 +    client->mode |= SILC_UMODE_GONE;
 +  } else {
 +    if (client->mode & SILC_UMODE_GONE)
 +      /* Remove the gone status */
 +      client->mode &= ~SILC_UMODE_GONE;
 +  }
 +
 +  /* Send UMODE change to primary router */
 +  if (!server->standalone)
 +    silc_server_send_notify_umode(server, server->router->connection, TRUE,
 +                                client->id, client->mode);
 +
 +  /* Send command reply to sender */
 +  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
 +                                              SILC_STATUS_OK, ident, 1,
 +                                              2, tmp_mask, 4);
 +  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        packet->data, packet->len, FALSE);
 +  silc_buffer_free(packet);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Checks that client has rights to add or remove channel modes. If any
 +   of the checks fails FALSE is returned. */
 +
 +int silc_server_check_cmode_rights(SilcChannelEntry channel,
 +                                 SilcChannelClientEntry client,
 +                                 uint32 mode)
 +{
 +  int is_op = client->mode & SILC_CHANNEL_UMODE_CHANOP;
 +  int is_fo = client->mode & SILC_CHANNEL_UMODE_CHANFO;
 +
 +  /* Check whether has rights to change anything */
 +  if (!is_op && !is_fo)
 +    return FALSE;
 +
 +  /* Check whether has rights to change everything */
 +  if (is_op && is_fo)
 +    return TRUE;
 +
 +  /* We know that client is channel operator, check that they are not
 +     changing anything that requires channel founder rights. Rest of the
 +     modes are available automatically for channel operator. */
 +
 +  if (mode & SILC_CHANNEL_MODE_PRIVKEY) {
 +    if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
 +      if (is_op && !is_fo)
 +      return FALSE;
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
 +      if (is_op && !is_fo)
 +      return FALSE;
 +    }
 +  }
 +  
 +  if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
 +    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE))
 +      if (is_op && !is_fo)
 +      return FALSE;
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
 +      if (is_op && !is_fo)
 +      return FALSE;
 +    }
 +  }
 +
 +  if (mode & SILC_CHANNEL_MODE_CIPHER) {
 +    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER))
 +      if (is_op && !is_fo)
 +      return FALSE;
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
 +      if (is_op && !is_fo)
 +      return FALSE;
 +    }
 +  }
 +  
 +  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
 +    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH))
 +      if (is_op && !is_fo)
 +      return FALSE;
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
 +      if (is_op && !is_fo)
 +      return FALSE;
 +    }
 +  }
 +  
 +  return TRUE;
 +}
 +
 +/* Server side command of CMODE. Changes channel mode */
 +
 +SILC_SERVER_CMD_FUNC(cmode)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcIDListData idata = (SilcIDListData)client;
 +  SilcChannelID *channel_id;
 +  SilcChannelEntry channel;
 +  SilcChannelClientEntry chl;
 +  SilcBuffer packet, cidp;
 +  unsigned char *tmp, *tmp_id, *tmp_mask;
 +  char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
 +  uint32 mode_mask, tmp_len, tmp_len2;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 2, 7);
 +
 +  /* Get Channel ID */
 +  tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
 +  if (!tmp_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
 +  if (!channel_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +
 +  /* Get the channel mode mask */
 +  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 +  if (!tmp_mask) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +  SILC_GET32_MSB(mode_mask, tmp_mask);
 +
 +  /* Get channel entry */
 +  channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                         channel_id, NULL);
 +  if (!channel) {
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
 +    }
 +  }
 +
 +  /* Check whether this client is on the channel */
 +  if (!silc_server_client_on_channel(client, channel)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                        SILC_STATUS_ERR_NOT_ON_CHANNEL);
 +    goto out;
 +  }
 +
 +  /* Get entry to the channel user list */
 +  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
 +
 +  /* Check that client has rights to change any requested channel modes */
 +  if (!silc_server_check_cmode_rights(channel, chl, mode_mask)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +    goto out;
 +  }
 +
 +  /*
 +   * Check the modes. Modes that requires nothing special operation are
 +   * not checked here.
 +   */
 +
 +  if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
 +    /* Channel uses private keys to protect traffic. Client(s) has set the
 +       key locally they want to use, server does not know that key. */
 +    /* Nothing interesting to do here */
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
 +      /* The mode is removed and we need to generate and distribute
 +       new channel key. Clients are not using private channel keys
 +       anymore after this. */
 +      
 +      /* Re-generate channel key */
 +      if (!silc_server_create_channel_key(server, channel, 0))
 +      goto out;
 +      
 +      /* Send the channel key. This sends it to our local clients and if
 +       we are normal server to our router as well. */
 +      silc_server_send_channel_key(server, NULL, channel, 
 +                                 server->server_type == SILC_ROUTER ? 
 +                                 FALSE : !server->standalone);
 +      
 +      cipher = channel->channel_key->cipher->name;
 +      hmac = (char *)silc_hmac_get_name(channel->hmac);
 +    }
 +  }
 +
 +  if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
 +    /* User limit is set on channel */
 +    uint32 user_limit;
 +      
 +    /* Get user limit */
 +    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
 +    if (!tmp) {
 +      if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      goto out;
 +      }
 +    } else {
 +      SILC_GET32_MSB(user_limit, tmp);
 +      channel->user_limit = user_limit;
 +    }
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
 +      /* User limit mode is unset. Remove user limit */
 +      channel->user_limit = 0;
 +  }
 +
 +  if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
 +    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
 +      /* Passphrase has been set to channel */
 +      
 +      /* Get the passphrase */
 +      tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
 +      if (!tmp) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      goto out;
 +      }
 +
 +      /* Save the passphrase */
 +      passphrase = channel->passphrase = strdup(tmp);
 +    }
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
 +      /* Passphrase mode is unset. remove the passphrase */
 +      if (channel->passphrase) {
 +      silc_free(channel->passphrase);
 +      channel->passphrase = NULL;
 +      }
 +    }
 +  }
 +
 +  if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
 +    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
 +      /* Cipher to use protect the traffic */
 +      SilcCipher newkey, oldkey;
 +
 +      /* Get cipher */
 +      cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
 +      if (!cipher) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      goto out;
 +      }
 +
 +      /* Delete old cipher and allocate the new one */
 +      if (!silc_cipher_alloc(cipher, &newkey)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
 +      goto out;
 +      }
 +
 +      oldkey = channel->channel_key;
 +      channel->channel_key = newkey;
 +
 +      /* Re-generate channel key */
 +      if (!silc_server_create_channel_key(server, channel, 0)) {
 +      /* We don't have new key, revert to old one */
 +      channel->channel_key = oldkey;
 +      goto out;
 +      }
 +
 +      /* Remove old channel key for good */
 +      silc_cipher_free(oldkey);
 +
 +      /* Send the channel key. This sends it to our local clients and if
 +       we are normal server to our router as well. */
 +      silc_server_send_channel_key(server, NULL, channel, 
 +                                 server->server_type == SILC_ROUTER ? 
 +                                 FALSE : !server->standalone);
 +    }
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
 +      /* Cipher mode is unset. Remove the cipher and revert back to 
 +       default cipher */
 +      SilcCipher newkey, oldkey;
 +      cipher = channel->cipher;
 +
 +      /* Delete old cipher and allocate default one */
 +      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER, &newkey)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                 SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
 +      goto out;
 +      }
 +
 +      oldkey = channel->channel_key;
 +      channel->channel_key = newkey;
 +
 +      /* Re-generate channel key */
 +      if (!silc_server_create_channel_key(server, channel, 0)) {
 +      /* We don't have new key, revert to old one */
 +      channel->channel_key = oldkey;
 +      goto out;
 +      }
 +      
 +      /* Remove old channel key for good */
 +      silc_cipher_free(oldkey);
 +
 +      /* Send the channel key. This sends it to our local clients and if
 +       we are normal server to our router as well. */
 +      silc_server_send_channel_key(server, NULL, channel, 
 +                                 server->server_type == SILC_ROUTER ? 
 +                                 FALSE : !server->standalone);
 +    }
 +  }
 +
 +  if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
 +    if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
 +      /* HMAC to use protect the traffic */
 +      unsigned char hash[32];
 +      SilcHmac newhmac;
 +
 +      /* Get hmac */
 +      hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
 +      if (!hmac) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      goto out;
 +      }
 +
 +      /* Delete old hmac and allocate the new one */
 +      if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
 +      goto out;
 +      }
 +
 +      silc_hmac_free(channel->hmac);
 +      channel->hmac = newhmac;
 +
 +      /* Set the HMAC key out of current channel key. The client must do
 +       this locally. */
 +      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
 +                   channel->key_len / 8, hash);
 +      silc_hmac_set_key(channel->hmac, hash, 
 +                      silc_hash_len(silc_hmac_get_hash(channel->hmac)));
 +      memset(hash, 0, sizeof(hash));
 +    }
 +  } else {
 +    if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
 +      /* Hmac mode is unset. Remove the hmac and revert back to 
 +       default hmac */
 +      SilcHmac newhmac;
 +      unsigned char hash[32];
 +      hmac = channel->hmac_name;
 +
 +      /* Delete old hmac and allocate default one */
 +      silc_hmac_free(channel->hmac);
 +      if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
 +      goto out;
 +      }
 +
 +      silc_hmac_free(channel->hmac);
 +      channel->hmac = newhmac;
 +
 +      /* Set the HMAC key out of current channel key. The client must do
 +       this locally. */
 +      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
 +                   channel->key_len / 8, 
 +                   hash);
 +      silc_hmac_set_key(channel->hmac, hash, 
 +                      silc_hash_len(silc_hmac_get_hash(channel->hmac)));
 +      memset(hash, 0, sizeof(hash));
 +    }
 +  }
 +
 +  if (mode_mask & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
 +    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
 +      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
 +      /* Set the founder authentication */
 +      SilcAuthPayload auth;
 +      
 +      tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len);
 +      if (!tmp) {
 +        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +        goto out;
 +      }
 +
 +      auth = silc_auth_payload_parse(tmp, tmp_len);
 +      if (!auth) {
 +        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +        goto out;
 +      }
 +
 +      /* Save the public key */
 +      tmp = silc_pkcs_public_key_encode(idata->public_key, &tmp_len);
 +      silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
 +      silc_free(tmp);
 +      
 +      channel->founder_method = silc_auth_get_method(auth);
 +
 +      if (channel->founder_method == SILC_AUTH_PASSWORD) {
 +        tmp = silc_auth_get_data(auth, &tmp_len);
 +        channel->founder_passwd = 
 +          silc_calloc(tmp_len + 1, sizeof(*channel->founder_passwd));
 +        memcpy(channel->founder_passwd, tmp, tmp_len);
 +        channel->founder_passwd_len = tmp_len;
 +      } else {
 +        /* Verify the payload before setting the mode */
 +        if (!silc_auth_verify(auth, channel->founder_method, 
 +                              channel->founder_key, 0, idata->hash,
 +                              client->id, SILC_ID_CLIENT)) {
 +          silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
 +                                                SILC_STATUS_ERR_AUTH_FAILED);
 +          goto out;
 +        }
 +      }
 +
 +      silc_auth_payload_free(auth);
 +      }
 +    }
 +  } else {
 +    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
 +      if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
 +      if (channel->founder_key)
 +        silc_pkcs_public_key_free(channel->founder_key);
 +      if (channel->founder_passwd) {
 +        silc_free(channel->founder_passwd);
 +        channel->founder_passwd = NULL;
 +      }
 +      }
 +    }
 +  }
 +
 +  /* Finally, set the mode */
 +  channel->mode = mode_mask;
 +
 +  /* Send CMODE_CHANGE notify. */
 +  cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
 +                                   SILC_NOTIFY_TYPE_CMODE_CHANGE, 5,
 +                                   cidp->data, cidp->len, 
 +                                   tmp_mask, 4,
 +                                   cipher, cipher ? strlen(cipher) : 0,
 +                                   hmac, hmac ? strlen(hmac) : 0,
 +                                   passphrase, passphrase ? 
 +                                   strlen(passphrase) : 0);
 +
 +  /* Set CMODE notify type to network */
 +  if (!server->standalone)
 +    silc_server_send_notify_cmode(server, server->router->connection,
 +                                server->server_type == SILC_ROUTER ? 
 +                                TRUE : FALSE, channel,
 +                                mode_mask, client->id, SILC_ID_CLIENT,
 +                                cipher, hmac, passphrase);
 +
 +  /* Send command reply to sender */
 +  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
 +                                              SILC_STATUS_OK, ident, 2,
 +                                              2, tmp_id, tmp_len2,
 +                                              3, tmp_mask, 4);
 +  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        packet->data, packet->len, FALSE);
 +    
 +  silc_buffer_free(packet);
 +  silc_free(channel_id);
 +  silc_buffer_free(cidp);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side of CUMODE command. Changes client's mode on a channel. */
 +
 +SILC_SERVER_CMD_FUNC(cumode)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcIDListData idata = (SilcIDListData)client;
 +  SilcChannelID *channel_id;
 +  SilcClientID *client_id;
 +  SilcChannelEntry channel;
 +  SilcClientEntry target_client;
 +  SilcChannelClientEntry chl;
 +  SilcBuffer packet, idp;
 +  unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
 +  uint32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
 +  int notify = FALSE;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
 +
 +  /* Get Channel ID */
 +  tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
 +  if (!tmp_ch_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len);
 +  if (!channel_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +
 +  /* Get channel entry */
 +  channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                         channel_id, NULL);
 +  if (!channel) {
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
 +    }
 +  }
 +
 +  /* Check whether sender is on the channel */
 +  if (!silc_server_client_on_channel(client, channel)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                        SILC_STATUS_ERR_NOT_ON_CHANNEL);
 +    goto out;
 +  }
 +
 +  /* Check that client has rights to change other's rights */
 +  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
 +  sender_mask = chl->mode;
 +  
 +  /* Get the target client's channel mode mask */
 +  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +  if (!tmp_mask) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +  SILC_GET32_MSB(target_mask, tmp_mask);
 +
 +  /* Get target Client ID */
 +  tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
 +  if (!tmp_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                        SILC_STATUS_ERR_NO_CLIENT_ID);
 +    goto out;
 +  }
 +  client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
 +  if (!client_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                        SILC_STATUS_ERR_NO_CLIENT_ID);
 +    goto out;
 +  }
 +
 +  /* Get target client's entry */
 +  target_client = silc_idlist_find_client_by_id(server->local_list, 
 +                                              client_id, TRUE, NULL);
 +  if (!target_client) {
 +    target_client = silc_idlist_find_client_by_id(server->global_list, 
 +                                                client_id, TRUE, NULL);
 +  }
 +
 +  if (target_client != client &&
 +      !(sender_mask & SILC_CHANNEL_UMODE_CHANFO) &&
 +      !(sender_mask & SILC_CHANNEL_UMODE_CHANOP)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +    goto out;
 +  }
 +
 +  /* Check whether target client is on the channel */
 +  if (target_client != client) {
 +    if (!silc_server_client_on_channel(target_client, channel)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                               SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
 +      goto out;
 +    }
 +
 +    /* Get entry to the channel user list */
 +    silc_hash_table_find(channel->user_list, target_client, NULL, 
 +                       (void *)&chl);
 +  }
 +
 +  /* 
 +   * Change the mode 
 +   */
 +
 +  /* If the target client is founder, no one else can change their mode
 +     but themselves. */
 +  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +    goto out;
 +  }
 +
 +  if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
 +    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
 +      /* The client tries to claim the founder rights. */
 +      unsigned char *tmp_auth;
 +      uint32 tmp_auth_len, auth_len;
 +      void *auth;
 +      
 +      if (target_client != client) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                            SILC_STATUS_ERR_NOT_YOU);
 +      goto out;
 +      }
 +
 +      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
 +        !channel->founder_key || !idata->public_key ||
 +        !silc_pkcs_public_key_compare(channel->founder_key, 
 +                                      idata->public_key)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                            SILC_STATUS_ERR_NOT_YOU);
 +      goto out;
 +      }
 +
 +      tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
 +      if (!tmp_auth) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +      goto out;
 +      }
 +
 +      auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
 +            (void *)channel->founder_passwd : (void *)channel->founder_key);
 +      auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
 +                channel->founder_passwd_len : 0);
 +      
 +      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
 +                               channel->founder_method, auth, auth_len,
 +                               idata->hash, client->id, SILC_ID_CLIENT)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                            SILC_STATUS_ERR_AUTH_FAILED);
 +      goto out;
 +      }
 +      
 +      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
 +      notify = TRUE;
 +    }
 +  } else {
 +    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
 +      if (target_client == client) {
 +      /* Remove channel founder rights from itself */
 +      chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
 +      notify = TRUE;
 +      } else {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                            SILC_STATUS_ERR_NOT_YOU);
 +      goto out;
 +      }
 +    }
 +  }
 +
 +  if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
 +    /* Promote to operator */
 +    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
 +      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
 +        !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +      goto out;
 +      }
 +
 +      chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
 +      notify = TRUE;
 +    }
 +  } else {
 +    if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
 +      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
 +        !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
 +                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +      goto out;
 +      }
 +
 +      /* Demote to normal user */
 +      chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
 +      notify = TRUE;
 +    }
 +  }
 +
 +  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +  tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
 +
 +  /* Send notify to channel, notify only if mode was actually changed. */
 +  if (notify) {
 +    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
 +                                     SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
 +                                     idp->data, idp->len,
 +                                     tmp_mask, 4, 
 +                                     tmp_id, tmp_len);
 +
 +    /* Set CUMODE notify type to network */
 +    if (!server->standalone)
 +      silc_server_send_notify_cumode(server, server->router->connection,
 +                                   server->server_type == SILC_ROUTER ? 
 +                                   TRUE : FALSE, channel,
 +                                   target_mask, client->id, 
 +                                   SILC_ID_CLIENT,
 +                                   target_client->id);
 +  }
 +
 +  /* Send command reply to sender */
 +  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
 +                                              SILC_STATUS_OK, ident, 3,
 +                                              2, tmp_mask, 4,
 +                                              3, tmp_ch_id, tmp_ch_len,
 +                                              4, tmp_id, tmp_len);
 +  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        packet->data, packet->len, FALSE);
 +    
 +  silc_buffer_free(packet);
 +  silc_free(channel_id);
 +  silc_free(client_id);
 +  silc_buffer_free(idp);
 +
 + out:
 +  silc_server_command_free(cmd);
  }
  
 -SILC_SERVER_CMD_FUNC(topic)
 +/* Server side of KICK command. Kicks client out of channel. */
 +
 +SILC_SERVER_CMD_FUNC(kick)
  {
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcClientEntry target_client;
 +  SilcChannelID *channel_id;
 +  SilcClientID *client_id;
 +  SilcChannelEntry channel;
 +  SilcChannelClientEntry chl;
 +  SilcBuffer idp;
 +  uint32 tmp_len, target_idp_len;
 +  unsigned char *tmp, *comment, *target_idp;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
 +
 +  /* Get Channel ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
 +  if (!channel_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +
 +  /* Get channel entry */
 +  channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                         channel_id, NULL);
 +  if (!channel) {
 +    channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
 +    }
 +  }
 +
 +  /* Check whether sender is on the channel */
 +  if (!silc_server_client_on_channel(client, channel)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                        SILC_STATUS_ERR_NOT_ON_CHANNEL);
 +    goto out;
 +  }
 +
 +  /* Check that the kicker is channel operator or channel founder */
 +  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
 +  if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +    goto out;
 +  }
 +  
 +  /* Get target Client ID */
 +  target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
 +  if (!target_idp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                        SILC_STATUS_ERR_NO_CLIENT_ID);
 +    goto out;
 +  }
 +  client_id = silc_id_payload_parse_id(target_idp, target_idp_len);
 +  if (!client_id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                        SILC_STATUS_ERR_NO_CLIENT_ID);
 +    goto out;
 +  }
 +
 +  /* Get target client's entry */
 +  target_client = silc_idlist_find_client_by_id(server->local_list, 
 +                                              client_id, TRUE, NULL);
 +  if (!target_client) {
 +    target_client = silc_idlist_find_client_by_id(server->global_list, 
 +                                                client_id, TRUE, NULL);
 +  }
 +
 +  /* Check that the target client is not channel founder. Channel founder
 +     cannot be kicked from the channel. */
 +  silc_hash_table_find(channel->user_list, target_client, NULL, (void *)&chl);
 +  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
 +    goto out;
 +  }
 +  
 +  /* Check whether target client is on the channel */
 +  if (!silc_server_client_on_channel(target_client, channel)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
 +                                        SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
 +    goto out;
 +  }
 +
 +  /* Get comment */
 +  tmp_len = 0;
 +  comment = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
 +  if (tmp_len > 128)
 +    comment = NULL;
 +
 +  /* Send command reply to sender */
 +  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, 
 +                                      SILC_STATUS_OK);
 +
 +  /* Send KICKED notify to local clients on the channel */
 +  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
 +                                   SILC_NOTIFY_TYPE_KICKED, 3,
 +                                   target_idp, target_idp_len,
 +                                   comment, comment ? strlen(comment) : 0,
 +                                   idp->data, idp->len);
 +  silc_buffer_free(idp);
 +
 +  /* Remove the client from the channel. If the channel does not exist
 +     after removing the client then the client kicked itself off the channel
 +     and we don't have to send anything after that. */
 +  if (!silc_server_remove_from_one_channel(server, NULL, channel, 
 +                                         target_client, FALSE))
 +    goto out;
 +
 +  /* Send KICKED notify to primary route */
 +  if (!server->standalone)
 +    silc_server_send_notify_kicked(server, server->router->connection,
 +                                 server->server_type == SILC_ROUTER ?
 +                                 TRUE : FALSE, channel,
 +                                 target_client->id, client->id, comment);
 +
 +  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
 +    /* Re-generate channel key */
 +    if (!silc_server_create_channel_key(server, channel, 0))
 +      goto out;
 +    
 +    /* Send the channel key to the channel. The key of course is not sent
 +       to the client who was kicked off the channel. */
 +    silc_server_send_channel_key(server, target_client->connection, channel, 
 +                               server->server_type == SILC_ROUTER ? 
 +                               FALSE : !server->standalone);
 +  }
 +
 + out:
 +  silc_server_command_free(cmd);
  }
  
 -SILC_SERVER_CMD_FUNC(invite)
 +/* Server side of OPER command. Client uses this comand to obtain server
 +   operator privileges to this server/router. */
 +
 +SILC_SERVER_CMD_FUNC(oper)
  {
-   SilcServerConfigSectionAdminConnection *admin;
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  unsigned char *username, *auth;
 +  uint32 tmp_len;
++  SilcServerConfigSectionAdmin *admin;
 +  SilcIDListData idata = (SilcIDListData)client;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
 +
 +  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
 +
 +  /* Get the username */
 +  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!username) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +
 +  /* Get the admin configuration */
 +  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
 +                                      username, client->nickname);
 +  if (!admin) {
 +    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
 +                                        username, client->nickname);
 +    if (!admin) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
 +                                          SILC_STATUS_ERR_AUTH_FAILED);
 +      goto out;
 +    }
 +  }
 +
 +  /* Get the authentication payload */
 +  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 +  if (!auth) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +
 +  /* Verify the authentication data */
 +  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
 +                           admin->auth_data, admin->auth_data_len,
 +                           idata->hash, client->id, SILC_ID_CLIENT)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
 +                                        SILC_STATUS_ERR_AUTH_FAILED);
 +    goto out;
 +  }
 +
 +  /* Client is now server operator */
 +  client->mode |= SILC_UMODE_SERVER_OPERATOR;
 +
 +  /* Update statistics */
 +  if (client->connection)
 +    server->stat.my_server_ops++;
 +  if (server->server_type == SILC_ROUTER)
 +    server->stat.server_ops++;
 +
 +  /* Send UMODE change to primary router */
 +  if (!server->standalone)
 +    silc_server_send_notify_umode(server, server->router->connection, TRUE,
 +                                client->id, client->mode);
 +
 +  /* Send reply to the sender */
 +  silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
 +                                      SILC_STATUS_OK);
 +
 + out:
 +  silc_server_command_free(cmd);
  }
  
 -/* Quits connection to client. This gets called if client won't
 -   close the connection even when it has issued QUIT command. */
 +/* Server side of SILCOPER command. Client uses this comand to obtain router
 +   operator privileges to this router. */
 +
 +SILC_SERVER_CMD_FUNC(silcoper)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  unsigned char *username, *auth;
 +  uint32 tmp_len;
-   SilcServerConfigSectionAdminConnection *admin;
++  SilcServerConfigSectionAdmin *admin;
 +  SilcIDListData idata = (SilcIDListData)client;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
 +
 +  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
 +
 +  if (server->server_type != SILC_ROUTER) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
 +                                        SILC_STATUS_ERR_AUTH_FAILED);
 +    goto out;
 +  }
 +
 +  /* Get the username */
 +  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!username) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +
 +  /* Get the admin configuration */
 +  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
 +                                      username, client->nickname);
 +  if (!admin) {
 +    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
 +                                        username, client->nickname);
 +    if (!admin) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
 +                                          SILC_STATUS_ERR_AUTH_FAILED);
 +      goto out;
 +    }
 +  }
 +
 +  /* Get the authentication payload */
 +  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 +  if (!auth) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +
 +  /* Verify the authentication data */
 +  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
 +                           admin->auth_data, admin->auth_data_len,
 +                           idata->hash, client->id, SILC_ID_CLIENT)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
 +                                        SILC_STATUS_ERR_AUTH_FAILED);
 +    goto out;
 +  }
 +
 +  /* Client is now router operator */
 +  client->mode |= SILC_UMODE_ROUTER_OPERATOR;
 +
 +  /* Update statistics */
 +  if (client->connection)
 +    server->stat.my_router_ops++;
 +  if (server->server_type == SILC_ROUTER)
 +    server->stat.router_ops++;
  
 -SILC_TASK_CALLBACK(silc_server_command_quit_cb)
 -{
 -  SilcServer server = (SilcServer)context;
 -  SilcSocketConnection sock = server->sockets[fd];
 +  /* Send UMODE change to primary router */
 +  if (!server->standalone)
 +    silc_server_send_notify_umode(server, server->router->connection, TRUE,
 +                                client->id, client->mode);
  
 -  /* Free all client specific data, such as client entry and entires
 -     on channels this client may be on. */
 -  silc_server_free_sock_user_data(server, sock);
 +  /* Send reply to the sender */
 +  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
 +                                      SILC_STATUS_OK);
  
 -  /* Close the connection on our side */
 -  silc_server_close_connection(server, sock);
 + out:
 +  silc_server_command_free(cmd);
  }
  
 -/* Quits SILC session. This is the normal way to disconnect client. */
 - 
 -SILC_SERVER_CMD_FUNC(quit)
 +/* Server side command of CONNECT. Connects us to the specified remote
 +   server or router. */
 +
 +SILC_SERVER_CMD_FUNC(connect)
  {
    SilcServerCommandContext cmd = (SilcServerCommandContext)context;
    SilcServer server = cmd->server;
 -  SilcSocketConnection sock = cmd->sock;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  unsigned char *tmp, *host;
 +  uint32 tmp_len;
 +  uint32 port = SILC_PORT;
  
 -  SILC_LOG_DEBUG(("Start"));
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CONNECT, cmd, 1, 2);
  
 -  /* We quit the connection with little timeout */
 -  silc_task_register(server->timeout_queue, sock->sock,
 -                   silc_server_command_quit_cb, server,
 -                   0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 +  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
 +
 +  /* Check whether client has the permissions. */
 +  if (client->mode == SILC_UMODE_NONE) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
 +                                        SILC_STATUS_ERR_NO_SERVER_PRIV);
 +    goto out;
 +  }
 +
 +  if (server->server_type == SILC_ROUTER && 
 +      client->mode & SILC_UMODE_SERVER_OPERATOR) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
 +                                        SILC_STATUS_ERR_NO_ROUTER_PRIV);
 +    goto out;
 +  }
 +
 +  /* Get the remote server */
 +  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!host) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +
 +  /* Get port */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 +  if (tmp)
 +    SILC_GET32_MSB(port, tmp);
  
 +  /* Create the connection. It is done with timeout and is async. */
 +  silc_server_create_connection(server, host, port);
 +
 +  /* Send reply to the sender */
 +  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
 +                                      SILC_STATUS_OK);
 +
 + out:
    silc_server_command_free(cmd);
  }
  
 -SILC_SERVER_CMD_FUNC(kill)
 -{
 -}
 +/* Server side of command BAN. This is used to manage the ban list of the
 +   channel. To add clients and remove clients from the ban list. */
  
 -SILC_SERVER_CMD_FUNC(info)
 +SILC_SERVER_CMD_FUNC(ban)
  {
 -}
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcBuffer packet;
 +  SilcChannelEntry channel;
 +  SilcChannelClientEntry chl;
 +  SilcChannelID *channel_id = NULL;
 +  unsigned char *id, *add, *del;
 +  uint32 id_len, tmp_len;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +
 +  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
  
 -SILC_SERVER_CMD_FUNC(connect)
 -{
 -}
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_BAN, cmd, 0, 3);
  
 -SILC_SERVER_CMD_FUNC(ping)
 -{
 -}
 +  /* Get Channel ID */
 +  id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
 +  if (id) {
 +    channel_id = silc_id_payload_parse_id(id, id_len);
 +    if (!channel_id) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
 +                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
 +      goto out;
 +    }
 +  }
  
 -SILC_SERVER_CMD_FUNC(oper)
 -{
 -}
 +  /* Get channel entry. The server must know about the channel since the
 +     client is expected to be on the channel. */
 +  channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                         channel_id, NULL);
 +  if (!channel) {
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
 +    }
 +  }
  
 -typedef struct {
 -  char *channel_name;
 -  char *nickname;
 -  char *username;
 -  char *hostname;
 -  SilcChannelList *channel;
 -  SilcServer server;
 -} JoinInternalContext;
 +  /* Check whether this client is on the channel */
 +  if (!silc_server_client_on_channel(client, channel)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
 +                                        SILC_STATUS_ERR_NOT_ON_CHANNEL);
 +    goto out;
 +  }
  
 -SILC_TASK_CALLBACK(silc_server_command_join_notify)
 -{
 -  JoinInternalContext *ctx = (JoinInternalContext *)context;
 +  /* Get entry to the channel user list */
 +  if (!silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
 +                                        SILC_STATUS_ERR_NOT_ON_CHANNEL);
 +    goto out;
 +  }
  
 -  if (ctx->channel->key && ctx->channel->key_len) {
 -    silc_server_send_notify_to_channel(ctx->server, ctx->channel,
 -                                     "%s (%s@%s) has joined channel %s",
 -                                     ctx->nickname, ctx->username,
 -                                     ctx->hostname, ctx->channel_name);
 -    silc_free(ctx);
 -  } else {
 -    silc_task_register(ctx->server->timeout_queue, fd,
 -                     silc_server_command_join_notify, context,
 -                     0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 +  /* The client must be at least channel operator. */
 +  if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_PRIV);
 +    goto out;
    }
 -}
  
 -/* Server side of command JOIN. Joins client into requested channel. If 
 -   the channel does not exist it will be created. */
 +  /* Get the new ban and add it to the ban list */
 +  add = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 +  if (add) {
 +    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));
 +    if (add[tmp_len - 1] == ',')
 +      add[tmp_len - 1] = '\0';
 +
 +    strncat(channel->ban_list, add, tmp_len);
 +    strncat(channel->ban_list, ",", 1);
 +  }
  
 -SILC_SERVER_CMD_FUNC(join)
 +  /* Get the ban to be removed and remove it from the list */
 +  del = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
 +  if (del && channel->ban_list) {
 +    char *start, *end, *n;
 +
 +    if (!strncmp(channel->ban_list, del, strlen(channel->ban_list) - 1)) {
 +      silc_free(channel->ban_list);
 +      channel->ban_list = NULL;
 +    } else {
 +      start = strstr(channel->ban_list, del);
 +      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;
 +      }
 +    }
 +  }
 +
 +  /* Send the BAN notify type to our primary router. */
 +  if (!server->standalone && (add || del))
 +    silc_server_send_notify_ban(server, server->router->connection,
 +                              server->server_type == SILC_ROUTER ?
 +                              TRUE : FALSE, channel, add, del);
 +
 +  /* Send the reply back to the client */
 +  packet = 
 +    silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
 +                                       SILC_STATUS_OK, ident, 2,
 +                                       2, id, id_len,
 +                                       3, channel->ban_list, 
 +                                       channel->ban_list ? 
 +                                       strlen(channel->ban_list) -1 : 0);
 +  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        packet->data, packet->len, FALSE);
 +    
 +  silc_buffer_free(packet);
 +
 + out:
 +  silc_free(channel_id);
 +  silc_server_command_free(cmd);
 +}
 +
 +/* Server side command of CLOSE. Closes connection to a specified server. */
 + 
 +SILC_SERVER_CMD_FUNC(close)
  {
    SilcServerCommandContext cmd = (SilcServerCommandContext)context;
    SilcServer server = cmd->server;
 -  SilcSocketConnection sock = cmd->sock;
 -  SilcBuffer buffer = cmd->packet->buffer;
 -  int argc, i, tmp_len;
 -  char *tmp, *channel_name = NULL, *cipher = NULL, *id_string = NULL;
 -  unsigned char *passphrase;
 -  SilcChannelList *channel;
 -  SilcServerID *router_id;
 -  SilcIDCache *id_cache;
 -  SilcBuffer packet, sp_buf;
 -  SilcClientList *client;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +  SilcServerEntry server_entry;
 +  SilcSocketConnection sock;
 +  unsigned char *tmp;
 +  uint32 tmp_len;
 +  unsigned char *name;
 +  uint32 port = SILC_PORT;
  
 -  SILC_LOG_DEBUG(("Start"));
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CLOSE, cmd, 1, 2);
  
 -#define LCC(x) server->local_list->channel_cache[(x) - 32]
 -#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
 +  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
  
 -  /* Check number of parameters */
 -  argc = silc_command_get_arg_num(cmd->payload);
 -  if (argc < 1) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 -                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +  /* Check whether client has the permissions. */
 +  if (client->mode == SILC_UMODE_NONE) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
 +                                        SILC_STATUS_ERR_NO_SERVER_PRIV);
      goto out;
    }
 -  if (argc > 3) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 -                                        SILC_STATUS_ERR_TOO_MANY_PARAMS);
 +
 +  /* Get the remote server */
 +  name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!name) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
      goto out;
    }
  
 -  /* Get channel name */
 -  tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
 -  if (silc_server_command_bad_chars(tmp) == TRUE) {
 -    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
 -                                        SILC_STATUS_ERR_BAD_CHANNEL);
 +  /* Get port */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 +  if (tmp)
 +    SILC_GET32_MSB(port, tmp);
 +
 +  server_entry = silc_idlist_find_server_by_conn(server->local_list,
 +                                               name, port, FALSE, NULL);
 +  if (!server_entry)
 +    server_entry = silc_idlist_find_server_by_conn(server->global_list,
 +                                                 name, port, FALSE, NULL);
 +  if (!server_entry) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
 +                                        SILC_STATUS_ERR_NO_SERVER_ID);
      goto out;
    }
 -  channel_name = strdup(tmp);
  
 -  /* Get passphrase */
 -  tmp = silc_command_get_arg_type(cmd->payload, 2, &tmp_len);
 -  if (tmp) {
 -    passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
 -    memcpy(passphrase, tmp, tmp_len);
 +  /* Send reply to the sender */
 +  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
 +                                      SILC_STATUS_OK);
 +
 +  /* Close the connection to the server */
 +  sock = (SilcSocketConnection)server_entry->connection;
 +
 +  /* If we shutdown primary router connection manually then don't trigger
 +     any reconnect or backup router connections, by setting the router
 +     to NULL here. */
 +  if (server->router == server_entry) {
 +    server->id_entry->router = NULL;
 +    server->router = NULL;
 +    server->standalone = TRUE;
    }
 +  silc_server_free_sock_user_data(server, sock, NULL);
 +  silc_server_close_connection(server, sock);
    
 -  /* Get cipher name */
 -  cipher = silc_command_get_arg_type(cmd->payload, 3, NULL);
 + out:
 +  silc_server_command_free(cmd);
 +}
  
 -  /* See if the channel exists */
 -  if (silc_idcache_find_by_data(LCC(channel_name[0]), LCCC(channel_name[0]), 
 -                              channel_name, &id_cache) == FALSE) {
 -    /* Channel not found */
 -    id_cache = NULL;
 -
 -    /* If we are standalone server we don't have a router, we just create 
 -       the channel by  ourselves. */
 -    if (server->standalone) {
 -      router_id = server->id;
 -      channel = silc_server_new_channel(server, router_id, 
 -                                      cipher, channel_name);
 -      goto join_channel;
 -    }
 -
 -    /* No channel ID found, the channel does not exist on our server.
 -       We send JOIN command to our router which will handle the joining
 -       procedure (either creates the channel if it doesn't exist or
 -       joins the client to it) - if we are normal server. */
 -    if (server->server_type == SILC_SERVER) {
 -
 -      /* Forward the received JOIN command to the router */
 -      silc_buffer_push(buffer, buffer->data - buffer->head);
 -      silc_server_packet_forward(server, (SilcSocketConnection)
 -                               server->id_entry->router->connection,
 -                               buffer->data, buffer->len,
 -                               TRUE);
 -      
 -      /* Add the command to be pending. It will be re-executed after
 -       router has replied back to us. */
 -      cmd->pending = TRUE;
 -      silc_server_command_pending(SILC_COMMAND_JOIN, 
 -                                silc_server_command_join, context);
 -      return;
 -    }
 +/* Server side command of SHUTDOWN. Shutdowns the server and closes all
 +   active connections. */
 + 
 +SILC_SERVER_CMD_FUNC(shutdown)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SHUTDOWN, cmd, 0, 0);
 +
 +  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    goto out;
 +
 +  /* Check whether client has the permission. */
 +  if (client->mode == SILC_UMODE_NONE) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
 +                                        SILC_STATUS_ERR_NO_SERVER_PRIV);
 +    goto out;
    }
  
 -  /* If we are router and the channel does not exist we will check our
 -     global list for the channel. */
 -  if (!id_cache && server->server_type == SILC_ROUTER) {
 +  /* Send reply to the sender */
 +  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
 +                                      SILC_STATUS_OK);
 +
 +  /* Then, gracefully, or not, bring the server down. */
 +  silc_server_stop(server);
 +  exit(0);
 +
 + out:
 +  silc_server_command_free(cmd);
 +}
 + 
 +/* Server side command of LEAVE. Removes client from a channel. */
 +
 +SILC_SERVER_CMD_FUNC(leave)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcSocketConnection sock = cmd->sock;
 +  SilcClientEntry id_entry = (SilcClientEntry)cmd->sock->user_data;
 +  SilcChannelID *id = NULL;
 +  SilcChannelEntry channel;
 +  uint32 len;
 +  unsigned char *tmp;
 +
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 2);
 +
 +  /* Get Channel ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
 +  id = silc_id_payload_parse_id(tmp, len);
 +  if (!id) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
 +  }
  
 -    /* Notify all routers about the new channel in SILC network. */
 -    if (!server->standalone) {
 -#if 0
 -      silc_server_send_new_id(server, server->id_entry->router->connection, 
 -                            TRUE,
 -                            xxx, SILC_ID_CHANNEL, SILC_ID_CHANNEL_LEN);
 -#endif
 +  /* Get channel entry */
 +  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
 +  if (!channel) {
 +    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
 +    if (!channel) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
      }
 +  }
  
 +  /* Check whether this client is on the channel */
 +  if (!silc_server_client_on_channel(id_entry, channel)) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
 +                                        SILC_STATUS_ERR_NOT_ON_CHANNEL);
 +    goto out;
    }
  
 -  channel = (SilcChannelList *)id_cache->context;
 +  /* Notify routers that they should remove this client from their list
 +     of clients on the channel. Send LEAVE notify type. */
 +  if (!server->standalone)
 +    silc_server_send_notify_leave(server, server->router->connection,
 +                                server->server_type == SILC_ROUTER ?
 +                                TRUE : FALSE, channel, id_entry->id);
  
 - join_channel:
 +  silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
 +                                      SILC_STATUS_OK);
  
 -  /* XXX must check whether the client already is on the channel */
 +  /* Remove client from channel */
 +  if (!silc_server_remove_from_one_channel(server, sock, channel, id_entry,
 +                                         TRUE))
 +    /* If the channel does not exist anymore we won't send anything */
 +    goto out;
  
 -  /* Join the client to the channel */
 -  i = channel->user_list_count;
 -  channel->user_list = silc_realloc(channel->user_list, 
 -                                  sizeof(*channel->user_list) * (i + 1));
 -  channel->user_list[i].mode = SILC_CHANNEL_UMODE_NONE;
 +  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
 +    /* Re-generate channel key */
 +    if (!silc_server_create_channel_key(server, channel, 0))
 +      goto out;
  
 -  /* If the JOIN request was forwarded to us we will make a bit slower
 -     query to get the client pointer. Otherwise, we get the client pointer
 -     real easy. */
 -  if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
 -    client = (SilcClientList *)sock->user_data;
 -    channel->user_list[i].client = client;
 -  } else {
 -    void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
 -    client = silc_idlist_find_client_by_id(server->local_list->clients, id);
 -    channel->user_list[i].client = client;
 -    silc_free(id);
 +    /* Send the channel key */
 +    silc_server_send_channel_key(server, NULL, channel, 
 +                               server->server_type == SILC_ROUTER ? 
 +                               FALSE : !server->standalone);
    }
 -  channel->user_list_count++;
  
 -  i = client->channel_count;
 -  client->channel = silc_realloc(client->channel, 
 -                               sizeof(*client->channel) * (i + 1));
 -  client->channel[i] = channel;
 -  client->channel_count++;
 + out:
 +  silc_free(id);
 +  silc_server_command_free(cmd);
 +}
  
 -  /* Notify router about new user on channel. If we are normal server
 -     we send it to our router, if we are router we send it to our
 -     primary route. */
 -  if (!server->standalone) {
 +/* Server side of command USERS. Resolves clients and their USERS currently
 +   joined on the requested channel. The list of Client ID's and their modes
 +   on the channel is sent back. */
  
 -  }
 +SILC_SERVER_CMD_FUNC(users)
 +{
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcChannelEntry channel;
 +  SilcChannelID *id = NULL;
 +  SilcBuffer packet, idp;
 +  unsigned char *channel_id;
 +  uint32 channel_id_len;
 +  SilcBuffer client_id_list;
 +  SilcBuffer client_mode_list;
 +  unsigned char lc[4];
 +  uint32 list_count = 0;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  char *channel_name;
  
 -  /* Send Channel ID to the client */
 -  if (!cmd->pending) {
 -    id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 -    sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
 -    if (!channel->topic)
 -      packet = 
 -      silc_command_encode_payload_va(SILC_COMMAND_JOIN, 3,
 -                                     sp_buf->data, sp_buf->len,
 -                                     channel_name, strlen(channel_name),
 -                                     id_string, SILC_ID_CHANNEL_LEN);
 -    else
 -      packet = 
 -      silc_command_encode_payload_va(SILC_COMMAND_JOIN, 4,
 -                                     sp_buf->data, sp_buf->len,
 -                                     channel_name, strlen(channel_name),
 -                                     id_string, SILC_ID_CHANNEL_LEN,
 -                                     channel->topic, 
 -                                     strlen(channel->topic));
 -
 -    if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
 -      void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
 -      silc_server_packet_send_dest(cmd->server, cmd->sock, 
 -                                 SILC_PACKET_COMMAND_REPLY, 0,
 -                                 id, cmd->packet->src_id_type,
 -                                 packet->data, packet->len, FALSE);
 -      silc_free(id);
 -    } else
 -      silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
 -                            packet->data, packet->len, FALSE);
 -    
 -    silc_buffer_free(packet);
 -    silc_free(sp_buf);
 +  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2);
 +
 +  /* Get Channel ID */
 +  channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
 +
 +  /* Get channel name */
 +  channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +
 +  if (!channel_id && !channel_name) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
 +                                        SILC_STATUS_ERR_NO_CHANNEL_ID);
 +    goto out;
    }
  
 -  /* Send channel key to the client. Client cannot start transmitting
 -     to the channel until we have sent the key. */
 -  if (!cmd->pending) {
 -    tmp_len = strlen(channel->channel_key->cipher->name);
 -    packet = 
 -      silc_channel_key_encode_payload(SILC_ID_CHANNEL_LEN, 
 -                                    id_string, tmp_len, 
 -                                    channel->channel_key->cipher->name,
 -                                    channel->key_len, channel->key);
 -    
 -    silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
 -                          packet->data, packet->len, FALSE);
 -    silc_buffer_free(packet);
 +  if (channel_id) {
 +    id = silc_id_payload_parse_id(channel_id, channel_id_len);
 +    if (!id) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
 +                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
 +      goto out;
 +    }
    }
  
 -  if (id_string)
 -    silc_free(id_string);
 +  /* If we are server and we don't know about this channel we will send
 +     the command to our router. If we know about the channel then we also
 +     have the list of users already. */
 +  if (id)
 +    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
 +  else
 +    channel = silc_idlist_find_channel_by_name(server->local_list, 
 +                                             channel_name, NULL);
 +
 +  if (!channel || channel->disabled) {
 +    if (server->server_type != SILC_ROUTER && !server->standalone &&
 +      !cmd->pending) {
 +      SilcBuffer tmpbuf;
 +      
 +      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +      
 +      /* Send USERS command */
 +      silc_server_packet_send(server, server->router->connection,
 +                            SILC_PACKET_COMMAND, cmd->packet->flags,
 +                            tmpbuf->data, tmpbuf->len, TRUE);
 +      
 +      /* Reprocess this packet after received reply */
 +      silc_server_command_pending(server, SILC_COMMAND_USERS, 
 +                                silc_command_get_ident(cmd->payload),
 +                                silc_server_command_users,
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_command_set_ident(cmd->payload, ident);
 +      silc_buffer_free(tmpbuf);
 +      silc_free(id);
 +      goto out;
 +    }
  
 -  /* Finally, send notify message to all clients on the channel about
 -     new user on the channel. */
 -  if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
 -    if (!cmd->pending) {
 -      silc_server_send_notify_to_channel(server, channel,
 -                                       "%s (%s@%s) has joined channel %s",
 -                                       client->nickname, client->username,
 -                                       sock->hostname ? sock->hostname :
 -                                       sock->ip, channel_name);
 -    } else {
 -      /* This is pending command request. Send the notify after we have
 -       received the key for the channel from the router. */
 -      JoinInternalContext *ctx = silc_calloc(1, sizeof(*ctx));
 -      ctx->channel_name = channel_name;
 -      ctx->nickname = client->nickname;
 -      ctx->username = client->username;
 -      ctx->hostname = sock->hostname ? sock->hostname : sock->ip;
 -      ctx->channel = channel;
 -      ctx->server = server;
 -      silc_task_register(server->timeout_queue, sock->sock,
 -                       silc_server_command_join_notify, ctx,
 -                       0, 100000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 +    /* Check the global list as well. */
 +    if (id)
 +      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
 +    else
 +      channel = silc_idlist_find_channel_by_name(server->global_list, 
 +                                               channel_name, NULL);
 +    if (!channel) {
 +      /* Channel really does not exist */
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
      }
    }
  
 +  /* If the channel is private or secret do not send anything, unless the
 +     user requesting this command is on the channel. */
 +  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
 +    if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)
 +      && !silc_server_client_on_channel(cmd->sock->user_data, channel)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
 +    }
 +  } else {
 +    if (channel->mode & 
 +      (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
 +                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL);
 +      goto out;
 +    }
 +  }
 +
 +  /* Get the users list */
 +  silc_server_get_users_on_channel(server, channel, &client_id_list,
 +                                 &client_mode_list, &list_count);
 +
 +  /* List count */
 +  SILC_PUT32_MSB(list_count, lc);
 +
 +  /* Send reply */
 +  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 +  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
 +                                              SILC_STATUS_OK, ident, 4,
 +                                              2, idp->data, idp->len,
 +                                              3, lc, 4,
 +                                              4, client_id_list->data,
 +                                              client_id_list->len,
 +                                              5, client_mode_list->data,
 +                                              client_mode_list->len);
 +  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        packet->data, packet->len, FALSE);
 +    
 +  silc_buffer_free(idp);
 +  silc_buffer_free(packet);
 +  silc_buffer_free(client_id_list);
 +  silc_buffer_free(client_mode_list);
 +  silc_free(id);
 +
   out:
    silc_server_command_free(cmd);
 -#undef LCC
 -#undef LCCC
  }
  
 -/* Server side of command MOTD. Sends servers current "message of the
 -   day" to the client. */
 +/* Server side of command GETKEY. This fetches the client's public key
 +   from the server where to the client is connected. */
  
 -SILC_SERVER_CMD_FUNC(motd)
 +SILC_SERVER_CMD_FUNC(getkey)
  {
 +  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
 +  SilcServer server = cmd->server;
 +  SilcBuffer packet;
 +  SilcClientEntry client;
 +  SilcServerEntry server_entry;
 +  SilcClientID *client_id = NULL;
 +  SilcServerID *server_id = NULL;
 +  SilcIDPayload idp = NULL;
 +  uint16 ident = silc_command_get_ident(cmd->payload);
 +  unsigned char *tmp, *pkdata;
 +  uint32 tmp_len, pklen;
 +  SilcBuffer pk = NULL;
 +  SilcIdType id_type;
 +  SilcPublicKey public_key;
  
    SILC_LOG_DEBUG(("Start"));
  
 -}
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  if (!tmp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
 +  idp = silc_id_payload_parse(tmp, tmp_len);
 +  if (!idp) {
 +    silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
 +                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 +    goto out;
 +  }
  
 -SILC_SERVER_CMD_FUNC(umode)
 -{
 -}
 +  id_type = silc_id_payload_get_type(idp);
 +  if (id_type == SILC_ID_CLIENT) {
 +    client_id = silc_id_payload_get_id(idp);
 +
 +    /* If the client is not found from local list there is no chance it
 +       would be locally connected client so send the command further. */
 +    client = silc_idlist_find_client_by_id(server->local_list, 
 +                                         client_id, TRUE, NULL);
 +    if (!client)
 +      client = silc_idlist_find_client_by_id(server->global_list, 
 +                                           client_id, TRUE, NULL);
 +    
 +    if ((!client && !cmd->pending && !server->standalone) ||
 +      (client && !client->connection && !cmd->pending) ||
 +      (client && !client->data.public_key && !cmd->pending)) {
 +      SilcBuffer tmpbuf;
 +      uint16 old_ident;
 +      SilcSocketConnection dest_sock;
 +      
 +      dest_sock = silc_server_get_client_route(server, NULL, 0, 
 +                                             client_id, NULL);
 +      if (!dest_sock)
 +      goto out;
 +      
 +      old_ident = silc_command_get_ident(cmd->payload);
 +      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +      
 +      silc_server_packet_send(server, dest_sock,
 +                            SILC_PACKET_COMMAND, cmd->packet->flags,
 +                            tmpbuf->data, tmpbuf->len, TRUE);
 +      
 +      /* Reprocess this packet after received reply from router */
 +      silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
 +                                silc_command_get_ident(cmd->payload),
 +                                silc_server_command_getkey,
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_command_set_ident(cmd->payload, old_ident);
 +      silc_buffer_free(tmpbuf);
 +      goto out;
 +    }
  
 -SILC_SERVER_CMD_FUNC(cmode)
 -{
 -}
 +    if (!client) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
 +                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
 +      goto out;
 +    }
  
 -SILC_SERVER_CMD_FUNC(kick)
 -{
 -}
 +    /* The client is locally connected, just get the public key and
 +       send it back. If they key does not exist then do not send it, 
 +       send just OK reply */
 +    public_key = client->data.public_key;
 +    if (!public_key) {
 +      pkdata = NULL;
 +      pklen = 0;
 +    } else {
 +      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
 +      pk = silc_buffer_alloc(4 + tmp_len);
 +      silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
 +      silc_buffer_format(pk,
 +                       SILC_STR_UI_SHORT(tmp_len),
 +                       SILC_STR_UI_SHORT(SILC_SKE_PK_TYPE_SILC),
 +                       SILC_STR_UI_XNSTRING(tmp, tmp_len),
 +                       SILC_STR_END);
 +      silc_free(tmp);
 +      pkdata = pk->data;
 +      pklen = pk->len;
 +    }
 +  } else if (id_type == SILC_ID_SERVER) {
 +    server_id = silc_id_payload_get_id(idp);
 +
 +    /* If the server is not found from local list there is no chance it
 +       would be locally connected server so send the command further. */
 +    server_entry = silc_idlist_find_server_by_id(server->local_list, 
 +                                               server_id, TRUE, NULL);
 +    if (!server_entry)
 +      server_entry = silc_idlist_find_server_by_id(server->global_list, 
 +                                                 server_id, TRUE, NULL);
 +    
 +    if (server_entry != server->id_entry &&
 +      ((!server_entry && !cmd->pending && !server->standalone) ||
 +       (server_entry && !server_entry->connection && !cmd->pending &&
 +        !server->standalone) ||
 +       (server_entry && !server_entry->data.public_key && !cmd->pending &&
 +        !server->standalone))) {
 +      SilcBuffer tmpbuf;
 +      uint16 old_ident;
 +      
 +      old_ident = silc_command_get_ident(cmd->payload);
 +      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
 +      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 +      
 +      silc_server_packet_send(server, server->router->connection,
 +                            SILC_PACKET_COMMAND, cmd->packet->flags,
 +                            tmpbuf->data, tmpbuf->len, TRUE);
 +      
 +      /* Reprocess this packet after received reply from router */
 +      silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
 +                                silc_command_get_ident(cmd->payload),
 +                                silc_server_command_getkey,
 +                                silc_server_command_dup(cmd));
 +      cmd->pending = TRUE;
 +      silc_command_set_ident(cmd->payload, old_ident);
 +      silc_buffer_free(tmpbuf);
 +      goto out;
 +    }
  
 -SILC_SERVER_CMD_FUNC(restart)
 -{
 -}
 - 
 -SILC_SERVER_CMD_FUNC(close)
 -{
 -}
 - 
 -SILC_SERVER_CMD_FUNC(die)
 -{
 -}
 - 
 -SILC_SERVER_CMD_FUNC(silcoper)
 -{
 -}
 +    if (!server_entry) {
 +      silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
 +                                          SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
 +      goto out;
 +    }
  
 -SILC_SERVER_CMD_FUNC(leave)
 -{
 -}
 +    /* If they key does not exist then do not send it, send just OK reply */
 +    public_key = (!server_entry->data.public_key ? 
 +                (server_entry == server->id_entry ? server->public_key :
 +                 NULL) : server_entry->data.public_key);
 +    if (!public_key) {
 +      pkdata = NULL;
 +      pklen = 0;
 +    } else {
 +      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
 +      pk = silc_buffer_alloc(4 + tmp_len);
 +      silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
 +      silc_buffer_format(pk,
 +                       SILC_STR_UI_SHORT(tmp_len),
 +                       SILC_STR_UI_SHORT(SILC_SKE_PK_TYPE_SILC),
 +                       SILC_STR_UI_XNSTRING(tmp, tmp_len),
 +                       SILC_STR_END);
 +      silc_free(tmp);
 +      pkdata = pk->data;
 +      pklen = pk->len;
 +    }
 +  } else {
 +    goto out;
 +  }
  
 -SILC_SERVER_CMD_FUNC(names)
 -{
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
 +  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_GETKEY,
 +                                              SILC_STATUS_OK, ident, 
 +                                              pkdata ? 2 : 1,
 +                                              2, tmp, tmp_len,
 +                                              3, pkdata, pklen);
 +  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
 +                        packet->data, packet->len, FALSE);
 +  silc_buffer_free(packet);
 +
 +  if (pk)
 +    silc_buffer_free(pk);
 +
 + out:
 +  if (idp)
 +    silc_id_payload_free(idp);
 +  silc_free(client_id);
 +  silc_free(server_id);
 +  silc_server_command_free(cmd);
  }
index d1f6db8a8c391041b24b8777f72288b2c8d3d338,0000000000000000000000000000000000000000..2f3aa2f7206a5153be05e38422658f1fcd04fba5
mode 100644,000000..100644
--- /dev/null
@@@ -1,2605 -1,0 +1,2605 @@@
-   SilcServerConfigSectionClientConnection *client = NULL;
 +/*
 +
 +  packet_receive.c
 +
 +  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +  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
 +  the Free Software Foundation; either version 2 of the License, or
 +  (at your option) any later version.
 +  
 +  This program is distributed in the hope that it will be useful,
 +  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +  GNU General Public License for more details.
 +
 +*/
 +/*
 + * Server packet routines to handle received packets.
 + */
 +/* $Id$ */
 +
 +#include "serverincludes.h"
 +#include "server_internal.h"
 +
 +extern char *server_version;
 +
 +/* Received notify packet. Server can receive notify packets from router. 
 +   Server then relays the notify messages to clients if needed. */
 +
 +void silc_server_notify(SilcServer server,
 +                      SilcSocketConnection sock,
 +                      SilcPacketContext *packet)
 +{
 +  SilcNotifyPayload payload;
 +  SilcNotifyType type;
 +  SilcArgumentPayload args;
 +  SilcChannelID *channel_id = NULL, *channel_id2;
 +  SilcClientID *client_id, *client_id2;
 +  SilcServerID *server_id;
 +  SilcChannelEntry channel;
 +  SilcClientEntry client;
 +  SilcServerEntry server_entry;
 +  SilcChannelClientEntry chl;
 +  SilcIDCacheEntry cache;
 +  SilcHashTableList htl;
 +  uint32 mode;
 +  unsigned char *tmp;
 +  uint32 tmp_len;
 +  bool local;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
 +      packet->src_id_type != SILC_ID_SERVER)
 +    return;
 +
 +  if (!packet->dst_id)
 +    return;
 +
 +  /* If the packet is destined directly to a client then relay the packet
 +     before processing it. */
 +  if (packet->dst_id_type == SILC_ID_CLIENT) {
 +    SilcIDListData idata;
 +    SilcSocketConnection dst_sock;
 +
 +    /* Get the route to the client */
 +    dst_sock = silc_server_get_client_route(server, packet->dst_id,
 +                                          packet->dst_id_len, NULL, &idata);
 +    if (dst_sock)
 +      /* Relay the packet */
 +      silc_server_relay_packet(server, dst_sock, idata->send_key,
 +                             idata->hmac_receive, idata->psn_send++,
 +                             packet, TRUE);
 +  }
 +
 +  /* Parse the Notify Payload */
 +  payload = silc_notify_payload_parse(packet->buffer->data,
 +                                    packet->buffer->len);
 +  if (!payload)
 +    return;
 +
 +  /* If we are router and this packet is not already broadcast packet
 +     we will broadcast it. The sending socket really cannot be router or
 +     the router is buggy. If this packet is coming from router then it must
 +     have the broadcast flag set already and we won't do anything. */
 +  if (!server->standalone && server->server_type == SILC_ROUTER &&
 +      sock->type == SILC_SOCKET_TYPE_SERVER &&
 +      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
 +    SILC_LOG_DEBUG(("Broadcasting received Notify packet"));
 +    if (packet->dst_id_type == SILC_ID_CHANNEL) {
 +      /* Packet is destined to channel */
 +      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
 +                                packet->dst_id_type);
 +      if (!channel_id)
 +      goto out;
 +
 +      silc_server_packet_send_dest(server, server->router->connection, 
 +                                 packet->type,
 +                                 packet->flags | SILC_PACKET_FLAG_BROADCAST, 
 +                                 channel_id, SILC_ID_CHANNEL,
 +                                 packet->buffer->data, packet->buffer->len, 
 +                                 FALSE);
 +      silc_server_backup_send_dest(server, (SilcServerEntry)sock->user_data, 
 +                                 packet->type, packet->flags,
 +                                 channel_id, SILC_ID_CHANNEL,
 +                                 packet->buffer->data, packet->buffer->len, 
 +                                 FALSE, TRUE);
 +    } else {
 +      /* Packet is destined to client or server */
 +      silc_server_packet_send(server, server->router->connection, 
 +                            packet->type,
 +                            packet->flags | SILC_PACKET_FLAG_BROADCAST, 
 +                            packet->buffer->data, packet->buffer->len, 
 +                            FALSE);
 +      silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
 +                            packet->type, packet->flags,
 +                            packet->buffer->data, packet->buffer->len, 
 +                            FALSE, TRUE);
 +    }
 +  }
 +
 +  type = silc_notify_get_type(payload);
 +  args = silc_notify_get_args(payload);
 +  if (!args)
 +    goto out;
 +
 +  switch(type) {
 +  case SILC_NOTIFY_TYPE_JOIN:
 +    /* 
 +     * Distribute the notify to local clients on the channel
 +     */
 +    SILC_LOG_DEBUG(("JOIN notify"));
 +
 +    /* Get Channel ID */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!channel_id)
 +      goto out;
 +
 +    /* Get channel entry */
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +    silc_free(channel_id);
 +
 +    /* Get client ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!client_id)
 +      goto out;
 +
 +    /* If the the client is not in local list we check global list (ie. the
 +       channel will be global channel) and if it does not exist then create
 +       entry for the client. */
 +    client = silc_idlist_find_client_by_id(server->global_list, 
 +                                         client_id, server->server_type, 
 +                                         NULL);
 +    if (!client) {
 +      client = silc_idlist_find_client_by_id(server->local_list, 
 +                                           client_id, server->server_type,
 +                                           NULL);
 +      if (!client) {
 +      /* If router did not find the client the it is bogus */
 +      if (server->server_type != SILC_SERVER)
 +        goto out;
 +
 +      client = 
 +        silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
 +                               silc_id_dup(client_id, SILC_ID_CLIENT), 
 +                               sock->user_data, NULL, 0);
 +      if (!client) {
 +        SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
 +        silc_free(client_id);
 +        goto out;
 +      }
 +
 +      client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
 +      }
 +    }
 +
 +    /* Do not process the notify if the client is not registered */
 +    if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
 +      break;
 +
 +    /* Do not add client to channel if it is there already */
 +    if (silc_server_client_on_channel(client, channel)) {
 +      SILC_LOG_DEBUG(("Client already on channel"));
 +      break;
 +    }
 +
 +    /* Send to channel */
 +    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
 +                                     FALSE, packet->buffer->data, 
 +                                     packet->buffer->len, FALSE);
 +
 +    if (server->server_type != SILC_ROUTER && 
 +      sock->type == SILC_SOCKET_TYPE_ROUTER)
 +      /* The channel is global now */
 +      channel->global_users = TRUE;
 +
 +    SILC_LOG_DEBUG(("Joining to channel %s", channel->channel_name));
 +
 +    /* JOIN the global client to the channel (local clients (if router 
 +       created the channel) is joined in the pending JOIN command). */
 +    chl = silc_calloc(1, sizeof(*chl));
 +    chl->client = client;
 +    chl->channel = channel;
 +
 +    /* If this is the first one on the channel then it is the founder of
 +       the channel. */
 +    if (!silc_hash_table_count(channel->user_list))
 +      chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 +
 +    silc_hash_table_add(channel->user_list, client, chl);
 +    silc_hash_table_add(client->channels, channel, chl);
 +    silc_free(client_id);
 +
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_LEAVE:
 +    /* 
 +     * Distribute the notify to local clients on the channel
 +     */
 +    SILC_LOG_DEBUG(("LEAVE notify"));
 +
 +    if (!channel_id) {
 +      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
 +                                packet->dst_id_type);
 +      if (!channel_id)
 +      goto out;
 +    }
 +
 +    /* Get channel entry */
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) { 
 +      channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +
 +    /* Get client ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp) {
 +      silc_free(channel_id);
 +      goto out;
 +    }
 +    client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!client_id) {
 +      silc_free(channel_id);
 +      goto out;
 +    }
 +
 +    /* Get client entry */
 +    client = silc_idlist_find_client_by_id(server->global_list, 
 +                                         client_id, TRUE, NULL);
 +    if (!client) {
 +      client = silc_idlist_find_client_by_id(server->local_list, 
 +                                           client_id, TRUE, NULL);
 +      if (!client) {
 +      silc_free(client_id);
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +    silc_free(client_id);
 +
 +    /* Check if on channel */
 +    if (!silc_server_client_on_channel(client, channel))
 +      break;
 +
 +    /* Send the leave notify to channel */
 +    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
 +                                     FALSE, packet->buffer->data, 
 +                                     packet->buffer->len, FALSE);
 +
 +    /* Remove the user from channel */
 +    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_SIGNOFF:
 +    /* 
 +     * Distribute the notify to local clients on the channel
 +     */
 +    SILC_LOG_DEBUG(("SIGNOFF notify"));
 +
 +    /* Get client ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!client_id)
 +      goto out;
 +
 +    /* Get client entry */
 +    client = silc_idlist_find_client_by_id(server->global_list, 
 +                                         client_id, TRUE, &cache);
 +    if (!client) {
 +      client = silc_idlist_find_client_by_id(server->local_list, 
 +                                           client_id, TRUE, &cache);
 +      if (!client) {
 +      silc_free(client_id);
 +      goto out;
 +      }
 +    }
 +    silc_free(client_id);
 +
 +    /* Get signoff message */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (tmp_len > 128)
 +      tmp = NULL;
 +
 +    /* Update statistics */
 +    server->stat.clients--;
 +    if (server->server_type == SILC_ROUTER)
 +      server->stat.cell_clients--;
 +    SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
 +    SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
 +
 +    /* Remove the client from all channels. */
 +    silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
 +
 +    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
 +    cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_TOPIC_SET:
 +    /* 
 +     * Distribute the notify to local clients on the channel
 +     */
 +
 +    SILC_LOG_DEBUG(("TOPIC SET notify"));
 +
 +    if (!channel_id) {
 +      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
 +                                packet->dst_id_type);
 +      if (!channel_id)
 +      goto out;
 +    }
 +
 +    /* Get channel entry */
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +
 +    /* Get the topic */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp) {
 +      silc_free(channel_id);
 +      goto out;
 +    }
 +
 +    silc_free(channel->topic);
 +    channel->topic = strdup(tmp);
 +
 +    /* Send the same notify to the channel */
 +    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
 +                                     FALSE, packet->buffer->data, 
 +                                     packet->buffer->len, FALSE);
 +    silc_free(channel_id);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_NICK_CHANGE:
 +    {
 +      /* 
 +       * Distribute the notify to local clients on the channel
 +       */
 +      unsigned char *id, *id2;
 +
 +      SILC_LOG_DEBUG(("NICK CHANGE notify"));
 +      
 +      /* Get old client ID */
 +      id = silc_argument_get_arg_type(args, 1, &tmp_len);
 +      if (!id)
 +      goto out;
 +      client_id = silc_id_payload_parse_id(id, tmp_len);
 +      if (!client_id)
 +      goto out;
 +      
 +      /* Get new client ID */
 +      id2 = silc_argument_get_arg_type(args, 2, &tmp_len);
 +      if (!id2)
 +      goto out;
 +      client_id2 = silc_id_payload_parse_id(id2, tmp_len);
 +      if (!client_id2)
 +      goto out;
 +      
 +      SILC_LOG_DEBUG(("Old Client ID id(%s)", 
 +                    silc_id_render(client_id, SILC_ID_CLIENT)));
 +      SILC_LOG_DEBUG(("New Client ID id(%s)", 
 +                    silc_id_render(client_id2, SILC_ID_CLIENT)));
 +
 +      /* Replace the Client ID */
 +      client = silc_idlist_replace_client_id(server->global_list, client_id,
 +                                           client_id2);
 +      if (!client)
 +      client = silc_idlist_replace_client_id(server->local_list, client_id, 
 +                                             client_id2);
 +
 +      if (client) {
 +      /* The nickname is not valid anymore, set it NULL. This causes that
 +         the nickname will be queried if someone wants to know it. */
 +      if (client->nickname)
 +        silc_free(client->nickname);
 +      client->nickname = NULL;
 +
 +      /* Send the NICK_CHANGE notify type to local clients on the channels
 +         this client is joined to. */
 +      silc_server_send_notify_on_channels(server, NULL, client, 
 +                                          SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
 +                                          id, tmp_len, 
 +                                          id2, tmp_len);
 +      }
 +
 +      silc_free(client_id);
 +      if (!client)
 +      silc_free(client_id2);
 +      break;
 +    }
 +
 +  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
 +    /* 
 +     * Distribute the notify to local clients on the channel
 +     */
 +    
 +    SILC_LOG_DEBUG(("CMODE CHANGE notify"));
 +      
 +    if (!channel_id) {
 +      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
 +                                packet->dst_id_type);
 +      if (!channel_id)
 +      goto out;
 +    }
 +
 +    /* Get channel entry */
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +
 +    /* Get the mode */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp) {
 +      silc_free(channel_id);
 +      goto out;
 +    }
 +
 +    SILC_GET32_MSB(mode, tmp);
 +
 +    /* Check if mode changed */
 +    if (channel->mode == mode)
 +      break;
 +
 +    /* Send the same notify to the channel */
 +    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
 +                                     FALSE, packet->buffer->data, 
 +                                     packet->buffer->len, FALSE);
 +
 +    /* If the channel had private keys set and the mode was removed then
 +       we must re-generate and re-distribute a new channel key */
 +    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
 +      !(mode & SILC_CHANNEL_MODE_PRIVKEY)) {
 +      /* Re-generate channel key */
 +      if (!silc_server_create_channel_key(server, channel, 0))
 +      goto out;
 +      
 +      /* Send the channel key. This sends it to our local clients and if
 +       we are normal server to our router as well. */
 +      silc_server_send_channel_key(server, NULL, channel, 
 +                                 server->server_type == SILC_ROUTER ? 
 +                                 FALSE : !server->standalone);
 +    }
 +
 +    /* Change mode */
 +    channel->mode = mode;
 +    silc_free(channel_id);
 +
 +    /* Get the hmac */
 +    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
 +    if (tmp) {
 +      unsigned char hash[32];
 +
 +      if (channel->hmac)
 +      silc_hmac_free(channel->hmac);
 +      if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
 +      goto out;
 +
 +      /* Set the HMAC key out of current channel key. The client must do
 +       this locally. */
 +      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
 +                   channel->key_len / 8, 
 +                   hash);
 +      silc_hmac_set_key(channel->hmac, hash, 
 +                      silc_hash_len(silc_hmac_get_hash(channel->hmac)));
 +      memset(hash, 0, sizeof(hash));
 +    }
 +
 +    /* Get the passphrase */
 +    tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
 +    if (tmp) {
 +      silc_free(channel->passphrase);
 +      channel->passphrase = strdup(tmp);
 +    }
 +
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
 +    {
 +      /* 
 +       * Distribute the notify to local clients on the channel
 +       */
 +      SilcChannelClientEntry chl2 = NULL;
 +      bool notify_sent = FALSE;
 +      
 +      SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
 +      
 +      if (!channel_id) {
 +      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
 +                                  packet->dst_id_type);
 +      if (!channel_id)
 +        goto out;
 +      }
 +
 +      /* Get channel entry */
 +      channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                               channel_id, NULL);
 +      if (!channel) {
 +        silc_free(channel_id);
 +        goto out;
 +      }
 +      }
 +
 +      /* Get the mode */
 +      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +      if (!tmp) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +      
 +      SILC_GET32_MSB(mode, tmp);
 +      
 +      /* Get target client */
 +      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
 +      if (!tmp)
 +      goto out;
 +      client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +      if (!client_id)
 +      goto out;
 +      
 +      /* Get client entry */
 +      client = silc_idlist_find_client_by_id(server->global_list, 
 +                                           client_id, TRUE, NULL);
 +      if (!client) {
 +      client = silc_idlist_find_client_by_id(server->local_list, 
 +                                             client_id, TRUE, NULL);
 +      if (!client) {
 +        silc_free(client_id);
 +        goto out;
 +      }
 +      }
 +      silc_free(client_id);
 +
 +      /* Get entry to the channel user list */
 +      silc_hash_table_list(channel->user_list, &htl);
 +      while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +      /* If the mode is channel founder and we already find a client 
 +         to have that mode on the channel we will enforce the sender
 +         to change the channel founder mode away. There can be only one
 +         channel founder on the channel. */
 +      if (server->server_type == SILC_ROUTER &&
 +          mode & SILC_CHANNEL_UMODE_CHANFO &&
 +          chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
 +        SilcBuffer idp;
 +        unsigned char cumode[4];
 +
 +        if (chl->client == client && chl->mode == mode) {
 +          notify_sent = TRUE;
 +          break;
 +        }
 +
 +        mode &= ~SILC_CHANNEL_UMODE_CHANFO;
 +        silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
 +                                       client->id, SILC_ID_CLIENT,
 +                                       client->id);
 +        
 +        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +        SILC_PUT32_MSB(mode, cumode);
 +        silc_server_send_notify_to_channel(server, sock, channel, FALSE, 
 +                                           SILC_NOTIFY_TYPE_CUMODE_CHANGE,
 +                                           3, idp->data, idp->len,
 +                                           cumode, 4,
 +                                           idp->data, idp->len);
 +        silc_buffer_free(idp);
 +        notify_sent = TRUE;
 +
 +        /* Force the mode change if we alredy set the mode */
 +        if (chl2) {
 +          chl2->mode = mode;
 +          silc_free(channel_id);
 +          silc_hash_table_list_reset(&htl);
 +          goto out;
 +        }
 +      }
 +      
 +      if (chl->client == client) {
 +        if (chl->mode == mode) {
 +          notify_sent = TRUE;
 +          break;
 +        }
 +
 +        SILC_LOG_DEBUG(("Changing the channel user mode"));
 +
 +        /* Change the mode */
 +        chl->mode = mode;
 +        if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
 +          break;
 +        
 +        chl2 = chl;
 +      }
 +      }
 +      silc_hash_table_list_reset(&htl);
 +      
 +      /* Send the same notify to the channel */
 +      if (!notify_sent)
 +      silc_server_packet_send_to_channel(server, sock, channel, 
 +                                         packet->type, 
 +                                         FALSE, packet->buffer->data, 
 +                                         packet->buffer->len, FALSE);
 +      
 +      silc_free(channel_id);
 +      break;
 +    }
 +
 +  case SILC_NOTIFY_TYPE_INVITE:
 +
 +    if (packet->dst_id_type == SILC_ID_CLIENT)
 +      goto out;
 +
 +    SILC_LOG_DEBUG(("INVITE notify"));
 +
 +    /* Get Channel ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!channel_id)
 +      goto out;
 +
 +    /* Get channel entry */
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +    silc_free(channel_id);
 +
 +    /* Get the added invite */
 +    tmp = silc_argument_get_arg_type(args, 3, &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);
 +    }
 +
 +    /* Get the deleted invite */
 +    tmp = silc_argument_get_arg_type(args, 4, &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;
 +      }
 +      }
 +    }
 +
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
 +    /*
 +     * Distribute to the local clients on the channel and change the
 +     * channel ID.
 +     */
 +
 +    SILC_LOG_DEBUG(("CHANNEL CHANGE"));
 +
 +    if (sock->type != SILC_SOCKET_TYPE_ROUTER)
 +      break;
 +
 +    /* Get the old Channel ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!channel_id)
 +      goto out;
 +
 +    /* Get the channel entry */
 +    channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +
 +    /* Send the notify to the channel */
 +    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
 +                                     FALSE, packet->buffer->data, 
 +                                     packet->buffer->len, FALSE);
 +
 +    /* Get the new Channel ID */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    channel_id2 = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!channel_id2)
 +      goto out;
 +
 +    SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
 +                  silc_id_render(channel_id, SILC_ID_CHANNEL)));
 +    SILC_LOG_DEBUG(("New Channel ID id(%s)", 
 +                  silc_id_render(channel_id2, SILC_ID_CHANNEL)));
 +
 +    /* Replace the Channel ID */
 +    if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
 +                                      channel_id2))
 +      if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
 +                                        channel_id2)) {
 +      silc_free(channel_id2);
 +      channel_id2 = NULL;
 +      }
 +
 +    if (channel_id2) {
 +      SilcBuffer users = NULL, users_modes = NULL;
 +
 +      /* Re-announce this channel which ID was changed. */
 +      silc_server_send_new_channel(server, sock, FALSE, channel->channel_name,
 +                                 channel->id, 
 +                                 silc_id_get_len(channel->id, 
 +                                                 SILC_ID_CHANNEL),
 +                                 channel->mode);
 +
 +      /* Re-announce our clients on the channel as the ID has changed now */
 +      silc_server_announce_get_channel_users(server, channel, &users,
 +                                           &users_modes);
 +      if (users) {
 +      silc_buffer_push(users, users->data - users->head);
 +      silc_server_packet_send(server, sock,
 +                              SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
 +                              users->data, users->len, FALSE);
 +      silc_buffer_free(users);
 +      }
 +      if (users_modes) {
 +      silc_buffer_push(users_modes, users_modes->data - users_modes->head);
 +      silc_server_packet_send_dest(server, sock,
 +                                   SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
 +                                   channel->id, SILC_ID_CHANNEL,
 +                                   users_modes->data, 
 +                                   users_modes->len, FALSE);
 +      silc_buffer_free(users_modes);
 +      }
 +
 +      /* Re-announce channel's topic */
 +      if (channel->topic) {
 +      silc_server_send_notify_topic_set(server, sock,
 +                                        server->server_type == SILC_ROUTER ?
 +                                        TRUE : FALSE, channel, 
 +                                        channel->id, SILC_ID_CHANNEL,
 +                                        channel->topic);
 +      }
 +    }
 +
 +    silc_free(channel_id);
 +
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
 +    /* 
 +     * Remove the server entry and all clients that this server owns.
 +     */
 +
 +    SILC_LOG_DEBUG(("SERVER SIGNOFF notify"));
 +
 +    /* Get Server ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    server_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!server_id)
 +      goto out;
 +
 +    /* Get server entry */
 +    server_entry = silc_idlist_find_server_by_id(server->global_list, 
 +                                               server_id, TRUE, NULL);
 +    local = TRUE;
 +    if (!server_entry) {
 +      server_entry = silc_idlist_find_server_by_id(server->local_list, 
 +                                                 server_id, TRUE, NULL);
 +      local = TRUE;
 +      if (!server_entry) {
 +      /* If we are normal server then we might not have the server. Check
 +         whether router was kind enough to send the list of all clients
 +         that actually was to be removed. Remove them if the list is
 +         available. */
 +      if (server->server_type != SILC_ROUTER &&
 +          silc_argument_get_arg_num(args) > 1) {
 +        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)
 +            continue;
 +          client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +          if (!client_id)
 +            continue;
 +
 +          /* Get client entry */
 +          client = silc_idlist_find_client_by_id(server->global_list, 
 +                                                 client_id, TRUE, &cache);
 +          local = TRUE;
 +          if (!client) {
 +            client = silc_idlist_find_client_by_id(server->local_list, 
 +                                                   client_id, TRUE, &cache);
 +            local = FALSE;
 +            if (!client) {
 +              silc_free(client_id);
 +              continue;
 +            }
 +          }
 +          silc_free(client_id);
 +
 +          /* Update statistics */
 +          server->stat.clients--;
 +          if (server->server_type == SILC_ROUTER)
 +            server->stat.cell_clients--;
 +          SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
 +          SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
 +
 +          /* Remove the client from all channels. */
 +          silc_server_remove_from_channels(server, NULL, client, 
 +                                           TRUE, NULL, FALSE);
 +
 +          /* Remove the client */
 +          silc_idlist_del_client(local ? server->local_list :
 +                                 server->global_list, client);
 +        }
 +      }
 +
 +      silc_free(server_id);
 +      goto out;
 +      }
 +    }
 +    silc_free(server_id);
 +
 +    /* Free all client entries that this server owns as they will
 +       become invalid now as well. */
 +    silc_server_remove_clients_by_server(server, server_entry, TRUE);
 +
 +    /* Remove the server entry */
 +    silc_idlist_del_server(local ? server->local_list :
 +                         server->global_list, server_entry);
 +
 +    /* XXX update statistics */
 +
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_KICKED:
 +    /* 
 +     * Distribute the notify to local clients on the channel
 +     */
 +    
 +    SILC_LOG_DEBUG(("KICKED notify"));
 +      
 +    if (!channel_id) {
 +      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
 +                                packet->dst_id_type);
 +      if (!channel_id)
 +      goto out;
 +    }
 +
 +    /* Get channel entry */
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +    silc_free(channel_id);
 +
 +    /* Get client ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!client_id)
 +      goto out;
 +
 +    /* If the the client is not in local list we check global list */
 +    client = silc_idlist_find_client_by_id(server->global_list, 
 +                                         client_id, TRUE, NULL);
 +    if (!client) {
 +      client = silc_idlist_find_client_by_id(server->local_list, 
 +                                           client_id, TRUE, NULL);
 +      if (!client) {
 +      silc_free(client_id);
 +      goto out;
 +      }
 +    }
 +
 +    /* Send to channel */
 +    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
 +                                     FALSE, packet->buffer->data, 
 +                                     packet->buffer->len, FALSE);
 +
 +    /* Remove the client from channel */
 +    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
 +
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_KILLED:
 +    {
 +      /* 
 +       * Distribute the notify to local clients on channels
 +       */
 +      unsigned char *id;
 +      uint32 id_len;
 +    
 +      SILC_LOG_DEBUG(("KILLED notify"));
 +      
 +      /* Get client ID */
 +      id = silc_argument_get_arg_type(args, 1, &id_len);
 +      if (!id)
 +      goto out;
 +      client_id = silc_id_payload_parse_id(id, id_len);
 +      if (!client_id)
 +      goto out;
 +
 +      /* If the the client is not in local list we check global list */
 +      client = silc_idlist_find_client_by_id(server->global_list, 
 +                                           client_id, TRUE, NULL);
 +      if (!client) {
 +      client = silc_idlist_find_client_by_id(server->local_list, 
 +                                             client_id, TRUE, NULL);
 +      if (!client) {
 +        silc_free(client_id);
 +        goto out;
 +      }
 +      }
 +      silc_free(client_id);
 +
 +      /* If the client is one of ours, then close the connection to the
 +       client now. This removes the client from all channels as well. */
 +      if (packet->dst_id_type == SILC_ID_CLIENT && client->connection) {
 +      sock = client->connection;
 +      silc_server_free_client_data(server, NULL, client, FALSE, NULL);
 +      silc_server_close_connection(server, sock);
 +      break;
 +      }
 +
 +      /* Get comment */
 +      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +      if (tmp_len > 128)
 +      tmp = NULL;
 +
 +      /* Send the notify to local clients on the channels except to the
 +       client who is killed. */
 +      silc_server_send_notify_on_channels(server, client, client,
 +                                        SILC_NOTIFY_TYPE_KILLED, 
 +                                        tmp ? 2 : 1,
 +                                        id, id_len, 
 +                                        tmp, tmp_len);
 +
 +      /* Remove the client from all channels */
 +      silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, 
 +                                     FALSE);
 +
 +      break;
 +    }
 +
 +  case SILC_NOTIFY_TYPE_UMODE_CHANGE:
 +    /*
 +     * Save the mode of the client.
 +     */
 +
 +    SILC_LOG_DEBUG(("UMODE_CHANGE notify"));
 +
 +    /* Get client ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!client_id)
 +      goto out;
 +
 +    /* Get client entry */
 +    client = silc_idlist_find_client_by_id(server->global_list, 
 +                                         client_id, TRUE, NULL);
 +    if (!client) {
 +      client = silc_idlist_find_client_by_id(server->local_list, 
 +                                           client_id, TRUE, NULL);
 +      if (!client) {
 +      silc_free(client_id);
 +      goto out;
 +      }
 +    }
 +    silc_free(client_id);
 +
 +    /* Get the mode */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    SILC_GET32_MSB(mode, tmp);
 +
 +#define SILC_UMODE_STATS_UPDATE(oper, mod)    \
 +do {                                          \
 +    if (client->mode & (mod)) {                       \
 +      if (!(mode & (mod))) {                  \
 +      if (client->connection)                 \
 +        server->stat.my_ ## oper ## _ops--;   \
 +        if (server->server_type == SILC_ROUTER)       \
 +        server->stat. oper ## _ops--;         \
 +      }                                               \
 +    } else {                                  \
 +      if (mode & (mod)) {                     \
 +      if (client->connection)                 \
 +        server->stat.my_ ## oper ## _ops++;   \
 +        if (server->server_type == SILC_ROUTER)       \
 +        server->stat. oper ## _ops++;         \
 +      }                                               \
 +    }                                         \
 +} while(0)
 +
 +    /* Update statistics */
 +    SILC_UMODE_STATS_UPDATE(server, SILC_UMODE_SERVER_OPERATOR);
 +    SILC_UMODE_STATS_UPDATE(router, SILC_UMODE_ROUTER_OPERATOR);
 +
 +    /* Save the mode */
 +    client->mode = mode;
 +
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_BAN:
 +    /*
 +     * Save the ban
 +     */
 +
 +    SILC_LOG_DEBUG(("BAN notify"));
 +    
 +    /* Get Channel ID */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
 +    if (!channel_id)
 +      goto out;
 +    
 +    /* Get channel entry */
 +    channel = silc_idlist_find_channel_by_id(server->global_list, 
 +                                           channel_id, NULL);
 +    if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->local_list, 
 +                                             channel_id, NULL);
 +      if (!channel) {
 +      silc_free(channel_id);
 +      goto out;
 +      }
 +    }
 +    silc_free(channel_id);
 +
 +    /* Get the new ban and add it to the ban list */
 +    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);
 +    }
 +
 +    /* 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;
 +      }
 +      }
 +    }
 +    break;
 +
 +    /* Ignore rest of the notify types for now */
 +  case SILC_NOTIFY_TYPE_NONE:
 +  case SILC_NOTIFY_TYPE_MOTD:
 +    break;
 +  default:
 +    break;
 +  }
 +
 + out:
 +  silc_notify_payload_free(payload);
 +}
 +
 +void silc_server_notify_list(SilcServer server,
 +                           SilcSocketConnection sock,
 +                           SilcPacketContext *packet)
 +{
 +  SilcPacketContext *new;
 +  SilcBuffer buffer;
 +  uint16 len;
 +
 +  SILC_LOG_DEBUG(("Processing Notify List"));
 +
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
 +      packet->src_id_type != SILC_ID_SERVER)
 +    return;
 +
 +  /* Make copy of the original packet context, except for the actual
 +     data buffer, which we will here now fetch from the original buffer. */
 +  new = silc_packet_context_alloc();
 +  new->type = SILC_PACKET_NOTIFY;
 +  new->flags = packet->flags;
 +  new->src_id = packet->src_id;
 +  new->src_id_len = packet->src_id_len;
 +  new->src_id_type = packet->src_id_type;
 +  new->dst_id = packet->dst_id;
 +  new->dst_id_len = packet->dst_id_len;
 +  new->dst_id_type = packet->dst_id_type;
 +
 +  buffer = silc_buffer_alloc(1024);
 +  new->buffer = buffer;
 +
 +  while (packet->buffer->len) {
 +    SILC_GET16_MSB(len, packet->buffer->data + 2);
 +    if (len > packet->buffer->len)
 +      break;
 +
 +    if (len > buffer->truelen) {
 +      silc_buffer_free(buffer);
 +      buffer = silc_buffer_alloc(1024 + len);
 +    }
 +
 +    silc_buffer_pull_tail(buffer, len);
 +    silc_buffer_put(buffer, packet->buffer->data, len);
 +
 +    /* Process the Notify */
 +    silc_server_notify(server, sock, new);
 +
 +    silc_buffer_push_tail(buffer, len);
 +    silc_buffer_pull(packet->buffer, len);
 +  }
 +
 +  silc_buffer_free(buffer);
 +  silc_free(new);
 +}
 +
 +/* Received private message. This resolves the destination of the message 
 +   and sends the packet. This is used by both server and router.  If the
 +   destination is our locally connected client this sends the packet to
 +   the client. This may also send the message for further routing if
 +   the destination is not in our server (or router). */
 +
 +void silc_server_private_message(SilcServer server,
 +                               SilcSocketConnection sock,
 +                               SilcPacketContext *packet)
 +{
 +  SilcSocketConnection dst_sock;
 +  SilcIDListData idata;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (packet->src_id_type != SILC_ID_CLIENT ||
 +      packet->dst_id_type != SILC_ID_CLIENT || !packet->dst_id)
 +    return;
 +
 +  /* Get the route to the client */
 +  dst_sock = silc_server_get_client_route(server, packet->dst_id,
 +                                        packet->dst_id_len, NULL, &idata);
 +  if (!dst_sock) {
 +    /* Send IDENTIFY command reply with error status to indicate that
 +       such destination ID does not exist or is invalid */
 +    SilcBuffer idp = silc_id_payload_encode_data(packet->dst_id,
 +                                               packet->dst_id_len,
 +                                               packet->dst_id_type);
 +    if (!idp)
 +      return;
 +
 +    if (packet->src_id_type == SILC_ID_CLIENT) {
 +      SilcClientID *client_id = silc_id_str2id(packet->src_id,
 +                                             packet->src_id_len,
 +                                             packet->src_id_type);
 +      silc_server_send_dest_command_reply(server, sock, 
 +                                        client_id, SILC_ID_CLIENT,
 +                                        SILC_COMMAND_IDENTIFY,
 +                                        SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
 +                                        0, 1, 2, idp->data, idp->len);
 +      silc_free(client_id);
 +    } else {
 +      silc_server_send_command_reply(server, sock, SILC_COMMAND_IDENTIFY,
 +                                   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
 +                                   0, 1, 2, idp->data, idp->len);
 +    }
 +
 +    silc_buffer_free(idp);
 +    return;
 +  }
 +
 +  /* Send the private message */
 +  silc_server_send_private_message(server, dst_sock, idata->send_key,
 +                                 idata->hmac_send, idata->psn_send++,
 +                                 packet);
 +}
 +
 +/* Received private message key packet.. This packet is never for us. It is to
 +   the client in the packet's destination ID. Sending of this sort of packet
 +   equals sending private message, ie. it is sent point to point from
 +   one client to another. */
 +
 +void silc_server_private_message_key(SilcServer server,
 +                                   SilcSocketConnection sock,
 +                                   SilcPacketContext *packet)
 +{
 +  SilcSocketConnection dst_sock;
 +  SilcIDListData idata;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (packet->src_id_type != SILC_ID_CLIENT ||
 +      packet->dst_id_type != SILC_ID_CLIENT)
 +    return;
 +
 +  if (!packet->dst_id)
 +    return;
 +
 +  /* Get the route to the client */
 +  dst_sock = silc_server_get_client_route(server, packet->dst_id,
 +                                        packet->dst_id_len, NULL, &idata);
 +  if (!dst_sock)
 +    return;
 +
 +  /* Relay the packet */
 +  silc_server_relay_packet(server, dst_sock, idata->send_key,
 +                         idata->hmac_send, idata->psn_send++, packet, FALSE);
 +}
 +
 +/* Processes incoming command reply packet. The command reply packet may
 +   be destined to one of our clients or it may directly for us. We will 
 +   call the command reply routine after processing the packet. */
 +
 +void silc_server_command_reply(SilcServer server,
 +                             SilcSocketConnection sock,
 +                             SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcClientEntry client = NULL;
 +  SilcSocketConnection dst_sock;
 +  SilcIDListData idata;
 +  SilcClientID *id = NULL;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  /* Source must be server or router */
 +  if (packet->src_id_type != SILC_ID_SERVER &&
 +      sock->type != SILC_SOCKET_TYPE_ROUTER)
 +    return;
 +
 +  if (packet->dst_id_type == SILC_ID_CHANNEL)
 +    return;
 +
 +  if (packet->dst_id_type == SILC_ID_CLIENT) {
 +    /* Destination must be one of ours */
 +    id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
 +    if (!id)
 +      return;
 +    client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
 +    if (!client) {
 +      SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
 +      silc_free(id);
 +      return;
 +    }
 +  }
 +
 +  if (packet->dst_id_type == SILC_ID_SERVER) {
 +    /* For now this must be for us */
 +    if (memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
 +      SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
 +      return;
 +    }
 +  }
 +
 +  /* Execute command reply locally for the command */
 +  silc_server_command_reply_process(server, sock, buffer);
 +
 +  if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
 +    /* Relay the packet to the client */
 +    
 +    dst_sock = (SilcSocketConnection)client->connection;
 +    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 +                   + packet->dst_id_len + packet->padlen);
 +    
 +    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
 +    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
 +    
 +    idata = (SilcIDListData)client;
 +    
 +    /* Encrypt packet */
 +    silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
 +                      dst_sock->outbuf, buffer->len);
 +    
 +    /* Send the packet */
 +    silc_server_packet_send_real(server, dst_sock, TRUE);
 +
 +    silc_free(id);
 +  }
 +}
 +
 +/* Process received channel message. The message can be originated from
 +   client or server. */
 +
 +void silc_server_channel_message(SilcServer server,
 +                               SilcSocketConnection sock,
 +                               SilcPacketContext *packet)
 +{
 +  SilcChannelEntry channel = NULL;
 +  SilcChannelID *id = NULL;
 +  void *sender = NULL;
 +  void *sender_entry = NULL;
 +  bool local = TRUE;
 +
 +  SILC_LOG_DEBUG(("Processing channel message"));
 +
 +  /* Sanity checks */
 +  if (packet->dst_id_type != SILC_ID_CHANNEL) {
 +    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
 +    goto out;
 +  }
 +
 +  /* Find channel entry */
 +  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
 +  if (!id)
 +    goto out;
 +  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
 +  if (!channel) {
 +    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
 +    if (!channel) {
 +      SILC_LOG_DEBUG(("Could not find channel"));
 +      goto out;
 +    }
 +  }
 +
 +  /* See that this client is on the channel. 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_len, 
 +                        packet->src_id_type);
 +  if (!sender)
 +    goto out;
 +  if (packet->src_id_type == SILC_ID_CLIENT) {
 +    sender_entry = silc_idlist_find_client_by_id(server->local_list, 
 +                                               sender, TRUE, NULL);
 +    if (!sender_entry) {
 +      local = FALSE;
 +      sender_entry = silc_idlist_find_client_by_id(server->global_list, 
 +                                                 sender, TRUE, NULL);
 +    }
 +    if (!sender_entry || !silc_server_client_on_channel(sender_entry, 
 +                                                      channel)) {
 +      SILC_LOG_DEBUG(("Client not on channel"));
 +      goto out;
 +    }
 +
 +    /* If the packet is coming from router, but the client entry is
 +       local entry to us then some router is rerouting this to us and it is
 +       not allowed. */
 +    if (server->server_type == SILC_ROUTER &&
 +      sock->type == SILC_SOCKET_TYPE_ROUTER && local) {
 +      SILC_LOG_DEBUG(("Channel message rerouted to the sender, drop it"));
 +      goto out;
 +    }
 +  }
 +
 +  /* Distribute the packet to our local clients. This will send the
 +     packet for further routing as well, if needed. */
 +  silc_server_packet_relay_to_channel(server, sock, channel, sender,
 +                                    packet->src_id_type, sender_entry,
 +                                    packet->buffer->data,
 +                                    packet->buffer->len, FALSE);
 +
 + out:
 +  if (sender)
 +    silc_free(sender);
 +  if (id)
 +    silc_free(id);
 +}
 +
 +/* Received channel key packet. We distribute the key to all of our locally
 +   connected clients on the channel. */
 +
 +void silc_server_channel_key(SilcServer server,
 +                           SilcSocketConnection sock,
 +                           SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcChannelEntry channel;
 +
 +  if (packet->src_id_type != SILC_ID_SERVER ||
 +      (server->server_type == SILC_ROUTER &&
 +       sock->type == SILC_SOCKET_TYPE_ROUTER))
 +    return;
 +
 +  /* Save the channel key */
 +  channel = silc_server_save_channel_key(server, buffer, NULL);
 +  if (!channel)
 +    return;
 +
 +  /* Distribute the key to everybody who is on the channel. If we are router
 +     we will also send it to locally connected servers. */
 +  silc_server_send_channel_key(server, sock, channel, FALSE);
 +  
 +  if (server->server_type != SILC_BACKUP_ROUTER) {
 +    /* Distribute to local cell backup routers. */
 +    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
 +                          SILC_PACKET_CHANNEL_KEY, 0,
 +                          buffer->data, buffer->len, FALSE, TRUE);
 +  }
 +}
 +
 +/* Received New Client packet and processes it.  Creates Client ID for the
 +   client. Client becomes registered after calling this functions. */
 +
 +SilcClientEntry silc_server_new_client(SilcServer server,
 +                                     SilcSocketConnection sock,
 +                                     SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcClientEntry client;
 +  SilcClientID *client_id;
 +  SilcBuffer reply;
 +  SilcIDListData idata;
 +  char *username = NULL, *realname = NULL, *id_string;
 +  uint16 username_len;
 +  uint32 id_len;
 +  int ret;
 +  char *hostname, *nickname;
 +  int nickfail = 0;
 +
 +  SILC_LOG_DEBUG(("Creating new client"));
 +
 +  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
 +    return NULL;
 +
 +  /* Take client entry */
 +  client = (SilcClientEntry)sock->user_data;
 +  idata = (SilcIDListData)client;
 +
 +  /* Remove the old cache entry. */
 +  if (!silc_idcache_del_by_context(server->local_list->clients, client)) {
 +    SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
 +    silc_server_disconnect_remote(server, sock, "Server closed connection: "
 +                                  "Unknown client");
 +    return NULL;
 +  }
 +
 +  /* Parse incoming packet */
 +  ret = silc_buffer_unformat(buffer,
 +                           SILC_STR_UI16_NSTRING_ALLOC(&username, 
 +                                                       &username_len),
 +                           SILC_STR_UI16_STRING_ALLOC(&realname),
 +                           SILC_STR_END);
 +  if (ret == -1) {
 +    silc_free(username);
 +    silc_free(realname);
 +    silc_server_disconnect_remote(server, sock, "Server closed connection: "
 +                                  "Incomplete client information");
 +    return NULL;
 +  }
 +
 +  if (!username) {
 +    silc_free(username);
 +    silc_free(realname);
 +    silc_server_disconnect_remote(server, sock, "Server closed connection: "
 +                                  "Incomplete client information");
 +    return NULL;
 +  }
 +
 +  if (username_len > 128)
 +    username[128] = '\0';
 +
 +  /* Check for bad characters for nickname, and modify the nickname if
 +     it includes those. */
 +  if (silc_server_name_bad_chars(username, username_len)) {
 +    nickname = silc_server_name_modify_bad(username, username_len);
 +  } else {
 +    nickname = strdup(username);
 +  }
 +
 +  /* Make sanity checks for the hostname of the client. If the hostname
 +     is provided in the `username' check that it is the same than the
 +     resolved hostname, or if not resolved the hostname that appears in
 +     the client's public key. If the hostname is not present then put
 +     it from the resolved name or from the public key. */
 +  if (strchr(username, '@')) {
 +    SilcPublicKeyIdentifier pident;
 +    int tlen = strcspn(username, "@");
 +    char *phostname = NULL;
 +
 +    hostname = silc_calloc((strlen(username) - tlen) + 1, sizeof(char));
 +    memcpy(hostname, username + tlen + 1, strlen(username) - tlen - 1);
 +
 +    if (strcmp(sock->hostname, sock->ip) && 
 +      strcmp(sock->hostname, hostname)) {
 +      silc_free(username);
 +      silc_free(hostname);
 +      silc_free(realname);
 +      silc_server_disconnect_remote(server, sock, 
 +                                  "Server closed connection: "
 +                                  "Incomplete client information");
 +      return NULL;
 +    }
 +    
 +    pident = silc_pkcs_decode_identifier(client->data.public_key->identifier);
 +    if (pident) {
 +      phostname = strdup(pident->host);
 +      silc_pkcs_free_identifier(pident);
 +    }
 +
 +    if (!strcmp(sock->hostname, sock->ip) && 
 +      phostname && strcmp(phostname, hostname)) {
 +      silc_free(username);
 +      silc_free(hostname);
 +      silc_free(phostname);
 +      silc_free(realname);
 +      silc_server_disconnect_remote(server, sock, 
 +                                  "Server closed connection: "
 +                                  "Incomplete client information");
 +      return NULL;
 +    }
 +    
 +    silc_free(phostname);
 +  } else {
 +    /* The hostname is not present, add it. */
 +    char *newusername;
 +    /* XXX For now we cannot take the host name from the public key since
 +       they are not trusted or we cannot verify them as trusted. Just take
 +       what the resolved name or address is. */
 +#if 0
 +    if (strcmp(sock->hostname, sock->ip)) {
 +#endif
 +      newusername = silc_calloc(strlen(username) + 
 +                              strlen(sock->hostname) + 2,
 +                              sizeof(*newusername));
 +      strncat(newusername, username, strlen(username));
 +      strncat(newusername, "@", 1);
 +      strncat(newusername, sock->hostname, strlen(sock->hostname));
 +      silc_free(username);
 +      username = newusername;
 +#if 0
 +    } else {
 +      SilcPublicKeyIdentifier pident = 
 +      silc_pkcs_decode_identifier(client->data.public_key->identifier);
 +      
 +      if (pident) {
 +      newusername = silc_calloc(strlen(username) + 
 +                                strlen(pident->host) + 2,
 +                                sizeof(*newusername));
 +      strncat(newusername, username, strlen(username));
 +      strncat(newusername, "@", 1);
 +      strncat(newusername, pident->host, strlen(pident->host));
 +      silc_free(username);
 +      username = newusername;
 +      silc_pkcs_free_identifier(pident);
 +      }
 +    }
 +#endif
 +  }
 +
 +  /* Create Client ID */
 +  while (!silc_id_create_client_id(server, server->id, server->rng, 
 +                                 server->md5hash, nickname, &client_id)) {
 +    nickfail++;
 +    snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail);
 +  }
 +
 +  /* Update client entry */
 +  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
 +  client->nickname = nickname;
 +  client->username = username;
 +  client->userinfo = realname ? realname : strdup(" ");
 +  client->id = client_id;
 +  id_len = silc_id_get_len(client_id, SILC_ID_CLIENT);
 +
 +  /* Add the client again to the ID cache */
 +  silc_idcache_add(server->local_list->clients, client->nickname,
 +                 client_id, client, 0, NULL);
 +
 +  /* Notify our router about new client on the SILC network */
 +  if (!server->standalone)
 +    silc_server_send_new_id(server, (SilcSocketConnection) 
 +                          server->router->connection, 
 +                          server->server_type == SILC_ROUTER ? TRUE : FALSE,
 +                          client->id, SILC_ID_CLIENT, id_len);
 +  
 +  /* Send the new client ID to the client. */
 +  id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
 +  reply = silc_buffer_alloc(2 + 2 + id_len);
 +  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
 +  silc_buffer_format(reply,
 +                   SILC_STR_UI_SHORT(SILC_ID_CLIENT),
 +                   SILC_STR_UI_SHORT(id_len),
 +                   SILC_STR_UI_XNSTRING(id_string, id_len),
 +                   SILC_STR_END);
 +  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
 +                        reply->data, reply->len, FALSE);
 +  silc_free(id_string);
 +  silc_buffer_free(reply);
 +
 +  /* Send some nice info to the client */
 +  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                        ("Welcome to the SILC Network %s",
 +                         username));
 +  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                        ("Your host is %s, running version %s",
 +                         server->config->server_info->server_name,
 +                         server_version));
 +  if (server->server_type == SILC_ROUTER) {
 +    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                          ("There are %d clients on %d servers in SILC "
 +                           "Network", server->stat.clients,
 +                           server->stat.servers + 1));
 +    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                          ("There are %d clients on %d server in our cell",
 +                           server->stat.cell_clients,
 +                           server->stat.cell_servers + 1));
 +    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                          ("I have %d clients, %d channels, %d servers and "
 +                           "%d routers",
 +                           server->stat.my_clients, 
 +                           server->stat.my_channels,
 +                           server->stat.my_servers,
 +                           server->stat.my_routers));
 +    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                          ("There are %d server operators and %d router "
 +                           "operators online",
 +                           server->stat.server_ops,
 +                           server->stat.router_ops));
 +    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                          ("I have %d operators online",
 +                           server->stat.my_router_ops +
 +                           server->stat.my_server_ops));
 +  } else {
 +    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                          ("I have %d clients and %d channels formed",
 +                           server->stat.my_clients,
 +                           server->stat.my_channels));
 +    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                          ("%d operators online",
 +                           server->stat.my_server_ops));
 +  }
 +  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                        ("Your connection is secured with %s cipher, "
 +                         "key length %d bits",
 +                         idata->send_key->cipher->name,
 +                         idata->send_key->cipher->key_len));
 +  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
 +                        ("Your current nickname is %s",
 +                         client->nickname));
 +
 +  /* Send motd */
 +  silc_server_send_motd(server, sock);
 +
 +  return client;
 +}
 +
 +/* Create new server. This processes received New Server packet and
 +   saves the received Server ID. The server is our locally connected
 +   server thus we save all the information and save it to local list. 
 +   This funtion can be used by both normal server and router server.
 +   If normal server uses this it means that its router has connected
 +   to the server. If router uses this it means that one of the cell's
 +   servers is connected to the router. */
 +
 +SilcServerEntry silc_server_new_server(SilcServer server,
 +                                     SilcSocketConnection sock,
 +                                     SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcServerEntry new_server, server_entry;
 +  SilcServerID *server_id;
 +  SilcIDListData idata;
 +  unsigned char *server_name, *id_string;
 +  uint16 id_len, name_len;
 +  int ret;
 +  bool local = TRUE;
 +
 +  SILC_LOG_DEBUG(("Creating new server"));
 +
 +  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
 +      sock->type != SILC_SOCKET_TYPE_ROUTER)
 +    return NULL;
 +
 +  /* Take server entry */
 +  new_server = (SilcServerEntry)sock->user_data;
 +  idata = (SilcIDListData)new_server;
 +
 +  /* Remove the old cache entry */
 +  if (!silc_idcache_del_by_context(server->local_list->servers, new_server)) {
 +    silc_idcache_del_by_context(server->global_list->servers, new_server);
 +    local = FALSE;
 +  }
 +
 +  /* Parse the incoming packet */
 +  ret = silc_buffer_unformat(buffer,
 +                           SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
 +                           SILC_STR_UI16_NSTRING_ALLOC(&server_name, 
 +                                                       &name_len),
 +                           SILC_STR_END);
 +  if (ret == -1) {
 +    if (id_string)
 +      silc_free(id_string);
 +    if (server_name)
 +      silc_free(server_name);
 +    return NULL;
 +  }
 +
 +  if (id_len > buffer->len) {
 +    silc_free(id_string);
 +    silc_free(server_name);
 +    return NULL;
 +  }
 +
 +  if (name_len > 256)
 +    server_name[255] = '\0';
 +
 +  /* Get Server ID */
 +  server_id = silc_id_str2id(id_string, id_len, SILC_ID_SERVER);
 +  if (!server_id) {
 +    silc_free(id_string);
 +    silc_free(server_name);
 +    return NULL;
 +  }
 +  silc_free(id_string);
 +
 +  /* Check that we do not have this ID already */
 +  server_entry = silc_idlist_find_server_by_id(server->local_list, 
 +                                             server_id, TRUE, NULL);
 +  if (server_entry) {
 +    silc_idcache_del_by_context(server->local_list->servers, server_entry);
 +  } else {
 +    server_entry = silc_idlist_find_server_by_id(server->global_list, 
 +                                               server_id, TRUE, NULL);
 +    if (server_entry) 
 +      silc_idcache_del_by_context(server->global_list->servers, server_entry);
 +  }
 +
 +  /* Update server entry */
 +  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
 +  new_server->server_name = server_name;
 +  new_server->id = server_id;
 +  
 +  SILC_LOG_DEBUG(("New server id(%s)",
 +                silc_id_render(server_id, SILC_ID_SERVER)));
 +
 +  /* Add again the entry to the ID cache. */
 +  silc_idcache_add(local ? server->local_list->servers : 
 +                 server->global_list->servers, server_name, server_id, 
 +                 new_server, 0, NULL);
 +
 +  /* Distribute the information about new server in the SILC network
 +     to our router. If we are normal server we won't send anything
 +     since this connection must be our router connection. */
 +  if (server->server_type == SILC_ROUTER && !server->standalone &&
 +      server->router->connection != sock)
 +    silc_server_send_new_id(server, server->router->connection,
 +                          TRUE, new_server->id, SILC_ID_SERVER, 
 +                          silc_id_get_len(server_id, SILC_ID_SERVER));
 +
 +  if (server->server_type == SILC_ROUTER)
 +    server->stat.cell_servers++;
 +
 +  /* Check whether this router connection has been replaced by an
 +     backup router. If it has been then we'll disable the server and will
 +     ignore everything it will send until the backup router resuming
 +     protocol has been completed. */
 +  if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
 +      silc_server_backup_replaced_get(server, server_id, NULL)) {
 +    /* Send packet to the server indicating that it cannot use this
 +       connection as it has been replaced by backup router. */
 +    SilcBuffer packet = silc_buffer_alloc(2);
 +    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 +    silc_buffer_format(packet,
 +                     SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_REPLACED),
 +                     SILC_STR_UI_CHAR(0),
 +                     SILC_STR_END);
 +    silc_server_packet_send(server, sock, 
 +                          SILC_PACKET_RESUME_ROUTER, 0, 
 +                          packet->data, packet->len, TRUE);
 +    silc_buffer_free(packet);
 +
 +    /* Mark the router disabled. The data sent earlier will go but nothing
 +       after this does not go to this connection. */
 +    idata->status |= SILC_IDLIST_STATUS_DISABLED;
 +  } else {
 +    /* If it is router announce our stuff to it. */
 +    if (sock->type == SILC_SOCKET_TYPE_ROUTER && 
 +      server->server_type == SILC_ROUTER) {
 +      silc_server_announce_servers(server, FALSE, 0, sock);
 +      silc_server_announce_clients(server, 0, sock);
 +      silc_server_announce_channels(server, 0, sock);
 +    }
 +  }
 +
 +  return new_server;
 +}
 +
 +/* Processes incoming New ID packet. New ID Payload is used to distribute
 +   information about newly registered clients and servers. */
 +
 +static void silc_server_new_id_real(SilcServer server, 
 +                                  SilcSocketConnection sock,
 +                                  SilcPacketContext *packet,
 +                                  int broadcast)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcIDList id_list;
 +  SilcServerEntry router, server_entry;
 +  SilcSocketConnection router_sock;
 +  SilcIDPayload idp;
 +  SilcIdType id_type;
 +  void *id;
 +
 +  SILC_LOG_DEBUG(("Processing new ID"));
 +
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
 +      server->server_type == SILC_SERVER ||
 +      packet->src_id_type != SILC_ID_SERVER)
 +    return;
 +
 +  idp = silc_id_payload_parse(buffer->data, buffer->len);
 +  if (!idp)
 +    return;
 +
 +  id_type = silc_id_payload_get_type(idp);
 +
 +  /* Normal server cannot have other normal server connections */
 +  server_entry = (SilcServerEntry)sock->user_data;
 +  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER &&
 +      server_entry->server_type == SILC_SERVER)
 +    goto out;
 +
 +  id = silc_id_payload_get_id(idp);
 +  if (!id)
 +    goto out;
 +
 +  /* If the packet is coming from server then use the sender as the
 +     origin of the the packet. If it came from router then check the real
 +     sender of the packet and use that as the origin. */
 +  if (sock->type == SILC_SOCKET_TYPE_SERVER) {
 +    id_list = server->local_list;
 +    router_sock = sock;
 +    router = sock->user_data;
 +
 +    /* If the sender is backup router and ID is server (and we are not
 +       backup router) then switch the entry to global list. */
 +    if (server_entry->server_type == SILC_BACKUP_ROUTER && 
 +      id_type == SILC_ID_SERVER && 
 +      server->id_entry->server_type != SILC_BACKUP_ROUTER) {
 +      id_list = server->global_list;
 +      router_sock = server->router ? server->router->connection : sock;
 +    }
 +  } else {
 +    void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
 +                                   packet->src_id_type);
 +    router = silc_idlist_find_server_by_id(server->global_list,
 +                                         sender_id, TRUE, NULL);
 +    if (!router)
 +      router = silc_idlist_find_server_by_id(server->local_list,
 +                                           sender_id, TRUE, NULL);
 +    silc_free(sender_id);
 +    router_sock = sock;
 +    id_list = server->global_list;
 +  }
 +
 +  if (!router)
 +    goto out;
 +
 +  switch(id_type) {
 +  case SILC_ID_CLIENT:
 +    {
 +      SilcClientEntry entry;
 +
 +      /* Check that we do not have this client already */
 +      entry = silc_idlist_find_client_by_id(server->global_list, 
 +                                          id, server->server_type, 
 +                                          NULL);
 +      if (!entry)
 +      entry = silc_idlist_find_client_by_id(server->local_list, 
 +                                            id, server->server_type,
 +                                            NULL);
 +      if (entry) {
 +      SILC_LOG_DEBUG(("Ignoring client that we already have"));
 +      goto out;
 +      }
 +
 +      SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
 +                    silc_id_render(id, SILC_ID_CLIENT),
 +                    sock->type == SILC_SOCKET_TYPE_SERVER ?
 +                    "Server" : "Router", sock->hostname));
 +    
 +      /* As a router we keep information of all global information in our
 +       global list. Cell wide information however is kept in the local
 +       list. */
 +      entry = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
 +                                   id, router, NULL, 0);
 +      if (!entry) {
 +      SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
 +
 +      /* Inform the sender that the ID is not usable */
 +      silc_server_send_notify_signoff(server, sock, FALSE, id, NULL);
 +      goto out;
 +      }
 +      entry->nickname = NULL;
 +      entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
 +
 +      if (sock->type == SILC_SOCKET_TYPE_SERVER)
 +      server->stat.cell_clients++;
 +      server->stat.clients++;
 +    }
 +    break;
 +
 +  case SILC_ID_SERVER:
 +    {
 +      SilcServerEntry entry;
 +
 +      /* If the ID is mine, ignore it. */
 +      if (SILC_ID_SERVER_COMPARE(id, server->id)) {
 +      SILC_LOG_DEBUG(("Ignoring my own ID as new ID"));
 +      break;
 +      }
 +
 +      /* If the ID is the sender's ID, ignore it (we have it already) */
 +      if (SILC_ID_SERVER_COMPARE(id, router->id)) {
 +      SILC_LOG_DEBUG(("Ignoring sender's own ID"));
 +      break;
 +      }
 +      
 +      /* Check that we do not have this server already */
 +      entry = silc_idlist_find_server_by_id(server->global_list, 
 +                                          id, server->server_type, 
 +                                          NULL);
 +      if (!entry)
 +      entry = silc_idlist_find_server_by_id(server->local_list, 
 +                                            id, server->server_type,
 +                                            NULL);
 +      if (entry) {
 +      SILC_LOG_DEBUG(("Ignoring server that we already have"));
 +      goto out;
 +      }
 +
 +      SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
 +                    silc_id_render(id, SILC_ID_SERVER),
 +                    sock->type == SILC_SOCKET_TYPE_SERVER ?
 +                    "Server" : "Router", sock->hostname));
 +      
 +      /* As a router we keep information of all global information in our 
 +       global list. Cell wide information however is kept in the local
 +       list. */
 +      entry = silc_idlist_add_server(id_list, NULL, 0, id, router, 
 +                                   router_sock);
 +      if (!entry) {
 +      SILC_LOG_ERROR(("Could not add new server to the ID Cache"));
 +      goto out;
 +      }
 +      entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
 +      
 +      if (sock->type == SILC_SOCKET_TYPE_SERVER)
 +      server->stat.cell_servers++;
 +      server->stat.servers++;
 +    }
 +    break;
 +
 +  case SILC_ID_CHANNEL:
 +    SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
 +    goto out;
 +    break;
 +
 +  default:
 +    goto out;
 +    break;
 +  }
 +
 +  /* If the sender of this packet is server and we are router we need to
 +     broadcast this packet to other routers in the network. */
 +  if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
 +      sock->type == SILC_SOCKET_TYPE_SERVER &&
 +      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
 +    SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
 +    silc_server_packet_send(server, server->router->connection,
 +                          packet->type, 
 +                          packet->flags | SILC_PACKET_FLAG_BROADCAST,
 +                          buffer->data, buffer->len, FALSE);
 +    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
 +                          packet->type, packet->flags,
 +                          packet->buffer->data, packet->buffer->len, 
 +                          FALSE, TRUE);
 +  }
 +
 + out:
 +  silc_id_payload_free(idp);
 +}
 +
 +
 +/* Processes incoming New ID packet. New ID Payload is used to distribute
 +   information about newly registered clients and servers. */
 +
 +void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
 +                      SilcPacketContext *packet)
 +{
 +  silc_server_new_id_real(server, sock, packet, TRUE);
 +}
 +
 +/* Receoved New Id List packet, list of New ID payloads inside one
 +   packet. Process the New ID payloads one by one. */
 +
 +void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
 +                           SilcPacketContext *packet)
 +{
 +  SilcPacketContext *new_id;
 +  SilcBuffer idp;
 +  uint16 id_len;
 +
 +  SILC_LOG_DEBUG(("Processing New ID List"));
 +
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
 +      packet->src_id_type != SILC_ID_SERVER)
 +    return;
 +
 +  /* If the sender of this packet is server and we are router we need to
 +     broadcast this packet to other routers in the network. Broadcast
 +     this list packet instead of multiple New ID packets. */
 +  if (!server->standalone && server->server_type == SILC_ROUTER &&
 +      sock->type == SILC_SOCKET_TYPE_SERVER &&
 +      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
 +    SILC_LOG_DEBUG(("Broadcasting received New ID List packet"));
 +    silc_server_packet_send(server, server->router->connection,
 +                          packet->type, 
 +                          packet->flags | SILC_PACKET_FLAG_BROADCAST,
 +                          packet->buffer->data, packet->buffer->len, FALSE);
 +    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
 +                          packet->type, packet->flags,
 +                          packet->buffer->data, packet->buffer->len, 
 +                          FALSE, TRUE);
 +  }
 +
 +  /* Make copy of the original packet context, except for the actual
 +     data buffer, which we will here now fetch from the original buffer. */
 +  new_id = silc_packet_context_alloc();
 +  new_id->type = SILC_PACKET_NEW_ID;
 +  new_id->flags = packet->flags;
 +  new_id->src_id = packet->src_id;
 +  new_id->src_id_len = packet->src_id_len;
 +  new_id->src_id_type = packet->src_id_type;
 +  new_id->dst_id = packet->dst_id;
 +  new_id->dst_id_len = packet->dst_id_len;
 +  new_id->dst_id_type = packet->dst_id_type;
 +
 +  idp = silc_buffer_alloc(256);
 +  new_id->buffer = idp;
 +
 +  while (packet->buffer->len) {
 +    SILC_GET16_MSB(id_len, packet->buffer->data + 2);
 +    if ((id_len > packet->buffer->len) ||
 +      (id_len > idp->truelen))
 +      break;
 +
 +    silc_buffer_pull_tail(idp, 4 + id_len);
 +    silc_buffer_put(idp, packet->buffer->data, 4 + id_len);
 +
 +    /* Process the New ID */
 +    silc_server_new_id_real(server, sock, new_id, FALSE);
 +
 +    silc_buffer_push_tail(idp, 4 + id_len);
 +    silc_buffer_pull(packet->buffer, 4 + id_len);
 +  }
 +
 +  silc_buffer_free(idp);
 +  silc_free(new_id);
 +}
 +
 +/* Received New Channel packet. Information about new channels in the 
 +   network are distributed using this packet. Save the information about
 +   the new channel. This usually comes from router but also normal server
 +   can send this to notify channels it has when it connects to us. */
 +
 +void silc_server_new_channel(SilcServer server,
 +                           SilcSocketConnection sock,
 +                           SilcPacketContext *packet)
 +{
 +  SilcChannelPayload payload;
 +  SilcChannelID *channel_id;
 +  char *channel_name;
 +  uint32 name_len;
 +  unsigned char *id;
 +  uint32 id_len;
 +  uint32 mode;
 +  SilcServerEntry server_entry;
 +  SilcChannelEntry channel;
 +
 +  SILC_LOG_DEBUG(("Processing New Channel"));
 +
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
 +      packet->src_id_type != SILC_ID_SERVER ||
 +      server->server_type == SILC_SERVER)
 +    return;
 +
 +  /* Parse the channel payload */
 +  payload = silc_channel_payload_parse(packet->buffer->data,
 +                                     packet->buffer->len);
 +  if (!payload)
 +    return;
 +    
 +  /* Get the channel ID */
 +  channel_id = silc_channel_get_id_parse(payload);
 +  if (!channel_id) {
 +    silc_channel_payload_free(payload);
 +    return;
 +  }
 +
 +  channel_name = silc_channel_get_name(payload, &name_len);
 +  if (name_len > 256)
 +    channel_name[255] = '\0';
 +
 +  id = silc_channel_get_id(payload, &id_len);
 +
 +  server_entry = (SilcServerEntry)sock->user_data;
 +
 +  if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
 +    /* Add the channel to global list as it is coming from router. It 
 +       cannot be our own channel as it is coming from router. */
 +
 +    /* Check that we don't already have this channel */
 +    channel = silc_idlist_find_channel_by_name(server->local_list, 
 +                                             channel_name, NULL);
 +    if (!channel)
 +      channel = silc_idlist_find_channel_by_name(server->global_list, 
 +                                               channel_name, NULL);
 +    if (!channel) {
 +      SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
 +                    silc_id_render(channel_id, SILC_ID_CHANNEL), 
 +                    sock->hostname));
 +    
 +      silc_idlist_add_channel(server->global_list, strdup(channel_name), 
 +                            0, channel_id, sock->user_data, NULL, NULL, 0);
 +      server->stat.channels++;
 +    }
 +  } else {
 +    /* The channel is coming from our server, thus it is in our cell
 +       we will add it to our local list. */
 +    SilcBuffer chk;
 +
 +    SILC_LOG_DEBUG(("Channel id(%s) from [Server] %s",
 +                  silc_id_render(channel_id, SILC_ID_CHANNEL), 
 +                  sock->hostname));
 +
 +    /* Check that we don't already have this channel */
 +    channel = silc_idlist_find_channel_by_name(server->local_list, 
 +                                             channel_name, NULL);
 +    if (!channel)
 +      channel = silc_idlist_find_channel_by_name(server->global_list, 
 +                                               channel_name, NULL);
 +
 +    /* If the channel does not exist, then create it. This creates a new
 +       key to the channel as well that we will send to the server. */
 +    if (!channel) {
 +      /* The protocol says that the Channel ID's IP address must be based
 +       on the router's IP address.  Check whether the ID is based in our
 +       IP and if it is not then create a new ID and enforce the server
 +       to switch the ID. */
 +      if (server_entry->server_type != SILC_BACKUP_ROUTER &&
 +        !SILC_ID_COMPARE(channel_id, server->id, server->id->ip.data_len)) {
 +      SilcChannelID *tmp;
 +      SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
 +      
 +      if (silc_id_create_channel_id(server, server->id, server->rng, &tmp)) {
 +        silc_server_send_notify_channel_change(server, sock, FALSE, 
 +                                               channel_id, tmp);
 +        silc_free(channel_id);
 +        channel_id = tmp;
 +      }
 +      }
 +
 +      /* Create the channel with the provided Channel ID */
 +      channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
 +                                                     channel_name,
 +                                                     channel_id, FALSE);
 +      if (!channel) {
 +      silc_channel_payload_free(payload);
 +      silc_free(channel_id);
 +      return;
 +      }
 +
 +      /* Get the mode and set it to the channel */
 +      channel->mode = silc_channel_get_mode(payload);
 +
 +      /* 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);
 +      chk = silc_channel_key_payload_encode(id_len, id,
 +                                          strlen(channel->channel_key->
 +                                                 cipher->name),
 +                                          channel->channel_key->cipher->name,
 +                                          channel->key_len / 8, 
 +                                          channel->key);
 +      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
 +                            chk->data, chk->len, FALSE);
 +      silc_buffer_free(chk);
 +
 +    } else {
 +      /* The channel exist by that name, check whether the ID's match.
 +       If they don't then we'll force the server to use the ID we have.
 +       We also create a new key for the channel. */
 +      SilcBuffer users = NULL, users_modes = NULL;
 +
 +      if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
 +      /* They don't match, send CHANNEL_CHANGE notify to the server to
 +         force the ID change. */
 +      SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
 +      silc_server_send_notify_channel_change(server, sock, FALSE, 
 +                                             channel_id, channel->id);
 +      }
 +
 +      /* If the mode is different from what we have then enforce the
 +       mode change. */
 +      mode = silc_channel_get_mode(payload);
 +      if (channel->mode != mode) {
 +      SILC_LOG_DEBUG(("Forcing the server to change channel mode"));
 +      silc_server_send_notify_cmode(server, sock, FALSE, channel,
 +                                    channel->mode, server->id,
 +                                    SILC_ID_SERVER,
 +                                    channel->cipher, channel->hmac_name,
 +                                    channel->passphrase);
 +      }
 +
 +      /* Create new key for the channel and send it to the server and
 +       everybody else possibly on the channel. */
 +
 +      if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
 +      if (!silc_server_create_channel_key(server, channel, 0))
 +        return;
 +      
 +      /* Send to the channel */
 +      silc_server_send_channel_key(server, sock, channel, FALSE);
 +      id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 +      id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
 +
 +      /* Send to the server */
 +      chk = silc_channel_key_payload_encode(id_len, id,
 +                                            strlen(channel->channel_key->
 +                                                   cipher->name),
 +                                            channel->channel_key->
 +                                            cipher->name,
 +                                            channel->key_len / 8, 
 +                                            channel->key);
 +      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
 +                              chk->data, chk->len, FALSE);
 +      silc_buffer_free(chk);
 +      silc_free(id);
 +      }
 +
 +      silc_free(channel_id);
 +
 +      /* 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. */
 +      silc_server_announce_get_channel_users(server, channel, &users,
 +                                           &users_modes);
 +      if (users) {
 +      silc_buffer_push(users, users->data - users->head);
 +      silc_server_packet_send(server, sock,
 +                              SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
 +                              users->data, users->len, FALSE);
 +      silc_buffer_free(users);
 +      }
 +      if (users_modes) {
 +      silc_buffer_push(users_modes, users_modes->data - users_modes->head);
 +      silc_server_packet_send_dest(server, sock,
 +                                   SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
 +                                   channel->id, SILC_ID_CHANNEL,
 +                                   users_modes->data, 
 +                                   users_modes->len, FALSE);
 +      silc_buffer_free(users_modes);
 +      }
 +    }
 +  }
 +
 +  silc_channel_payload_free(payload);
 +}
 +
 +/* Received New Channel List packet, list of New Channel List payloads inside
 +   one packet. Process the New Channel payloads one by one. */
 +
 +void silc_server_new_channel_list(SilcServer server,
 +                                SilcSocketConnection sock,
 +                                SilcPacketContext *packet)
 +{
 +  SilcPacketContext *new;
 +  SilcBuffer buffer;
 +  uint16 len1, len2;
 +
 +  SILC_LOG_DEBUG(("Processing New Channel List"));
 +
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
 +      packet->src_id_type != SILC_ID_SERVER ||
 +      server->server_type == SILC_SERVER)
 +    return;
 +
 +  /* If the sender of this packet is server and we are router we need to
 +     broadcast this packet to other routers in the network. Broadcast
 +     this list packet instead of multiple New Channel packets. */
 +  if (!server->standalone && server->server_type == SILC_ROUTER &&
 +      sock->type == SILC_SOCKET_TYPE_SERVER &&
 +      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
 +    SILC_LOG_DEBUG(("Broadcasting received New Channel List packet"));
 +    silc_server_packet_send(server, server->router->connection,
 +                          packet->type, 
 +                          packet->flags | SILC_PACKET_FLAG_BROADCAST,
 +                          packet->buffer->data, packet->buffer->len, FALSE);
 +    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
 +                          packet->type, packet->flags,
 +                          packet->buffer->data, packet->buffer->len, 
 +                          FALSE, TRUE);
 +  }
 +
 +  /* Make copy of the original packet context, except for the actual
 +     data buffer, which we will here now fetch from the original buffer. */
 +  new = silc_packet_context_alloc();
 +  new->type = SILC_PACKET_NEW_CHANNEL;
 +  new->flags = packet->flags;
 +  new->src_id = packet->src_id;
 +  new->src_id_len = packet->src_id_len;
 +  new->src_id_type = packet->src_id_type;
 +  new->dst_id = packet->dst_id;
 +  new->dst_id_len = packet->dst_id_len;
 +  new->dst_id_type = packet->dst_id_type;
 +
 +  buffer = silc_buffer_alloc(512);
 +  new->buffer = buffer;
 +
 +  while (packet->buffer->len) {
 +    SILC_GET16_MSB(len1, packet->buffer->data);
 +    if ((len1 > packet->buffer->len) ||
 +      (len1 > buffer->truelen))
 +      break;
 +
 +    SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
 +    if ((len2 > packet->buffer->len) ||
 +      (len2 > buffer->truelen))
 +      break;
 +
 +    silc_buffer_pull_tail(buffer, 8 + len1 + len2);
 +    silc_buffer_put(buffer, packet->buffer->data, 8 + len1 + len2);
 +
 +    /* Process the New Channel */
 +    silc_server_new_channel(server, sock, new);
 +
 +    silc_buffer_push_tail(buffer, 8 + len1 + len2);
 +    silc_buffer_pull(packet->buffer, 8 + len1 + len2);
 +  }
 +
 +  silc_buffer_free(buffer);
 +  silc_free(new);
 +}
 +
 +/* Received key agreement packet. This packet is never for us. It is to
 +   the client in the packet's destination ID. Sending of this sort of packet
 +   equals sending private message, ie. it is sent point to point from
 +   one client to another. */
 +
 +void silc_server_key_agreement(SilcServer server,
 +                             SilcSocketConnection sock,
 +                             SilcPacketContext *packet)
 +{
 +  SilcSocketConnection dst_sock;
 +  SilcIDListData idata;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (packet->src_id_type != SILC_ID_CLIENT ||
 +      packet->dst_id_type != SILC_ID_CLIENT)
 +    return;
 +
 +  if (!packet->dst_id)
 +    return;
 +
 +  /* Get the route to the client */
 +  dst_sock = silc_server_get_client_route(server, packet->dst_id,
 +                                        packet->dst_id_len, NULL, &idata);
 +  if (!dst_sock)
 +    return;
 +
 +  /* Relay the packet */
 +  silc_server_relay_packet(server, dst_sock, idata->send_key,
 +                         idata->hmac_send, idata->psn_send++,
 +                         packet, FALSE);
 +}
 +
 +/* Received connection auth request packet that is used during connection
 +   phase to resolve the mandatory authentication method.  This packet can
 +   actually be received at anytime but usually it is used only during
 +   the connection authentication phase. Now, protocol says that this packet
 +   can come from client or server, however, we support only this coming
 +   from client and expect that server always knows what authentication
 +   method to use. */
 +
 +void silc_server_connection_auth_request(SilcServer server,
 +                                       SilcSocketConnection sock,
 +                                       SilcPacketContext *packet)
 +{
-   client = silc_server_config_find_client_conn(server->config,
++  SilcServerConfigSectionClient *client = NULL;
 +  uint16 conn_type;
 +  int ret, port;
 +  SilcAuthMethod auth_meth;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (packet->src_id_type && packet->src_id_type != SILC_ID_CLIENT)
 +    return;
 +
 +  /* Parse the payload */
 +  ret = silc_buffer_unformat(packet->buffer,
 +                           SILC_STR_UI_SHORT(&conn_type),
 +                           SILC_STR_UI_SHORT(NULL),
 +                           SILC_STR_END);
 +  if (ret == -1)
 +    return;
 +
 +  if (conn_type != SILC_SOCKET_TYPE_CLIENT)
 +    return;
 +
 +  /* Get the authentication method for the client */
 +  auth_meth = SILC_AUTH_NONE;
 +  port = server->sockets[server->sock]->port; /* Listenning port */
-     client = silc_server_config_find_client_conn(server->config,
++  client = silc_server_config_find_client(server->config,
 +                                             sock->ip,
 +                                             port);
 +  if (!client)
++    client = silc_server_config_find_client(server->config,
 +                                               sock->hostname,
 +                                               port);
 +  if (client)
 +    auth_meth = client->auth_meth;
 +        
 +  /* Send it back to the client */
 +  silc_server_send_connection_auth_request(server, sock,
 +                                         conn_type,
 +                                         auth_meth);
 +}
 +
 +/* Received REKEY packet. The sender of the packet wants to regenerate
 +   its session keys. This starts the REKEY protocol. */
 +
 +void silc_server_rekey(SilcServer server,
 +                     SilcSocketConnection sock,
 +                     SilcPacketContext *packet)
 +{
 +  SilcProtocol protocol;
 +  SilcServerRekeyInternalContext *proto_ctx;
 +  SilcIDListData idata = (SilcIDListData)sock->user_data;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  /* Allocate internal protocol context. This is sent as context
 +     to the protocol. */
 +  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 +  proto_ctx->server = (void *)server;
 +  proto_ctx->sock = sock;
 +  proto_ctx->responder = TRUE;
 +  proto_ctx->pfs = idata->rekey->pfs;
 +      
 +  /* Perform rekey protocol. Will call the final callback after the
 +     protocol is over. */
 +  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
 +                    &protocol, proto_ctx, silc_server_rekey_final);
 +  sock->protocol = protocol;
 +
 +  if (proto_ctx->pfs == FALSE)
 +    /* Run the protocol */
 +    silc_protocol_execute(protocol, server->schedule, 0, 0);
 +}
 +
 +/* Received file transger packet. This packet is never for us. It is to
 +   the client in the packet's destination ID. Sending of this sort of packet
 +   equals sending private message, ie. it is sent point to point from
 +   one client to another. */
 +
 +void silc_server_ftp(SilcServer server,
 +                   SilcSocketConnection sock,
 +                   SilcPacketContext *packet)
 +{
 +  SilcSocketConnection dst_sock;
 +  SilcIDListData idata;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (packet->src_id_type != SILC_ID_CLIENT ||
 +      packet->dst_id_type != SILC_ID_CLIENT)
 +    return;
 +
 +  if (!packet->dst_id)
 +    return;
 +
 +  /* Get the route to the client */
 +  dst_sock = silc_server_get_client_route(server, packet->dst_id,
 +                                        packet->dst_id_len, NULL, &idata);
 +  if (!dst_sock)
 +    return;
 +
 +  /* Relay the packet */
 +  silc_server_relay_packet(server, dst_sock, idata->send_key,
 +                         idata->hmac_send, idata->psn_send++,
 +                         packet, FALSE);
 +}
diff --combined apps/silcd/packet_send.c
index 2c8786c3a5ada9eafd45c8a1fb44ca8b75334be4,0000000000000000000000000000000000000000..d2d21517068d73afe44e4b0cf78d543a2fad3669
mode 100644,000000..100644
--- /dev/null
@@@ -1,1890 -1,0 +1,1891 @@@
-   char *motd;
 +/*
 +
 +  packet_send.c
 +
 +  Author: Pekka Riikonen <priikone@silcnet.org>
 +
 +  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
 +  the Free Software Foundation; either version 2 of the License, or
 +  (at your option) any later version.
 +  
 +  This program is distributed in the hope that it will be useful,
 +  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +  GNU General Public License for more details.
 +
 +*/
 +/*
 + * Server packet routines to send packets. 
 + */
 +/* $Id$ */
 +
 +#include "serverincludes.h"
 +#include "server_internal.h"
 +
 +/* Routine that sends packet or marks packet to be sent. This is used
 +   directly only in special cases. Normal cases should use
 +   silc_server_packet_send. Returns < 0 error. */
 +
 +int silc_server_packet_send_real(SilcServer server,
 +                               SilcSocketConnection sock,
 +                               bool force_send)
 +{
 +  int ret;
 +
 +  /* If disconnecting, ignore the data */
 +  if (SILC_IS_DISCONNECTING(sock))
 +    return -1;
 +
 +  /* If rekey protocol is active we must assure that all packets are
 +     sent through packet queue. */
 +  if (SILC_SERVER_IS_REKEY(sock))
 +    force_send = FALSE;
 +
 +  /* If outbound data is already pending do not force send */
 +  if (SILC_IS_OUTBUF_PENDING(sock))
 +    force_send = FALSE;
 +
 +  /* Send the packet */
 +  ret = silc_packet_send(sock, force_send);
 +  if (ret != -2)
 +    return ret;
 +
 +  /* Mark that there is some outgoing data available for this connection. 
 +     This call sets the connection both for input and output (the input
 +     is set always and this call keeps the input setting, actually). 
 +     Actual data sending is performed by silc_server_packet_process. */
 +  SILC_SET_CONNECTION_FOR_OUTPUT(server->schedule, sock->sock);
 +
 +  /* Mark to socket that data is pending in outgoing buffer. This flag
 +     is needed if new data is added to the buffer before the earlier
 +     put data is sent to the network. */
 +  SILC_SET_OUTBUF_PENDING(sock);
 +
 +  return 0;
 +}
 +
 +/* Assembles a new packet to be sent out to network. This doesn't actually
 +   send the packet but creates the packet and fills the outgoing data
 +   buffer and marks the packet ready to be sent to network. However, If 
 +   argument force_send is TRUE the packet is sent immediately and not put 
 +   to queue. Normal case is that the packet is not sent immediately. */
 +
 +void silc_server_packet_send(SilcServer server,
 +                           SilcSocketConnection sock, 
 +                           SilcPacketType type, 
 +                           SilcPacketFlags flags,
 +                           unsigned char *data, 
 +                           uint32 data_len,
 +                           bool force_send)
 +{
 +  void *dst_id = NULL;
 +  SilcIdType dst_id_type = SILC_ID_NONE;
 +  SilcIDListData idata = (SilcIDListData)sock->user_data;
 +
 +  if (!sock)
 +    return;
 +
 +  /* If disconnecting, ignore the data */
 +  if (SILC_IS_DISCONNECTING(sock))
 +    return;
 +
 +  /* If entry is disabled do not sent anything. */
 +  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
 +    return;
 +
 +  /* Get data used in the packet sending, keys and stuff */
 +  switch(sock->type) {
 +  case SILC_SOCKET_TYPE_CLIENT:
 +    if (sock->user_data) {
 +      dst_id = ((SilcClientEntry)sock->user_data)->id;
 +      dst_id_type = SILC_ID_CLIENT;
 +    }
 +    break;
 +  case SILC_SOCKET_TYPE_SERVER:
 +  case SILC_SOCKET_TYPE_ROUTER:
 +    if (sock->user_data) {
 +      dst_id = ((SilcServerEntry)sock->user_data)->id;
 +      dst_id_type = SILC_ID_SERVER;
 +    }
 +    break;
 +  default:
 +    break;
 +  }
 +
 +  silc_server_packet_send_dest(server, sock, type, flags, dst_id,
 +                             dst_id_type, data, data_len, force_send);
 +}
 +
 +/* Assembles a new packet to be sent out to network. This doesn't actually
 +   send the packet but creates the packet and fills the outgoing data
 +   buffer and marks the packet ready to be sent to network. However, If 
 +   argument force_send is TRUE the packet is sent immediately and not put 
 +   to queue. Normal case is that the packet is not sent immediately. 
 +   Destination information is sent as argument for this function. */
 +
 +void silc_server_packet_send_dest(SilcServer server,
 +                                SilcSocketConnection sock, 
 +                                SilcPacketType type, 
 +                                SilcPacketFlags flags,
 +                                void *dst_id,
 +                                SilcIdType dst_id_type,
 +                                unsigned char *data, 
 +                                uint32 data_len,
 +                                bool force_send)
 +{
 +  SilcPacketContext packetdata;
 +  SilcIDListData idata = (SilcIDListData)sock->user_data;
 +  SilcCipher cipher = NULL;
 +  SilcHmac hmac = NULL;
 +  uint32 sequence = 0;
 +  unsigned char *dst_id_data = NULL;
 +  uint32 dst_id_len = 0;
 +  int block_len = 0;
 +
 +  /* If disconnecting, ignore the data */
 +  if (SILC_IS_DISCONNECTING(sock))
 +    return;
 +
 +  /* If entry is disabled do not sent anything. */
 +  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
 +    return;
 +
 +  SILC_LOG_DEBUG(("Sending packet, type %d", type));
 +
 +  if (dst_id) {
 +    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
 +    dst_id_len = silc_id_get_len(dst_id, dst_id_type);
 +  }
 +
 +  if (idata) {
 +    cipher = idata->send_key;
 +    hmac = idata->hmac_send;
 +    sequence = idata->psn_send++;
 +    block_len = silc_cipher_get_block_len(cipher);
 +  }
 +
 +  /* Set the packet context pointers */
 +  packetdata.type = type;
 +  packetdata.flags = flags;
 +  packetdata.src_id = silc_id_id2str(server->id, server->id_type);
 +  packetdata.src_id_len = silc_id_get_len(server->id, server->id_type);
 +  packetdata.src_id_type = server->id_type;
 +  packetdata.dst_id = dst_id_data;
 +  packetdata.dst_id_len = dst_id_len;
 +  packetdata.dst_id_type = dst_id_type;
 +  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
 +    packetdata.src_id_len + dst_id_len;
 +  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
 +
 +  /* Prepare outgoing data buffer for packet sending */
 +  silc_packet_send_prepare(sock, 
 +                         SILC_PACKET_HEADER_LEN +
 +                         packetdata.src_id_len + 
 +                         packetdata.dst_id_len,
 +                         packetdata.padlen,
 +                         data_len);
 +
 +  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
 +
 +  packetdata.buffer = sock->outbuf;
 +
 +  /* Put the data to the buffer */
 +  if (data && data_len)
 +    silc_buffer_put(sock->outbuf, data, data_len);
 +
 +  /* Create the outgoing packet */
 +  silc_packet_assemble(&packetdata, cipher);
 +
 +  /* Encrypt the packet */
 +  silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, sock->outbuf->len);
 +
 +  SILC_LOG_HEXDUMP(("Outgoing packet (%d), len %d", sequence, 
 +                  sock->outbuf->len),
 +                 sock->outbuf->data, sock->outbuf->len);
 +
 +  /* Now actually send the packet */
 +  silc_server_packet_send_real(server, sock, force_send);
 +
 +  if (packetdata.src_id)
 +    silc_free(packetdata.src_id);
 +  if (packetdata.dst_id)
 +    silc_free(packetdata.dst_id);
 +}
 +
 +/* Assembles a new packet to be sent out to network. This doesn't actually
 +   send the packet but creates the packet and fills the outgoing data
 +   buffer and marks the packet ready to be sent to network. However, If 
 +   argument force_send is TRUE the packet is sent immediately and not put 
 +   to queue. Normal case is that the packet is not sent immediately. 
 +   The source and destination information is sent as argument for this
 +   function. */
 +
 +void silc_server_packet_send_srcdest(SilcServer server,
 +                                   SilcSocketConnection sock, 
 +                                   SilcPacketType type, 
 +                                   SilcPacketFlags flags,
 +                                   void *src_id,
 +                                   SilcIdType src_id_type,
 +                                   void *dst_id,
 +                                   SilcIdType dst_id_type,
 +                                   unsigned char *data, 
 +                                   uint32 data_len,
 +                                   bool force_send)
 +{
 +  SilcPacketContext packetdata;
 +  SilcIDListData idata;
 +  SilcCipher cipher = NULL;
 +  SilcHmac hmac = NULL;
 +  uint32 sequence = 0;
 +  unsigned char *dst_id_data = NULL;
 +  uint32 dst_id_len = 0;
 +  unsigned char *src_id_data = NULL;
 +  uint32 src_id_len = 0;
 +  int block_len = 0;
 +
 +  SILC_LOG_DEBUG(("Sending packet, type %d", type));
 +
 +  /* Get data used in the packet sending, keys and stuff */
 +  idata = (SilcIDListData)sock->user_data;
 +
 +  if (dst_id) {
 +    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
 +    dst_id_len = silc_id_get_len(dst_id, dst_id_type);
 +  }
 +
 +  if (src_id) {
 +    src_id_data = silc_id_id2str(src_id, src_id_type);
 +    src_id_len = silc_id_get_len(src_id, src_id_type);
 +  }
 +
 +  if (idata) {
 +    cipher = idata->send_key;
 +    hmac = idata->hmac_send;
 +    sequence = idata->psn_send++;
 +    block_len = silc_cipher_get_block_len(cipher);
 +  }
 +
 +  /* Set the packet context pointers */
 +  packetdata.type = type;
 +  packetdata.flags = flags;
 +  packetdata.src_id = src_id_data;
 +  packetdata.src_id_len = src_id_len;
 +  packetdata.src_id_type = src_id_type;
 +  packetdata.dst_id = dst_id_data;
 +  packetdata.dst_id_len = dst_id_len;
 +  packetdata.dst_id_type = dst_id_type;
 +  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
 +    packetdata.src_id_len + dst_id_len;
 +  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
 +
 +  /* Prepare outgoing data buffer for packet sending */
 +  silc_packet_send_prepare(sock, 
 +                         SILC_PACKET_HEADER_LEN +
 +                         packetdata.src_id_len + 
 +                         packetdata.dst_id_len,
 +                         packetdata.padlen,
 +                         data_len);
 +
 +  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
 +
 +  packetdata.buffer = sock->outbuf;
 +
 +  /* Put the data to the buffer */
 +  if (data && data_len)
 +    silc_buffer_put(sock->outbuf, data, data_len);
 +
 +  /* Create the outgoing packet */
 +  silc_packet_assemble(&packetdata, cipher);
 +
 +  /* Encrypt the packet */
 +  silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, sock->outbuf->len);
 +
 +  SILC_LOG_HEXDUMP(("Outgoing packet (%d), len %d", sequence, 
 +                  sock->outbuf->len),
 +                 sock->outbuf->data, sock->outbuf->len);
 +
 +  /* Now actually send the packet */
 +  silc_server_packet_send_real(server, sock, force_send);
 +
 +  if (packetdata.src_id)
 +    silc_free(packetdata.src_id);
 +  if (packetdata.dst_id)
 +    silc_free(packetdata.dst_id);
 +}
 +
 +/* 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
 +   function. This does not test or set the broadcast flag. */
 +
 +void silc_server_packet_broadcast(SilcServer server,
 +                                SilcSocketConnection sock,
 +                                SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcIDListData idata;
 +  void *id;
 +
 +  SILC_LOG_DEBUG(("Broadcasting received broadcast packet"));
 +
 +  /* 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_len, packet->src_id_type);
 +  if (id && !SILC_ID_SERVER_COMPARE(id, server->router->id)) {
 +    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_send, idata->psn_send++,
 +                      sock->outbuf, sock->outbuf->len);
 +
 +    SILC_LOG_HEXDUMP(("Broadcasted packet (%d), len %d", idata->psn_send - 1,
 +                    sock->outbuf->len),
 +                   sock->outbuf->data, sock->outbuf->len);
 +
 +    /* Now actually send the packet */
 +    silc_server_packet_send_real(server, sock, TRUE);
 +    silc_free(id);
 +    return;
 +  }
 +
 +  SILC_LOG_DEBUG(("Will not broadcast to primary route since it is the "
 +                "original sender of this packet"));
 +  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_send, idata->psn_send++,
 +                    sock->outbuf, sock->outbuf->len);
 +
 +  SILC_LOG_HEXDUMP(("Routed packet (%d), len %d", idata->psn_send - 1, 
 +                  sock->outbuf->len),
 +                 sock->outbuf->data, sock->outbuf->len);
 +
 +  /* Now actually send the packet */
 +  silc_server_packet_send_real(server, sock, TRUE);
 +}
 +
 +/* This routine can be used to send a packet to table of clients provided
 +   in `clients'. If `route' is FALSE the packet is routed only to local
 +   clients (for server locally connected, and for router local cell). */
 +
 +void silc_server_packet_send_clients(SilcServer server,
 +                                   SilcClientEntry *clients,
 +                                   uint32 clients_count,
 +                                   SilcPacketType type, 
 +                                   SilcPacketFlags flags,
 +                                   bool route,
 +                                   unsigned char *data, 
 +                                   uint32 data_len,
 +                                   bool force_send)
 +{
 +  SilcSocketConnection sock = NULL;
 +  SilcClientEntry client = NULL;
 +  SilcServerEntry *routed = NULL;
 +  uint32 routed_count = 0;
 +  bool gone = FALSE;
 +  int i, k;
 +
 +  SILC_LOG_DEBUG(("Sending packet to list of clients"));
 +
 +  /* Send to all clients in table */
 +  for (i = 0; i < clients_count; i++) {
 +    client = clients[i];
 +
 +    /* If client has router set it is not locally connected client and
 +       we will route the message to the router set in the client. Though,
 +       send locally connected server in all cases. */
 +    if (server->server_type == SILC_ROUTER && client->router && 
 +      ((!route && client->router->router == server->id_entry) || route)) {
 +
 +      /* Check if we have sent the packet to this route already */
 +      for (k = 0; k < routed_count; k++)
 +      if (routed[k] == client->router)
 +        break;
 +      if (k < routed_count)
 +      continue;
 +
 +      /* Route only once to router */
 +      sock = (SilcSocketConnection)client->router->connection;
 +      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
 +      if (gone)
 +        continue;
 +      gone = TRUE;
 +      }
 +
 +      /* Send the packet */
 +      silc_server_packet_send_dest(server, sock, type, flags,
 +                                 client->router->id, SILC_ID_SERVER,
 +                                 data, data_len, force_send);
 +
 +      /* Mark this route routed already */
 +      routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
 +      routed[routed_count++] = client->router;
 +      continue;
 +    }
 +
 +    if (client->router)
 +      continue;
 +
 +    /* Send to locally connected client */
 +    sock = (SilcSocketConnection)client->connection;
 +    silc_server_packet_send_dest(server, sock, type, flags,
 +                               client->id, SILC_ID_CLIENT,
 +                               data, data_len, force_send);
 +  }
 +
 +  silc_free(routed);
 +}
 +
 +/* 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
 +   a channel message. If FALSE normal encryption process is used. */
 +
 +static void
 +silc_server_packet_send_to_channel_real(SilcServer server,
 +                                      SilcSocketConnection sock,
 +                                      SilcPacketContext *packet,
 +                                      SilcCipher cipher,
 +                                      SilcHmac hmac,
 +                                      uint32 sequence,
 +                                      unsigned char *data,
 +                                      uint32 data_len,
 +                                      bool channel_message,
 +                                      bool force_send)
 +{
 +  int block_len;
 +
 +  if (!sock)
 +    return;
 +
 +  packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
 +    packet->src_id_len + packet->dst_id_len;
 +
 +  block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
 +  if (channel_message)
 +    packet->padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
 +                                       packet->src_id_len +
 +                                       packet->dst_id_len), block_len);
 +  else
 +    packet->padlen = SILC_PACKET_PADLEN(packet->truelen, block_len);
 +
 +  /* Prepare outgoing data buffer for packet sending */
 +  silc_packet_send_prepare(sock, 
 +                         SILC_PACKET_HEADER_LEN +
 +                         packet->src_id_len + 
 +                         packet->dst_id_len,
 +                         packet->padlen,
 +                         data_len);
 +
 +  packet->buffer = sock->outbuf;
 +
 +  /* Put the data to buffer, assemble and encrypt the packet. The packet
 +     is encrypted with normal session key shared with the client, unless
 +     the `channel_message' is TRUE. */
 +  silc_buffer_put(sock->outbuf, data, data_len);
 +  silc_packet_assemble(packet, cipher);
 +  if (channel_message)
 +    silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, 
 +                      SILC_PACKET_HEADER_LEN + packet->src_id_len + 
 +                      packet->dst_id_len + packet->padlen);
 +  else
 +    silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, 
 +                      sock->outbuf->len);
 +    
 +  SILC_LOG_HEXDUMP(("Channel packet (%d), len %d", sequence, 
 +                  sock->outbuf->len),
 +                 sock->outbuf->data, sock->outbuf->len);
 +
 +  /* Now actually send the packet */
 +  silc_server_packet_send_real(server, sock, force_send);
 +}
 +
 +/* This routine is used by the server to send packets to channel. The 
 +   packet sent with this function is distributed to all clients on
 +   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). If `sender'
 +   is provided then the packet is not sent to that connection since it
 +   originally came from it. If `send_to_clients' is FALSE then the
 +   packet is not sent clients, only servers. */
 +
 +void silc_server_packet_send_to_channel(SilcServer server,
 +                                      SilcSocketConnection sender,
 +                                      SilcChannelEntry channel,
 +                                      SilcPacketType type,
 +                                      bool route,
 +                                      unsigned char *data,
 +                                      uint32 data_len,
 +                                      bool force_send)
 +{
 +  SilcSocketConnection sock = NULL;
 +  SilcPacketContext packetdata;
 +  SilcClientEntry client = NULL;
 +  SilcServerEntry *routed = NULL;
 +  SilcChannelClientEntry chl;
 +  SilcHashTableList htl;
 +  SilcIDListData idata;
 +  uint32 routed_count = 0;
 +  bool gone = FALSE;
 +  int k;
 +
 +  /* This doesn't send channel message packets */
 +  assert(type != SILC_PACKET_CHANNEL_MESSAGE);
 +  
 +  SILC_LOG_DEBUG(("Sending packet to channel"));
 +
 +  /* Set the packet context pointers. */
 +  packetdata.flags = 0;
 +  packetdata.type = type;
 +  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
 +  packetdata.src_id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
 +  packetdata.src_id_type = SILC_ID_SERVER;
 +  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 +  packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
 +  packetdata.dst_id_type = SILC_ID_CHANNEL;
 +
 +  /* If there are global users in the channel we will send the message
 +     first to our router for further routing. */
 +  if (route && server->server_type != SILC_ROUTER && !server->standalone &&
 +      channel->global_users) {
 +    SilcServerEntry router;
 +
 +    /* Get data used in packet header encryption, keys and stuff. */
 +    router = server->router;
 +    sock = (SilcSocketConnection)router->connection;
 +    idata = (SilcIDListData)router;
 +    
 +    if (sock != sender) {
 +      SILC_LOG_DEBUG(("Sending packet to router for routing"));
 +      
 +      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
 +                                            idata->send_key, 
 +                                            idata->hmac_send, 
 +                                            idata->psn_send++,
 +                                            data, data_len, FALSE, 
 +                                            force_send);
 +    }
 +  }
 +
 +  routed = silc_calloc(silc_hash_table_count(channel->user_list), 
 +                     sizeof(*routed));
 +
 +  /* Send the message to clients on the channel's client list. */
 +  silc_hash_table_list(channel->user_list, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +    client = chl->client;
 +    if (!client)
 +      continue;
 +
 +    /* If client has router set it is not locally connected client and
 +       we will route the message to the router set in the client. Though,
 +       send locally connected server in all cases. */
 +    if (server->server_type == SILC_ROUTER && client->router && 
 +      ((!route && client->router->router == server->id_entry) || route)) {
 +
 +      /* Check if we have sent the packet to this route already */
 +      for (k = 0; k < routed_count; k++)
 +      if (routed[k] == client->router)
 +        break;
 +      if (k < routed_count)
 +      continue;
 +
 +      /* Get data used in packet header encryption, keys and stuff. */
 +      sock = (SilcSocketConnection)client->router->connection;
 +      idata = (SilcIDListData)client->router;
 +
 +      if (sender && sock == sender)
 +      continue;
 +
 +      /* Route only once to router */
 +      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
 +      if (gone)
 +        continue;
 +      gone = TRUE;
 +      }
 +
 +      /* Send the packet */
 +      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
 +                                            idata->send_key, 
 +                                            idata->hmac_send, 
 +                                            idata->psn_send++,
 +                                            data, data_len, FALSE, 
 +                                            force_send);
 +
 +      /* Mark this route routed already */
 +      routed[routed_count++] = client->router;
 +      continue;
 +    }
 +
 +    if (client->router)
 +      continue;
 +
 +    /* Send to locally connected client */
 +
 +    /* Get data used in packet header encryption, keys and stuff. */
 +    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_send, 
 +                                          idata->psn_send++, 
 +                                          data, data_len, FALSE, 
 +                                          force_send);
 +  }
 +
 +  silc_hash_table_list_reset(&htl);
 +  silc_free(routed);
 +  silc_free(packetdata.src_id);
 +  silc_free(packetdata.dst_id);
 +}
 +
 +/* This checks whether the relayed packet came from router. If it did
 +   then we'll need to encrypt it with the channel key. This is called
 +   from the silc_server_packet_relay_to_channel. */
 +
 +static bool
 +silc_server_packet_relay_to_channel_encrypt(SilcServer server,
 +                                          SilcSocketConnection sock,
 +                                          SilcChannelEntry channel,
 +                                          unsigned char *data,
 +                                          unsigned int data_len)
 +{
 +  /* If we are router and the packet came from router and private key
 +     has not been set for the channel then we must encrypt the packet
 +     as it was decrypted with the session key shared between us and the
 +     router which sent it. This is so, because cells does not share the
 +     same channel key. */
 +  if (server->server_type == SILC_ROUTER &&
 +      sock->type == SILC_SOCKET_TYPE_ROUTER &&
 +      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) &&
 +      channel->channel_key) {
 +    SilcBuffer chp;
 +    uint32 iv_len, i;
 +    uint16 dlen, flags;
 +
 +    iv_len = silc_cipher_get_block_len(channel->channel_key);
 +    if (channel->iv[0] == '\0')
 +      for (i = 0; i < iv_len; i++) channel->iv[i] = 
 +                                   silc_rng_get_byte(server->rng);
 +    else
 +      silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
 +    
 +    /* Encode new payload. This encrypts it also. */
 +    SILC_GET16_MSB(flags, data);
 +    SILC_GET16_MSB(dlen, data + 2);
 +
 +    if (dlen > data_len) {
 +      SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
 +      return FALSE;
 +    }
 +
 +    chp = silc_channel_message_payload_encode(flags, dlen, data + 4,
 +                                            iv_len, channel->iv,
 +                                            channel->channel_key,
 +                                            channel->hmac);
 +    memcpy(data, chp->data, chp->len);
 +    silc_buffer_free(chp);
 +  }
 +
 +  return TRUE;
 +}
 +
 +/* This routine is explicitly used to relay messages to some channel.
 +   Packets sent with this function we have received earlier and are
 +   totally encrypted. This just sends the packet to all clients on
 +   the channel. If the sender of the packet is someone on the channel 
 +   the message will not be sent to that client. The SILC Packet header
 +   is encrypted with the session key shared between us and the client.
 +   MAC is also computed before encrypting the header. Rest of the
 +   packet will be untouched. */
 +
 +void silc_server_packet_relay_to_channel(SilcServer server,
 +                                       SilcSocketConnection sender_sock,
 +                                       SilcChannelEntry channel,
 +                                       void *sender, 
 +                                       SilcIdType sender_type,
 +                                       void *sender_entry,
 +                                       unsigned char *data,
 +                                       uint32 data_len,
 +                                       bool force_send)
 +{
 +  bool found = FALSE;
 +  SilcSocketConnection sock = NULL;
 +  SilcPacketContext packetdata;
 +  SilcClientEntry client = NULL;
 +  SilcServerEntry *routed = NULL;
 +  SilcChannelClientEntry chl;
 +  uint32 routed_count = 0;
 +  SilcIDListData idata;
 +  SilcHashTableList htl;
 +  bool gone = FALSE;
 +  int k;
 +
 +  SILC_LOG_DEBUG(("Relaying packet to channel"));
 +
 +  /* This encrypts the packet, if needed. It will be encrypted if
 +     it came from the router thus it needs to be encrypted with the
 +     channel key. If the channel key does not exist, then we know we
 +     don't have a single local user on the channel. */
 +  if (!silc_server_packet_relay_to_channel_encrypt(server, sender_sock,
 +                                                 channel, data,
 +                                                 data_len))
 +    return;
 +
 +  /* Set the packet context pointers. */
 +  packetdata.flags = 0;
 +  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
 +  packetdata.src_id = silc_id_id2str(sender, sender_type);
 +  packetdata.src_id_len = silc_id_get_len(sender, sender_type);
 +  packetdata.src_id_type = sender_type;
 +  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 +  packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
 +  packetdata.dst_id_type = SILC_ID_CHANNEL;
 +
 +  /* If there are global users in the channel we will send the message
 +     first to our router for further routing. */
 +  if (server->server_type != SILC_ROUTER && !server->standalone &&
 +      channel->global_users) {
 +    SilcServerEntry router = server->router;
 +
 +    /* Check that the sender is not our router. */
 +    if (sender_sock != (SilcSocketConnection)router->connection) {
 +
 +      /* Get data used in packet header encryption, keys and stuff. */
 +      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_send, 
 +                                            idata->psn_send++, 
 +                                            data, data_len, TRUE, 
 +                                            force_send);
 +    }
 +  }
 +
 +  routed = silc_calloc(silc_hash_table_count(channel->user_list), 
 +                     sizeof(*routed));
 +
 +  /* Mark that to the route the original sender if from is not routed */
 +  if (sender_type == SILC_ID_CLIENT) {
 +    client = (SilcClientEntry)sender_entry;
 +    if (client->router) {
 +      routed[routed_count++] = client->router;
 +      SILC_LOG_DEBUG(("************* router %s", 
 +                    silc_id_render(client->router->id, SILC_ID_SERVER)));
 +    }
 +  }
 +
 +  /* Send the message to clients on the channel's client list. */
 +  silc_hash_table_list(channel->user_list, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +    client = chl->client;
 +    if (!client)
 +      continue;
 +
 +    /* Do not send to the sender */
 +    if (!found && client == sender_entry) {
 +      found = TRUE;
 +      continue;
 +    }
 +
 +    /* If the client has set router it means that it is not locally
 +       connected client and we will route the packet further. */
 +    if (server->server_type == SILC_ROUTER && client->router) {
 +
 +      /* Sender maybe server as well so we want to make sure that
 +       we won't send the message to the server it came from. */
 +      if (!found && SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
 +      found = TRUE;
 +      routed[routed_count++] = client->router;
 +      continue;
 +      }
 +
 +      /* Check if we have sent the packet to this route already */
 +      for (k = 0; k < routed_count; k++)
 +      if (routed[k] == client->router)
 +        break;
 +      if (k < routed_count)
 +      continue;
 +      
 +      /* Get data used in packet header encryption, keys and stuff. */
 +      sock = (SilcSocketConnection)client->router->connection;
 +      idata = (SilcIDListData)client->router;
 +
 +      /* Do not send to the sender. Check first whether the true
 +       sender's router is same as this client's router. Also check
 +       if the sender socket is the same as this client's router
 +       socket. */
 +      if (sender_entry &&
 +        ((SilcClientEntry)sender_entry)->router == client->router)
 +      continue;
 +      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));
 +
 +      /* Mark this route routed already. */
 +      routed[routed_count++] = client->router;
 +      
 +      /* If the remote connection is router then we'll decrypt the
 +       channel message and re-encrypt it with the session key shared
 +       between us and the remote router. This is done because the
 +       channel keys are cell specific and we have different channel
 +       key than the remote router has. */
 +      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
 +      if (gone)
 +        continue;
 +
 +      SILC_LOG_DEBUG(("Remote is router, encrypt with session key"));
 +      gone = TRUE;
 +
 +      /* If private key mode is not set then decrypt the packet
 +         and re-encrypt it */
 +      if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
 +        unsigned char *tmp = silc_calloc(data_len, sizeof(*data));
 +        memcpy(tmp, data, data_len);
 +
 +        /* Decrypt the channel message (we don't check the MAC) */
 +        if (channel->channel_key &&
 +            !silc_channel_message_payload_decrypt(tmp, data_len, 
 +                                                  channel->channel_key,
 +                                                  NULL)) {
 +          memset(tmp, 0, data_len);
 +          silc_free(tmp);
 +          continue;
 +        }
 +
 +        /* Now re-encrypt and send it to the router */
 +        silc_server_packet_send_srcdest(server, sock, 
 +                                        SILC_PACKET_CHANNEL_MESSAGE, 0,
 +                                        sender, sender_type,
 +                                        channel->id, SILC_ID_CHANNEL,
 +                                        tmp, data_len, force_send);
 +
 +        /* Free the copy of the channel message */
 +        memset(tmp, 0, data_len);
 +        silc_free(tmp);
 +      } else {
 +        /* Private key mode is set, we don't have the channel key, so
 +           just re-encrypt the entire packet and send it to the router. */
 +        silc_server_packet_send_srcdest(server, sock, 
 +                                        SILC_PACKET_CHANNEL_MESSAGE, 0,
 +                                        sender, sender_type,
 +                                        channel->id, SILC_ID_CHANNEL,
 +                                        data, data_len, force_send);
 +      }
 +      continue;
 +      }
 +
 +      /* Send the packet (to normal server) */
 +      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
 +                                            idata->send_key, 
 +                                            idata->hmac_send, 
 +                                            idata->psn_send++, 
 +                                            data, data_len, TRUE, 
 +                                            force_send);
 +
 +      continue;
 +    }
 +
 +    if (client->router)
 +      continue;
 +
 +    /* Get data used in packet header encryption, keys and stuff. */
 +    sock = (SilcSocketConnection)client->connection;
 +    idata = (SilcIDListData)client;
 +
 +    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,
 +                                          idata->send_key, 
 +                                          idata->hmac_send, 
 +                                          idata->psn_send++, 
 +                                          data, data_len, TRUE, 
 +                                          force_send);
 +  }
 +
 +  silc_hash_table_list_reset(&htl);
 +  silc_free(routed);
 +  silc_free(packetdata.src_id);
 +  silc_free(packetdata.dst_id);
 +}
 +
 +/* This function is used to send packets strictly to all local clients
 +   on a particular channel.  This is used for example to distribute new
 +   channel key to all our locally connected clients on the channel. 
 +   The packets are always encrypted with the session key shared between
 +   the client, this means these are not _to the channel_ but _to the client_
 +   on the channel. */
 +
 +void silc_server_packet_send_local_channel(SilcServer server,
 +                                         SilcChannelEntry channel,
 +                                         SilcPacketType type,
 +                                         SilcPacketFlags flags,
 +                                         unsigned char *data,
 +                                         uint32 data_len,
 +                                         bool force_send)
 +{
 +  SilcChannelClientEntry chl;
 +  SilcHashTableList htl;
 +  SilcSocketConnection sock = NULL;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  /* Send the message to clients on the channel's client list. */
 +  silc_hash_table_list(channel->user_list, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +    if (chl->client && !chl->client->router) {
 +      sock = (SilcSocketConnection)chl->client->connection;
 +
 +      /* Send the packet to the client */
 +      silc_server_packet_send_dest(server, sock, type, flags, chl->client->id,
 +                                 SILC_ID_CLIENT, data, data_len,
 +                                 force_send);
 +    }
 +  }
 +  silc_hash_table_list_reset(&htl);
 +}
 +
 +/* Routine used to send (relay, route) private messages to some destination.
 +   If the private message key does not exist then the message is re-encrypted,
 +   otherwise we just pass it along. This really is not used to send new
 +   private messages (as server does not send them) but to relay received
 +   private messages. */
 +
 +void silc_server_send_private_message(SilcServer server,
 +                                    SilcSocketConnection dst_sock,
 +                                    SilcCipher cipher,
 +                                    SilcHmac hmac,
 +                                    uint32 sequence,
 +                                    SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +
 +  /* Re-encrypt and send if private messge key does not exist */
 +  if (!(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY)) {
 +
 +    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 +                   + packet->dst_id_len + packet->padlen);
 +    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
 +    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
 +
 +    /* Re-encrypt packet */
 +    silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, buffer->len);
 +
 +    /* Send the packet */
 +    silc_server_packet_send_real(server, dst_sock, FALSE);
 +
 +  } else {
 +    /* Key exist so encrypt just header and send it */
 +    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 +                   + packet->dst_id_len + packet->padlen);
 +    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
 +    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
 +
 +    /* Encrypt header */
 +    silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, 
 +                      SILC_PACKET_HEADER_LEN + packet->src_id_len + 
 +                      packet->dst_id_len + packet->padlen);
 +
 +    silc_server_packet_send_real(server, dst_sock, FALSE);
 +  }
 +}
 +
 +/* Sends current motd to client */
 +
 +void silc_server_send_motd(SilcServer server,
 +                         SilcSocketConnection sock)
 +{
-   if (server->config && server->config->motd && 
-       server->config->motd->motd_file) {
++  char *motd, *motd_file = NULL;
 +  uint32 motd_len;
 +
-     motd = silc_file_readfile(server->config->motd->motd_file, &motd_len);
++  if (server->config)
++    motd_file = server->config->server_info->motd_file;
 +
++  if (motd_file) {
++    motd = silc_file_readfile(motd_file, &motd_len);
 +    if (!motd)
 +      return;
 +
 +    silc_server_send_notify(server, sock, FALSE, SILC_NOTIFY_TYPE_MOTD, 1,
 +                          motd, motd_len);
 +    silc_free(motd);
 +  }
 +}
 +
 +/* Sends error message. Error messages may or may not have any 
 +   implications. */
 +
 +void silc_server_send_error(SilcServer server,
 +                          SilcSocketConnection sock,
 +                          const char *fmt, ...)
 +{
 +  va_list ap;
 +  unsigned char buf[4096];
 +
 +  memset(buf, 0, sizeof(buf));
 +  va_start(ap, fmt);
 +  vsprintf(buf, fmt, ap);
 +  va_end(ap);
 +
 +  silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
 +                        buf, strlen(buf), FALSE);
 +}
 +
 +/* Sends notify message. If format is TRUE the variable arguments are
 +   formatted and the formatted string is sent as argument payload. If it is
 +   FALSE then each argument is sent as separate argument and their format
 +   in the argument list must be { argument data, argument length }. */
 +
 +void silc_server_send_notify(SilcServer server,
 +                           SilcSocketConnection sock,
 +                           bool broadcast,
 +                           SilcNotifyType type,
 +                           uint32 argc, ...)
 +{
 +  va_list ap;
 +  SilcBuffer packet;
 +
 +  va_start(ap, argc);
 +
 +  packet = silc_notify_payload_encode(type, argc, ap);
 +  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 
 +                        broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
 +                        packet->data, packet->len, FALSE);
 +
 +  /* Send to backup routers if this is being broadcasted to primary
 +     router. */
 +  if (server->router && server->router->connection &&
 +      sock == server->router->connection && broadcast)
 +    silc_server_backup_send(server, NULL, SILC_PACKET_NOTIFY, 0,
 +                          packet->data, packet->len, FALSE, TRUE);
 +
 +  silc_buffer_free(packet);
 +  va_end(ap);
 +}
 +
 +/* Sends notify message and gets the arguments from the `args' Argument
 +   Payloads. */
 +
 +void silc_server_send_notify_args(SilcServer server,
 +                                SilcSocketConnection sock,
 +                                bool broadcast,
 +                                SilcNotifyType type,
 +                                uint32 argc,
 +                                SilcBuffer args)
 +{
 +  SilcBuffer packet;
 +
 +  packet = silc_notify_payload_encode_args(type, argc, args);
 +  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 
 +                        broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
 +                        packet->data, packet->len, FALSE);
 +  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,
 +                                          bool broadcast,
 +                                          SilcChannelID *old_id,
 +                                          SilcChannelID *new_id)
 +{
 +  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,
 +                                       bool broadcast,
 +                                       SilcClientID *old_id,
 +                                       SilcClientID *new_id)
 +{
 +  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,
 +                                bool broadcast,
 +                                SilcChannelEntry channel,
 +                                SilcClientID *client_id)
 +{
 +  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,
 +                                 bool broadcast,
 +                                 SilcChannelEntry channel,
 +                                 SilcClientID *client_id)
 +{
 +  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,
 +                                 bool broadcast,
 +                                 SilcChannelEntry channel,
 +                                 uint32 mode_mask,
 +                                 void *id, SilcIdType id_type,
 +                                 char *cipher, char *hmac,
 +                                 char *passphrase)
 +{
 +  SilcBuffer idp;
 +  unsigned char mode[4];
 +
 +  idp = silc_id_payload_encode((void *)id, id_type);
 +  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,
 +                             5, idp->data, idp->len,
 +                             mode, 4,
 +                             cipher, cipher ? strlen(cipher) : 0,
 +                             hmac, hmac ? strlen(hmac) : 0,
 +                             passphrase, passphrase ? 
 +                             strlen(passphrase) : 0);
 +  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,
 +                                  bool broadcast,
 +                                  SilcChannelEntry channel,
 +                                  uint32 mode_mask,
 +                                  void *id, SilcIdType id_type,
 +                                  SilcClientID *target)
 +{
 +  SilcBuffer idp1, idp2;
 +  unsigned char mode[4];
 +
 +  idp1 = silc_id_payload_encode((void *)id, id_type);
 +  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,
 +                                   bool broadcast,
 +                                   SilcClientID *client_id,
 +                                   const 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 TOPIC_SET notify type. This tells that `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,
 +                                     bool broadcast,
 +                                     SilcChannelEntry channel,
 +                                     void *id, SilcIdType id_type,
 +                                     char *topic)
 +{
 +  SilcBuffer idp;
 +
 +  idp = silc_id_payload_encode(id, id_type);
 +  silc_server_send_notify_dest(server, sock, broadcast,
 +                             (void *)channel->id, SILC_ID_CHANNEL,
 +                             SILC_NOTIFY_TYPE_TOPIC_SET,
 +                             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,
 +                                  bool broadcast,
 +                                  SilcChannelEntry channel,
 +                                  SilcClientID *client_id,
 +                                  SilcClientID *kicker,
 +                                  char *comment)
 +{
 +  SilcBuffer idp1;
 +  SilcBuffer idp2;
 +
 +  idp1 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
 +  idp2 = silc_id_payload_encode((void *)kicker, SILC_ID_CLIENT);
 +  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
 +                             SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_KICKED, 3,
 +                             idp1->data, idp1->len,
 +                             comment, comment ? strlen(comment) : 0,
 +                             idp2->data, idp2->len);
 +  silc_buffer_free(idp1);
 +  silc_buffer_free(idp2);
 +}
 +
 +/* Send KILLED notify type. This tells that the `client_id' client was
 +   killed from the network.  The `comment' may indicate the reason
 +   for the killing. */
 +
 +void silc_server_send_notify_killed(SilcServer server,
 +                                  SilcSocketConnection sock,
 +                                  bool broadcast,
 +                                  SilcClientID *client_id,
 +                                  char *comment)
 +{
 +  SilcBuffer idp;
 +
 +  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
 +  silc_server_send_notify_dest(server, sock, broadcast, (void *)client_id,
 +                             SILC_ID_CLIENT, SILC_NOTIFY_TYPE_KILLED,
 +                             comment ? 2 : 1, idp->data, idp->len,
 +                             comment, comment ? strlen(comment) : 0);
 +  silc_buffer_free(idp);
 +}
 +
 +/* Sends UMODE_CHANGE notify type. This tells that `client_id' client's
 +   user mode in the SILC Network was changed. This function is used to
 +   send the packet between routers as broadcast packet. */
 +
 +void silc_server_send_notify_umode(SilcServer server,
 +                                 SilcSocketConnection sock,
 +                                 bool broadcast,
 +                                 SilcClientID *client_id,
 +                                 uint32 mode_mask)
 +{
 +  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(server, sock, broadcast,
 +                        SILC_NOTIFY_TYPE_UMODE_CHANGE, 2,
 +                        idp->data, idp->len, 
 +                        mode, 4);
 +  silc_buffer_free(idp);
 +}
 +
 +/* Sends BAN notify type. This tells that ban has been either `add'ed
 +   or `del'eted on the `channel. This function is used to send the packet
 +   between routers as broadcast packet. */
 +
 +void silc_server_send_notify_ban(SilcServer server,
 +                               SilcSocketConnection sock,
 +                               bool broadcast,
 +                               SilcChannelEntry channel,
 +                               char *add, char *del)
 +{
 +  SilcBuffer idp;
 +
 +  idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
 +  silc_server_send_notify(server, sock, broadcast,
 +                        SILC_NOTIFY_TYPE_BAN, 3,
 +                        idp->data, idp->len,
 +                        add, add ? strlen(add) : 0,
 +                        del, del ? strlen(del) : 0);
 +  silc_buffer_free(idp);
 +}
 +
 +/* Sends INVITE notify type. This tells that invite has been either `add'ed
 +   or `del'eted on the `channel.  The sender of the invite is the `client_id'.
 +   This function is used to send the packet between routers as broadcast
 +   packet. */
 +
 +void silc_server_send_notify_invite(SilcServer server,
 +                                  SilcSocketConnection sock,
 +                                  bool broadcast,
 +                                  SilcChannelEntry channel,
 +                                  SilcClientID *client_id,
 +                                  char *add, char *del)
 +{
 +  SilcBuffer idp, idp2;
 +
 +  idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
 +  idp2 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
 +  silc_server_send_notify(server, sock, broadcast,
 +                        SILC_NOTIFY_TYPE_INVITE, 5,
 +                        idp->data, idp->len,
 +                        channel->channel_name, strlen(channel->channel_name),
 +                        idp2->data, idp2->len,
 +                        add, add ? strlen(add) : 0,
 +                        del, del ? strlen(del) : 0);
 +  silc_buffer_free(idp);
 +  silc_buffer_free(idp2);
 +}
 +
 +/* Sends notify message destined to specific entity. */
 +
 +void silc_server_send_notify_dest(SilcServer server,
 +                                SilcSocketConnection sock,
 +                                bool broadcast,
 +                                void *dest_id,
 +                                SilcIdType dest_id_type,
 +                                SilcNotifyType type,
 +                                uint32 argc, ...)
 +{
 +  va_list ap;
 +  SilcBuffer packet;
 +
 +  va_start(ap, argc);
 +
 +  packet = silc_notify_payload_encode(type, argc, ap);
 +  silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY, 
 +                             broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
 +                             dest_id, dest_id_type,
 +                             packet->data, packet->len, FALSE);
 +  silc_buffer_free(packet);
 +  va_end(ap);
 +}
 +
 +/* Sends notify message to a channel. The notify message sent is 
 +   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 `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,
 +                                      bool route_notify,
 +                                      SilcNotifyType type,
 +                                      uint32 argc, ...)
 +{
 +  va_list ap;
 +  SilcBuffer packet;
 +
 +  va_start(ap, argc);
 +
 +  packet = silc_notify_payload_encode(type, argc, ap);
 +  silc_server_packet_send_to_channel(server, sender, channel, 
 +                                   SILC_PACKET_NOTIFY, route_notify,
 +                                   packet->data, packet->len, FALSE);
 +  silc_buffer_free(packet);
 +  va_end(ap);
 +}
 +
 +/* Send notify message to all channels the client has joined. It is quaranteed
 +   that the message is sent only once to a client (ie. if a client is joined
 +   on two same channel it will receive only one notify message). Also, this
 +   sends only to local clients (locally connected if we are server, and to
 +   local servers if we are router). If `sender' is provided the packet is
 +   not sent to that client at all. */
 +
 +void silc_server_send_notify_on_channels(SilcServer server,
 +                                       SilcClientEntry sender,
 +                                       SilcClientEntry client,
 +                                       SilcNotifyType type,
 +                                       uint32 argc, ...)
 +{
 +  int k;
 +  SilcSocketConnection sock = NULL;
 +  SilcPacketContext packetdata;
 +  SilcClientEntry c;
 +  SilcClientEntry *sent_clients = NULL;
 +  uint32 sent_clients_count = 0;
 +  SilcServerEntry *routed = NULL;
 +  uint32 routed_count = 0;
 +  SilcHashTableList htl, htl2;
 +  SilcChannelEntry channel;
 +  SilcChannelClientEntry chl, chl2;
 +  SilcIDListData idata;
 +  SilcBuffer packet;
 +  unsigned char *data;
 +  uint32 data_len;
 +  bool force_send = FALSE;
 +  va_list ap;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (!silc_hash_table_count(client->channels))
 +    return;
 +
 +  va_start(ap, argc);
 +  packet = silc_notify_payload_encode(type, argc, ap);
 +  data = packet->data;
 +  data_len = packet->len;
 +
 +  /* Set the packet context pointers. */
 +  packetdata.flags = 0;
 +  packetdata.type = SILC_PACKET_NOTIFY;
 +  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
 +  packetdata.src_id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
 +  packetdata.src_id_type = SILC_ID_SERVER;
 +
 +  silc_hash_table_list(client->channels, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +    channel = chl->channel;
 +
 +    /* Send the message to all clients on the channel's client list. */
 +    silc_hash_table_list(channel->user_list, &htl2);
 +    while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
 +      c = chl2->client;
 +      
 +      if (sender && c == sender)
 +      continue;
 +
 +      /* Check if we have sent the packet to this client already */
 +      for (k = 0; k < sent_clients_count; k++)
 +      if (sent_clients[k] == c)
 +        break;
 +      if (k < sent_clients_count)
 +      continue;
 +
 +      /* If we are router and if this client has router set it is not
 +       locally connected client and we will route the message to the
 +       router set in the client. */
 +      if (c && c->router && server->server_type == SILC_ROUTER) {
 +      /* Check if we have sent the packet to this route already */
 +      for (k = 0; k < routed_count; k++)
 +        if (routed[k] == c->router)
 +          break;
 +      if (k < routed_count)
 +        continue;
 +      
 +      /* Get data used in packet header encryption, keys and stuff. */
 +      sock = (SilcSocketConnection)c->router->connection;
 +      idata = (SilcIDListData)c->router;
 +
 +      {
 +        SILC_LOG_DEBUG(("*****************"));
 +        SILC_LOG_DEBUG(("client->router->id %s",
 +                        silc_id_render(c->router->id, SILC_ID_SERVER)));
 +        SILC_LOG_DEBUG(("client->router->connection->user_data->id %s",
 +                        silc_id_render(((SilcServerEntry)sock->user_data)->id, SILC_ID_SERVER)));
 +      }
 +
 +      packetdata.dst_id = silc_id_id2str(c->router->id, SILC_ID_SERVER);
 +      packetdata.dst_id_len = silc_id_get_len(c->router->id, SILC_ID_SERVER);
 +      packetdata.dst_id_type = SILC_ID_SERVER;
 +
 +      /* Send the packet */
 +      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
 +                                              idata->send_key, 
 +                                              idata->hmac_send, 
 +                                              idata->psn_send++, 
 +                                              data, data_len, FALSE, 
 +                                              force_send);
 +      
 +      silc_free(packetdata.dst_id);
 +
 +      /* We want to make sure that the packet is routed to same router
 +         only once. Mark this route as sent route. */
 +      routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
 +      routed[routed_count++] = c->router;
 +      continue;
 +      }
 +
 +      if (c && c->router)
 +      continue;
 +
 +      /* Send to locally connected client */
 +      if (c) {
 +      
 +      /* Get data used in packet header encryption, keys and stuff. */
 +      sock = (SilcSocketConnection)c->connection;
 +      idata = (SilcIDListData)c;
 +      
 +      packetdata.dst_id = silc_id_id2str(c->id, SILC_ID_CLIENT);
 +      packetdata.dst_id_len = silc_id_get_len(c->id, SILC_ID_CLIENT);
 +      packetdata.dst_id_type = SILC_ID_CLIENT;
 +
 +      /* Send the packet */
 +      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
 +                                              idata->send_key, 
 +                                              idata->hmac_send, 
 +                                              idata->psn_send++, 
 +                                              data, data_len, FALSE, 
 +                                              force_send);
 +
 +      silc_free(packetdata.dst_id);
 +
 +      /* Make sure that we send the notify only once per client. */
 +      sent_clients = silc_realloc(sent_clients, sizeof(*sent_clients) * 
 +                                  (sent_clients_count + 1));
 +      sent_clients[sent_clients_count++] = c;
 +      }
 +    }
 +    silc_hash_table_list_reset(&htl2);
 +  }
 +
 +  silc_hash_table_list_reset(&htl);
 +  silc_free(routed);
 +  silc_free(sent_clients);
 +  silc_free(packetdata.src_id);
 +  silc_buffer_free(packet);
 +  va_end(ap);
 +}
 +
 +/* Sends New ID Payload to remote end. The packet is used to distribute
 +   information about new registered clients, servers, channel etc. usually
 +   to routers so that they can keep these information up to date. 
 +   If the argument `broadcast' is TRUE then the packet is sent as
 +   broadcast packet. */
 +
 +void silc_server_send_new_id(SilcServer server,
 +                           SilcSocketConnection sock,
 +                           bool broadcast,
 +                           void *id, SilcIdType id_type, 
 +                           uint32 id_len)
 +{
 +  SilcBuffer idp;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  idp = silc_id_payload_encode(id, id_type);
 +  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
 +                        broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
 +                        idp->data, idp->len, FALSE);
 +
 +  /* Send to backup routers if this is being broadcasted to primary
 +     router. */
 +  if (server->router && server->router->connection &&
 +      sock == server->router->connection && broadcast)
 +    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_ID, 0,
 +                          idp->data, idp->len, FALSE, TRUE);
 +
 +  silc_buffer_free(idp);
 +}
 +
 +/* Send New Channel Payload to notify about newly created channel in the
 +   SILC network. Router uses this to notify other routers in the network 
 +   about new channel. This packet is broadcasted by router. */
 +
 +void silc_server_send_new_channel(SilcServer server,
 +                                SilcSocketConnection sock,
 +                                bool broadcast,
 +                                char *channel_name,
 +                                void *channel_id, 
 +                                uint32 channel_id_len,
 +                                uint32 mode)
 +{
 +  SilcBuffer packet;
 +  unsigned char *cid;
 +  uint32 name_len = strlen(channel_name);
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  cid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
 +  if (!cid)
 +    return;
 +
 +  /* Encode the channel payload */
 +  packet = silc_channel_payload_encode(channel_name, name_len,
 +                                     cid, channel_id_len, mode);
 +
 +  silc_server_packet_send(server, sock, SILC_PACKET_NEW_CHANNEL, 
 +                        broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
 +                        packet->data, packet->len, FALSE);
 +
 +  /* Send to backup routers if this is being broadcasted to primary
 +     router. */
 +  if (server->server_type == SILC_ROUTER &&
 +      server->router && server->router->connection &&
 +      sock == server->router->connection && broadcast)
 +    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
 +                          packet->data, packet->len, FALSE, TRUE);
 +
 +  silc_free(cid);
 +  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 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. 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 sender,
 +                                SilcChannelEntry channel,
 +                                unsigned char route)
 +{
 +  SilcBuffer packet;
 +  unsigned char *chid;
 +  uint32 tmp_len;
 + 
 +  SILC_LOG_DEBUG(("Sending key to channel %s", channel->channel_name));
 + 
 +  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_get_len(channel->id,
 +                                                         SILC_ID_CHANNEL),
 +                                         chid, tmp_len,
 +                                           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, 
 +                            uint16 ident,
 +                            uint32 argc, ...)
 +{
 +  SilcBuffer packet;
 +  va_list ap;
 +
 +  va_start(ap, argc);
 +
 +  packet = silc_command_payload_encode_vap(command, ident, argc, ap);
 +  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND, 0,
 +                        packet->data, packet->len, TRUE);
 +  silc_buffer_free(packet);
 +  va_end(ap);
 +}
 +
 +/* Generic function to send any command reply. The arguments must be sent
 +   already encoded into correct form in correct order. */
 +
 +void silc_server_send_command_reply(SilcServer server, 
 +                                  SilcSocketConnection sock,
 +                                  SilcCommand command, 
 +                                  SilcCommandStatus status,
 +                                  uint16 ident,
 +                                  uint32 argc, ...)
 +{
 +  SilcBuffer packet;
 +  va_list ap;
 +
 +  va_start(ap, argc);
 +
 +  packet = silc_command_reply_payload_encode_vap(command, status, ident, 
 +                                               argc, ap);
 +  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
 +                        packet->data, packet->len, TRUE);
 +  silc_buffer_free(packet);
 +  va_end(ap);
 +}
 +
 +/* Generic function to send any command reply. The arguments must be sent
 +   already encoded into correct form in correct order. */
 +
 +void silc_server_send_dest_command_reply(SilcServer server, 
 +                                       SilcSocketConnection sock,
 +                                       void *dst_id,
 +                                       SilcIdType dst_id_type,
 +                                       SilcCommand command, 
 +                                       SilcCommandStatus status,
 +                                       uint16 ident,
 +                                       uint32 argc, ...)
 +{
 +  SilcBuffer packet;
 +  va_list ap;
 +
 +  va_start(ap, argc);
 +
 +  packet = silc_command_reply_payload_encode_vap(command, status, ident, 
 +                                               argc, ap);
 +  silc_server_packet_send_dest(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
 +                             dst_id, dst_id_type, packet->data, 
 +                             packet->len, TRUE);
 +  silc_buffer_free(packet);
 +  va_end(ap);
 +}
 +
 +/* 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);
 +}
 +
 +/* Generic function to relay packet we've received. This is used to relay
 +   packets to a client but generally can be used to other purposes as well. */
 +
 +void silc_server_relay_packet(SilcServer server,
 +                            SilcSocketConnection dst_sock,
 +                            SilcCipher cipher,
 +                            SilcHmac hmac,
 +                            uint32 sequence,
 +                            SilcPacketContext *packet,
 +                            bool force_send)
 +{
 +  silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 +                 + packet->dst_id_len + packet->padlen);
 +
 +  silc_packet_send_prepare(dst_sock, 0, 0, packet->buffer->len);
 +  silc_buffer_put(dst_sock->outbuf, packet->buffer->data, packet->buffer->len);
 +  
 +  /* Re-encrypt packet */
 +  silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, 
 +                    packet->buffer->len);
 +  
 +  /* Send the packet */
 +  silc_server_packet_send_real(server, dst_sock, force_send);
 +
 +  silc_buffer_pull(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 +                 + packet->dst_id_len + packet->padlen);
 +}
 +
 +/* Routine used to send the connection authentication packet. */
 +
 +void silc_server_send_connection_auth_request(SilcServer server,
 +                                            SilcSocketConnection sock,
 +                                            uint16 conn_type,
 +                                            SilcAuthMethod auth_meth)
 +{
 +  SilcBuffer packet;
 +
 +  packet = silc_buffer_alloc(4);
 +  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 +  silc_buffer_format(packet,
 +                   SILC_STR_UI_SHORT(conn_type),
 +                   SILC_STR_UI_SHORT(auth_meth),
 +                   SILC_STR_END);
 +
 +  silc_server_packet_send(server, sock, SILC_PACKET_CONNECTION_AUTH_REQUEST,
 +                        0, packet->data, packet->len, FALSE);
 +  silc_buffer_free(packet);
 +}
 +
 +/* Purge the outgoing packet queue to the network if there is data. This
 +   function can be used to empty the packet queue. It is guaranteed that
 +   after this function returns the outgoing data queue is empty. */
 +
 +void silc_server_packet_queue_purge(SilcServer server,
 +                                  SilcSocketConnection sock)
 +{
 +  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
 +      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
 +    server->stat.packets_sent++;
 +
 +    if (sock->outbuf->data - sock->outbuf->head)
 +      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 +
 +    silc_packet_send(sock, TRUE);
 +
 +    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
 +    SILC_UNSET_OUTBUF_PENDING(sock);
 +    silc_buffer_clear(sock->outbuf);
 +  }
 +}
diff --combined apps/silcd/protocol.c
index d5a287d53275d437c322328f1f6b95d31ea73fc5,60d491705041f334956f622a56042bcf5fedee93..a7a1b707d7d1a39000626dbf555ac8f20a52cd2d
@@@ -2,9 -2,9 +2,9 @@@
  
    protocol.c
  
 -  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 +  Author: Pekka Riikonen <priikone@silcnet.org>
  
 -  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
  /*
   * Server side of the protocols.
   */
 -/*
 - * $Id$
 - * $Log$
 - * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
 - *    Importet from internal CVS/Added Log headers.
 - *
 - *
 - */
 +/* $Id$ */
  
  #include "serverincludes.h"
  #include "server_internal.h"
  
  SILC_TASK_CALLBACK(silc_server_protocol_connection_auth);
 -SILC_TASK_CALLBACK(silc_server_protocol_channel_auth);
  SILC_TASK_CALLBACK(silc_server_protocol_key_exchange);
 +SILC_TASK_CALLBACK(silc_server_protocol_rekey);
  
 -/* SILC client protocol list */
 -const SilcProtocolObject silc_protocol_list[] =
 -{
 -  { SILC_PROTOCOL_SERVER_CONNECTION_AUTH, 
 -    silc_server_protocol_connection_auth },
 -  { SILC_PROTOCOL_SERVER_CHANNEL_AUTH, 
 -    silc_server_protocol_channel_auth },
 -  { SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
 -    silc_server_protocol_key_exchange },
 -
 -  { SILC_PROTOCOL_SERVER_NONE, NULL },
 -};
 +extern char *silc_version_string;
  
  /*
   * Key Exhange protocol functions
   */
  
 +static bool 
 +silc_verify_public_key_internal(SilcServer server, SilcSocketConnection sock,
 +                              SilcSocketType conn_type,
 +                              unsigned char *pk, uint32 pk_len, 
 +                              SilcSKEPKType pk_type)
 +{
 +  char file[256], filename[256], *fingerprint;
 +  struct stat st;
 +
 +  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
 +    SILC_LOG_WARNING(("We don't support %s (%s) port %d public key type %d", 
 +                    sock->hostname, sock->ip, sock->port, pk_type));
 +    return FALSE;
 +  }
 +
 +  /* Accept client keys without verification */
 +  if (conn_type == SILC_SOCKET_TYPE_CLIENT) {
 +    SILC_LOG_DEBUG(("Accepting client public key without verification"));
 +    return TRUE;
 +  }
 +
 +  /* XXX For now, accept server keys without verification too. We are
 +     currently always doing mutual authentication so the proof of posession
 +     of the private key is verified, and if server is authenticated in
 +     conn auth protocol with public key we MUST have the key already. */
 +  return TRUE;
 +  /* Rest is unreachable code! */
 +  
 +  memset(filename, 0, sizeof(filename));
 +  memset(file, 0, sizeof(file));
 +  snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", sock->hostname, 
 +         sock->port);
 +  snprintf(filename, sizeof(filename) - 1, SILC_ETCDIR "/serverkeys/%s", 
 +         file);
 +
 +  /* Create serverkeys directory if it doesn't exist. */
 +  if (stat(SILC_ETCDIR "/serverkeys", &st) < 0) {
 +    /* If dir doesn't exist */
 +    if (errno == ENOENT) {  
 +      if (mkdir(SILC_ETCDIR "/serverkeys", 0755) < 0) {
 +      SILC_LOG_ERROR(("Couldn't create `%s' directory\n", 
 +                      SILC_ETCDIR "/serverkeys"));
 +      return TRUE;
 +      }
 +    } else {
 +      SILC_LOG_ERROR(("%s\n", strerror(errno)));
 +      return TRUE;
 +    }
 +  }
 +
 +  /* Take fingerprint of the public key */
 +  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
 +  SILC_LOG_DEBUG(("Received server %s (%s) port %d public key (%s)", 
 +                sock->hostname, sock->ip, sock->port, fingerprint));
 +  silc_free(fingerprint);
 +
 +  /* Check whether this key already exists */
 +  if (stat(filename, &st) < 0) {
 +    /* We don't have it, then cache it. */
 +    SILC_LOG_DEBUG(("New public key from server"));
 +
 +    silc_pkcs_save_public_key_data(filename, pk, pk_len, 
 +                                 SILC_PKCS_FILE_PEM);
 +    return TRUE;
 +  } else {
 +    /* The key already exists, verify it. */
 +    SilcPublicKey public_key;
 +    unsigned char *encpk;
 +    uint32 encpk_len;
 +
 +    SILC_LOG_DEBUG(("We have the public key saved locally"));
 +
 +    /* Load the key file */
 +    if (!silc_pkcs_load_public_key(filename, &public_key, 
 +                                 SILC_PKCS_FILE_PEM))
 +      if (!silc_pkcs_load_public_key(filename, &public_key, 
 +                                   SILC_PKCS_FILE_BIN)) {
 +      SILC_LOG_WARNING(("Could not load local copy of the %s (%s) port %d "
 +                        "server public key", sock->hostname, sock->ip, 
 +                        sock->port));
 +
 +      /* Save the key for future checking */
 +      unlink(filename);
 +      silc_pkcs_save_public_key_data(filename, pk, pk_len,
 +                                     SILC_PKCS_FILE_PEM);
 +      return TRUE;
 +      }
 +  
 +    /* Encode the key data */
 +    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
 +    if (!encpk) {
 +      SILC_LOG_WARNING(("Local copy of the server %s (%s) port %d public key "
 +                      "is malformed", sock->hostname, sock->ip, sock->port));
 +
 +      /* Save the key for future checking */
 +      unlink(filename);
 +      silc_pkcs_save_public_key_data(filename, pk, pk_len, SILC_PKCS_FILE_PEM);
 +      return TRUE;
 +    }
 +
 +    if (memcmp(pk, encpk, encpk_len)) {
 +      SILC_LOG_WARNING(("%s (%s) port %d server public key does not match "
 +                      "with local copy", sock->hostname, sock->ip, 
 +                      sock->port));
 +      SILC_LOG_WARNING(("It is possible that the key has expired or changed"));
 +      SILC_LOG_WARNING(("It is also possible that some one is performing "
 +                      "man-in-the-middle attack"));
 +      SILC_LOG_WARNING(("Will not accept the server %s (%s) port %d public "
 +                      "key",
 +                      sock->hostname, sock->ip, sock->port));
 +      return FALSE;
 +    }
 +
 +    /* Local copy matched */
 +    return TRUE;
 +  }
 +}
 +
 +/* Callback that is called when we have received KE2 payload from
 +   responder. We try to verify the public key now. */
 +
 +static void 
 +silc_server_protocol_ke_verify_key(SilcSKE ske,
 +                                 unsigned char *pk_data,
 +                                 uint32 pk_len,
 +                                 SilcSKEPKType pk_type,
 +                                 void *context,
 +                                 SilcSKEVerifyCbCompletion completion,
 +                                 void *completion_context)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcServerKEInternalContext *ctx = 
 +    (SilcServerKEInternalContext *)protocol->context;
 +  SilcServer server = (SilcServer)ctx->server;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (silc_verify_public_key_internal(server, ctx->sock, 
 +                                    (ctx->responder == FALSE ?
 +                                     SILC_SOCKET_TYPE_ROUTER:
 +                                     ctx->sconfig ? SILC_SOCKET_TYPE_SERVER :
 +                                     ctx->rconfig ? SILC_SOCKET_TYPE_ROUTER :
 +                                     SILC_SOCKET_TYPE_CLIENT),
 +                                    pk_data, pk_len, pk_type))
 +    completion(ske, SILC_SKE_STATUS_OK, completion_context);
 +  else
 +    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, 
 +             completion_context);
 +}
 +
  /* Packet sending callback. This function is provided as packet sending
     routine to the Key Exchange functions. */
  
@@@ -205,228 -73,65 +205,228 @@@ static void silc_server_protocol_ke_sen
  
  /* Sets the negotiated key material into use for particular connection. */
  
 -static void silc_server_protocol_ke_set_keys(SilcSKE ske,
 -                                           SilcSocketConnection sock,
 -                                           SilcSKEKeyMaterial *keymat,
 -                                           SilcCipher cipher,
 -                                           SilcPKCS pkcs,
 -                                           SilcHash hash,
 -                                           int is_responder)
 +int silc_server_protocol_ke_set_keys(SilcServer server,
 +                                   SilcSKE ske,
 +                                   SilcSocketConnection sock,
 +                                   SilcSKEKeyMaterial *keymat,
 +                                   SilcCipher cipher,
 +                                   SilcPKCS pkcs,
 +                                   SilcHash hash,
 +                                   SilcHmac hmac,
 +                                   SilcSKEDiffieHellmanGroup group,
 +                                   bool is_responder)
  {
 -  SilcIDListUnknown *conn_data;
 -  SilcHash nhash;
 +  SilcUnknownEntry conn_data;
 +  SilcIDListData idata;
  
    SILC_LOG_DEBUG(("Setting new key into use"));
  
    conn_data = silc_calloc(1, sizeof(*conn_data));
 +  idata = (SilcIDListData)conn_data;
  
    /* Allocate cipher to be used in the communication */
 -  silc_cipher_alloc(cipher->cipher->name, &conn_data->send_key);
 -  silc_cipher_alloc(cipher->cipher->name, &conn_data->receive_key);
 +  if (!silc_cipher_alloc(cipher->cipher->name, &idata->send_key)) {
 +    silc_free(conn_data);
 +    return FALSE;
 +  }
 +  if (!silc_cipher_alloc(cipher->cipher->name, &idata->receive_key)) {
 +    silc_free(conn_data);
 +    return FALSE;
 +  }
    
 +  if (!silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, 
 +                     &idata->hmac_send)) {
 +    silc_cipher_free(idata->send_key);
 +    silc_cipher_free(idata->receive_key);
 +    silc_free(conn_data);
 +    return FALSE;
 +  }
 +
 +  if (!silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, 
 +                     &idata->hmac_receive)) {
 +    silc_cipher_free(idata->send_key);
 +    silc_cipher_free(idata->receive_key);
 +    silc_hmac_free(idata->hmac_send);
 +    silc_free(conn_data);
 +    return FALSE;
 +  }
 +
    if (is_responder == TRUE) {
 -    conn_data->send_key->cipher->set_key(conn_data->send_key->context, 
 -                                       keymat->receive_enc_key, 
 -                                       keymat->enc_key_len);
 -    conn_data->send_key->set_iv(conn_data->send_key, keymat->receive_iv);
 -    conn_data->receive_key->cipher->set_key(conn_data->receive_key->context, 
 -                                          keymat->send_enc_key, 
 -                                          keymat->enc_key_len);
 -    conn_data->receive_key->set_iv(conn_data->receive_key, keymat->send_iv);
 -    
 +    silc_cipher_set_key(idata->send_key, keymat->receive_enc_key, 
 +                      keymat->enc_key_len);
 +    silc_cipher_set_iv(idata->send_key, keymat->receive_iv);
 +    silc_cipher_set_key(idata->receive_key, keymat->send_enc_key, 
 +                      keymat->enc_key_len);
 +    silc_cipher_set_iv(idata->receive_key, keymat->send_iv);
 +    silc_hmac_set_key(idata->hmac_send, keymat->receive_hmac_key, 
 +                    keymat->hmac_key_len);
 +    silc_hmac_set_key(idata->hmac_receive, keymat->send_hmac_key, 
 +                    keymat->hmac_key_len);
    } else {
 -    conn_data->send_key->cipher->set_key(conn_data->send_key->context, 
 -                                       keymat->send_enc_key, 
 -                                       keymat->enc_key_len);
 -    conn_data->send_key->set_iv(conn_data->send_key, keymat->send_iv);
 -    conn_data->receive_key->cipher->set_key(conn_data->receive_key->context, 
 -                                          keymat->receive_enc_key, 
 -                                          keymat->enc_key_len);
 -    conn_data->receive_key->set_iv(conn_data->receive_key, keymat->receive_iv);
 +    silc_cipher_set_key(idata->send_key, keymat->send_enc_key, 
 +                      keymat->enc_key_len);
 +    silc_cipher_set_iv(idata->send_key, keymat->send_iv);
 +    silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key, 
 +                      keymat->enc_key_len);
 +    silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
 +    silc_hmac_set_key(idata->hmac_send, keymat->send_hmac_key, 
 +                    keymat->hmac_key_len);
 +    silc_hmac_set_key(idata->hmac_receive, keymat->receive_hmac_key, 
 +                    keymat->hmac_key_len);
    }
  
 -  /* Allocate PKCS to be used */
 +  idata->rekey = silc_calloc(1, sizeof(*idata->rekey));
 +  idata->rekey->send_enc_key = 
 +    silc_calloc(keymat->enc_key_len / 8,
 +              sizeof(*idata->rekey->send_enc_key));
 +  memcpy(idata->rekey->send_enc_key, 
 +       keymat->send_enc_key, keymat->enc_key_len / 8);
 +  idata->rekey->enc_key_len = keymat->enc_key_len / 8;
 +
 +  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
 +    idata->rekey->pfs = TRUE;
 +  idata->rekey->ske_group = silc_ske_group_get_number(group);
 +
 +  /* Save the hash */
 +  if (!silc_hash_alloc(hash->hash->name, &idata->hash)) {
 +    silc_cipher_free(idata->send_key);
 +    silc_cipher_free(idata->receive_key);
 +    silc_hmac_free(idata->hmac_send);
 +    silc_hmac_free(idata->hmac_receive);
 +    silc_free(conn_data);
 +    return FALSE;
 +  }
 +
 +  /* Save the remote host's public key */
 +  silc_pkcs_public_key_decode(ske->ke1_payload->pk_data, 
 +                            ske->ke1_payload->pk_len, &idata->public_key);
 +  if (ske->prop->flags & SILC_SKE_SP_FLAG_MUTUAL)
 +    silc_hash_make(server->sha1hash, ske->ke1_payload->pk_data,
 +                 ske->ke1_payload->pk_len, idata->fingerprint);
 +
 +  sock->user_data = (void *)conn_data;
 +
 +  SILC_LOG_INFO(("%s (%s) security properties: %s %s %s", 
 +               sock->hostname, sock->ip,
 +               idata->send_key->cipher->name,
 +               (char *)silc_hmac_get_name(idata->hmac_send),
 +               idata->hash->hash->name));
 +
 +  return TRUE;
 +}
 +
 +/* Check remote host version string */
 +
 +SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
 +                                   uint32 len, void *context)
 +{
 +  SilcSKEStatus status = SILC_SKE_STATUS_OK;
 +  char *cp;
 +  int maj = 0, min = 0, build = 0, maj2 = 0, min2 = 0, build2 = 0;
 +
 +  SILC_LOG_INFO(("%s (%s) is version %s", ske->sock->hostname,
 +               ske->sock->ip, version));
 +
 +  /* Check for initial version string */
 +  if (!strstr(version, "SILC-1.0-"))
 +    status = SILC_SKE_STATUS_BAD_VERSION;
 +
 +  /* Check software version */
 +
 +  cp = version + 9;
 +  if (!cp)
 +    status = SILC_SKE_STATUS_BAD_VERSION;
 +
 +  maj = atoi(cp);
 +  cp = strchr(cp, '.');
 +  if (cp) {
 +    min = atoi(cp + 1);
 +    cp++;
 +  }
 +  if (cp) {
 +    cp = strchr(cp, '.');
 +    if (cp)
 +      build = atoi(cp + 1);
 +  }
 +
 +  cp = silc_version_string + 9;
 +  if (!cp)
 +    status = SILC_SKE_STATUS_BAD_VERSION;
 +
 +  maj2 = atoi(cp);
 +  cp = strchr(cp, '.');
 +  if (cp) {
 +    min2 = atoi(cp + 1);
 +    cp++;
 +  }
 +  if (cp) {
 +    cp = strchr(cp, '.');
 +    if (cp)
 +      build2 = atoi(cp + 1);
 +  }
 +
 +  if (maj != maj2)
 +    status = SILC_SKE_STATUS_BAD_VERSION;
  #if 0
 -  /* XXX Do we ever need to allocate PKCS for the connection??
 -     If yes, we need to change KE protocol to get the initiators
 -     public key. */
 -  silc_pkcs_alloc(pkcs->pkcs->name, &conn_data->pkcs);
 -  silc_pkcs_set_public_key(conn_data->pkcs, ske->ke2_payload->pk_data, 
 -                         ske->ke2_payload->pk_len);
 +  if (min > min2)
 +    status = SILC_SKE_STATUS_BAD_VERSION;
  #endif
  
 -  /* Save HMAC key to be used in the communication. */
 -  silc_hash_alloc(hash->hash->name, &nhash);
 -  silc_hmac_alloc(nhash, &conn_data->hmac);
 -  conn_data->hmac_key_len = keymat->hmac_key_len;
 -  conn_data->hmac_key = silc_calloc(conn_data->hmac_key_len,
 -                                  sizeof(unsigned char));
 -  memcpy(conn_data->hmac_key, keymat->hmac_key, keymat->hmac_key_len);
 +  /* XXX < 0.6 is not allowed */
 +  if (maj == 0 && min < 5)
 +    status = SILC_SKE_STATUS_BAD_VERSION;
  
 -  sock->user_data = (void *)conn_data;
 +  /* XXX backward support for 0.6.1 */
 +  if (maj == 0 && min == 6 && build < 2)
 +    ske->backward_version = 1;
 +
 +  return status;
 +}
 +
 +/* Callback that is called by the SKE to indicate that it is safe to
 +   continue the execution of the protocol. This is used only if we are
 +   initiator.  Is given as argument to the silc_ske_initiator_finish or
 +   silc_ske_responder_phase_2 functions. This is called due to the fact
 +   that the public key verification process is asynchronous and we must
 +   not continue the protocl until the public key has been verified and
 +   this callback is called. */
 +
 +static void silc_server_protocol_ke_continue(SilcSKE ske, void *context)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcServerKEInternalContext *ctx = 
 +    (SilcServerKEInternalContext *)protocol->context;
 +  SilcServer server = (SilcServer)ctx->server;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (ske->status != SILC_SKE_STATUS_OK) {
 +    SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
 +                    silc_ske_map_status(ske->status)));
 +    SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
 +                  silc_ske_map_status(ske->status)));
 +    
 +    protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +    silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +    return;
 +  }
 +
 +  /* Send Ok to the other end. We will end the protocol as responder
 +     sends Ok to us when we will take the new keys into use. */
 +  if (ctx->responder == FALSE) {
 +    silc_ske_end(ctx->ske);
 +
 +    /* End the protocol on the next round */
 +    protocol->state = SILC_PROTOCOL_STATE_END;
 +  }
 +
 +  /* Advance protocol state and call the next state if we are responder. 
 +     This happens when this callback was sent to silc_ske_responder_phase_2
 +     function. */
 +  if (ctx->responder == TRUE) {
 +    protocol->state++;
 +    silc_protocol_execute(protocol, server->schedule, 0, 100000);
 +  }
  }
  
  /* Performs key exchange protocol. This is used for both initiator
@@@ -439,7 -144,7 +439,7 @@@ SILC_TASK_CALLBACK(silc_server_protocol
    SilcServerKEInternalContext *ctx = 
      (SilcServerKEInternalContext *)protocol->context;
    SilcServer server = (SilcServer)ctx->server;
 -  SilcSKEStatus status = 0;
 +  SilcSKEStatus status = SILC_SKE_STATUS_OK;
  
    SILC_LOG_DEBUG(("Start"));
  
        /* Allocate Key Exchange object */
        ske = silc_ske_alloc();
        ctx->ske = ske;
 +      ske->rng = server->rng;
 +      
 +      silc_ske_set_callbacks(ske, silc_server_protocol_ke_send_packet, NULL,
 +                           silc_server_protocol_ke_verify_key,
 +                           silc_server_protocol_ke_continue,
 +                           silc_ske_check_version, context);
        
        if (ctx->responder == TRUE) {
        /* Start the key exchange by processing the received security
           properties packet from initiator. */
        status = silc_ske_responder_start(ske, ctx->rng, ctx->sock,
 -                                        ctx->packet, NULL, NULL);
 +                                        silc_version_string,
 +                                        ctx->packet->buffer, TRUE);
        } else {
        SilcSKEStartPayload *start_payload;
  
        /* Assemble security properties. */
 -      silc_ske_assemble_security_properties(ske, &start_payload);
 +      silc_ske_assemble_security_properties(ske, SILC_SKE_SP_FLAG_MUTUAL, 
 +                                            silc_version_string,
 +                                            &start_payload);
  
        /* Start the key exchange by sending our security properties
           to the remote end. */
        status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock,
 -                                        start_payload,
 -                                        silc_server_protocol_ke_send_packet,
 -                                        context);
 +                                        start_payload);
        }
  
 +      /* Return now if the procedure is pending. */
 +      if (status == SILC_SKE_STATUS_PENDING)
 +      return;
 +
        if (status != SILC_SKE_STATUS_OK) {
 -      SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
 -                        status));
 -      SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
 -                      status));
 +      SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
 +                        silc_ske_map_status(status)));
 +      SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
 +                      silc_ske_map_status(status)));
  
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
 +      silc_protocol_execute(protocol, server->schedule, 0, 300000);
        return;
        }
  
        /* Advance protocol state and call the next state if we are responder */
        protocol->state++;
        if (ctx->responder == TRUE)
 -      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000);
 +      silc_protocol_execute(protocol, server->schedule, 0, 100000);
      }
      break;
    case 2:
         */
        if (ctx->responder == TRUE) {
        /* Sends the selected security properties to the initiator. */
 -      status = 
 -        silc_ske_responder_phase_1(ctx->ske, 
 -                                   ctx->ske->start_payload,
 -                                   silc_server_protocol_ke_send_packet,
 -                                   context);
 +      status = silc_ske_responder_phase_1(ctx->ske, 
 +                                          ctx->ske->start_payload);
        } else {
        /* Call Phase-1 function. This processes the Key Exchange Start
           paylaod reply we just got from the responder. The callback
           function will receive the processed payload where we will
           save it. */
 -      status = 
 -        silc_ske_initiator_phase_1(ctx->ske,
 -                                   ctx->packet,
 -                                   NULL, NULL);
 +      status = silc_ske_initiator_phase_1(ctx->ske, ctx->packet->buffer);
        }
  
 +      /* Return now if the procedure is pending. */
 +      if (status == SILC_SKE_STATUS_PENDING)
 +      return;
 +
        if (status != SILC_SKE_STATUS_OK) {
 -      SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
 -                        status));
 -      SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
 -                      status));
 +      SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
 +                        silc_ske_map_status(status)));
 +      SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
 +                      silc_ske_map_status(status)));
  
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
 +      silc_protocol_execute(protocol, server->schedule, 0, 300000);
        return;
        }
  
        /* Advance protocol state and call next state if we are initiator */
        protocol->state++;
        if (ctx->responder == FALSE)
 -      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000);
 +      silc_protocol_execute(protocol, server->schedule, 0, 100000);
      }
      break;
    case 3:
        if (ctx->responder == TRUE) {
        /* Process the received Key Exchange 1 Payload packet from
           the initiator. This also creates our parts of the Diffie
 -         Hellman algorithm. */
 -      status = 
 -        silc_ske_responder_phase_2(ctx->ske, ctx->packet, NULL, NULL);
 +         Hellman algorithm. The silc_server_protocol_ke_continue
 +         will be called after the public key has been verified. */
 +      status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
        } else {
        /* Call the Phase-2 function. This creates Diffie Hellman
           key exchange parameters and sends our public part inside
           Key Exhange 1 Payload to the responder. */
 -      status = 
 -        silc_ske_initiator_phase_2(ctx->ske,
 -                                   silc_server_protocol_ke_send_packet,
 -                                   context);
 +      status = silc_ske_initiator_phase_2(ctx->ske,
 +                                          server->public_key,
 +                                          server->private_key);
 +      protocol->state++;
        }
  
 +      /* Return now if the procedure is pending. */
 +      if (status == SILC_SKE_STATUS_PENDING)
 +      return;
 +
        if (status != SILC_SKE_STATUS_OK) {
 -      SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
 -                        status));
 -      SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
 -                      status));
 +      SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
 +                        silc_ske_map_status(status)));
 +      SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
 +                      silc_ske_map_status(status)));
  
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
 +      silc_protocol_execute(protocol, server->schedule, 0, 300000);
        return;
        }
 -
 -      /* Advance protocol state and call the next state if we are responder */
 -      protocol->state++;
 -      if (ctx->responder == TRUE)
 -      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000);
      }
      break;
    case 4:
         * Finish protocol
         */
        if (ctx->responder == TRUE) {
 -      unsigned char *pk, *prv;
 -      unsigned int pk_len, prv_len;
 -
 -      /* Get our public key to be sent to the initiator */
 -      pk = silc_pkcs_get_public_key(server->public_key, &pk_len);
 -
 -      /* Get out private key to sign some data. */
 -      prv = silc_pkcs_get_private_key(server->public_key, &prv_len);
 -
        /* This creates the key exchange material and sends our
           public parts to the initiator inside Key Exchange 2 Payload. */
 -      status = 
 -        silc_ske_responder_finish(ctx->ske, 
 -                                  pk, pk_len, prv, prv_len,
 -                                  SILC_SKE_PK_TYPE_SILC,
 -                                  silc_server_protocol_ke_send_packet,
 -                                  context);
 -
 -      memset(pk, 0, pk_len);
 -      memset(prv, 0, prv_len);
 -      silc_free(pk);
 -      silc_free(prv);
 +      status = silc_ske_responder_finish(ctx->ske, 
 +                                         server->public_key, 
 +                                         server->private_key,
 +                                         SILC_SKE_PK_TYPE_SILC);
 +
 +      /* End the protocol on the next round */
 +      protocol->state = SILC_PROTOCOL_STATE_END;
        } else {
        /* Finish the protocol. This verifies the Key Exchange 2 payload
 -         sent by responder. */
 -      status = 
 -        silc_ske_initiator_finish(ctx->ske,
 -                                  ctx->packet, NULL, NULL);
 +         sent by responder. The silc_server_protocol_ke_continue will
 +         be called after the public key has been verified. */
 +      status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
        }
  
 +      /* Return now if the procedure is pending. */
 +      if (status == SILC_SKE_STATUS_PENDING)
 +      return;
 +
        if (status != SILC_SKE_STATUS_OK) {
 -      SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
 -                        status));
 -      SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
 -                      status));
 +      SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
 +                        silc_ske_map_status(status)));
 +      SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
 +                      silc_ske_map_status(status)));
  
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
 +      silc_protocol_execute(protocol, server->schedule, 0, 300000);
        return;
        }
 -
 -      /* Send Ok to the other end. We will end the protocol as responder
 -       sends Ok to us when we will take the new keys into use. */
 -      if (ctx->responder == FALSE)
 -      silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context);
 -
 -      /* End the protocol on the next round */
 -      protocol->state = SILC_PROTOCOL_STATE_END;
      }
      break;
 +
    case SILC_PROTOCOL_STATE_END:
      {
        /* 
         * End protocol
         */
        SilcSKEKeyMaterial *keymat;
 -
 -      /* Send Ok to the other end if we are responder. If we are 
 -       initiator we have sent this already. */
 -      if (ctx->responder == TRUE)
 -      silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context);
 +      int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher);
 +      int hash_len = ctx->ske->prop->hash->hash->hash_len;
  
        /* Process the key material */
        keymat = silc_calloc(1, sizeof(*keymat));
 -      silc_ske_process_key_material(ctx->ske, 16, (16 * 8), 16, keymat);
 +      status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len,
 +                                           keymat);
 +      if (status != SILC_SKE_STATUS_OK) {
 +      protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +      silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +      silc_ske_free_key_material(keymat);
 +      return;
 +      }
 +      ctx->keymat = keymat;
  
 -      /* Take the new keys into use. */
 -      silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat,
 -                                     ctx->ske->prop->cipher,
 -                                     ctx->ske->prop->pkcs,
 -                                     ctx->ske->prop->hash,
 -                                     ctx->responder);
 +      /* Send Ok to the other end if we are responder. If we are initiator
 +       we have sent this already. */
 +      if (ctx->responder == TRUE)
 +      silc_ske_end(ctx->ske);
  
        /* Unregister the timeout task since the protocol has ended. 
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
        if (ctx->timeout_task)
 -      silc_task_unregister(server->timeout_queue, ctx->timeout_task);
 +      silc_schedule_task_del(server->schedule, ctx->timeout_task);
 +
 +      /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +      silc_protocol_cancel(protocol, server->schedule);
  
        /* Call the final callback */
        if (protocol->final_callback)
 -      protocol->execute_final(server->timeout_queue, 0, protocol, fd);
 +      silc_protocol_execute_final(protocol, server->schedule);
        else
        silc_protocol_free(protocol);
      }
      break;
 +
    case SILC_PROTOCOL_STATE_ERROR:
      /*
       * Error occured
       */
  
 +    /* Send abort notification */
 +    silc_ske_abort(ctx->ske, ctx->ske->status);
 +
      /* Unregister the timeout task since the protocol has ended. 
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
      if (ctx->timeout_task)
 -      silc_task_unregister(server->timeout_queue, ctx->timeout_task);
 +      silc_schedule_task_del(server->schedule, ctx->timeout_task);
 +
 +    /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +    silc_protocol_cancel(protocol, server->schedule);
  
      /* On error the final callback is always called. */
      if (protocol->final_callback)
 -      protocol->execute_final(server->timeout_queue, 0, protocol, fd);
 +      silc_protocol_execute_final(protocol, server->schedule);
      else
        silc_protocol_free(protocol);
      break;
 +
 +  case SILC_PROTOCOL_STATE_FAILURE:
 +    /*
 +     * We have received failure from remote
 +     */
 +
 +    /* Unregister the timeout task since the protocol has ended. 
 +       This was the timeout task to be executed if the protocol is
 +       not completed fast enough. */
 +    if (ctx->timeout_task)
 +      silc_schedule_task_del(server->schedule, ctx->timeout_task);
 +
 +    /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +    silc_protocol_cancel(protocol, server->schedule);
 +    
 +    /* On error the final callback is always called. */
 +    if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +    else
 +      silc_protocol_free(protocol);
 +    break;
 +
    case SILC_PROTOCOL_STATE_UNKNOWN:
      break;
    }
   * Connection Authentication protocol functions
   */
  
 +static int 
 +silc_server_password_authentication(SilcServer server, char *remote_auth, 
 +                                  char *local_auth)
 +{
 +  if (!remote_auth || !local_auth)
 +    return FALSE;
 +
 +  if (!memcmp(remote_auth, local_auth, strlen(local_auth)))
 +    return TRUE;
 +
 +  return FALSE;
 +}
 +
 +static int
 +silc_server_public_key_authentication(SilcServer server,
 +                                    SilcPublicKey pub_key,
 +                                    unsigned char *sign,
 +                                    uint32 sign_len,
 +                                    SilcSKE ske)
 +{
 +  SilcPKCS pkcs;
 +  int len;
 +  SilcBuffer auth;
 +
 +  if (!pub_key || !sign)
 +    return FALSE;
 +
 +  silc_pkcs_alloc(pub_key->name, &pkcs);
 +  if (!silc_pkcs_public_key_set(pkcs, pub_key)) {
 +    silc_pkcs_free(pkcs);
 +    return FALSE;
 +  }
 +
 +  /* Make the authentication data. Protocol says it is HASH plus
 +     KE Start Payload. */
 +  len = ske->hash_len + ske->start_payload_copy->len;
 +  auth = silc_buffer_alloc(len);
 +  silc_buffer_pull_tail(auth, len);
 +  silc_buffer_format(auth,
 +                   SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
 +                   SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
 +                                        ske->start_payload_copy->len),
 +                   SILC_STR_END);
 +
 +  /* Verify signature */
 +  if (silc_pkcs_verify_with_hash(pkcs, ske->prop->hash, sign, sign_len, 
 +                               auth->data, auth->len)) {
 +    silc_pkcs_free(pkcs);
 +    silc_buffer_free(auth);
 +    return TRUE;
 +  }
 +
 +  silc_pkcs_free(pkcs);
 +  silc_buffer_free(auth);
 +  return FALSE;
 +}
 +
 +static int
 +silc_server_get_public_key_auth(SilcServer server,
 +                              unsigned char *auth_data,
 +                              uint32 *auth_data_len,
 +                              SilcSKE ske)
 +{
 +  int len;
 +  SilcPKCS pkcs;
 +  SilcBuffer auth;
 +
 +  pkcs = server->pkcs;
 +
 +  /* Make the authentication data. Protocol says it is HASH plus
 +     KE Start Payload. */
 +  len = ske->hash_len + ske->start_payload_copy->len;
 +  auth = silc_buffer_alloc(len);
 +  silc_buffer_pull_tail(auth, len);
 +  silc_buffer_format(auth,
 +                   SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
 +                   SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
 +                                        ske->start_payload_copy->len),
 +                   SILC_STR_END);
 +
 +  if (silc_pkcs_sign_with_hash(pkcs, ske->prop->hash, auth->data, 
 +                             auth->len, auth_data, auth_data_len)) {
 +    silc_buffer_free(auth);
 +    return TRUE;
 +  }
 +
 +  silc_buffer_free(auth);
 +  return FALSE;
 +}
 +
  /* Performs connection authentication protocol. If responder, we 
     authenticate the remote data received. If initiator, we will send
     authentication data to the remote end. */
@@@ -843,31 -426,22 +843,31 @@@ SILC_TASK_CALLBACK(silc_server_protocol
        /*
         * We are receiving party
         */
 -      unsigned short payload_len;
 -      unsigned short conn_type;
 -      unsigned char *auth_data;
 +      int ret;
 +      uint16 payload_len;
 +      uint16 conn_type;
 +      unsigned char *auth_data = NULL;
 +
 +      SILC_LOG_INFO(("Performing authentication protocol for %s (%s)",
 +                     ctx->sock->hostname, ctx->sock->ip));
  
        /* Parse the received authentication data packet. The received
           payload is Connection Auth Payload. */
 -      silc_buffer_unformat(ctx->packet,
 -                           SILC_STR_UI_SHORT(&payload_len),
 -                           SILC_STR_UI_SHORT(&conn_type),
 -                           SILC_STR_END);
 +      ret = silc_buffer_unformat(ctx->packet->buffer,
 +                                 SILC_STR_UI_SHORT(&payload_len),
 +                                 SILC_STR_UI_SHORT(&conn_type),
 +                                 SILC_STR_END);
 +      if (ret == -1) {
 +        SILC_LOG_DEBUG(("Bad payload in authentication packet"));
 +        protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +        silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +        return;
 +      }
        
 -      if (payload_len != ctx->packet->len) {
 -        SILC_LOG_ERROR(("Bad payload in authentication packet"));
 +      if (payload_len != ctx->packet->buffer->len) {
          SILC_LOG_DEBUG(("Bad payload in authentication packet"));
          protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -        protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
 +        silc_protocol_execute(protocol, server->schedule, 0, 300000);
          return;
        }
        
        if (conn_type < SILC_SOCKET_TYPE_CLIENT || 
            conn_type > SILC_SOCKET_TYPE_ROUTER) {
          SILC_LOG_ERROR(("Bad connection type %d", conn_type));
 -        SILC_LOG_DEBUG(("Bad connection type %d", conn_type));
          protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -        protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
 +        silc_protocol_execute(protocol, server->schedule, 0, 300000);
          return;
        }
        
        if (payload_len > 0) {
          /* Get authentication data */
 -        silc_buffer_pull(ctx->packet, 4);
 -        silc_buffer_unformat(ctx->packet,
 -                             SILC_STR_UI_XNSTRING_ALLOC(&auth_data, 
 -                                                        payload_len),
 -                             SILC_STR_END);
 -      } else {
 -        auth_data = NULL;
 +        silc_buffer_pull(ctx->packet->buffer, 4);
 +        ret = silc_buffer_unformat(ctx->packet->buffer,
 +                                   SILC_STR_UI_XNSTRING_ALLOC(&auth_data, 
 +                                                              payload_len),
 +                                   SILC_STR_END);
 +        if (ret == -1) {
 +          SILC_LOG_DEBUG(("Bad payload in authentication packet"));
 +          protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +          silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +          return;
 +        }
        }
  
        /* 
  
        /* Remote end is client */
        if (conn_type == SILC_SOCKET_TYPE_CLIENT) {
-         SilcServerConfigSectionClientConnection *client = ctx->cconfig;
 -        SilcConfigServerSectionClientConnection *client = NULL;
 -        client = 
 -          silc_config_server_find_client_conn(server->config,
 -                                              ctx->sock->ip,
 -                                              ctx->sock->port);
 -        if (!client)
 -          client = 
 -            silc_config_server_find_client_conn(server->config,
 -                                                ctx->sock->hostname,
 -                                                ctx->sock->port);
++        SilcServerConfigSectionClient *client = ctx->cconfig;
          
          if (client) {
            switch(client->auth_meth) {
 -          case SILC_PROTOCOL_CONN_AUTH_NONE:
 +          case SILC_AUTH_NONE:
              /* No authentication required */
              SILC_LOG_DEBUG(("No authentication required"));
              break;
              
 -          case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
 +          case SILC_AUTH_PASSWORD:
              /* Password authentication */
              SILC_LOG_DEBUG(("Password authentication"));
 -            if (auth_data) {
 -              if (!memcmp(client->auth_data, auth_data, strlen(auth_data))) {
 -                memset(auth_data, 0, payload_len);
 -                silc_free(auth_data);
 -                auth_data = NULL;
 -                break;
 -              }
 -            }
 +            ret = silc_server_password_authentication(server, auth_data,
 +                                                      client->auth_data);
 +
 +            if (ret)
 +              break;
  
              /* Authentication failed */
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
 +            silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -            protocol->execute(server->timeout_queue, 0
 -                              protocol, fd, 0, 300000);
 +            silc_protocol_execute(protocol, server->schedule
 +                                  0, 300000);
              return;
              break;
              
 -          case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
 +          case SILC_AUTH_PUBLIC_KEY:
              /* Public key authentication */
              SILC_LOG_DEBUG(("Public key authentication"));
 -            if (auth_data) {
 -              SilcIDListUnknown *conn_data;
 -              SilcPKCS pkcs;
 -              
 -              conn_data = (SilcIDListUnknown *)ctx->sock->user_data;
 -              
 -              /* Load public key from file */
 -              if (silc_pkcs_load_public_key(client->auth_data,
 -                                            &pkcs) == FALSE) {
 -                
 -                /* Authentication failed */
 -                SILC_LOG_ERROR(("Authentication failed "
 -                                "- could not read public key file"));
 -                memset(auth_data, 0, payload_len);
 -                silc_free(auth_data);
 -                auth_data = NULL;
 -                protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -                protocol->execute(server->timeout_queue, 0, 
 -                                  protocol, fd, 0, 300000);
 -                return;
 -              }
 -              
 -              /* Verify hash value HASH from KE protocol */
 -              if (pkcs->pkcs->verify(pkcs->context,
 -                                     auth_data, payload_len,
 -                                     ctx->ske->hash, 
 -                                     ctx->ske->hash_len)
 -                  == TRUE) {
 -                silc_pkcs_free(pkcs);
 -                break;
 -              }
 -            }
 +            ret = silc_server_public_key_authentication(server, 
 +                                                        client->auth_data,
 +                                                        auth_data,
 +                                                        payload_len, 
 +                                                        ctx->ske);
 +
 +            if (ret)
 +              break;
  
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
 +            silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -            protocol->execute(server->timeout_queue, 0
 -                              protocol, fd, 0, 300000);
 +            silc_protocol_execute(protocol, server->schedule
 +                                  0, 300000);
              return;
            }
          } else {
 -          SILC_LOG_DEBUG(("No configuration for remote connection"));
 -          SILC_LOG_ERROR(("Remote connection not configured"));
 +          SILC_LOG_DEBUG(("No configuration for remote client connection"));
 +          SILC_LOG_ERROR(("Remote client connection not configured"));
            SILC_LOG_ERROR(("Authentication failed"));
 -          memset(auth_data, 0, payload_len);
            silc_free(auth_data);
 -          auth_data = NULL;
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -          protocol->execute(server->timeout_queue, 0
 -                            protocol, fd, 0, 300000);
 +          silc_protocol_execute(protocol, server->schedule
 +                                0, 300000);
            return;
          }
        }
        
        /* Remote end is server */
        if (conn_type == SILC_SOCKET_TYPE_SERVER) {
-         SilcServerConfigSectionServerConnection *serv = ctx->sconfig;
 -        SilcConfigServerSectionServerConnection *serv = NULL;
 -        serv = 
 -          silc_config_server_find_server_conn(server->config,
 -                                              ctx->sock->ip,
 -                                              ctx->sock->port);
 -        if (!serv)
 -          serv = 
 -            silc_config_server_find_server_conn(server->config,
 -                                                ctx->sock->hostname,
 -                                                ctx->sock->port);
++        SilcServerConfigSectionServer *serv = ctx->sconfig;
          
          if (serv) {
            switch(serv->auth_meth) {
 -          case SILC_PROTOCOL_CONN_AUTH_NONE:
 +          case SILC_AUTH_NONE:
              /* No authentication required */
              SILC_LOG_DEBUG(("No authentication required"));
              break;
              
 -          case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
 +          case SILC_AUTH_PASSWORD:
              /* Password authentication */
              SILC_LOG_DEBUG(("Password authentication"));
 -            if (auth_data) {
 -              if (!memcmp(serv->auth_data, auth_data, strlen(auth_data))) {
 -                memset(auth_data, 0, payload_len);
 -                silc_free(auth_data);
 -                auth_data = NULL;
 -                break;
 -              }
 -            }
 +            ret = silc_server_password_authentication(server, auth_data,
 +                                                      serv->auth_data);
 +
 +            if (ret)
 +              break;
              
              /* Authentication failed */
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
 +            silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -            protocol->execute(server->timeout_queue, 0
 -                              protocol, fd, 0, 300000);
 +            silc_protocol_execute(protocol, server->schedule
 +                                  0, 300000);
              return;
              break;
 -            
 -          case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
 +
 +          case SILC_AUTH_PUBLIC_KEY:
              /* Public key authentication */
              SILC_LOG_DEBUG(("Public key authentication"));
 -            if (auth_data) {
 -              SilcIDListUnknown *conn_data;
 -              SilcPKCS pkcs;
 -              
 -              conn_data = (SilcIDListUnknown *)ctx->sock->user_data;
 -              
 -              /* Load public key from file */
 -              if (silc_pkcs_load_public_key(serv->auth_data,
 -                                            &pkcs) == FALSE) {
 -                
 -                /* Authentication failed */
 -                SILC_LOG_ERROR(("Authentication failed "
 -                                "- could not read public key file"));
 -                memset(auth_data, 0, payload_len);
 -                silc_free(auth_data);
 -                auth_data = NULL;
 -                protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -                protocol->execute(server->timeout_queue, 0, 
 -                                  protocol, fd, 0, 300000);
 -                return;
 -              }
 -              
 -              /* Verify hash value HASH from KE protocol */
 -              if (pkcs->pkcs->verify(pkcs->context,
 -                                     auth_data, payload_len,
 -                                     ctx->ske->hash, 
 -                                     ctx->ske->hash_len)
 -                  == TRUE) {
 -                silc_pkcs_free(pkcs);
 -                break;
 -              }
 -            }
 +            ret = silc_server_public_key_authentication(server, 
 +                                                        serv->auth_data,
 +                                                        auth_data,
 +                                                        payload_len, 
 +                                                        ctx->ske);
 +                                                        
 +            if (ret)
 +              break;
  
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
 +            silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -            protocol->execute(server->timeout_queue, 0
 -                              protocol, fd, 0, 300000);
 +            silc_protocol_execute(protocol, server->schedule
 +                                  0, 300000);
              return;
            }
          } else {
 -          SILC_LOG_DEBUG(("No configuration for remote connection"));
 -          SILC_LOG_ERROR(("Remote connection not configured"));
 +          SILC_LOG_DEBUG(("No configuration for remote server connection"));
 +          SILC_LOG_ERROR(("Remote server connection not configured"));
            SILC_LOG_ERROR(("Authentication failed"));
 -          memset(auth_data, 0, payload_len);
 -          silc_free(auth_data);
 -          auth_data = NULL;
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -          protocol->execute(server->timeout_queue, 0, 
 -                            protocol, fd, 0, 300000);
 +          silc_protocol_execute(protocol, server->schedule, 
 +                                0, 300000);
 +          silc_free(auth_data);
            return;
          }
        }
        
        /* Remote end is router */
        if (conn_type == SILC_SOCKET_TYPE_ROUTER) {
-         SilcServerConfigSectionServerConnection *serv = ctx->rconfig;
 -        SilcConfigServerSectionServerConnection *serv = NULL;
 -        serv = 
 -          silc_config_server_find_router_conn(server->config,
 -                                              ctx->sock->ip,
 -                                              ctx->sock->port);
 -        if (!serv)
 -          serv = 
 -            silc_config_server_find_router_conn(server->config,
 -                                                ctx->sock->hostname,
 -                                                ctx->sock->port);
 -        
++        SilcServerConfigSectionRouter *serv = ctx->rconfig;
 +
          if (serv) {
            switch(serv->auth_meth) {
 -          case SILC_PROTOCOL_CONN_AUTH_NONE:
 +          case SILC_AUTH_NONE:
              /* No authentication required */
              SILC_LOG_DEBUG(("No authentication required"));
              break;
              
 -          case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
 +          case SILC_AUTH_PASSWORD:
              /* Password authentication */
              SILC_LOG_DEBUG(("Password authentication"));
 -            if (auth_data) {
 -              if (!memcmp(serv->auth_data, auth_data, strlen(auth_data))) {
 -                memset(auth_data, 0, payload_len);
 -                silc_free(auth_data);
 -                auth_data = NULL;
 -                break;
 -              }
 -            }
 +            ret = silc_server_password_authentication(server, auth_data,
 +                                                      serv->auth_data);
 +
 +            if (ret)
 +              break;
              
              /* Authentication failed */
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
 +            silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -            protocol->execute(server->timeout_queue, 0
 -                              protocol, fd, 0, 300000);
 +            silc_protocol_execute(protocol, server->schedule
 +                                  0, 300000);
              return;
              break;
              
 -          case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
 +          case SILC_AUTH_PUBLIC_KEY:
              /* Public key authentication */
              SILC_LOG_DEBUG(("Public key authentication"));
 -            if (auth_data) {
 -              SilcIDListUnknown *conn_data;
 -              SilcPKCS pkcs;
 -              
 -              conn_data = (SilcIDListUnknown *)ctx->sock->user_data;
 -              
 -              /* Load public key from file */
 -              if (silc_pkcs_load_public_key(serv->auth_data,
 -                                            &pkcs) == FALSE) {
 -                
 -                /* Authentication failed */
 -                SILC_LOG_ERROR(("Authentication failed "
 -                                "- could not read public key file"));
 -                memset(auth_data, 0, payload_len);
 -                silc_free(auth_data);
 -                auth_data = NULL;
 -                protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -                protocol->execute(server->timeout_queue, 0, 
 -                                  protocol, fd, 0, 300000);
 -                return;
 -              }
 -              
 -              /* Verify hash value HASH from KE protocol */
 -              if (pkcs->pkcs->verify(pkcs->context,
 -                                     auth_data, payload_len,
 -                                     ctx->ske->hash, 
 -                                     ctx->ske->hash_len)
 -                  == TRUE) {
 -                silc_pkcs_free(pkcs);
 -                break;
 -              }
 -            }
 -
 +            ret = silc_server_public_key_authentication(server, 
 +                                                        serv->auth_data,
 +                                                        auth_data,
 +                                                        payload_len, 
 +                                                        ctx->ske);
 +                                                        
 +            if (ret)
 +              break;
 +            
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
 +            silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -            protocol->execute(server->timeout_queue, 0
 -                              protocol, fd, 0, 300000);
 +            silc_protocol_execute(protocol, server->schedule
 +                                  0, 300000);
              return;
            }
          } else {
 -          SILC_LOG_DEBUG(("No configuration for remote connection"));
 -          SILC_LOG_ERROR(("Remote connection not configured"));
 +          SILC_LOG_DEBUG(("No configuration for remote router connection"));
 +          SILC_LOG_ERROR(("Remote router connection not configured"));
            SILC_LOG_ERROR(("Authentication failed"));
 -          memset(auth_data, 0, payload_len);
            silc_free(auth_data);
 -          auth_data = NULL;
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
 -          protocol->execute(server->timeout_queue, 0
 -                            protocol, fd, 0, 300000);
 +          silc_protocol_execute(protocol, server->schedule
 +                                0, 300000);
            return;
          }
        }
        
 -      if (auth_data) {
 -        memset(auth_data, 0, payload_len);
 -        silc_free(auth_data);
 -      }
 -      
 +      silc_free(auth_data);
 +
        /* Save connection type. This is later used to create the
           ID for the connection. */
        ctx->conn_type = conn_type;
          
        /* Advance protocol state. */
        protocol->state = SILC_PROTOCOL_STATE_END;
 -      protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
 +      silc_protocol_execute(protocol, server->schedule, 0, 0);
  
        } else {
        /* 
        SilcBuffer packet;
        int payload_len = 0;
        unsigned char *auth_data = NULL;
 -      unsigned int auth_data_len = 0;
 +      uint32 auth_data_len = 0;
        
        switch(ctx->auth_meth) {
 -      case SILC_PROTOCOL_CONN_AUTH_NONE:
 +      case SILC_AUTH_NONE:
          /* No authentication required */
          break;
          
 -      case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
 +      case SILC_AUTH_PASSWORD:
          /* Password authentication */
          if (ctx->auth_data && ctx->auth_data_len) {
 -          auth_data = ctx->auth_data;
 +          auth_data = strdup(ctx->auth_data);
            auth_data_len = ctx->auth_data_len;
            break;
          }
 -
 -        /* No authentication data exits. Ask interactively from user. */
 -        /* XXX */
 -
          break;
          
 -      case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
 -        /* Public key authentication */
 -        /* XXX TODO */
 -        break;
 +      case SILC_AUTH_PUBLIC_KEY:
 +        {
 +          unsigned char sign[1024];
 +
 +          /* Public key authentication */
 +          silc_server_get_public_key_auth(server, sign, &auth_data_len,
 +                                          ctx->ske);
 +          auth_data = silc_calloc(auth_data_len, sizeof(*auth_data));
 +          memcpy(auth_data, sign, auth_data_len);
 +          break;
 +        }
        }
        
        payload_len = 4 + auth_data_len;
        /* 
         * End protocol
         */
 +      unsigned char ok[4];
  
 -      /* Succesfully authenticated */
 -      silc_server_packet_send(server, ctx->sock, SILC_PACKET_SUCCESS, 
 -                            0, NULL, 0, TRUE);
 +      SILC_PUT32_MSB(SILC_AUTH_OK, ok);
 +
 +      /* Authentication successful */
 +      silc_server_packet_send(server, ctx->sock, SILC_PACKET_SUCCESS,
 +                            0, ok, 4, TRUE);
  
        /* Unregister the timeout task since the protocol has ended. 
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
        if (ctx->timeout_task)
 -      silc_task_unregister(server->timeout_queue, ctx->timeout_task);
 +      silc_schedule_task_del(server->schedule, ctx->timeout_task);
  
 +      /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +      silc_protocol_cancel(protocol, server->schedule);
 +    
        /* Protocol has ended, call the final callback */
        if (protocol->final_callback)
 -      protocol->execute_final(server->timeout_queue, 0, protocol, fd);
 +      silc_protocol_execute_final(protocol, server->schedule);
        else
        silc_protocol_free(protocol);
      }
    case SILC_PROTOCOL_STATE_ERROR:
      {
        /*
 -       * Error 
 +       * Error. Send notify to remote.
         */
 +      unsigned char error[4];
 +
 +      SILC_PUT32_MSB(SILC_AUTH_FAILED, error);
  
        /* Authentication failed */
        silc_server_packet_send(server, ctx->sock, SILC_PACKET_FAILURE,
 -                            0, NULL, 0, TRUE);
 +                            0, error, 4, TRUE);
  
        /* Unregister the timeout task since the protocol has ended. 
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
        if (ctx->timeout_task)
 -      silc_task_unregister(server->timeout_queue, ctx->timeout_task);
 +      silc_schedule_task_del(server->schedule, ctx->timeout_task);
  
 +      /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +      silc_protocol_cancel(protocol, server->schedule);
 +    
        /* On error the final callback is always called. */
        if (protocol->final_callback)
 -      protocol->execute_final(server->timeout_queue, 0, protocol, fd);
 +      silc_protocol_execute_final(protocol, server->schedule);
        else
        silc_protocol_free(protocol);
      }
      break;
 +
 +  case SILC_PROTOCOL_STATE_FAILURE:
 +    /*
 +     * We have received failure from remote
 +     */
 +
 +    /* Unregister the timeout task since the protocol has ended. 
 +       This was the timeout task to be executed if the protocol is
 +       not completed fast enough. */
 +    if (ctx->timeout_task)
 +      silc_schedule_task_del(server->schedule, ctx->timeout_task);
 +
 +    /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +    silc_protocol_cancel(protocol, server->schedule);
 +    
 +    /* On error the final callback is always called. */
 +    if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +    else
 +      silc_protocol_free(protocol);
 +    break;
 +
 +  case SILC_PROTOCOL_STATE_UNKNOWN:
 +    break;
 +  }
 +}
 +
 +/*
 + * Re-key protocol routines
 + */
 +
 +/* Actually takes the new keys into use. */
 +
 +static void 
 +silc_server_protocol_rekey_validate(SilcServer server,
 +                                  SilcServerRekeyInternalContext *ctx,
 +                                  SilcIDListData idata,
 +                                  SilcSKEKeyMaterial *keymat,
 +                                  bool send)
 +{
 +  if (ctx->responder == TRUE) {
 +    if (send) {
 +      silc_cipher_set_key(idata->send_key, keymat->receive_enc_key, 
 +                        keymat->enc_key_len);
 +      silc_cipher_set_iv(idata->send_key, keymat->receive_iv);
 +      silc_hmac_set_key(idata->hmac_send, keymat->receive_hmac_key, 
 +                      keymat->hmac_key_len);
 +    } else {
 +      silc_cipher_set_key(idata->receive_key, keymat->send_enc_key, 
 +                        keymat->enc_key_len);
 +      silc_cipher_set_iv(idata->receive_key, keymat->send_iv);
 +      silc_hmac_set_key(idata->hmac_receive, keymat->send_hmac_key, 
 +                      keymat->hmac_key_len);
 +    }
 +  } else {
 +    if (send) {
 +      silc_cipher_set_key(idata->send_key, keymat->send_enc_key, 
 +                        keymat->enc_key_len);
 +      silc_cipher_set_iv(idata->send_key, keymat->send_iv);
 +      silc_hmac_set_key(idata->hmac_send, keymat->send_hmac_key, 
 +                      keymat->hmac_key_len);
 +    } else {
 +      silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key, 
 +                        keymat->enc_key_len);
 +      silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
 +      silc_hmac_set_key(idata->hmac_receive, keymat->receive_hmac_key, 
 +                      keymat->hmac_key_len);
 +    }
 +  }
 +
 +  /* Save the current sending encryption key */
 +  if (!send) {
 +    memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
 +    silc_free(idata->rekey->send_enc_key);
 +    idata->rekey->send_enc_key = 
 +      silc_calloc(keymat->enc_key_len / 8,
 +                sizeof(*idata->rekey->send_enc_key));
 +    memcpy(idata->rekey->send_enc_key, keymat->send_enc_key, 
 +         keymat->enc_key_len / 8);
 +    idata->rekey->enc_key_len = keymat->enc_key_len / 8;
 +  }
 +}
 +
 +/* This function actually re-generates (when not using PFS) the keys and
 +   takes them into use. */
 +
 +void silc_server_protocol_rekey_generate(SilcServer server,
 +                                       SilcServerRekeyInternalContext *ctx,
 +                                       bool send)
 +{
 +  SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
 +  SilcSKEKeyMaterial *keymat;
 +  uint32 key_len = silc_cipher_get_key_len(idata->send_key);
 +  uint32 hash_len = idata->hash->hash->hash_len;
 +
 +  SILC_LOG_DEBUG(("Generating new %s session keys (no PFS)",
 +                send ? "sending" : "receiving"));
 +
 +  /* Generate the new key */
 +  keymat = silc_calloc(1, sizeof(*keymat));
 +  silc_ske_process_key_material_data(idata->rekey->send_enc_key,
 +                                   idata->rekey->enc_key_len,
 +                                   16, key_len, hash_len, 
 +                                   idata->hash, keymat);
 +
 +  /* Set the keys into use */
 +  silc_server_protocol_rekey_validate(server, ctx, idata, keymat, send);
 +
 +  silc_ske_free_key_material(keymat);
 +}
 +
 +/* This function actually re-generates (with PFS) the keys and
 +   takes them into use. */
 +
 +void 
 +silc_server_protocol_rekey_generate_pfs(SilcServer server,
 +                                      SilcServerRekeyInternalContext *ctx,
 +                                      bool send)
 +{
 +  SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
 +  SilcSKEKeyMaterial *keymat;
 +  uint32 key_len = silc_cipher_get_key_len(idata->send_key);
 +  uint32 hash_len = idata->hash->hash->hash_len;
 +  unsigned char *tmpbuf;
 +  uint32 klen;
 +
 +  SILC_LOG_DEBUG(("Generating new %s session keys (with PFS)",
 +                send ? "sending" : "receiving"));
 +
 +  /* Encode KEY to binary data */
 +  tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen);
 +
 +  /* Generate the new key */
 +  keymat = silc_calloc(1, sizeof(*keymat));
 +  silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len, 
 +                                   idata->hash, keymat);
 +
 +  /* Set the keys into use */
 +  silc_server_protocol_rekey_validate(server, ctx, idata, keymat, send);
 +
 +  memset(tmpbuf, 0, klen);
 +  silc_free(tmpbuf);
 +  silc_ske_free_key_material(keymat);
 +}
 +
 +/* Packet sending callback. This function is provided as packet sending
 +   routine to the Key Exchange functions. */
 +
 +static void 
 +silc_server_protocol_rekey_send_packet(SilcSKE ske,
 +                                     SilcBuffer packet,
 +                                     SilcPacketType type,
 +                                     void *context)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcServerRekeyInternalContext *ctx = 
 +    (SilcServerRekeyInternalContext *)protocol->context;
 +  SilcServer server = (SilcServer)ctx->server;
 +
 +  /* Send the packet immediately */
 +  silc_server_packet_send(server, ctx->sock,
 +                        type, 0, packet->data, packet->len, FALSE);
 +}
 +
 +/* Performs re-key as defined in the SILC protocol specification. */
 +
 +SILC_TASK_CALLBACK(silc_server_protocol_rekey)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcServerRekeyInternalContext *ctx = 
 +    (SilcServerRekeyInternalContext *)protocol->context;
 +  SilcServer server = (SilcServer)ctx->server;
 +  SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
 +  SilcSKEStatus status;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
 +    protocol->state = SILC_PROTOCOL_STATE_START;
 +
 +  SILC_LOG_DEBUG(("State=%d", protocol->state));
 +
 +  switch(protocol->state) {
 +  case SILC_PROTOCOL_STATE_START:
 +    {
 +      /* 
 +       * Start protocol.
 +       */
 +
 +      if (ctx->responder == TRUE) {
 +      /*
 +       * We are receiving party
 +       */
 +
 +      if (ctx->pfs == TRUE) {
 +        /* 
 +         * Use Perfect Forward Secrecy, ie. negotiate the key material
 +         * using the SKE protocol.
 +         */
 +
 +        if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
 +          /* Error in protocol */
 +          protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +          silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +          return;
 +        }
 +
 +        ctx->ske = silc_ske_alloc();
 +        ctx->ske->rng = server->rng;
 +        ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
 +        silc_ske_group_get_by_number(idata->rekey->ske_group,
 +                                     &ctx->ske->prop->group);
 +
 +        silc_ske_set_callbacks(ctx->ske, 
 +                               silc_server_protocol_rekey_send_packet, 
 +                               NULL, NULL, NULL, silc_ske_check_version,
 +                               context);
 +      
 +        status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
 +        if (status != SILC_SKE_STATUS_OK) {
 +          SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
 +                            silc_ske_map_status(status)));
 +          
 +          protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +          silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +          return;
 +        }
 +
 +        /* Advance the protocol state */
 +        protocol->state++;
 +        silc_protocol_execute(protocol, server->schedule, 0, 0);
 +      } else {
 +        /*
 +         * Do normal and simple re-key.
 +         */
 +
 +        /* Send the REKEY_DONE to indicate we will take new keys into use */
 +        silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
 +                                0, NULL, 0, FALSE);
 +
 +        /* After we send REKEY_DONE we must set the sending encryption
 +           key to the new key since all packets after this packet must
 +           encrypted with the new key. */
 +        silc_server_protocol_rekey_generate(server, ctx, TRUE);
 +
 +        /* The protocol ends in next stage. */
 +        protocol->state = SILC_PROTOCOL_STATE_END;
 +      }
 +      
 +      } else {
 +      /*
 +       * We are the initiator of this protocol
 +       */
 +
 +      /* Start the re-key by sending the REKEY packet */
 +      silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY,
 +                              0, NULL, 0, FALSE);
 +
 +      if (ctx->pfs == TRUE) {
 +        /* 
 +         * Use Perfect Forward Secrecy, ie. negotiate the key material
 +         * using the SKE protocol.
 +         */
 +        ctx->ske = silc_ske_alloc();
 +        ctx->ske->rng = server->rng;
 +        ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
 +        silc_ske_group_get_by_number(idata->rekey->ske_group,
 +                                     &ctx->ske->prop->group);
 +
 +        silc_ske_set_callbacks(ctx->ske, 
 +                               silc_server_protocol_rekey_send_packet, 
 +                               NULL, NULL, NULL, silc_ske_check_version,
 +                               context);
 +      
 +        status = silc_ske_initiator_phase_2(ctx->ske, NULL, NULL);
 +        if (status != SILC_SKE_STATUS_OK) {
 +          SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
 +                            silc_ske_map_status(status)));
 +          
 +          protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +          silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +          return;
 +        }
 +
 +        /* Advance the protocol state */
 +        protocol->state++;
 +      } else {
 +        /*
 +         * Do normal and simple re-key.
 +         */
 +
 +        /* Send the REKEY_DONE to indicate we will take new keys into use 
 +           now. */ 
 +        silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
 +                                0, NULL, 0, FALSE);
 +
 +        /* After we send REKEY_DONE we must set the sending encryption
 +           key to the new key since all packets after this packet must
 +           encrypted with the new key. */
 +        silc_server_protocol_rekey_generate(server, ctx, TRUE);
 +
 +        /* The protocol ends in next stage. */
 +        protocol->state = SILC_PROTOCOL_STATE_END;
 +      }
 +      }
 +    }
 +    break;
 +
 +  case 2:
 +    /*
 +     * Second state, used only when oding re-key with PFS.
 +     */
 +    if (ctx->responder == TRUE) {
 +      if (ctx->pfs == TRUE) {
 +      /*
 +       * Send our KE packe to the initiator now that we've processed
 +       * the initiator's KE packet.
 +       */
 +      status = silc_ske_responder_finish(ctx->ske, NULL, NULL, 
 +                                         SILC_SKE_PK_TYPE_SILC);
 +      if (status != SILC_SKE_STATUS_OK) {
 +        SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
 +                          silc_ske_map_status(status)));
 +        
 +        protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +        silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +        return;
 +      }
 +      }
 +
 +    } else {
 +      if (ctx->pfs == TRUE) {
 +      /*
 +       * The packet type must be KE packet
 +       */
 +      if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
 +        /* Error in protocol */
 +        protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +        silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +        return;
 +      }
 +      
 +      status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
 +      if (status != SILC_SKE_STATUS_OK) {
 +        SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
 +                          silc_ske_map_status(status)));
 +        
 +        protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +        silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +        return;
 +      }
 +      }
 +    }
 +
 +    /* Send the REKEY_DONE to indicate we will take new keys into use 
 +       now. */ 
 +    silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
 +                          0, NULL, 0, FALSE);
 +    
 +    /* After we send REKEY_DONE we must set the sending encryption
 +       key to the new key since all packets after this packet must
 +       encrypted with the new key. */
 +    silc_server_protocol_rekey_generate_pfs(server, ctx, TRUE);
 +
 +    /* The protocol ends in next stage. */
 +    protocol->state = SILC_PROTOCOL_STATE_END;
 +    break;
 +
 +  case SILC_PROTOCOL_STATE_END:
 +    /* 
 +     * End protocol
 +     */
 +
 +    if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
 +      /* Error in protocol */
 +      protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +      silc_protocol_execute(protocol, server->schedule, 0, 300000);
 +      return;
 +    }
 +
 +    /* We received the REKEY_DONE packet and all packets after this is
 +       encrypted with the new key so set the decryption key to the new key */
 +    silc_server_protocol_rekey_generate(server, ctx, FALSE);
 +
 +    /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +    silc_protocol_cancel(protocol, server->schedule);
 +    
 +    /* Protocol has ended, call the final callback */
 +    if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +    else
 +      silc_protocol_free(protocol);
 +    break;
 +
 +  case SILC_PROTOCOL_STATE_ERROR:
 +    /*
 +     * Error occured
 +     */
 +
 +    if (ctx->pfs == TRUE) {
 +      /* Send abort notification */
 +      silc_ske_abort(ctx->ske, ctx->ske->status);
 +    }
 +
 +    /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +    silc_protocol_cancel(protocol, server->schedule);
 +    
 +    /* On error the final callback is always called. */
 +    if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +    else
 +      silc_protocol_free(protocol);
 +    break;
 +
 +  case SILC_PROTOCOL_STATE_FAILURE:
 +    /*
 +     * We have received failure from remote
 +     */
 +
 +    /* Assure that after calling final callback there cannot be pending
 +       executions for this protocol anymore. This just unregisters any 
 +       timeout callbacks for this protocol. */
 +    silc_protocol_cancel(protocol, server->schedule);
 +    
 +    /* On error the final callback is always called. */
 +    if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +    else
 +      silc_protocol_free(protocol);
 +    break;
 +
    case SILC_PROTOCOL_STATE_UNKNOWN:
      break;
    }
 +
  }
  
 -SILC_TASK_CALLBACK(silc_server_protocol_channel_auth)
 +/* Registers protocols used in server. */
 +
 +void silc_server_protocols_register(void)
 +{
 +  silc_protocol_register(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
 +                       silc_server_protocol_connection_auth);
 +  silc_protocol_register(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
 +                       silc_server_protocol_key_exchange);
 +  silc_protocol_register(SILC_PROTOCOL_SERVER_REKEY,
 +                       silc_server_protocol_rekey);
 +  silc_protocol_register(SILC_PROTOCOL_SERVER_BACKUP,
 +                       silc_server_protocol_backup);
 +}
 +
 +/* Unregisters protocols */
 +
 +void silc_server_protocols_unregister(void)
  {
 +  silc_protocol_unregister(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
 +                         silc_server_protocol_connection_auth);
 +  silc_protocol_unregister(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
 +                         silc_server_protocol_key_exchange);
 +  silc_protocol_unregister(SILC_PROTOCOL_SERVER_REKEY,
 +                         silc_server_protocol_rekey);
 +  silc_protocol_unregister(SILC_PROTOCOL_SERVER_BACKUP,
 +                         silc_server_protocol_backup);
  }
diff --combined apps/silcd/server.c
index d033284d457599c0614e9386425bcbd3794b2d38,636b428fdeb2ff2c698d0e4904be130d5b5e9e7d..f0ed13f8e6b1950302bfdb8045d0f1e150ffcdb1
@@@ -2,9 -2,9 +2,9 @@@
  
    server.c
  
 -  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 +  Author: Pekka Riikonen <priikone@silcnet.org>
  
 -  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
   * servicing the SILC connections. This is also a SILC router as a router 
   * is also normal server.
   */
 -/*
 - * $Id$
 - * $Log$
 - * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
 - *    Importet from internal CVS/Added Log headers.
 - *
 - *
 - */
 +/* $Id$ */
  
  #include "serverincludes.h"
  #include "server_internal.h"
  
  /* Static prototypes */
 +SILC_TASK_CALLBACK(silc_server_connect_router);
  SILC_TASK_CALLBACK(silc_server_connect_to_router);
  SILC_TASK_CALLBACK(silc_server_connect_to_router_second);
  SILC_TASK_CALLBACK(silc_server_connect_to_router_final);
@@@ -36,10 -42,25 +36,10 @@@ SILC_TASK_CALLBACK(silc_server_accept_n
  SILC_TASK_CALLBACK(silc_server_accept_new_connection_second);
  SILC_TASK_CALLBACK(silc_server_accept_new_connection_final);
  SILC_TASK_CALLBACK(silc_server_packet_process);
 -SILC_TASK_CALLBACK(silc_server_packet_parse);
 +SILC_TASK_CALLBACK(silc_server_packet_parse_real);
  SILC_TASK_CALLBACK(silc_server_timeout_remote);
 -
 -/* XXX */
 -void silc_server_packet_parse_type(SilcServer server, 
 -                                 SilcSocketConnection sock,
 -                                 SilcPacketContext *packet);
 -
 -static int silc_server_packet_check_mac(SilcServer server,
 -                                      SilcSocketConnection sock,
 -                                      SilcBuffer buffer);
 -static int silc_server_packet_decrypt_rest(SilcServer server, 
 -                                         SilcSocketConnection sock,
 -                                         SilcBuffer buffer);
 -static int silc_server_packet_decrypt_rest_special(SilcServer server, 
 -                                                 SilcSocketConnection sock,
 -                                                 SilcBuffer buffer);
 -
 -extern char server_version[];
 +SILC_TASK_CALLBACK(silc_server_failure_callback);
 +SILC_TASK_CALLBACK(silc_server_rekey_callback);
  
  /* Allocates a new SILC server object. This has to be done before the server
     can be used. After allocation one must call silc_server_init to initialize
  
  int silc_server_alloc(SilcServer *new_server)
  {
 +  SilcServer server;
 +
    SILC_LOG_DEBUG(("Allocating new server object"));
  
 -  *new_server = silc_calloc(1, sizeof(**new_server));
 -  if (*new_server == NULL) {
 -    SILC_LOG_ERROR(("Could not allocate new server object"));
 -    return FALSE;
 -  }
 +  server = silc_calloc(1, sizeof(*server));
 +  server->server_type = SILC_SERVER;
 +  server->standalone = TRUE;
 +  server->local_list = silc_calloc(1, sizeof(*server->local_list));
 +  server->global_list = silc_calloc(1, sizeof(*server->global_list));
 +  server->pending_commands = silc_dlist_init();
 +#ifdef SILC_SIM
 +  server->sim = silc_dlist_init();
 +#endif
  
 -  /* Set default values */
 -  (*new_server)->server_name = NULL;
 -  (*new_server)->server_type = SILC_SERVER;
 -  (*new_server)->standalone = FALSE;
 -  (*new_server)->id = NULL;
 -  (*new_server)->io_queue = NULL;
 -  (*new_server)->timeout_queue = NULL;
 -  (*new_server)->local_list = silc_calloc(1, sizeof(SilcIDListObject));
 -  (*new_server)->global_list = silc_calloc(1, sizeof(SilcIDListObject));
 -  (*new_server)->rng = NULL;
 -  (*new_server)->md5hash = NULL;
 -  (*new_server)->sha1hash = NULL;
 -  /*  (*new_server)->public_key = NULL;*/
 +  *new_server = server;
  
    return TRUE;
  }
  void silc_server_free(SilcServer server)
  {
    if (server) {
 -    if (server->local_list)
 -      silc_free(server->local_list);
 -    if (server->global_list)
 -      silc_free(server->global_list);
 +#ifdef SILC_SIM
 +    SilcSimContext *sim;
 +#endif
 +
 +    silc_free(server->local_list);
 +    silc_free(server->global_list);
      if (server->rng)
        silc_rng_free(server->rng);
  
 -    silc_math_primegen_uninit(); /* XXX */
 +    if (server->pkcs)
 +      silc_pkcs_free(server->pkcs);
 +
 +#ifdef SILC_SIM
 +    while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
 +      silc_dlist_del(server->sim, sim);
 +      silc_sim_free(sim);
 +    }
 +    silc_dlist_uninit(server->sim);
 +#endif
 +
 +    silc_free(server->params);
 +
 +    if (server->pending_commands)
 +      silc_dlist_uninit(server->pending_commands);
 +
      silc_free(server);
    }
  }
@@@ -113,98 -123,89 +113,93 @@@ int silc_server_init(SilcServer server
  {
    int *sock = NULL, sock_count, i;
    SilcServerID *id;
 -  SilcServerList *id_entry;
 -  SilcHashObject hash;
 +  SilcServerEntry id_entry;
 +  SilcIDListPurge purge;
-   SilcServerConfigSectionListenPort *listen;
  
    SILC_LOG_DEBUG(("Initializing server"));
    assert(server);
    assert(server->config);
  
 -  /* Set log files where log message should be saved. */
 -  server->config->server = server;
 -  silc_config_server_setlogfiles(server->config);
 - 
 +  /* Set public and private keys */
-   if (!server->config->server_keys ||
-       !server->config->server_keys->public_key || 
-       !server->config->server_keys->private_key) {
++  if (!server->config->server_info ||
++      !server->config->server_info->public_key || 
++      !server->config->server_info->private_key) {
 +    SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
 +    return FALSE;
 +  }
-   server->public_key = server->config->server_keys->public_key;
-   server->private_key = server->config->server_keys->private_key;
++  server->public_key = server->config->server_info->public_key;
++  server->private_key = server->config->server_info->private_key;
 +
 +  /* XXX After server is made as Silc Server Library this can be given
 +     as argument, for now this is hard coded */
 +  server->params = silc_calloc(1, sizeof(*server->params));
 +  server->params->retry_count = SILC_SERVER_RETRY_COUNT;
 +  server->params->retry_interval_min = SILC_SERVER_RETRY_INTERVAL_MIN;
 +  server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX;
 +  server->params->retry_keep_trying = FALSE;
 +  server->params->protocol_timeout = 60;
 +  server->params->require_reverse_mapping = FALSE;
 +
-   /* Set log files where log message should be saved. */
-   server->config->server = server;
-  
    /* Register all configured ciphers, PKCS and hash functions. */
-   if (!silc_server_config_register_ciphers(server->config))
 -  silc_config_server_register_ciphers(server->config);
 -  silc_config_server_register_pkcs(server->config);
 -  silc_config_server_register_hashfuncs(server->config);
++  if (!silc_server_config_register_ciphers(server))
 +    silc_cipher_register_default();
-   if (!silc_server_config_register_pkcs(server->config))
++  if (!silc_server_config_register_pkcs(server))
 +    silc_pkcs_register_default();
-   if (!silc_server_config_register_hashfuncs(server->config))
++  if (!silc_server_config_register_hashfuncs(server))
 +    silc_hash_register_default();
-   if (!silc_server_config_register_hmacs(server->config))
++  if (!silc_server_config_register_hmacs(server))
 +    silc_hmac_register_default();
  
    /* Initialize random number generator for the server. */
    server->rng = silc_rng_alloc();
    silc_rng_init(server->rng);
 -  silc_math_primegen_init(); /* XXX */
 +  silc_rng_global_init(server->rng);
  
    /* Initialize hash functions for server to use */
    silc_hash_alloc("md5", &server->md5hash);
    silc_hash_alloc("sha1", &server->sha1hash);
  
 -  /* Initialize none cipher */
 -  silc_cipher_alloc("none", &server->none_cipher);
 -
 -  /* XXXXX Generate RSA key pair */
 -  {
 -    unsigned char *public_key;
 -    unsigned char *private_key;
 -    unsigned int pk_len, prv_len;
 -
 -    if (silc_pkcs_alloc("rsa", &server->public_key) == FALSE) {
 -      SILC_LOG_ERROR(("Could not create RSA key pair"));
 -      goto err0;
 -    }
 -
 -    if (server->public_key->pkcs->init(server->public_key->context, 
 -                                     1024, server->rng) == FALSE) {
 -      SILC_LOG_ERROR(("Could not generate RSA key pair"));
 -      goto err0;
 -    }
 -
 -    public_key = 
 -      server->public_key->pkcs->get_public_key(server->public_key->context,
 -                                             &pk_len);
 -    private_key = 
 -      server->public_key->pkcs->get_private_key(server->public_key->context,
 -                                              &prv_len);
 -
 -    SILC_LOG_HEXDUMP(("public key"), public_key, pk_len);
 -    SILC_LOG_HEXDUMP(("private key"), private_key, prv_len);
 -
 -    /* XXX Save keys */
 -    silc_pkcs_save_public_key(server->public_key, "pubkey.pub",
 -                            public_key,  pk_len);
 +  /* Allocate PKCS context for local public and private keys */
 +  silc_pkcs_alloc(server->public_key->name, &server->pkcs);
 +  silc_pkcs_public_key_set(server->pkcs, server->public_key);
 +  silc_pkcs_private_key_set(server->pkcs, server->private_key);
  
 -    memset(public_key, 0, pk_len);
 -    memset(private_key, 0, prv_len);
 -    silc_free(public_key);
 -    silc_free(private_key);
 -  }
 -
 -  /* Create a listening server. Note that our server can listen on
 -     multiple ports. All listeners are created here and now. */
 -  /* XXX Still check this whether to use server_info or listen_port. */
 +  /* Create a listening server. Note that our server can listen on multiple
 +     ports. All listeners are created here and now. */
    sock_count = 0;
-   listen = server->config->listen_port;
-   while(listen) {
 -  while(server->config->listen_port) {
++  while (1) {
      int tmp;
  
--    tmp = silc_net_create_server(server->config->listen_port->port,
-                                server->config->listen_port->listener_ip);
 -                               server->config->listen_port->host);
 -    if (tmp < 0)
++    tmp = silc_net_create_server(server->config->server_info->port,
++                               server->config->server_info->server_ip);
 +
 +    if (tmp < 0) {
-       SILC_LOG_ERROR(("Could not create server listener: %s on %d",
-                     server->config->listen_port->listener_ip,
-                     server->config->listen_port->port));
++      SILC_LOG_ERROR(("Could not create server listener: %s on %hd",
++                    server->config->server_info->server_ip,
++                    server->config->server_info->port));
        goto err0;
 +    }
  
 -    sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
 +    sock = silc_realloc(sock, sizeof(*sock) * (sock_count + 1));
      sock[sock_count] = tmp;
 -    server->config->listen_port = server->config->listen_port->next;
      sock_count++;
-     listen = listen->next;
++    break;
    }
  
 +  /* Initialize ID caches */
 +  server->local_list->clients = 
 +    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor);
 +  server->local_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
 +  server->local_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 +
 +  /* 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. */
 +  server->global_list->clients = 
 +    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor);
 +  server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
 +  server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 +
    /* Allocate the entire socket list that is used in server. Eventually 
       all connections will have entry in this table (it is a table of 
       pointers to the actual object that is allocated individually 
      silc_net_set_socket_nonblock(sock[i]);
      server->sock = sock[i];
      
 +    /* Add ourselves also to the socket table. The entry allocated above
 +       is sent as argument for fast referencing in the future. */
 +    silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
 +    server->sockets[sock[i]] = newsocket;
 +    
 +    /* Perform name and address lookups to resolve the listenning address
 +       and port. */
 +    if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname, 
 +                                    &newsocket->ip)) {
 +      if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
 +        !newsocket->ip) {
 +      SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
 +                      newsocket->hostname ? newsocket->hostname :
 +                      newsocket->ip ? newsocket->ip : ""));
 +      server->stat.conn_failures++;
 +      goto err0;
 +      }
 +      if (!newsocket->hostname)
 +      newsocket->hostname = strdup(newsocket->ip);
 +    }
 +    newsocket->port = silc_net_get_local_port(sock[i]);
 +
      /* Create a Server ID for the server. */
 -    silc_id_create_server_id(sock[i], server->rng, &id);
 -    if (!id) {
 +    silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
 +    if (!id)
        goto err0;
 -    }
      
      server->id = id;
 +    server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
 +    server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
      server->id_type = SILC_ID_SERVER;
      server->server_name = server->config->server_info->server_name;
  
         beacuse we haven't established a route yet. It will be done later. 
         For now, NULL is sent as router. This allocates new entry to
         the ID list. */
 -    silc_idlist_add_server(&server->local_list->servers, 
 -                         server->config->server_info->server_name,
 -                         server->server_type, server->id, NULL,
 -                         server->send_key, server->receive_key,
 -                         NULL, NULL, &id_entry);
 -    if (!id_entry)
 +    id_entry = 
 +      silc_idlist_add_server(server->local_list,
 +                           server->config->server_info->server_name,
 +                           server->server_type, server->id, NULL, NULL);
 +    if (!id_entry) {
 +      SILC_LOG_ERROR(("Could not add ourselves to cache"));
        goto err0;
 +    }
 +    id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
      
 -    /* Add ourselves also to the socket table. The entry allocated above
 -       is sent as argument for fast referencing in the future. */
 -    silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, 
 -                    &newsocket);
 -    if (!newsocket)
 -      goto err0;
 -
 -    server->sockets[sock[i]] = newsocket;
 -
      /* Put the allocated socket pointer also to the entry allocated above 
         for fast back-referencing to the socket list. */
 -    id_entry->connection = (void *)server->sockets[sock[i]];
 +    newsocket->user_data = (void *)id_entry;
 +    id_entry->connection = (void *)newsocket;
      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) {
 +  /* Register protocols */
 +  silc_server_protocols_register();
 +
 +  /* Initialize the scheduler. */
 +  server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS);
 +  if (!server->schedule)
      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;
 -  }
  
 -  /* Initialize the scheduler */
 -  silc_schedule_init(server->io_queue, server->timeout_queue, 
 -                   server->generic_queue, 
 -                   SILC_SERVER_MAX_CONNECTIONS);
 -  
 -  /* Add the first task to the queue. This is task that is executed by
 +  /* Add the first task to the scheduler. This is task that is executed by
       timeout. It expires as soon as the caller calls silc_server_run. This
       task performs authentication protocol and key exchange with our
       primary router. */
 -  if (silc_task_register(server->timeout_queue, sock[0], 
 +  silc_schedule_task_add(server->schedule, sock[0], 
                         silc_server_connect_to_router,
                         (void *)server, 0, 1,
                         SILC_TASK_TIMEOUT,
 -                       SILC_TASK_PRI_NORMAL) == NULL) {
 -    goto err2;
 -  }
 +                       SILC_TASK_PRI_NORMAL);
 +
 +  /* Add listener task to the scheduler. This task receives new connections
 +     to the server. This task remains on the queue until the end of the 
 +     program. */
 +  silc_schedule_task_add(server->schedule, sock[0],
 +                       silc_server_accept_new_connection,
 +                       (void *)server, 0, 0, 
 +                       SILC_TASK_FD,
 +                       SILC_TASK_PRI_NORMAL);
 +  server->listenning = TRUE;
 +
 +  /* Send log file configuration */
 +  silc_server_config_setlogfiles(server->config, server->schedule);
  
    /* If server connections has been configured then we must be router as
       normal server cannot have server connections, only router connections. */
 -  if (server->config->servers)
 +  if (server->config->servers) {
-     SilcServerConfigSectionServerConnection *ptr = server->config->servers;
++    SilcServerConfigSectionServer *ptr = server->config->servers;
 +
      server->server_type = SILC_ROUTER;
 +    while (ptr) {
 +      if (ptr->backup_router) {
 +      server->server_type = SILC_BACKUP_ROUTER;
 +      server->backup_router = TRUE;
 +      server->id_entry->server_type = SILC_BACKUP_ROUTER;
 +      break;
 +      }
 +      ptr = ptr->next;
 +    }
 +  }
 +
 +  /* Register the ID Cache purge task. This periodically purges the ID cache
 +     and removes the expired cache entries. */
 +
 +  /* Clients local list */
 +  purge = silc_calloc(1, sizeof(*purge));
 +  purge->cache = server->local_list->clients;
 +  purge->schedule = server->schedule;
 +  purge->timeout = 600;
 +  silc_schedule_task_add(purge->schedule, 0, 
 +                       silc_idlist_purge,
 +                       (void *)purge, purge->timeout, 0,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 +
 +  /* Clients global list */
 +  purge = silc_calloc(1, sizeof(*purge));
 +  purge->cache = server->global_list->clients;
 +  purge->schedule = server->schedule;
 +  purge->timeout = 300;
 +  silc_schedule_task_add(purge->schedule, 0, 
 +                       silc_idlist_purge,
 +                       (void *)purge, purge->timeout, 0,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
  
    SILC_LOG_DEBUG(("Server initialized"));
  
    /* We are done here, return succesfully */
    return TRUE;
  
 - err2:
 -  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]);
    return FALSE;
  }
  
-     if (!server->config->identity || !server->config->identity->user || 
-       !server->config->identity->group) {
 +/* Fork server to background */
 +
 +void silc_server_daemonise(SilcServer server)
 +{
 +  int i;
 +
 +  SILC_LOG_DEBUG(("Forking SILC server to background"));
 +
 +  i = fork();
 +
 +  if (i < 0) {
 +    SILC_LOG_DEBUG(("fork() failed, cannot proceed"));
 +    exit(1);
 +  }
 +  else if (i) {
 +    if (geteuid())
 +      SILC_LOG_DEBUG(("Server started as user"));
 +    else
 +      SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
 +    exit(0);
 +  }
 +  setsid();
 +}
 +
 +/* Drop root privligies. If this cannot be done, die. */
 +
 +void silc_server_drop(SilcServer server)
 +{
 +  /* Are we executing silcd as root or a regular user? */
 +  if (!geteuid()) {
 +    struct passwd *pw;
 +    struct group *gr;
 +    char *user, *group;
 +
-     user=server->config->identity->user;
-     group=server->config->identity->group;
++    if (!server->config->server_info->user || !server->config->server_info->group) {
 +      fprintf(stderr, "Error:"
 +       "\tSILC server must not be run as root.  For the security of your\n"
 +       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
 +       "\tuser account.  Modify the [Identity] configuration section to run\n"
 +       "\tthe server as non-root user.\n");
 +      exit(1);
 +    }
 +
 +    /* Get the values given for user and group in configuration file */
++    user=server->config->server_info->user;
++    group=server->config->server_info->group;
 +
 +    /* Check whether the user/group information is text */ 
 +    if (atoi(user)!=0 || atoi(group)!=0) {
 +      SILC_LOG_DEBUG(("Invalid user and/or group information"));
 +      SILC_LOG_DEBUG(("User and/or group given as number"));
 +      fprintf(stderr, "Invalid user and/or group information\n");
 +      fprintf(stderr, "Please assign them as names, not numbers\n");
 +      exit(1);
 +    }
 +
 +    /* Catch the nasty incident of string "0" returning 0 from atoi */
 +    if (strcmp("0", user)==0 || strcmp("0", group)==0) {
 +      SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
 +      fprintf(stderr, "User and/or group configured to 0. Exiting\n");
 +      exit(1);
 +    }
 +
 +    if (!(pw=getpwnam(user))) {
 +      fprintf(stderr, "No such user %s found\n", user);
 +      exit(1);
 +    }
 +
 +    if (!(gr=getgrnam(group))) {
 +      fprintf(stderr, "No such group %s found\n", group);
 +      exit(1);
 +    }
 +
 +    /* Check whether user and/or group is set to root. If yes, exit
 +       immediately. Otherwise, setgid and setuid server to user.group */
 +    if (gr->gr_gid==0 || pw->pw_uid==0) {
 +      fprintf(stderr, "Error:"
 +       "\tSILC server must not be run as root.  For the security of your\n"
 +       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
 +       "\tuser account.  Modify the [Identity] configuration section to run\n"
 +       "\tthe server as non-root user.\n");
 +      exit(1);
 +    } else {
 +      SILC_LOG_DEBUG(("Changing to group %s", group));
 +      if (setgid(gr->gr_gid)==0) {
 +        SILC_LOG_DEBUG(("Setgid to %s", group));
 +      } else {
 +        SILC_LOG_DEBUG(("Setgid to %s failed", group));
 +        fprintf(stderr, "Tried to setgid %s but no such group. Exiting\n",
 +                group);
 +        exit(1);
 +      }
 +#if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
 +      if (setgroups(0, NULL)!=0) {
 +        SILC_LOG_DEBUG(("Setgroups to NULL failed"));
 +        fprintf(stderr, "Tried to setgroups NULL but failed. Exiting\n");
 +        exit(1);
 +      }
 +      if (initgroups(user, gr->gr_gid)!=0) {
 +        SILC_LOG_DEBUG(("Initgroups to user %s (gid=%d) failed", user, gr->gr_gid));
 +        fprintf(stderr, "Tried to initgroups %s (gid=%d) but no such user. Exiting\n",
 +                user, gr->gr_gid);
 +        exit(1);
 +      }
 +#endif
 +      SILC_LOG_DEBUG(("Changing to user %s", user));
 +      if (setuid(pw->pw_uid)==0) {
 +        SILC_LOG_DEBUG(("Setuid to %s", user));
 +      } else {
 +        SILC_LOG_DEBUG(("Setuid to %s failed", user));
 +        fprintf(stderr, "Tried to setuid %s but no such user. Exiting\n",
 +                user);
 +        exit(1);
 +      }
 +    }
 +  }
 +}
 +
 +/* The heart of the server. This runs the scheduler thus runs the server. 
 +   When this returns the server has been stopped and the program will
 +   be terminated. */
 +
 +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(server->schedule);
 +}
 +
  /* Stops the SILC server. This function is used to shutdown the server. 
     This is usually called after the scheduler has returned. After stopping 
     the server one should call silc_server_free. */
@@@ -498,161 -320,26 +492,161 @@@ void silc_server_stop(SilcServer server
  {
    SILC_LOG_DEBUG(("Stopping 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();
 +  if (server->schedule) {
 +    silc_schedule_stop(server->schedule);
 +    silc_schedule_uninit(server->schedule);
 +    server->schedule = NULL;
 +  }
 +
 +  silc_server_protocols_unregister();
  
    SILC_LOG_DEBUG(("Server stopped"));
  }
  
 -/* The heart of the server. This runs the scheduler thus runs the server. */
 +/* Function that is called when the network connection to a router has
 +   been established.  This will continue with the key exchange protocol
 +   with the remote router. */
  
 -void silc_server_run(SilcServer server)
 +void silc_server_start_key_exchange(SilcServer server,
 +                                  SilcServerConnection sconn,
 +                                  int sock)
  {
 -  SILC_LOG_DEBUG(("Running server"));
 +  SilcSocketConnection newsocket;
 +  SilcProtocol protocol;
 +  SilcServerKEInternalContext *proto_ctx;
 +  void *context;
  
 -  /* Start the scheduler, the heart of the SILC server. When this returns
 -     the program will be terminated. */
 -  silc_schedule();
 +  /* Cancel any possible retry timeouts */
 +  silc_schedule_task_del_by_callback(server->schedule,
 +                                   silc_server_connect_router);
 +
 +  /* Set socket options */
 +  silc_net_set_socket_nonblock(sock);
 +  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 +
 +  /* Create socket connection for the connection. Even though we
 +     know that we are connecting to a router we will mark the socket
 +     to be unknown connection until we have executed authentication
 +     protocol. */
 +  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
 +  server->sockets[sock] = newsocket;
 +  newsocket->hostname = strdup(sconn->remote_host);
 +  newsocket->ip = strdup(sconn->remote_host);
 +  newsocket->port = sconn->remote_port;
 +  sconn->sock = newsocket;
 +
 +  /* Allocate internal protocol context. This is sent as context
 +     to the protocol. */
 +  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 +  proto_ctx->server = (void *)server;
 +  proto_ctx->context = (void *)sconn;
 +  proto_ctx->sock = newsocket;
 +  proto_ctx->rng = server->rng;
 +  proto_ctx->responder = FALSE;
 +      
 +  /* Perform key exchange protocol. silc_server_connect_to_router_second
 +     will be called after the protocol is finished. */
 +  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
 +                    &protocol, proto_ctx,
 +                    silc_server_connect_to_router_second);
 +  newsocket->protocol = protocol;
 +      
 +  /* Register a timeout task that will be executed if the protocol
 +     is not executed within set limit. */
 +  proto_ctx->timeout_task = 
 +    silc_schedule_task_add(server->schedule, sock, 
 +                     silc_server_timeout_remote,
 +                     server, server->params->protocol_timeout,
 +                     server->params->protocol_timeout_usec,
 +                     SILC_TASK_TIMEOUT,
 +                     SILC_TASK_PRI_LOW);
 +
 +  /* Register the connection for network input and output. This sets
 +     that scheduler will listen for incoming packets for this connection 
 +     and sets that outgoing packets may be sent to this connection as 
 +     well. However, this doesn't set the scheduler for outgoing traffic,
 +     it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
 +     later when outgoing data is available. */
 +  context = (void *)server;
 +  SILC_REGISTER_CONNECTION_FOR_IO(sock);
 +  
 +  /* Run the protocol */
 +  silc_protocol_execute(protocol, server->schedule, 0, 0);
 +}
 +
 +/* Timeout callback that will be called to retry connecting to remote
 +   router. This is used by both normal and router server. This will wait
 +   before retrying the connecting. The timeout is generated by exponential
 +   backoff algorithm. */
 +
 +SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
 +{
 +  SilcServerConnection sconn = (SilcServerConnection)context;
 +  SilcServer server = sconn->server;
 +
 +  SILC_LOG_INFO(("Retrying connecting to a router"));
 +
 +  /* Calculate next timeout */
 +  if (sconn->retry_count >= 1) {
 +    sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
 +    if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX)
 +      sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
 +  } else {
 +    sconn->retry_timeout = server->params->retry_interval_min;
 +  }
 +  sconn->retry_count++;
 +  sconn->retry_timeout = sconn->retry_timeout +
 +    silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
 +
 +  /* If we've reached max retry count, give up. */
 +  if (sconn->retry_count > server->params->retry_count && 
 +      server->params->retry_keep_trying == FALSE) {
 +    SILC_LOG_ERROR(("Could not connect to router, giving up"));
 +    silc_free(sconn->remote_host);
 +    silc_free(sconn);
 +    return;
 +  }
 +
 +  /* Wait one before retrying */
 +  silc_schedule_task_add(server->schedule, fd, silc_server_connect_router,
 +                       context, sconn->retry_timeout, 
 +                       server->params->retry_interval_min_usec,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
  }
  
-   sock = silc_net_create_connection(server->config->listen_port->local_ip,
 +/* Generic routine to use connect to a router. */
 +
 +SILC_TASK_CALLBACK(silc_server_connect_router)
 +{    
 +  SilcServerConnection sconn = (SilcServerConnection)context;
 +  SilcServer server = sconn->server;
 +  int sock;
 +
 +  SILC_LOG_INFO(("Connecting to the %s %s on port %d", 
 +               (sconn->backup ? "backup router" : "router"), 
 +               sconn->remote_host, sconn->remote_port));
 +
 +  server->router_connect = time(0);
 +
 +  /* Connect to remote host */
++  sock = silc_net_create_connection(server->config->server_info->server_ip,
 +                                  sconn->remote_port, 
 +                                  sconn->remote_host);
 +  if (sock < 0) {
 +    SILC_LOG_ERROR(("Could not connect to router %s:%d",
 +                  sconn->remote_host, sconn->remote_port));
 +    if (!sconn->no_reconnect)
 +      silc_schedule_task_add(server->schedule, fd, 
 +                           silc_server_connect_to_router_retry,
 +                           context, 0, 1, SILC_TASK_TIMEOUT, 
 +                           SILC_TASK_PRI_NORMAL);
 +    return;
 +  }
 +
 +  /* Continue with key exchange protocol */
 +  silc_server_start_key_exchange(server, sconn, sock);
 +}
 +  
  /* This function connects to our primary router or if we are a router this
     establishes all our primary routes. This is called at the start of the
     server to do authentication and key exchange with our router - called
  SILC_TASK_CALLBACK(silc_server_connect_to_router)
  {
    SilcServer server = (SilcServer)context;
 -  SilcSocketConnection newsocket;
 -  int sock;
 +  SilcServerConnection sconn;
-   SilcServerConfigSectionServerConnection *ptr;
++  SilcServerConfigSectionRouter *ptr;
  
    SILC_LOG_DEBUG(("Connecting to router(s)"));
  
 -  /* if we are normal SILC server we need to connect to our cell's
 -     router. */
    if (server->server_type == SILC_SERVER) {
 -    SilcProtocol protocol;
 -    SilcServerKEInternalContext *proto_ctx;
 -
 -    /* Create connection to the router, if configured. */
 -    if (server->config->routers) {
 -      sock = silc_net_create_connection(server->config->routers->port, 
 -                                      server->config->routers->host);
 -      if (sock < 0) {
 -      SILC_LOG_ERROR(("Could not connect to router"));
 -      silc_schedule_stop();
 -      return;
 -      }
 -
 -      /* Set socket options */
 -      silc_net_set_socket_nonblock(sock);
 -      silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 -
 -      /* Create socket connection for the connection. Even though we
 -       know that we are connecting to a router we will mark the socket
 -       to be unknown connection until we have executed authentication
 -       protocol. */
 -      silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
 -      server->sockets[sock] = newsocket;
 -      newsocket->hostname = server->config->routers->host;
 -      newsocket->port = server->config->routers->port;
 -
 -      /* Allocate internal protocol context. This is sent as context
 -       to the protocol. */
 -      proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 -      proto_ctx->server = context;
 -      proto_ctx->sock = newsocket;
 -      proto_ctx->rng = server->rng;
 -      proto_ctx->responder = FALSE;
 -      
 -      /* Perform key exchange protocol. silc_server_connect_to_router_second
 -       will be called after the protocol is finished. */
 -      silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
 -                        &protocol, proto_ctx,
 -                        silc_server_connect_to_router_second);
 -      newsocket->protocol = protocol;
 -      
 -      /* Register a timeout task that will be executed if the protocol
 -       is not executed within 15 seconds. For now, this is a hard coded 
 -       limit. After 15 secs the connection will be closed if the key 
 -       exchange protocol has not been executed. */
 -      proto_ctx->timeout_task = 
 -      silc_task_register(server->timeout_queue, sock, 
 -                         silc_server_timeout_remote,
 -                         context, 15, 0,
 -                         SILC_TASK_TIMEOUT,
 -                         SILC_TASK_PRI_LOW);
 -
 -      /* Register the connection for network input and output. This sets
 -       that scheduler will listen for incoming packets for this connection 
 -       and sets that outgoing packets may be sent to this connection as 
 -       well. However, this doesn't set the scheduler for outgoing traffic,
 -       it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
 -       later when outgoing data is available. */
 -      SILC_REGISTER_CONNECTION_FOR_IO(sock);
 -      
 -      /* Run the protocol */
 -      protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
 -      return;
 -    }
 +    SILC_LOG_DEBUG(("We are normal server"));
 +  } else if (server->server_type == SILC_ROUTER) {
 +    SILC_LOG_DEBUG(("We are router"));
 +  } else {
 +    SILC_LOG_DEBUG(("We are backup router/normal server"));
    }
 -  
 -  /* if we are a SILC router we need to establish all of our primary
 -     routes. */
 -  if (server->server_type == SILC_ROUTER) {
 -    SilcConfigServerSectionServerConnection *ptr;
  
 -    /* Create the connections to all our routes */
 -    ptr = server->config->routers;
 -    while (ptr) {
 -      SilcProtocol protocol;
 -      SilcServerKEInternalContext *proto_ctx;
 -
 -      /* Create the connection to the remote end */
 -      sock = silc_net_create_connection(ptr->port, ptr->host);
 -      if (sock < 0) {
 -      SILC_LOG_ERROR(("Could not connect to router"));
 -      silc_schedule_stop();
 -      return;
 +  /* Create the connections to all our routes */
 +  ptr = server->config->routers;
 +  while (ptr) {
 +    
 +    SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
 +                  ptr->backup_router ? "Backup router" : "Router",
 +                  ptr->initiator ? "Initiator" : "Responder",
 +                  ptr->host, ptr->port));
 +
 +    if (ptr->initiator) {
 +      /* Allocate connection object for hold connection specific stuff. */
 +      sconn = silc_calloc(1, sizeof(*sconn));
 +      sconn->server = server;
 +      sconn->remote_host = strdup(ptr->host);
 +      sconn->remote_port = ptr->port;
 +      sconn->backup = ptr->backup_router;
 +      if (sconn->backup) {
 +      sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
 +      sconn->backup_replace_port = ptr->backup_replace_port;
        }
  
 -      /* Set socket options */
 -      silc_net_set_socket_nonblock(sock);
 -      silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 -
 -      /* Create socket connection for the connection. Even though we
 -       know that we are connecting to a router we will mark the socket
 -       to be unknown connection until we have executed authentication
 -       protocol. */
 -      silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
 -      server->sockets[sock] = newsocket;
 -      newsocket->hostname = ptr->host;
 -      newsocket->port = ptr->port;
 -
 -      /* Allocate internal protocol context. This is sent as context
 -       to the protocol. */
 -      proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 -      proto_ctx->server = context;
 -      proto_ctx->sock = newsocket;
 -      proto_ctx->rng = server->rng;
 -      proto_ctx->responder = FALSE;
 -      
 -      /* Perform key exchange protocol. silc_server_connect_to_router_final
 -       will be called after the protocol is finished. */
 -      silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
 -                        &protocol, proto_ctx,
 -                        silc_server_connect_to_router_second);
 -      newsocket->protocol = protocol;
 -
 -      /* Register a timeout task that will be executed if the protocol
 -       is not executed within 15 seconds. For now, this is a hard coded 
 -       limit. After 15 secs the connection will be closed if the key 
 -       exchange protocol has not been executed. */
 -      proto_ctx->timeout_task = 
 -      silc_task_register(server->timeout_queue, sock, 
 -                         silc_server_timeout_remote,
 -                         context, 15, 0,
 -                         SILC_TASK_TIMEOUT,
 -                         SILC_TASK_PRI_LOW);
 -
 -      /* Register the connection for network input and output. This sets
 -       that scheduler will listen for incoming packets for this connection 
 -       and sets that outgoing packets may be sent to this connection as 
 -       well. However, this doesn't set the scheduler for outgoing traffic,
 -       it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
 -       later when outgoing data is available. */
 -      SILC_REGISTER_CONNECTION_FOR_IO(sock);
 -      
 -      /* Run the protocol */
 -      protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
 -
 -      if (!ptr->next)
 -      return;
 +      if (!server->router_conn && !sconn->backup)
 +      server->router_conn = sconn;
  
 -      ptr = ptr->next;
 +      silc_schedule_task_add(server->schedule, fd, 
 +                           silc_server_connect_router,
 +                           (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
 +                           SILC_TASK_PRI_NORMAL);
      }
 +
 +    if (!ptr->next)
 +      return;
 +    
 +    ptr = ptr->next;
    }
  
    SILC_LOG_DEBUG(("No router(s), server will be standalone"));
    /* There wasn't a configured router, we will continue but we don't
       have a connection to outside world.  We will be standalone server. */
    server->standalone = TRUE;
 -
 -  /* Add a task to the queue. This task receives new connections to the 
 -     server. This task remains on the queue until the end of the program. */
 -  if (silc_task_register(server->io_queue, fd, 
 -                       silc_server_accept_new_connection,
 -                       (void *)server, 0, 0, 
 -                       SILC_TASK_FD,
 -                       SILC_TASK_PRI_NORMAL) == NULL) {
 -    silc_schedule_stop();
 -    return;
 -  }
  }
  
  /* Second part of connecting to router(s). Key exchange protocol has been
@@@ -726,104 -525,64 +720,104 @@@ SILC_TASK_CALLBACK(silc_server_connect_
    SilcServerKEInternalContext *ctx = 
      (SilcServerKEInternalContext *)protocol->context;
    SilcServer server = (SilcServer)ctx->server;
 -  SilcSocketConnection sock = NULL;
 +  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
 +  SilcSocketConnection sock = ctx->sock;
    SilcServerConnAuthInternalContext *proto_ctx;
-   SilcServerConfigSectionServerConnection *conn = NULL;
++  SilcServerConfigSectionRouter *conn = NULL;
  
    SILC_LOG_DEBUG(("Start"));
  
 -  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
 +  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
 +      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
      /* Error occured during protocol */
      silc_protocol_free(protocol);
 +    sock->protocol = NULL;
 +    silc_ske_free_key_material(ctx->keymat);
      if (ctx->packet)
 -      silc_buffer_free(ctx->packet);
 +      silc_packet_context_free(ctx->packet);
      if (ctx->ske)
        silc_ske_free(ctx->ske);
 -    if (ctx->dest_id)
 -      silc_free(ctx->dest_id);
 +    silc_free(ctx->dest_id);
      silc_free(ctx);
 -    sock->protocol = NULL;
 +    silc_schedule_task_del_by_callback(server->schedule,
 +                                     silc_server_failure_callback);
      silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
      return;
    }
    
 +  /* We now have the key material as the result of the key exchange
 +     protocol. Take the key material into use. Free the raw key material
 +     as soon as we've set them into use. */
 +  if (!silc_server_protocol_ke_set_keys(server, ctx->ske, 
 +                                      ctx->sock, ctx->keymat,
 +                                      ctx->ske->prop->cipher,
 +                                      ctx->ske->prop->pkcs,
 +                                      ctx->ske->prop->hash,
 +                                      ctx->ske->prop->hmac,
 +                                      ctx->ske->prop->group,
 +                                      ctx->responder)) {
 +    silc_protocol_free(protocol);
 +    sock->protocol = NULL;
 +    silc_ske_free_key_material(ctx->keymat);
 +    if (ctx->packet)
 +      silc_packet_context_free(ctx->packet);
 +    if (ctx->ske)
 +      silc_ske_free(ctx->ske);
 +    silc_free(ctx->dest_id);
 +    silc_free(ctx);
 +    silc_schedule_task_del_by_callback(server->schedule,
 +                                     silc_server_failure_callback);
 +    silc_server_disconnect_remote(server, sock, "Server closed connection: "
 +                                "Key exchange failed");
 +    return;
 +  }    
 +  silc_ske_free_key_material(ctx->keymat);
 +
    /* Allocate internal context for the authentication protocol. This
       is sent as context for the protocol. */
    proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
    proto_ctx->server = (void *)server;
 -  proto_ctx->sock = sock = server->sockets[fd];
 +  proto_ctx->context = (void *)sconn;
 +  proto_ctx->sock = sock;
    proto_ctx->ske = ctx->ske;     /* Save SKE object from previous protocol */
    proto_ctx->dest_id_type = ctx->dest_id_type;
    proto_ctx->dest_id = ctx->dest_id;
  
 -  /* Resolve the authentication method used in this connection */
 -  proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
 -  if (server->config->routers) {
 -    SilcConfigServerSectionServerConnection *conn = NULL;
 -
 -    /* Check if we find a match from user configured connections */
 -    conn = silc_config_server_find_router_conn(server->config,
 -                                             sock->hostname,
 -                                             sock->port);
 -    if (conn) {
 -      /* Match found. Use the configured authentication method */
 -      proto_ctx->auth_meth = conn->auth_meth;
 -      if (conn->auth_data) {
 -      proto_ctx->auth_data = strdup(conn->auth_data);
 -      proto_ctx->auth_data_len = strlen(conn->auth_data);
 -      }
 -    } else {
 -      /* No match found. */
 -      /* XXX */
 +  /* Resolve the authentication method used in this connection. Check if 
 +     we find a match from user configured connections */
 +  conn = silc_server_config_find_router_conn(server->config,
 +                                           sock->hostname,
 +                                           sock->port);
 +  if (conn) {
 +    /* Match found. Use the configured authentication method */
 +    proto_ctx->auth_meth = conn->auth_meth;
 +    if (conn->auth_data) {
 +      proto_ctx->auth_data = strdup(conn->auth_data);
 +      proto_ctx->auth_data_len = strlen(conn->auth_data);
      }
    } else {
 -    /* XXX */
 +    SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
 +                  sock->hostname, sock->ip, sock->port));
 +    silc_protocol_free(protocol);
 +    sock->protocol = NULL;
 +    if (ctx->packet)
 +      silc_packet_context_free(ctx->packet);
 +    if (ctx->ske)
 +      silc_ske_free(ctx->ske);
 +    silc_free(ctx->dest_id);
 +    silc_free(ctx);
 +    silc_schedule_task_del_by_callback(server->schedule,
 +                                     silc_server_failure_callback);
 +    silc_server_disconnect_remote(server, sock, "Server closed connection: "
 +                                "Key exchange failed");
 +    return;
    }
  
    /* Free old protocol as it is finished now */
    silc_protocol_free(protocol);
    if (ctx->packet)
 -    silc_buffer_free(ctx->packet);
 +    silc_packet_context_free(ctx->packet);
    silc_free(ctx);
    sock->protocol = NULL;
  
       this timelimit the connection will be terminated. Currently
       this is 15 seconds and is hard coded limit (XXX). */
    proto_ctx->timeout_task = 
 -    silc_task_register(server->timeout_queue, sock->sock, 
 +    silc_schedule_task_add(server->schedule, sock->sock, 
                       silc_server_timeout_remote,
                       (void *)server, 15, 0,
                       SILC_TASK_TIMEOUT,
                       SILC_TASK_PRI_LOW);
  
    /* Run the protocol */
 -  sock->protocol->execute(server->timeout_queue, 0, 
 -                        sock->protocol, sock->sock, 0, 0);
 +  silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
  }
  
  /* Finalizes the connection to router. Registers a server task to the
@@@ -858,46 -618,54 +852,46 @@@ SILC_TASK_CALLBACK(silc_server_connect_
    SilcServerConnAuthInternalContext *ctx = 
      (SilcServerConnAuthInternalContext *)protocol->context;
    SilcServer server = (SilcServer)ctx->server;
 +  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
    SilcSocketConnection sock = ctx->sock;
 -  SilcServerList *id_entry;
 -  SilcIDListUnknown *conn_data;
 +  SilcServerEntry id_entry;
    SilcBuffer packet;
 +  SilcServerHBContext hb_context;
    unsigned char *id_string;
 +  uint32 id_len;
 +  SilcIDListData idata;
  
    SILC_LOG_DEBUG(("Start"));
  
 -  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
 +  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
 +      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
      /* Error occured during protocol */
 -    silc_protocol_free(protocol);
 -    if (ctx->packet)
 -      silc_buffer_free(ctx->packet);
 -    if (ctx->ske)
 -      silc_ske_free(ctx->ske);
 -    if (ctx->dest_id)
 -      silc_free(ctx->dest_id);
 -    silc_free(ctx);
 -    sock->protocol = NULL;
 +    silc_free(ctx->dest_id);
      silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
 -    return;
 +    goto out;
    }
  
    /* Add a task to the queue. This task receives new connections to the 
       server. This task remains on the queue until the end of the program. */
 -  if (!server->listenning) {
 -    if (silc_task_register(server->io_queue, server->sock, 
 +  if (!server->listenning && !sconn->backup) {
 +    silc_schedule_task_add(server->schedule, server->sock, 
                           silc_server_accept_new_connection,
                           (void *)server, 0, 0, 
                           SILC_TASK_FD,
 -                         SILC_TASK_PRI_NORMAL) == NULL) {
 -      silc_schedule_stop();
 -      return;
 -    } else {
 -      server->listenning = TRUE;
 -    }
 +                         SILC_TASK_PRI_NORMAL);
 +    server->listenning = TRUE;
    }
  
    /* Send NEW_SERVER packet to the router. We will become registered
       to the SILC network after sending this packet. */
    id_string = silc_id_id2str(server->id, SILC_ID_SERVER);
 -  packet = silc_buffer_alloc(2 + 2 + SILC_ID_SERVER_LEN + 
 -                           strlen(server->server_name));
 +  id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
 +  packet = silc_buffer_alloc(2 + 2 + id_len + strlen(server->server_name));
    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
    silc_buffer_format(packet,
 -                   SILC_STR_UI_SHORT(SILC_ID_SERVER_LEN),
 -                   SILC_STR_UI_XNSTRING(id_string, SILC_ID_SERVER_LEN),
 +                   SILC_STR_UI_SHORT(id_len),
 +                   SILC_STR_UI_XNSTRING(id_string, id_len),
                     SILC_STR_UI_SHORT(strlen(server->server_name)),
                     SILC_STR_UI_XNSTRING(server->server_name,
                                          strlen(server->server_name)),
    silc_buffer_free(packet);
    silc_free(id_string);
  
 -  SILC_LOG_DEBUG(("Connected to router %s", sock->hostname));
 +  SILC_LOG_INFO(("Connected to router %s", sock->hostname));
 +
 +  /* Check that we do not have this ID already */
 +  id_entry = silc_idlist_find_server_by_id(server->local_list, 
 +                                         ctx->dest_id, TRUE, NULL);
 +  if (id_entry) {
 +    silc_idcache_del_by_context(server->local_list->servers, id_entry);
 +  } else {
 +    id_entry = silc_idlist_find_server_by_id(server->global_list, 
 +                                           ctx->dest_id, TRUE, NULL);
 +    if (id_entry) 
 +      silc_idcache_del_by_context(server->global_list->servers, id_entry);
 +  }
 +
 +  SILC_LOG_DEBUG(("New server id(%s)",
 +                silc_id_render(ctx->dest_id, SILC_ID_SERVER)));
  
 -  /* Add the connected router to local server list */
 -  server->standalone = FALSE;
 -  conn_data = (SilcIDListUnknown *)sock->user_data;
 -  silc_idlist_add_server(&server->local_list->servers, 
 -                       sock->hostname ? sock->hostname : sock->ip,
 -                       SILC_ROUTER, ctx->dest_id, NULL,
 -                       conn_data->send_key, conn_data->receive_key,
 -                       conn_data->pkcs, conn_data->hmac, &id_entry);
 +  /* Add the connected router to global server list */
 +  id_entry = silc_idlist_add_server(server->global_list, 
 +                                  strdup(sock->hostname),
 +                                  SILC_ROUTER, ctx->dest_id, NULL, sock);
 +  if (!id_entry) {
 +    silc_free(ctx->dest_id);
 +    silc_server_disconnect_remote(server, sock, "Server closed connection: "
 +                                "Authentication failed");
 +    goto out;
 +  }
  
 -  id_entry->hmac_key = conn_data->hmac_key;
 -  id_entry->hmac_key_len = conn_data->hmac_key_len;
 -  id_entry->connection = sock;
 +  silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
 +  silc_free(sock->user_data);
    sock->user_data = (void *)id_entry;
    sock->type = SILC_SOCKET_TYPE_ROUTER;
 -  server->id_entry->router = id_entry;
 +  idata = (SilcIDListData)sock->user_data;
 +  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
 +
 +  /* Perform keepalive. The `hb_context' will be freed automatically
 +     when finally calling the silc_socket_free function. XXX hardcoded 
 +     timeout!! */
 +  hb_context = silc_calloc(1, sizeof(*hb_context));
 +  hb_context->server = server;
 +  silc_socket_set_heartbeat(sock, 300, hb_context,
 +                          silc_server_perform_heartbeat,
 +                          server->schedule);
 +
 +  /* Register re-key timeout */
 +  idata->rekey->timeout = 3600; /* XXX hardcoded */
 +  idata->rekey->context = (void *)server;
 +  silc_schedule_task_add(server->schedule, sock->sock, 
 +                       silc_server_rekey_callback,
 +                       (void *)sock, idata->rekey->timeout, 0,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 +
 +  if (!sconn->backup) {
 +    /* Mark this router our primary router if we're still standalone */
 +    if (server->standalone) {
 +      server->id_entry->router = id_entry;
 +      server->router = id_entry;
 +      server->standalone = FALSE;
 +    
 +      /* If we are router then announce our possible servers. */
 +      if (server->server_type == SILC_ROUTER)
 +      silc_server_announce_servers(server, FALSE, 0, 
 +                                   server->router->connection);
 +
 +      /* Announce our clients and channels to the router */
 +      silc_server_announce_clients(server, 0, server->router->connection);
 +      silc_server_announce_channels(server, 0, server->router->connection);
 +    }
 +  } else {
 +    /* Add this server to be our backup router */
 +    silc_server_backup_add(server, id_entry, sconn->backup_replace_ip,
 +                         sconn->backup_replace_port, FALSE);
 +  }
 +
 +  sock->protocol = NULL;
  
 -  /* Free the temporary connection data context from key exchange */
 -  silc_free(conn_data);
 +  /* Call the completion callback to indicate that we've connected to
 +     the router */
 +  if (sconn->callback)
 +    (*sconn->callback)(server, id_entry, sconn->callback_context);
 +
 + out:
 +  /* Free the temporary connection data context */
 +  if (sconn) {
 +    silc_free(sconn->remote_host);
 +    silc_free(sconn->backup_replace_ip);
 +    silc_free(sconn);
 +  }
 +  if (sconn == server->router_conn)
 +    server->router_conn = NULL;
  
    /* Free the protocol object */
 +  if (sock->protocol == protocol)
 +    sock->protocol = NULL;
    silc_protocol_free(protocol);
    if (ctx->packet)
 -    silc_buffer_free(ctx->packet);
 +    silc_packet_context_free(ctx->packet);
    if (ctx->ske)
      silc_ske_free(ctx->ske);
 +  silc_free(ctx->auth_data);
    silc_free(ctx);
 -  sock->protocol = NULL;
  }
  
 -/* Accepts new connections to the server. Accepting new connections are
 -   done in three parts to make it async. */
 +/* Host lookup callbcak that is called after the incoming connection's
 +   IP and FQDN lookup is performed. This will actually check the acceptance
 +   of the incoming connection and will register the key exchange protocol
 +   for this connection. */
  
 -SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 +static void 
 +silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
 +                                       void *context)
  {
    SilcServer server = (SilcServer)context;
 -  SilcSocketConnection newsocket;
    SilcServerKEInternalContext *proto_ctx;
 -  int sock;
 +  void *cconfig, *sconfig, *rconfig;
-   SilcServerConfigSectionDenyConnection *deny;
++  SilcServerConfigSectionDeny *deny;
 +  int port;
  
 -  SILC_LOG_DEBUG(("Accepting new connection"));
 +  SILC_LOG_DEBUG(("Start"));
  
 -  sock = silc_net_accept_connection(server->sock);
 -  if (sock < 0) {
 -    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
 +  /* Check whether we could resolve both IP and FQDN. */
 +  if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
 +                  server->params->require_reverse_mapping)) {
 +    SILC_LOG_ERROR(("IP/DNS lookup failed %s",
 +                  sock->hostname ? sock->hostname :
 +                  sock->ip ? sock->ip : ""));
 +    server->stat.conn_failures++;
 +    silc_server_disconnect_remote(server, sock,
 +                                "Server closed connection: Unknown host");
      return;
    }
  
 -  /* Check max connections */
 -  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
 -    if (server->config->redirect) {
 -      /* XXX Redirecting connection to somewhere else now?? */
 -      /*silc_server_send_notify("Server is full, trying to redirect..."); */
 -    } else {
 -      SILC_LOG_ERROR(("Refusing connection, server is full"));
 -    }
 +  /* Register the connection for network input and output. This sets
 +     that scheduler will listen for incoming packets for this connection 
 +     and sets that outgoing packets may be sent to this connection as well.
 +     However, this doesn't set the scheduler for outgoing traffic, it
 +     will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
 +     later when outgoing data is available. */
 +  SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
 +
 +  SILC_LOG_INFO(("Incoming connection from %s (%s)", sock->hostname,
 +               sock->ip));
 +
 +  port = server->sockets[server->sock]->port; /* Listenning port */
 +
 +  /* Check whether this connection is denied to connect to us. */
-   deny = silc_server_config_denied_conn(server->config, sock->ip, port);
++  deny = silc_server_config_find_denied(server->config, sock->ip, port);
 +  if (!deny)
-     deny = silc_server_config_denied_conn(server->config, sock->hostname,
++    deny = silc_server_config_find_denied(server->config, sock->hostname,
 +                                        port);
 +  if (deny) {
 +    /* The connection is denied */
 +    SILC_LOG_INFO(("Connection %s (%s) is denied", 
 +                   sock->hostname, sock->ip));
-     silc_server_disconnect_remote(server, sock, deny->comment ?
-                                 deny->comment :
++    silc_server_disconnect_remote(server, sock, deny->reason ?
++                                deny->reason :
 +                                "Server closed connection: "
 +                                "Connection refused");
 +    server->stat.conn_failures++;
      return;
    }
  
 -  /* Set socket options */
 -  silc_net_set_socket_nonblock(sock);
 -  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 -
 -  /* We don't create a ID yet, since we don't know what type of connection
 -     this is yet. But, we do add the connection to the socket table. */
 -  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
 -  server->sockets[sock] = newsocket;
 -
 -  /* XXX This MUST be done async as this will block the entire server
 -     process. Either we have to do our own resolver stuff or in the future
 -     we can use threads. */
 -  /* Perform mandatory name and address lookups for the remote host. */
 -  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
 -  if (!newsocket->ip || !newsocket->hostname) {
 -    SILC_LOG_DEBUG(("IP lookup/DNS lookup failed"));
 -    SILC_LOG_ERROR(("IP lookup/DNS lookup failed"));
 +  /* Check whether we have configred this sort of connection at all. We
 +     have to check all configurations since we don't know what type of
 +     connection this is. */
-   if (!(cconfig = silc_server_config_find_client_conn(server->config,
++  if (!(cconfig = silc_server_config_find_client(server->config,
 +                                                    sock->ip, port)))
-     cconfig = silc_server_config_find_client_conn(server->config,
++    cconfig = silc_server_config_find_client(server->config,
 +                                                sock->hostname, 
 +                                                port);
 +  if (!(sconfig = silc_server_config_find_server_conn(server->config,
 +                                                   sock->ip, 
 +                                                   port)))
 +    sconfig = silc_server_config_find_server_conn(server->config,
 +                                                sock->hostname,
 +                                                port);
 +  if (!(rconfig = silc_server_config_find_router_conn(server->config,
 +                                                   sock->ip, port)))
 +    rconfig = silc_server_config_find_router_conn(server->config,
 +                                                sock->hostname, 
 +                                                port);
 +  if (!cconfig && !sconfig && !rconfig) {
 +    SILC_LOG_INFO(("Connection %s (%s) is not allowed", 
 +                   sock->hostname, sock->ip));
 +    silc_server_disconnect_remote(server, sock, 
 +                                "Server closed connection: "
 +                                "Connection refused");
 +    server->stat.conn_failures++;
      return;
    }
  
 +  /* The connection is allowed */
 +
    /* Allocate internal context for key exchange protocol. This is
       sent as context for the protocol. */
    proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
    proto_ctx->server = context;
 -  proto_ctx->sock = newsocket;
 +  proto_ctx->sock = sock;
    proto_ctx->rng = server->rng;
    proto_ctx->responder = TRUE;
 +  proto_ctx->cconfig = cconfig;
 +  proto_ctx->sconfig = sconfig;
 +  proto_ctx->rconfig = rconfig;
  
    /* Prepare the connection for key exchange protocol. We allocate the
       protocol but will not start it yet. The connector will be the
       initiator of the protocol thus we will wait for initiation from 
       there before we start the protocol. */
 +  server->stat.auth_attempts++;
    silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
 -                    &newsocket->protocol, proto_ctx, 
 +                    &sock->protocol, proto_ctx, 
                      silc_server_accept_new_connection_second);
  
    /* Register a timeout task that will be executed if the connector
 -     will not start the key exchange protocol within 15 seconds. For
 -     now, this is a hard coded limit. After 15 secs the connection will
 +     will not start the key exchange protocol within 60 seconds. For
 +     now, this is a hard coded limit. After 60 secs the connection will
       be closed if the key exchange protocol has not been started. */
    proto_ctx->timeout_task = 
 -    silc_task_register(server->timeout_queue, newsocket->sock, 
 -                     silc_server_timeout_remote,
 -                     context, 15, 0,
 -                     SILC_TASK_TIMEOUT,
 -                     SILC_TASK_PRI_LOW);
 +    silc_schedule_task_add(server->schedule, sock->sock, 
 +                         silc_server_timeout_remote,
 +                         context, 60, 0,
 +                         SILC_TASK_TIMEOUT,
 +                         SILC_TASK_PRI_LOW);
 +}
  
 -  /* Register the connection for network input and output. This sets
 -     that scheduler will listen for incoming packets for this connection 
 -     and sets that outgoing packets may be sent to this connection as well.
 -     However, this doesn't set the scheduler for outgoing traffic, it
 -     will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
 -     later when outgoing data is available. */
 -  SILC_REGISTER_CONNECTION_FOR_IO(sock);
 +/* Accepts new connections to the server. Accepting new connections are
 +   done in three parts to make it async. */
 +
 +SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 +{
 +  SilcServer server = (SilcServer)context;
 +  SilcSocketConnection newsocket;
 +  int sock;
 +
 +  SILC_LOG_DEBUG(("Accepting new connection"));
 +
 +  server->stat.conn_attempts++;
 +
 +  sock = silc_net_accept_connection(server->sock);
 +  if (sock < 0) {
 +    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
 +    server->stat.conn_failures++;
 +    return;
 +  }
 +
 +  /* Check max connections */
 +  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
 +    SILC_LOG_ERROR(("Refusing connection, server is full"));
 +    server->stat.conn_failures++;
 +    return;
 +  }
 +
 +  /* Set socket options */
 +  silc_net_set_socket_nonblock(sock);
 +  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 +
 +  /* We don't create a ID yet, since we don't know what type of connection
 +     this is yet. But, we do add the connection to the socket table. */
 +  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
 +  server->sockets[sock] = newsocket;
 +
 +  /* Perform asynchronous host lookup. This will lookup the IP and the
 +     FQDN of the remote connection. After the lookup is done the connection
 +     is accepted further. */
 +  silc_socket_host_lookup(newsocket, TRUE, 
 +                        silc_server_accept_new_connection_lookup, context, 
 +                        server->schedule);
  }
  
  /* Second part of accepting new connection. Key exchange protocol has been
@@@ -1190,77 -805,41 +1184,77 @@@ SILC_TASK_CALLBACK(silc_server_accept_n
    SilcServerKEInternalContext *ctx = 
      (SilcServerKEInternalContext *)protocol->context;
    SilcServer server = (SilcServer)ctx->server;
 -  SilcSocketConnection sock = NULL;
 +  SilcSocketConnection sock = ctx->sock;
    SilcServerConnAuthInternalContext *proto_ctx;
  
    SILC_LOG_DEBUG(("Start"));
  
 -  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
 +  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
 +      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
      /* Error occured during protocol */
      silc_protocol_free(protocol);
 +    sock->protocol = NULL;
 +    silc_ske_free_key_material(ctx->keymat);
      if (ctx->packet)
 -      silc_buffer_free(ctx->packet);
 +      silc_packet_context_free(ctx->packet);
      if (ctx->ske)
        silc_ske_free(ctx->ske);
 -    if (ctx->dest_id)
 -      silc_free(ctx->dest_id);
 +    silc_free(ctx->dest_id);
      silc_free(ctx);
 -    sock->protocol = NULL;
 +    silc_schedule_task_del_by_callback(server->schedule,
 +                                     silc_server_failure_callback);
      silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
 +    server->stat.auth_failures++;
      return;
    }
  
 +  /* We now have the key material as the result of the key exchange
 +     protocol. Take the key material into use. Free the raw key material
 +     as soon as we've set them into use. */
 +  if (!silc_server_protocol_ke_set_keys(server, ctx->ske, 
 +                                      ctx->sock, ctx->keymat,
 +                                      ctx->ske->prop->cipher,
 +                                      ctx->ske->prop->pkcs,
 +                                      ctx->ske->prop->hash,
 +                                      ctx->ske->prop->hmac,
 +                                      ctx->ske->prop->group,
 +                                      ctx->responder)) {
 +    silc_protocol_free(protocol);
 +    sock->protocol = NULL;
 +    silc_ske_free_key_material(ctx->keymat);
 +    if (ctx->packet)
 +      silc_packet_context_free(ctx->packet);
 +    if (ctx->ske)
 +      silc_ske_free(ctx->ske);
 +    silc_free(ctx->dest_id);
 +    silc_free(ctx);
 +    silc_schedule_task_del_by_callback(server->schedule,
 +                                     silc_server_failure_callback);
 +    silc_server_disconnect_remote(server, sock, "Server closed connection: "
 +                                "Key exchange failed");
 +    server->stat.auth_failures++;
 +    return;
 +  }    
 +  silc_ske_free_key_material(ctx->keymat);
 +
    /* Allocate internal context for the authentication protocol. This
       is sent as context for the protocol. */
    proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
    proto_ctx->server = (void *)server;
 -  proto_ctx->sock = sock = server->sockets[fd];
 +  proto_ctx->sock = sock;
    proto_ctx->ske = ctx->ske;  /* Save SKE object from previous protocol */
    proto_ctx->responder = TRUE;
    proto_ctx->dest_id_type = ctx->dest_id_type;
    proto_ctx->dest_id = ctx->dest_id;
 +  proto_ctx->cconfig = ctx->cconfig;
 +  proto_ctx->sconfig = ctx->sconfig;
 +  proto_ctx->rconfig = ctx->rconfig;
  
    /* Free old protocol as it is finished now */
    silc_protocol_free(protocol);
    if (ctx->packet)
 -    silc_buffer_free(ctx->packet);
 +    silc_packet_context_free(ctx->packet);
    silc_free(ctx);
    sock->protocol = NULL;
  
       this timelimit the connection will be terminated. Currently
       this is 60 seconds and is hard coded limit (XXX). */
    proto_ctx->timeout_task = 
 -    silc_task_register(server->timeout_queue, sock->sock, 
 -                     silc_server_timeout_remote,
 -                     (void *)server, 60, 0,
 -                     SILC_TASK_TIMEOUT,
 -                     SILC_TASK_PRI_LOW);
 +    silc_schedule_task_add(server->schedule, sock->sock, 
 +                         silc_server_timeout_remote,
 +                         (void *)server, 60, 0,
 +                         SILC_TASK_TIMEOUT,
 +                         SILC_TASK_PRI_LOW);
  }
  
  /* Final part of accepting new connection. The connection has now
@@@ -1294,188 -873,120 +1288,189 @@@ SILC_TASK_CALLBACK(silc_server_accept_n
      (SilcServerConnAuthInternalContext *)protocol->context;
    SilcServer server = (SilcServer)ctx->server;
    SilcSocketConnection sock = ctx->sock;
 +  SilcServerHBContext hb_context;
 +  SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
 +  void *id_entry;
  
    SILC_LOG_DEBUG(("Start"));
  
 -  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
 +  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
 +      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
      /* Error occured during protocol */
      silc_protocol_free(protocol);
 +    sock->protocol = NULL;
      if (ctx->packet)
 -      silc_buffer_free(ctx->packet);
 +      silc_packet_context_free(ctx->packet);
      if (ctx->ske)
        silc_ske_free(ctx->ske);
 -    if (ctx->dest_id)
 -      silc_free(ctx->dest_id);
 +    silc_free(ctx->dest_id);
      silc_free(ctx);
 -    sock->protocol = NULL;
 +    silc_schedule_task_del_by_callback(server->schedule,
 +                                     silc_server_failure_callback);
      silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
 +    server->stat.auth_failures++;
      return;
    }
  
 -  sock->type = ctx->conn_type;
 -  switch(sock->type) {
 +  entry->data.last_receive = time(NULL);
 +
 +  switch (ctx->conn_type) {
    case SILC_SOCKET_TYPE_CLIENT:
      {
 -      SilcClientList *id_entry = NULL;
 -      SilcIDListUnknown *conn_data = sock->user_data;
 +      SilcClientEntry client;
  
        SILC_LOG_DEBUG(("Remote host is client"));
 +      SILC_LOG_INFO(("Connection from %s (%s) is client", sock->hostname,
 +                   sock->ip));
 +
 +      /* Add the client to the client ID cache. The nickname and Client ID
 +       and other information is created after we have received NEW_CLIENT
 +       packet from client. */
 +      client = silc_idlist_add_client(server->local_list, 
 +                                    NULL, NULL, NULL, NULL, NULL, sock, 0);
 +      if (!client) {
 +      SILC_LOG_ERROR(("Could not add new client to cache"));
 +      silc_free(sock->user_data);
 +      silc_server_disconnect_remote(server, sock, 
 +                                    "Server closed connection: "
 +                                    "Authentication failed");
 +      server->stat.auth_failures++;
 +      goto out;
 +      }
  
 -      /* Add the client to the client ID list. We have not created the
 -       client ID for the client yet. This is done when client registers
 -       itself by sending NEW_CLIENT packet. */
 -      silc_idlist_add_client(&server->local_list->clients, 
 -                           NULL, NULL, NULL, NULL, NULL,
 -                           conn_data->send_key, conn_data->receive_key, 
 -                           conn_data->pkcs, conn_data->hmac, &id_entry);
 -
 -      id_entry->hmac_key = conn_data->hmac_key;
 -      id_entry->hmac_key_len = conn_data->hmac_key_len;
 -      id_entry->connection = sock;
 -
 -      /* Free the temporary connection data context from key exchange */
 -      silc_free(conn_data);
 +      /* Statistics */
 +      server->stat.my_clients++;
 +      server->stat.clients++;
 +      if (server->server_type == SILC_ROUTER)
 +      server->stat.cell_clients++;
  
 -      /* Mark the entry to the ID list to the socket connection for
 -       fast referencing in the future. */
 -      sock->user_data = (void *)id_entry;
 +      id_entry = (void *)client;
        break;
      }
    case SILC_SOCKET_TYPE_SERVER:
    case SILC_SOCKET_TYPE_ROUTER:
      {
 -      SilcServerList *id_entry;
 -      SilcIDListUnknown *conn_data = sock->user_data;
 -      
 +      SilcServerEntry new_server;
-       SilcServerConfigSectionServerConnection *conn = 
++      /* XXX FIXME: Now server and router has different table, so this is probably broken. */
++      SilcServerConfigSectionRouter *conn =
 +      ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
 +      ctx->sconfig : ctx->rconfig;
 +
        SILC_LOG_DEBUG(("Remote host is %s", 
 -                    sock->type == SILC_SOCKET_TYPE_SERVER ? 
 -                    "server" : "router"));
 -      
 -      /* Add the server to the ID list. We don't have the server's ID
 -       yet but we will receive it after the server sends NEW_SERVER
 -       packet to us. */
 -      silc_idlist_add_server(&server->local_list->servers, NULL,
 -                           sock->type == SILC_SOCKET_TYPE_SERVER ?
 -                           SILC_SERVER : SILC_ROUTER, NULL, NULL,
 -                           conn_data->send_key, conn_data->receive_key,
 -                           conn_data->pkcs, conn_data->hmac, &id_entry);
 -
 -      id_entry->hmac_key = conn_data->hmac_key;
 -      id_entry->hmac_key_len = conn_data->hmac_key_len;
 -      id_entry->connection = sock;
 -
 -      /* Free the temporary connection data context from key exchange */
 -      silc_free(conn_data);
 -
 -      /* Mark the entry to the ID list to the socket connection for
 -       fast referencing in the future. */
 -      sock->user_data = (void *)id_entry;
 -
 -      /* There is connection to other server now, if it is router then
 -       we will have connection to outside world.  If we are router but
 -       normal server connected to us then we will remain standalone,
 -       if we are standlone. */
 -      if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) {
 +                    ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
 +                    "server" : (conn->backup_router ? 
 +                                "backup router" : "router")));
 +      SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
 +                   sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
 +                   "server" : (conn->backup_router ? 
 +                               "backup router" : "router")));
 +
 +      /* Add the server into server cache. The server name and Server ID
 +       is updated after we have received NEW_SERVER packet from the
 +       server. We mark ourselves as router for this server if we really
 +       are router. */
 +      new_server = 
 +      silc_idlist_add_server((ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
 +                              server->local_list : (conn->backup_router ?
 +                                                    server->local_list :
 +                                                    server->global_list)),
 +                             NULL,
 +                             (ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
 +                              SILC_SERVER : SILC_ROUTER), 
 +                             NULL, 
 +                             (ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
 +                              server->id_entry : (conn->backup_router ? 
 +                                                  server->id_entry : NULL)),
 +                             sock);
 +      if (!new_server) {
 +      SILC_LOG_ERROR(("Could not add new server to cache"));
 +      silc_free(sock->user_data);
 +      silc_server_disconnect_remote(server, sock, 
 +                                    "Server closed connection: "
 +                                    "Authentication failed");
 +      server->stat.auth_failures++;
 +      goto out;
 +      }
 +
 +      /* Statistics */
 +      if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER)
 +      server->stat.my_servers++;
 +      else
 +      server->stat.my_routers++;
 +      server->stat.servers++;
 +
 +      id_entry = (void *)new_server;
 +
 +      /* If the incoming connection is router and marked as backup router
 +       then add it to be one of our backups */
 +      if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && conn->backup_router) {
 +      silc_server_backup_add(server, new_server, conn->backup_replace_ip,
 +                             conn->backup_replace_port, conn->backup_local);
 +
 +      /* Change it back to SERVER type since that's what it really is. */
 +      if (conn->backup_local)
 +        ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
 +
 +      new_server->server_type = SILC_BACKUP_ROUTER;
 +      }
 +
 +      /* Check whether this connection is to be our primary router connection
 +       if we do not already have the primary route. */
 +      if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
 +      if (silc_server_config_is_primary_route(server->config) &&
 +          !conn->initiator)
 +        break;
 +
        SILC_LOG_DEBUG(("We are not standalone server anymore"));
        server->standalone = FALSE;
 +      if (!server->id_entry->router) {
 +        server->id_entry->router = id_entry;
 +        server->router = id_entry;
 +      }
        }
 +
        break;
      }
    default:
 +    goto out;
      break;
    }
  
 +  sock->type = ctx->conn_type;
 +
 +  /* Add the common data structure to the ID entry. */
 +  silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
 +
 +  /* Add to sockets internal pointer for fast referencing */
 +  silc_free(sock->user_data);
 +  sock->user_data = id_entry;
 +
    /* Connection has been fully established now. Everything is ok. */
    SILC_LOG_DEBUG(("New connection authenticated"));
  
 +  /* Perform keepalive. The `hb_context' will be freed automatically
 +     when finally calling the silc_socket_free function. XXX hardcoded 
 +     timeout!! */
 +  hb_context = silc_calloc(1, sizeof(*hb_context));
 +  hb_context->server = server;
 +  silc_socket_set_heartbeat(sock, 400, hb_context,
 +                          silc_server_perform_heartbeat,
 +                          server->schedule);
 +
 + out:
 +  silc_schedule_task_del_by_callback(server->schedule,
 +                                   silc_server_failure_callback);
    silc_protocol_free(protocol);
    if (ctx->packet)
 -    silc_buffer_free(ctx->packet);
 +    silc_packet_context_free(ctx->packet);
    if (ctx->ske)
      silc_ske_free(ctx->ske);
 -  if (ctx->dest_id)
 -    silc_free(ctx->dest_id);
 +  silc_free(ctx->dest_id);
    silc_free(ctx);
    sock->protocol = NULL;
  }
  
 -typedef struct {
 -  SilcPacketContext *packetdata;
 -  SilcServer server;
 -  SilcSocketConnection sock;
 -  SilcCipher cipher;
 -  SilcHmac hmac;
 -} SilcServerInternalPacket;
 -
  /* This function is used to read packets from network and send packets to
     network. This is usually a generic task. */
  
@@@ -1483,52 -994,35 +1478,52 @@@ SILC_TASK_CALLBACK(silc_server_packet_p
  {
    SilcServer server = (SilcServer)context;
    SilcSocketConnection sock = server->sockets[fd];
 -  int ret, packetlen, paddedlen;
 +  SilcIDListData idata;
 +  SilcCipher cipher = NULL;
 +  SilcHmac hmac = NULL;
 +  uint32 sequence = 0;
 +  int ret;
 +
 +  if (!sock)
 +    return;
  
    SILC_LOG_DEBUG(("Processing packet"));
  
    /* Packet sending */
 +
    if (type == SILC_TASK_WRITE) {
 -    SILC_LOG_DEBUG(("Writing data to connection"));
 +    /* Do not send data to disconnected connection */
 +    if (SILC_IS_DISCONNECTED(sock))
 +      return;
 +
 +    server->stat.packets_sent++;
  
      if (sock->outbuf->data - sock->outbuf->head)
 -      silc_buffer_push(sock->outbuf, 
 -                     sock->outbuf->data - sock->outbuf->head);
 +     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
  
 -    /* Write the packet out to the connection */
 -    ret = silc_packet_write(fd, sock->outbuf);
 +    /* Send the packet */
 +    ret = silc_packet_send(sock, TRUE);
  
      /* If returned -2 could not write to connection now, will do
         it later. */
      if (ret == -2)
        return;
 -    
 -    /* Error */
 -    if (ret == -1)
 -      SILC_LOG_ERROR(("Could not write, packet dropped"));
  
 +    if (ret == -1) {
 +      SILC_LOG_ERROR(("Error sending packet to connection "
 +                    "%s:%d [%s]", sock->hostname, sock->port,  
 +                    (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
 +                     sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
 +                     sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
 +                     "Router")));
 +      return;
 +    }
 +    
      /* The packet has been sent and now it is time to set the connection
         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);
    }
  
    /* Packet receiving */
 -  if (type == SILC_TASK_READ) {
 -    SILC_LOG_DEBUG(("Reading data from connection"));
  
 -    /* Allocate the incoming data buffer if not done already. */
 -    if (!sock->inbuf)
 -      sock->inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
 +  /* Read some data from connection */
 +  ret = silc_packet_receive(sock);
 +  if (ret < 0) {
  
 -    /* Read some data from connection */
 -    ret = silc_packet_read(fd, sock->inbuf);
 -    
 -    /* If returned -2 data was not available now, will read it later. */
 -    if (ret == -2)
 -      return;
 -    
 -    /* Error */
 -    if (ret == -1) {
 -      SILC_LOG_ERROR(("Could not read, packet dropped"));
 -      return;
 -    }
 -    
 -    /* EOF */
 -    if (ret == 0) {
 -      SILC_LOG_DEBUG(("Read EOF"));
 -      
 -      /* If connection is disconnecting already we will finally
 -       close the connection */
 -      if (SILC_IS_DISCONNECTING(sock)) {
 -      if (sock->user_data)
 -        silc_server_free_sock_user_data(server, sock);
 -      silc_server_close_connection(server, sock);
 -      return;
 -      }
 -      
 -      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
 +    if (ret == -1)
 +      SILC_LOG_ERROR(("Error receiving packet from connection "
 +                    "%s:%d [%s] %s", sock->hostname, sock->port,  
 +                    (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
 +                     sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
 +                     sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
 +                     "Router"), strerror(errno)));
 +    return;
 +  }    
  
 +  /* EOF */
 +  if (ret == 0) {
 +    SILC_LOG_DEBUG(("Read EOF"));
 +      
 +    /* If connection is disconnecting already we will finally
 +       close the connection */
 +    if (SILC_IS_DISCONNECTING(sock)) {
        if (sock->user_data)
 -        silc_server_free_sock_user_data(server, sock);
 +      silc_server_free_sock_user_data(server, sock, NULL);
        silc_server_close_connection(server, sock);
        return;
      }
 -
 -    /* If connection is disconnecting or disconnected we will ignore
 -       what we read. */
 -    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
 -      SILC_LOG_DEBUG(("Ignoring read data from invalid connection"));
 -      return;
 -    }
 -
 -    /* Check whether we received a whole packet. If reading went without
 -       errors we either read a whole packet or the read packet is 
 -       incorrect and will be dropped. */
 -    SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
 -    if (sock->inbuf->len < paddedlen || (packetlen < SILC_PACKET_MIN_LEN)) {
 -      SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
 -      silc_buffer_clear(sock->inbuf);
 -      silc_server_disconnect_remote(server, sock, "Incorrect packet");
 -      return;
 -    }
 -
 -    /* Decrypt a packet coming from client. */
 -    if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
 -      SilcClientList *clnt = (SilcClientList *)sock->user_data;
 -      SilcServerInternalPacket *packet;
 -      int mac_len = 0;
        
 -      if (clnt->hmac)
 -      mac_len = clnt->hmac->hash->hash->hash_len;
 -
 -      if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
 -      /* Received possibly many packets at once */
 -
 -      while(sock->inbuf->len > 0) {
 -        SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
 -        if (sock->inbuf->len < paddedlen) {
 -          SILC_LOG_DEBUG(("Receive incorrect packet, dropped"));
 -          return;
 -        }
 -
 -        paddedlen += 2;
 -        packet = silc_calloc(1, sizeof(*packet));
 -        packet->server = server;
 -        packet->sock = sock;
 -        packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
 -        packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
 -        silc_buffer_pull_tail(packet->packetdata->buffer, 
 -                              SILC_BUFFER_END(packet->packetdata->buffer));
 -        silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, 
 -                        paddedlen + mac_len);
 -        if (clnt) {
 -          packet->cipher = clnt->receive_key;
 -          packet->hmac = clnt->hmac;
 -        }
 +    SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
 +    SILC_SET_DISCONNECTING(sock);
  
 -        SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
 -                          packet->packetdata->buffer->len),
 -                         packet->packetdata->buffer->data, 
 -                         packet->packetdata->buffer->len);
 -
 -        /* Parse the packet with timeout */
 -        silc_task_register(server->timeout_queue, fd, 
 -                           silc_server_packet_parse,
 -                           (void *)packet, 0, 100000, 
 +    if (sock->user_data) {
 +      char tmp[128];
 +      if (silc_socket_get_error(sock, tmp, sizeof(tmp) - 1))
 +      silc_server_free_sock_user_data(server, sock, tmp);
 +      else
 +      silc_server_free_sock_user_data(server, sock, NULL);
 +    } else if (server->router_conn && server->router_conn->sock == sock &&
 +           !server->router && server->standalone)
 +      silc_schedule_task_add(server->schedule, 0, 
 +                           silc_server_connect_to_router, 
 +                           server, 1, 0,
                             SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
  
 -        /* Pull the packet from inbuf thus we'll get the next one
 -           in the inbuf. */
 -        silc_buffer_pull(sock->inbuf, paddedlen);
 -        if (clnt->hmac)
 -          silc_buffer_pull(sock->inbuf, mac_len);
 -      }
 -      silc_buffer_clear(sock->inbuf);
 -      return;
 -      } else {
 -      SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
 -                       sock->inbuf->data, sock->inbuf->len);
 -      
 -      SILC_LOG_DEBUG(("Packet from client, length %d", paddedlen));
 -      
 -      packet = silc_calloc(1, sizeof(*packet));
 -      packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
 -      packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
 -      packet->server = server;
 -      packet->sock = sock;
 -      if (clnt) {
 -        packet->cipher = clnt->receive_key;
 -        packet->hmac = clnt->hmac;
 -      }
 -      silc_buffer_clear(sock->inbuf);
 -      
 -      /* The packet is ready to be parsed now. However, this is a client 
 -         connection so we will parse the packet with timeout. */
 -      silc_task_register(server->timeout_queue, fd, 
 -                         silc_server_packet_parse,
 -                         (void *)packet, 0, 100000, 
 -                         SILC_TASK_TIMEOUT,
 -                         SILC_TASK_PRI_NORMAL);
 -      return;
 -      }
 -    }
 -    
 -    /* Decrypt a packet coming from server connection */
 -    if (sock->type == SILC_SOCKET_TYPE_SERVER ||
 -      sock->type == SILC_SOCKET_TYPE_ROUTER) {
 -      SilcServerList *srvr = (SilcServerList *)sock->user_data;
 -      SilcServerInternalPacket *packet;
 -      int mac_len = 0;
 -      
 -      if (srvr->hmac)
 -      mac_len = srvr->hmac->hash->hash->hash_len;
 +    silc_server_close_connection(server, sock);
 +    return;
 +  }
  
 -      if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
 -      /* Received possibly many packets at once */
 +  /* If connection is disconnecting or disconnected we will ignore
 +     what we read. */
 +  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
 +    SILC_LOG_DEBUG(("Ignoring read data from disonnected connection"));
 +    return;
 +  }
  
 -      while(sock->inbuf->len > 0) {
 -        SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
 -        if (sock->inbuf->len < paddedlen) {
 -          SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
 -          return;
 -        }
 +  server->stat.packets_received++;
  
 -        paddedlen += 2;
 -        packet = silc_calloc(1, sizeof(*packet));
 -        packet->server = server;
 -        packet->sock = sock;
 -        packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
 -        packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
 -        silc_buffer_pull_tail(packet->packetdata->buffer, 
 -                              SILC_BUFFER_END(packet->packetdata->buffer));
 -        silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, 
 -                        paddedlen + mac_len);
 -        if (srvr) {
 -          packet->cipher = srvr->receive_key;
 -          packet->hmac = srvr->hmac;
 -        }
 +  /* Get keys and stuff from ID entry */
 +  idata = (SilcIDListData)sock->user_data;
 +  if (idata) {
 +    cipher = idata->receive_key;
 +    hmac = idata->hmac_receive;
 +    sequence = idata->psn_receive;
 +  }
 + 
 +  /* Process the packet. This will call the parser that will then
 +     decrypt and parse the packet. */
 +  ret = silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
 +                                  TRUE : FALSE, cipher, hmac, sequence, 
 +                                  silc_server_packet_parse, server);
 +
 +  /* If this socket connection is not authenticated yet and the packet
 +     processing failed we will drop the connection since it can be
 +     a malicious flooder. */
 +  if (sock->type == SILC_SOCKET_TYPE_UNKNOWN && ret == FALSE &&
 +      (!sock->protocol || sock->protocol->protocol->type ==
 +       SILC_PROTOCOL_SERVER_KEY_EXCHANGE)) {
 +    SILC_LOG_DEBUG(("Bad data sent from unknown connection %d", sock->sock));
 +    SILC_SET_DISCONNECTING(sock);
 +
 +    if (sock->user_data)
 +      silc_server_free_sock_user_data(server, sock, NULL);
 +    silc_server_close_connection(server, sock);
 +  }
 +}
 +  
 +/* Parses whole packet, received earlier. */
  
 -        SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
 -                          packet->packetdata->buffer->len),
 -                         packet->packetdata->buffer->data, 
 -                         packet->packetdata->buffer->len);
 +SILC_TASK_CALLBACK(silc_server_packet_parse_real)
 +{
 +  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
 +  SilcServer server = (SilcServer)parse_ctx->context;
 +  SilcSocketConnection sock = parse_ctx->sock;
 +  SilcPacketContext *packet = parse_ctx->packet;
 +  SilcIDListData idata = (SilcIDListData)sock->user_data;
 +  int ret;
  
 -        SILC_LOG_DEBUG(("Packet from %s %s, packet length %d", 
 -                        srvr->server_type == SILC_SERVER ? 
 -                        "server" : "router",
 -                        srvr->server_name, paddedlen));
 -      
 -        /* Parse it real soon as the packet is from server. */
 -        silc_task_register(server->timeout_queue, fd, 
 -                           silc_server_packet_parse,
 -                           (void *)packet, 0, 1, 
 -                           SILC_TASK_TIMEOUT,
 -                           SILC_TASK_PRI_NORMAL);
 +  SILC_LOG_DEBUG(("Start"));
  
 -        /* Pull the packet from inbuf thus we'll get the next one
 -           in the inbuf. */
 -        silc_buffer_pull(sock->inbuf, paddedlen);
 -        if (srvr->hmac)
 -          silc_buffer_pull(sock->inbuf, mac_len);
 -      }
 -      silc_buffer_clear(sock->inbuf);
 -      return;
 -      } else {
 +  /* Parse the packet */
 +  if (parse_ctx->normal)
 +    ret = silc_packet_parse(packet, idata ? idata->receive_key : NULL);
 +  else
 +    ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL);
  
 -      SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
 -                       sock->inbuf->data, sock->inbuf->len);
 -      
 -      SILC_LOG_DEBUG(("Packet from %s %s, packet length %d", 
 -                      srvr->server_type == SILC_SERVER ? 
 -                      "server" : "router",
 -                      srvr->server_name, paddedlen));
 -      
 -      packet = silc_calloc(1, sizeof(*packet));
 -      packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
 -      packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
 -      packet->server = server;
 -      packet->sock = sock;
 -      if (srvr) {
 -        packet->cipher = srvr->receive_key;
 -        packet->hmac = srvr->hmac;
 -      }
 -      silc_buffer_clear(sock->inbuf);
 -      
 -      /* The packet is ready to be parsed now. However, this is a client 
 -         connection so we will parse the packet with timeout. */
 -      silc_task_register(server->timeout_queue, fd, 
 -                         silc_server_packet_parse,
 -                         (void *)packet, 0, 1, 
 -                         SILC_TASK_TIMEOUT,
 -                         SILC_TASK_PRI_NORMAL);
 -      return;
 +  /* If entry is disabled ignore what we got. */
 +  if (ret != SILC_PACKET_RESUME_ROUTER &&
 +      idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
 +    SILC_LOG_DEBUG(("Connection is disabled"));
 +    goto out;
 +  }
 +
 +  if (ret == SILC_PACKET_NONE)
 +    goto out;
 +
 +  /* Check that the the current client ID is same as in the client's packet. */
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
 +    SilcClientEntry client = (SilcClientEntry)sock->user_data;
 +    if (client && client->id) {
 +      void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
 +                              packet->src_id_type);
 +      if (!id || !SILC_ID_CLIENT_COMPARE(client->id, id)) {
 +      silc_free(id);
 +      goto out;
        }
 +      silc_free(id);
      }
 +  }
  
 -    /* Decrypt a packet coming from client. */
 -    if (sock->type == SILC_SOCKET_TYPE_UNKNOWN) {
 -      SilcIDListUnknown *conn_data = (SilcIDListUnknown *)sock->user_data;
 -      SilcServerInternalPacket *packet;
 +  if (server->server_type == SILC_ROUTER) {
 +    /* Route the packet if it is not destined to us. Other ID types but
 +       server are handled separately after processing them. */
 +    if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
 +      packet->dst_id_type == SILC_ID_SERVER && 
 +      sock->type != SILC_SOCKET_TYPE_CLIENT &&
 +      memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
        
 -      SILC_LOG_HEXDUMP(("Incoming packet, len %d", sock->inbuf->len),
 -                     sock->inbuf->data, sock->inbuf->len);
 -
 -      SILC_LOG_DEBUG(("Packet from unknown connection, length %d", 
 -                    paddedlen));
 -
 -      packet = silc_calloc(1, sizeof(*packet));
 -      packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
 -      packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
 -      packet->server = server;
 -      packet->sock = sock;
 -      if (conn_data) {
 -      packet->cipher = conn_data->receive_key;
 -      packet->hmac = conn_data->hmac;
 -      }
 +      /* Route the packet to fastest route for the destination ID */
 +      void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len, 
 +                              packet->dst_id_type);
 +      if (!id)
 +      goto out;
 +      silc_server_packet_route(server,
 +                             silc_server_route_get(server, id,
 +                                                   packet->dst_id_type),
 +                             packet);
 +      silc_free(id);
 +      goto out;
 +    }
 +  }
  
 -      silc_buffer_clear(sock->inbuf);
 +  /* Parse the incoming packet type */
 +  silc_server_packet_parse_type(server, sock, packet);
  
 -      /* The packet is ready to be parsed now. However, this is unknown 
 -       connection so we will parse the packet with timeout. */
 -      silc_task_register(server->timeout_queue, fd, 
 -                       silc_server_packet_parse,
 -                       (void *)packet, 0, 100000, 
 -                       SILC_TASK_TIMEOUT,
 -                       SILC_TASK_PRI_NORMAL);
 -      return;
 +  if (server->server_type == SILC_ROUTER) {
 +    /* Broadcast packet if it is marked as broadcast packet and it is
 +       originated from router and we are router. */
 +    if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
 +      packet->flags & SILC_PACKET_FLAG_BROADCAST &&
 +      !server->standalone) {
 +      /* Broadcast to our primary route */
 +      silc_server_packet_broadcast(server, server->router->connection, packet);
 +
 +      /* If we have backup routers then we need to feed all broadcast
 +       data to those servers. */
 +      silc_server_backup_broadcast(server, sock, packet);
      }
    }
  
 -  SILC_LOG_ERROR(("Weird, nothing happened - ignoring"));
 + out:
 +  silc_packet_context_free(packet);
 +  silc_free(parse_ctx);
  }
  
 -/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called
 -   after packet has been totally decrypted and parsed. */
 +/* Parser callback called by silc_packet_receive_process. This merely
 +   registers timeout that will handle the actual parsing when appropriate. */
  
 -static int silc_server_packet_check_mac(SilcServer server,
 -                                      SilcSocketConnection sock,
 -                                      SilcBuffer buffer)
 -{
 -  SilcHmac hmac = NULL;
 -  unsigned char *hmac_key = NULL;
 -  unsigned int hmac_key_len = 0;
 -  unsigned int mac_len = 0;
 -
 -  switch(sock->type) {
 -  case SILC_SOCKET_TYPE_CLIENT:
 -    if (sock->user_data) {
 -      hmac = ((SilcClientList *)sock->user_data)->hmac;
 -      hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
 -      hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
 -    }
 -    break;
 -  case SILC_SOCKET_TYPE_SERVER:
 -  case SILC_SOCKET_TYPE_ROUTER:
 -    if (sock->user_data) {
 -      hmac = ((SilcServerList *)sock->user_data)->hmac;
 -      hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
 -      hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
 -    }
 -    break;
 -  default:
 -    if (sock->user_data) {
 -      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
 -      hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key;
 -      hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len;
 -    }
 -  }
 -
 -  /* Check MAC */
 -  if (hmac) {
 -    int headlen = buffer->data - buffer->head;
 -    unsigned char *packet_mac, mac[32];
 -    
 -    SILC_LOG_DEBUG(("Verifying MAC"));
 -
 -    mac_len = hmac->hash->hash->hash_len;
 -
 -    silc_buffer_push(buffer, headlen);
 -    
 -    /* Take mac from packet */
 -    packet_mac = buffer->tail;
 -    
 -    /* Make MAC and compare */
 -    memset(mac, 0, sizeof(mac));
 -    silc_hmac_make_with_key(hmac, 
 -                          buffer->data, buffer->len,
 -                          hmac_key, hmac_key_len, mac);
 -#if 0
 -    SILC_LOG_HEXDUMP(("PMAC"), packet_mac, mac_len);
 -    SILC_LOG_HEXDUMP(("CMAC"), mac, mac_len);
 -#endif
 -    if (memcmp(mac, packet_mac, mac_len)) {
 -      SILC_LOG_DEBUG(("MAC failed"));
 -      return FALSE;
 -    }
 -    
 -    SILC_LOG_DEBUG(("MAC is Ok"));
 -    memset(mac, 0, sizeof(mac));
 -
 -    silc_buffer_pull(buffer, headlen);
 -  }
 -  
 -  return TRUE;
 -}
 -
 -/* Decrypts rest of the packet (after decrypting just the SILC header).
 -   After calling this function the packet is ready to be parsed by calling 
 -   silc_packet_parse. */
 -
 -static int silc_server_packet_decrypt_rest(SilcServer server, 
 -                                         SilcSocketConnection sock,
 -                                         SilcBuffer buffer)
 +bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
 +                            void *context)
  {
 -  SilcCipher session_key = NULL;
 -  SilcHmac hmac = NULL;
 -  unsigned int mac_len = 0;
 -
 -  switch(sock->type) {
 -  case SILC_SOCKET_TYPE_CLIENT:
 -    if (sock->user_data) {
 -      session_key = ((SilcClientList *)sock->user_data)->receive_key;
 -      hmac = ((SilcClientList *)sock->user_data)->hmac;
 -    }
 -    break;
 -  case SILC_SOCKET_TYPE_SERVER:
 -  case SILC_SOCKET_TYPE_ROUTER:
 -    if (sock->user_data) {
 -      session_key = ((SilcServerList *)sock->user_data)->receive_key;
 -      hmac = ((SilcServerList *)sock->user_data)->hmac;
 -    }
 -    break;
 -  default:
 -    if (sock->user_data) {
 -      session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key;
 -      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
 -    }
 -  }
 -  
 -  /* Decrypt */
 -  if (session_key) {
 -
 -    /* Pull MAC from packet before decryption */
 -    if (hmac) {
 -      mac_len = hmac->hash->hash->hash_len;
 -      if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
 -      silc_buffer_push_tail(buffer, mac_len);
 -      } else {
 -      SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
 -      return FALSE;
 -      }
 -    }
 -
 -    SILC_LOG_DEBUG(("Decrypting rest of the packet"));
 -
 -    /* Decrypt rest of the packet */
 -    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
 -    silc_packet_decrypt(session_key, buffer, buffer->len);
 -    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
 -
 -    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len),
 -                   buffer->data, buffer->len);
 +  SilcServer server = (SilcServer)context;
 +  SilcSocketConnection sock = parser_context->sock;
 +  SilcIDListData idata = (SilcIDListData)sock->user_data;
 +
 +  if (idata)
 +    idata->psn_receive = parser_context->packet->sequence + 1;
 +
 +  /* If protocol for this connection is key exchange or rekey then we'll
 +     process all packets synchronously, since there might be packets in
 +     queue that we are not able to decrypt without first processing the
 +     packets before them. */
 +  if ((parser_context->packet->type == SILC_PACKET_REKEY ||
 +       parser_context->packet->type == SILC_PACKET_REKEY_DONE) ||
 +      (sock->protocol && sock->protocol->protocol && 
 +       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
 +      sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) {
 +    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
 +                                parser_context);
 +
 +    /* Reprocess data since we'll return FALSE here.  This is because
 +       the idata->receive_key might have become valid in the last packet
 +       and we want to call this processor with valid cipher. */
 +    if (idata)
 +      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
 +                                TRUE : FALSE, idata->receive_key, 
 +                                idata->hmac_receive, idata->psn_receive, 
 +                                silc_server_packet_parse, server);
 +    else
 +      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
 +                                TRUE : FALSE, NULL, NULL, 0, 
 +                                silc_server_packet_parse, server);
 +    return FALSE;
    }
  
 -  return TRUE;
 -}
 -
 -/* Decrypts rest of the SILC Packet header that has been decrypted partly
 -   already. This decrypts the padding of the packet also.  After calling 
 -   this function the packet is ready to be parsed by calling function 
 -   silc_packet_parse. */
 -
 -static int silc_server_packet_decrypt_rest_special(SilcServer server, 
 -                                                 SilcSocketConnection sock,
 -                                                 SilcBuffer buffer)
 -{
 -  SilcCipher session_key = NULL;
 -  SilcHmac hmac = NULL;
 -  unsigned int mac_len = 0;
 -
 -  switch(sock->type) {
 +  switch (sock->type) {
 +  case SILC_SOCKET_TYPE_UNKNOWN:
    case SILC_SOCKET_TYPE_CLIENT:
 -    if (sock->user_data) {
 -      session_key = ((SilcClientList *)sock->user_data)->receive_key;
 -      hmac = ((SilcClientList *)sock->user_data)->hmac;
 -    }
 +    /* Parse the packet with timeout */
 +    silc_schedule_task_add(server->schedule, sock->sock,
 +                         silc_server_packet_parse_real,
 +                         (void *)parser_context, 0, 100000,
 +                         SILC_TASK_TIMEOUT,
 +                         SILC_TASK_PRI_NORMAL);
      break;
    case SILC_SOCKET_TYPE_SERVER:
    case SILC_SOCKET_TYPE_ROUTER:
 -    if (sock->user_data) {
 -      session_key = ((SilcServerList *)sock->user_data)->receive_key;
 -      hmac = ((SilcServerList *)sock->user_data)->hmac;
 -    }
 +    /* Packets from servers are parsed immediately */
 +    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
 +                                parser_context);
      break;
    default:
 -    if (sock->user_data) {
 -      session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key;
 -      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
 -    }
 -  }
 -  
 -  /* Decrypt rest of the header plus padding */
 -  if (session_key) {
 -    unsigned short truelen, len1, len2, padlen;
 -
 -    /* Pull MAC from packet before decryption */
 -    if (hmac) {
 -      mac_len = hmac->hash->hash->hash_len;
 -      if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
 -      silc_buffer_push_tail(buffer, mac_len);
 -      } else {
 -      SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
 -      return FALSE;
 -      }
 -    }
 -  
 -    SILC_LOG_DEBUG(("Decrypting rest of the header"));
 -
 -    SILC_GET16_MSB(len1, &buffer->data[4]);
 -    SILC_GET16_MSB(len2, &buffer->data[6]);
 -
 -    truelen = SILC_PACKET_HEADER_LEN + len1 + len2;
 -    padlen = SILC_PACKET_PADLEN(truelen);
 -    len1 = (truelen + padlen) - (SILC_PACKET_MIN_HEADER_LEN - 2);
 -
 -    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
 -    silc_packet_decrypt(session_key, buffer, len1);
 -    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
 +    return TRUE;
    }
  
    return TRUE;
  }
  
 -/* Parses whole packet, received earlier. This packet is usually received
 -   from client. */
 -
 -SILC_TASK_CALLBACK(silc_server_packet_parse)
 -{
 -  SilcServerInternalPacket *packet = (SilcServerInternalPacket *)context;
 -  SilcServer server = packet->server;
 -  SilcSocketConnection sock = packet->sock;
 -  SilcBuffer buffer = packet->packetdata->buffer;
 -  int ret;
 -
 -  SILC_LOG_DEBUG(("Start"));
 -
 -  /* Decrypt start of the packet header */
 -  if (packet->cipher)
 -    silc_packet_decrypt(packet->cipher, buffer, SILC_PACKET_MIN_HEADER_LEN);
 -
 -  /* If the packet type is not any special type lets decrypt rest
 -     of the packet here. */
 -  if (buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE &&
 -      buffer->data[3] != SILC_PACKET_PRIVATE_MESSAGE) {
 -  normal:
 -    /* Normal packet, decrypt rest of the packet */
 -    if (!silc_server_packet_decrypt_rest(server, sock, buffer))
 -      goto out;
 -
 -    /* Parse the packet. Packet type is returned. */
 -    ret = silc_packet_parse(packet->packetdata);
 -    if (ret == SILC_PACKET_NONE)
 -      goto out;
 -
 -    /* Check MAC */
 -    if (!silc_server_packet_check_mac(server, sock, buffer))
 -      goto out;
 -  } else {
 -    /* If private message key is not set for private message it is
 -       handled as normal packet. Go back up. */
 -    if (buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
 -      !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
 -      goto normal;
 -
 -    /* Packet requires special handling, decrypt rest of the header.
 -       This only decrypts. This does not do any MAC checking, it must
 -       be done individually later when doing the special processing. */
 -    silc_server_packet_decrypt_rest_special(server, sock, buffer);
 -
 -    /* Parse the packet header in special way as this is "special"
 -       packet type. */
 -    ret = silc_packet_parse_special(packet->packetdata);
 -    if (ret == SILC_PACKET_NONE)
 -      goto out;
 -  }
 -  
 -  /* Parse the incoming packet type */
 -  silc_server_packet_parse_type(server, sock, packet->packetdata);
 -
 - out:
 -  silc_buffer_clear(sock->inbuf);
 -  //  silc_buffer_free(packetdata->packetdata->buffer);
 -  silc_free(packet->packetdata);
 -  silc_free(packet);
 -}
 -
  /* Parses the packet type and calls what ever routines the packet type
     requires. This is done for all incoming packets. */
  
@@@ -1781,19 -1570,16 +1776,19 @@@ void silc_server_packet_parse_type(Silc
                                   SilcSocketConnection sock,
                                   SilcPacketContext *packet)
  {
 -  SilcBuffer buffer = packet->buffer;
    SilcPacketType type = packet->type;
 +  SilcIDListData idata = (SilcIDListData)sock->user_data;
  
    SILC_LOG_DEBUG(("Parsing packet type %d", type));
  
    /* Parse the packet type */
 -  switch(type) {
 +  switch (type) {
    case SILC_PACKET_DISCONNECT:
      SILC_LOG_DEBUG(("Disconnect packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
      break;
 +
    case SILC_PACKET_SUCCESS:
      /*
       * Success received for something. For now we can have only
       * success message is for whatever protocol is executing currently.
       */
      SILC_LOG_DEBUG(("Success packet"));
 -    if (sock->protocol) {
 -      sock->protocol->execute(server->timeout_queue, 0,
 -                            sock->protocol, sock->sock, 0, 0);
 -    }
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    if (sock->protocol)
 +      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
      break;
 +
    case SILC_PACKET_FAILURE:
 +    /*
 +     * Failure received for something. For now we can have only
 +     * one protocol for connection executing at once hence this
 +     * failure message is for whatever protocol is executing currently.
 +     */
      SILC_LOG_DEBUG(("Failure packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    if (sock->protocol) {
 +      SilcServerFailureContext f;
 +      f = silc_calloc(1, sizeof(*f));
 +      f->server = server;
 +      f->sock = sock;
 +      
 +      /* We will wait 5 seconds to process this failure packet */
 +      silc_schedule_task_add(server->schedule, sock->sock,
 +                       silc_server_failure_callback, (void *)f, 5, 0,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 +    }
      break;
 +
    case SILC_PACKET_REJECT:
      SILC_LOG_DEBUG(("Reject packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
      return;
      break;
  
 +  case SILC_PACKET_NOTIFY:
 +    /*
 +     * Received notify packet. Server can receive notify packets from
 +     * router. Server then relays the notify messages to clients if needed.
 +     */
 +    SILC_LOG_DEBUG(("Notify packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      silc_server_notify_list(server, sock, packet);
 +    else
 +      silc_server_notify(server, sock, packet);
 +    break;
 +
      /* 
       * Channel packets
       */
    case SILC_PACKET_CHANNEL_MESSAGE:
      /*
       * Received channel message. Channel messages are special packets
 -     * (although probably most common ones) hence they are handled
 +     * (although probably most common ones) thus they are handled
       * specially.
       */
      SILC_LOG_DEBUG(("Channel Message packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    idata->last_receive = time(NULL);
      silc_server_channel_message(server, sock, packet);
      break;
  
       * never receives this channel and thus is ignored.
       */
      SILC_LOG_DEBUG(("Channel Key packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
      silc_server_channel_key(server, sock, packet);
      break;
  
       * Command packets
       */
    case SILC_PACKET_COMMAND:
 -    {
 -      /*
 -       * Recived command. Allocate command context and execute the command.
 -       */
 -      SilcServerCommandContext ctx;
 -
 -      SILC_LOG_DEBUG(("Command packet"));
 -
 -      /* Router cannot send command packet */
 -      if (sock->type == SILC_SOCKET_TYPE_ROUTER)
 -      break;
 -
 -      /* Allocate command context. This must be free'd by the
 -       command routine receiving it. */
 -      ctx = silc_calloc(1, sizeof(*ctx));
 -      ctx->server = server;
 -      ctx->sock = sock;
 -      ctx->packet = packet;   /* Save original packet */
 -
 -      /* Parse the command payload in the packet */
 -      ctx->payload = silc_command_parse_payload(buffer);
 -      if (!ctx->payload) {
 -      SILC_LOG_ERROR(("Bad command payload, packet dropped"));
 -      silc_free(ctx);
 -      return;
 -      }
 -
 -      /* Execute command. If this fails the packet is dropped. */
 -      SILC_SERVER_COMMAND_EXEC(ctx);
 -      silc_buffer_free(buffer);
 -    }
 +    /*
 +     * Recived command. Processes the command request and allocates the
 +     * command context and calls the command.
 +     */
 +    SILC_LOG_DEBUG(("Command packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    silc_server_command_process(server, sock, packet);
      break;
  
    case SILC_PACKET_COMMAND_REPLY:
      /*
 -     * Received command reply packet. Servers never send commands thus
 -     * they don't receive command reply packets either, except in cases
 -     * where server has forwarded command packet coming from client. 
 -     * This must be the case here or we will ignore the packet.
 +     * Received command reply packet. Received command reply to command. It
 +     * may be reply to command sent by us or reply to command sent by client
 +     * that we've routed further.
       */
      SILC_LOG_DEBUG(("Command Reply packet"));
 -    silc_server_packet_relay_command_reply(server, sock, packet);
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    silc_server_command_reply(server, sock, packet);
      break;
  
      /*
       * client or server.
       */
      SILC_LOG_DEBUG(("Private Message packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    idata->last_receive = time(NULL);
      silc_server_private_message(server, sock, packet);
      break;
  
    case SILC_PACKET_PRIVATE_MESSAGE_KEY:
 +    /*
 +     * Private message key packet.
 +     */
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    silc_server_private_message_key(server, sock, packet);
      break;
  
      /*
       */
    case SILC_PACKET_KEY_EXCHANGE:
      SILC_LOG_DEBUG(("KE packet"));
 -    if (sock->protocol && sock->protocol->protocol->type 
 -      == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +
 +    if (sock->protocol && sock->protocol->protocol &&
 +      sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
  
        SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
  
 -      proto_ctx->packet = buffer;
 +      proto_ctx->packet = silc_packet_context_dup(packet);
  
        /* Let the protocol handle the packet */
 -      sock->protocol->execute(server->timeout_queue, 0, 
 -                            sock->protocol, sock->sock, 0, 100000);
 +      silc_protocol_execute(sock->protocol, server->schedule, 0, 100000);
      } else {
        SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
                      "protocol active, packet dropped."));
 -
 -      /* XXX Trigger KE protocol?? Rekey actually, maybe. */
      }
      break;
  
    case SILC_PACKET_KEY_EXCHANGE_1:
      SILC_LOG_DEBUG(("KE 1 packet"));
 -    if (sock->protocol && sock->protocol->protocol->type 
 -      == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
  
 -      SilcServerKEInternalContext *proto_ctx = 
 -      (SilcServerKEInternalContext *)sock->protocol->context;
 +    if (sock->protocol && sock->protocol->protocol &&
 +      (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
 +       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
  
 -      if (proto_ctx->packet)
 -      silc_buffer_free(proto_ctx->packet);
 +      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
 +      SilcServerRekeyInternalContext *proto_ctx = 
 +        (SilcServerRekeyInternalContext *)sock->protocol->context;
 +      
 +      if (proto_ctx->packet)
 +        silc_packet_context_free(proto_ctx->packet);
 +      
 +      proto_ctx->packet = silc_packet_context_dup(packet);
  
 -      proto_ctx->packet = buffer;
 -      proto_ctx->dest_id_type = packet->src_id_type;
 -      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
 +      /* Let the protocol handle the packet */
 +      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 +      } else {
 +      SilcServerKEInternalContext *proto_ctx = 
 +        (SilcServerKEInternalContext *)sock->protocol->context;
 +      
 +      if (proto_ctx->packet)
 +        silc_packet_context_free(proto_ctx->packet);
 +      
 +      proto_ctx->packet = silc_packet_context_dup(packet);
 +      proto_ctx->dest_id_type = packet->src_id_type;
 +      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
 +                                          packet->src_id_type);
 +      if (!proto_ctx->dest_id)
 +        break;
  
 -      /* Let the protocol handle the packet */
 -      sock->protocol->execute(server->timeout_queue, 0, 
 -                            sock->protocol, sock->sock,
 +      /* Let the protocol handle the packet */
 +      silc_protocol_execute(sock->protocol, server->schedule, 
                              0, 100000);
 +      }
      } else {
        SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
                      "protocol active, packet dropped."));
  
    case SILC_PACKET_KEY_EXCHANGE_2:
      SILC_LOG_DEBUG(("KE 2 packet"));
 -    if (sock->protocol && sock->protocol->protocol->type 
 -      == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
  
 -      SilcServerKEInternalContext *proto_ctx = 
 -      (SilcServerKEInternalContext *)sock->protocol->context;
 +    if (sock->protocol && sock->protocol->protocol &&
 +      (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
 +       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
  
 -      if (proto_ctx->packet)
 -      silc_buffer_free(proto_ctx->packet);
 +      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
 +      SilcServerRekeyInternalContext *proto_ctx = 
 +        (SilcServerRekeyInternalContext *)sock->protocol->context;
 +      
 +      if (proto_ctx->packet)
 +        silc_packet_context_free(proto_ctx->packet);
 +      
 +      proto_ctx->packet = silc_packet_context_dup(packet);
  
 -      proto_ctx->packet = buffer;
 -      proto_ctx->dest_id_type = packet->src_id_type;
 -      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
 +      /* Let the protocol handle the packet */
 +      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 +      } else {
 +      SilcServerKEInternalContext *proto_ctx = 
 +        (SilcServerKEInternalContext *)sock->protocol->context;
 +      
 +      if (proto_ctx->packet)
 +        silc_packet_context_free(proto_ctx->packet);
 +      
 +      proto_ctx->packet = silc_packet_context_dup(packet);
 +      proto_ctx->dest_id_type = packet->src_id_type;
 +      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
 +                                          packet->src_id_type);
 +      if (!proto_ctx->dest_id)
 +        break;
  
 -      /* Let the protocol handle the packet */
 -      sock->protocol->execute(server->timeout_queue, 0, 
 -                            sock->protocol, sock->sock,
 +      /* Let the protocol handle the packet */
 +      silc_protocol_execute(sock->protocol, server->schedule, 
                              0, 100000);
 +      }
      } else {
        SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
                      "protocol active, packet dropped."));
      break;
  
    case SILC_PACKET_CONNECTION_AUTH_REQUEST:
 -    /* If we receive this packet we will send to the other end information
 -       about our mandatory authentication method for the connection. 
 -       This packet maybe received at any time. */
 +    /*
 +     * Connection authentication request packet. When we receive this packet
 +     * we will send to the other end information about our mandatory
 +     * authentication method for the connection. This packet maybe received
 +     * at any time. 
 +     */
 +    SILC_LOG_DEBUG(("Connection authentication request packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    silc_server_connection_auth_request(server, sock, packet);
 +    break;
  
      /*
       * Connection Authentication protocol packets
      /* Start of the authentication protocol. We receive here the 
         authentication data and will verify it. */
      SILC_LOG_DEBUG(("Connection auth packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +
      if (sock->protocol && sock->protocol->protocol->type 
        == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
  
        SilcServerConnAuthInternalContext *proto_ctx = 
        (SilcServerConnAuthInternalContext *)sock->protocol->context;
  
 -      proto_ctx->packet = buffer;
 +      proto_ctx->packet = silc_packet_context_dup(packet);
  
        /* Let the protocol handle the packet */
 -      sock->protocol->execute(server->timeout_queue, 0, 
 -                            sock->protocol, sock->sock, 0, 0);
 +      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
      } else {
        SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
                      "protocol active, packet dropped."));
       * SILC network.
       */
      SILC_LOG_DEBUG(("New ID packet"));
 -    silc_server_new_id(server, sock, packet);
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      silc_server_new_id_list(server, sock, packet);
 +    else
 +      silc_server_new_id(server, sock, packet);
      break;
  
    case SILC_PACKET_NEW_CLIENT:
       * ID we will send it to the client.
       */
      SILC_LOG_DEBUG(("New Client packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
      silc_server_new_client(server, sock, packet);
      break;
  
    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"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
      silc_server_new_server(server, sock, packet);
      break;
  
 -  default:
 -    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
 +  case SILC_PACKET_NEW_CHANNEL:
 +    /*
 +     * Received new channel packet. Information about new channel in the
 +     * network are distributed using this packet.
 +     */
 +    SILC_LOG_DEBUG(("New Channel packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      silc_server_new_channel_list(server, sock, packet);
 +    else
 +      silc_server_new_channel(server, sock, packet);
      break;
 -  }
 -  
 -}
  
 -/* Internal routine that sends packet or marks packet to be sent. This
 -   is used directly only in special cases. Normal cases should use
 -   silc_server_packet_send. Returns < 0 error. */
 +  case SILC_PACKET_HEARTBEAT:
 +    /*
 +     * Received heartbeat.
 +     */
 +    SILC_LOG_DEBUG(("Heartbeat packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    break;
  
 -static int silc_server_packet_send_real(SilcServer server,
 -                                      SilcSocketConnection sock,
 -                                      int force_send)
 -{
 -  /* Send now if forced to do so */
 -  if (force_send == TRUE) {
 -    int ret;
 -    SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
 -    ret = silc_packet_write(sock->sock, sock->outbuf);
 +  case SILC_PACKET_KEY_AGREEMENT:
 +    /*
 +     * Received heartbeat.
 +     */
 +    SILC_LOG_DEBUG(("Key agreement packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    silc_server_key_agreement(server, sock, packet);
 +    break;
  
 -    silc_buffer_clear(sock->outbuf);
 +  case SILC_PACKET_REKEY:
 +    /*
 +     * Received re-key packet. The sender wants to regenerate the session
 +     * keys.
 +     */
 +    SILC_LOG_DEBUG(("Re-key packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    silc_server_rekey(server, sock, packet);
 +    break;
  
 -    if (ret == -1)
 -      SILC_LOG_ERROR(("Could not write, packet dropped"));
 -    if (ret != -2) {
 -      silc_buffer_clear(sock->outbuf);
 -      return ret;
 +  case SILC_PACKET_REKEY_DONE:
 +    /*
 +     * The re-key is done.
 +     */
 +    SILC_LOG_DEBUG(("Re-key done packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +
 +    if (sock->protocol && sock->protocol->protocol &&
 +      sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
 +
 +      SilcServerRekeyInternalContext *proto_ctx = 
 +      (SilcServerRekeyInternalContext *)sock->protocol->context;
 +
 +      if (proto_ctx->packet)
 +      silc_packet_context_free(proto_ctx->packet);
 +
 +      proto_ctx->packet = silc_packet_context_dup(packet);
 +
 +      /* Let the protocol handle the packet */
 +      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 +    } else {
 +      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
 +                    "protocol active, packet dropped."));
      }
 +    break;
 +
 +  case SILC_PACKET_FTP:
 +    /* FTP packet */
 +    SILC_LOG_DEBUG(("FTP packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    silc_server_ftp(server, sock, packet);
 +    break;
  
 -    SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
 -  }  
 +  case SILC_PACKET_RESUME_ROUTER:
 +    /* Resume router packet received. This packet is received for backup
 +       router resuming protocol. */
 +    SILC_LOG_DEBUG(("Resume router packet"));
 +    if (packet->flags & SILC_PACKET_FLAG_LIST)
 +      break;
 +    silc_server_backup_resume_router(server, sock, packet);
 +    break;
  
 -  SILC_LOG_DEBUG(("Packet in queue"));
 +  default:
 +    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
 +    break;
 +  }
 +  
 +}
  
 -  /* Mark that there is some outgoing data available for this connection. 
 -     This call sets the connection both for input and output (the input
 -     is set always and this call keeps the input setting, actually). 
 -     Actual data sending is performed by silc_server_packet_process. */
 -  SILC_SET_CONNECTION_FOR_OUTPUT(sock->sock);
 +/* Creates connection to a remote router. */
  
 -  /* Mark to socket that data is pending in outgoing buffer. This flag
 -     is needed if new data is added to the buffer before the earlier
 -     put data is sent to the network. */
 -  SILC_SET_OUTBUF_PENDING(sock);
 +void silc_server_create_connection(SilcServer server,
 +                                 const char *remote_host, uint32 port)
 +{
 +  SilcServerConnection sconn;
 +
 +  /* Allocate connection object for hold connection specific stuff. */
 +  sconn = silc_calloc(1, sizeof(*sconn));
 +  sconn->server = server;
 +  sconn->remote_host = strdup(remote_host);
 +  sconn->remote_port = port;
 +  sconn->no_reconnect = TRUE;
 +
 +  silc_schedule_task_add(server->schedule, 0, 
 +                       silc_server_connect_router,
 +                       (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
 +                       SILC_TASK_PRI_NORMAL);
 +}
  
 -  return 0;
 +SILC_TASK_CALLBACK(silc_server_close_connection_final)
 +{
 +  silc_socket_free((SilcSocketConnection)context);
  }
  
 -/* Prepare outgoing data buffer for packet sending. This is internal
 -   routine and must always be called before sending any packets out. */
 +/* Closes connection to socket connection */
  
 -static void silc_server_packet_send_prepare(SilcServer server, 
 -                                          SilcSocketConnection sock,
 -                                          unsigned int header_len,
 -                                          unsigned int padlen,
 -                                          unsigned int data_len)
 +void silc_server_close_connection(SilcServer server,
 +                                SilcSocketConnection sock)
  {
 -  int totlen, oldlen;
 +  if (!server->sockets[sock->sock])
 +    return;
  
 -  totlen = header_len + padlen + data_len;
 +  SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
 +                  sock->port,
 +                  (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
 +                   sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
 +                   sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
 +                   "Router")));
  
 -  /* Prepare the outgoing buffer for packet sending. */
 -  if (!sock->outbuf) {
 -    /* Allocate new buffer. This is done only once per connection. */
 -    SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
 -    
 -    sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
 -    silc_buffer_pull_tail(sock->outbuf, totlen);
 -    silc_buffer_pull(sock->outbuf, header_len + padlen);
 -  } else {
 -    if (SILC_IS_OUTBUF_PENDING(sock)) {
 -      /* There is some pending data in the buffer. */
 +  /* We won't listen for this connection anymore */
 +  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
  
 -      if ((sock->outbuf->end - sock->outbuf->tail) < data_len) {
 -      SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
 -      /* XXX: not done yet */
 -      }
 -      oldlen = sock->outbuf->len;
 -      silc_buffer_pull_tail(sock->outbuf, totlen);
 -      silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen);
 -    } else {
 -      /* Buffer is free for use */
 -      silc_buffer_clear(sock->outbuf);
 -      silc_buffer_pull_tail(sock->outbuf, totlen);
 -      silc_buffer_pull(sock->outbuf, header_len + padlen);
 -    }
 -  }
 -}
 +  /* Unregister all tasks */
 +  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
  
 -/* Assembles a new packet to be sent out to network. This doesn't actually
 -   send the packet but creates the packet and fills the outgoing data
 -   buffer and marks the packet ready to be sent to network. However, If 
 -   argument force_send is TRUE the packet is sent immediately and not put 
 -   to queue. Normal case is that the packet is not sent immediately. */
 -
 -void silc_server_packet_send(SilcServer server,
 -                           SilcSocketConnection sock, 
 -                           SilcPacketType type, 
 -                           SilcPacketFlags flags,
 -                           unsigned char *data, 
 -                           unsigned int data_len,
 -                           int force_send)
 -{
 -  void *dst_id = NULL;
 -  SilcIdType dst_id_type = SILC_ID_NONE;
 +  /* Close the actual connection */
 +  silc_net_close_connection(sock->sock);
 +  server->sockets[sock->sock] = NULL;
  
 -  /* Get data used in the packet sending, keys and stuff */
 -  switch(sock->type) {
 -  case SILC_SOCKET_TYPE_CLIENT:
 -    if (((SilcClientList *)sock->user_data)->id) {
 -      dst_id = ((SilcClientList *)sock->user_data)->id;
 -      dst_id_type = SILC_ID_CLIENT;
 -    }
 -    break;
 -  case SILC_SOCKET_TYPE_SERVER:
 -  case SILC_SOCKET_TYPE_ROUTER:
 -    if (((SilcServerList *)sock->user_data)->id) {
 -      dst_id = ((SilcServerList *)sock->user_data)->id;
 -      dst_id_type = SILC_ID_SERVER;
 +  /* If sock->user_data is NULL then we'll check for active protocols
 +     here since the silc_server_free_sock_user_data has not been called
 +     for this connection. */
 +  if (!sock->user_data) {
 +    /* If any protocol is active cancel its execution. It will call
 +       the final callback which will finalize the disconnection. */
 +    if (sock->protocol) {
 +      silc_protocol_cancel(sock->protocol, server->schedule);
 +      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +      silc_protocol_execute_final(sock->protocol, server->schedule);
 +      sock->protocol = NULL;
 +      return;
      }
 -    break;
 -  default:
 -    break;
    }
  
 -  silc_server_packet_send_dest(server, sock, type, flags, dst_id,
 -                             dst_id_type, data, data_len, force_send);
 +  silc_schedule_task_add(server->schedule, 0, 
 +                       silc_server_close_connection_final,
 +                       (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
 +                       SILC_TASK_PRI_NORMAL);
  }
  
 -/* Assembles a new packet to be sent out to network. This doesn't actually
 -   send the packet but creates the packet and fills the outgoing data
 -   buffer and marks the packet ready to be sent to network. However, If 
 -   argument force_send is TRUE the packet is sent immediately and not put 
 -   to queue. Normal case is that the packet is not sent immediately. 
 -   Destination information is sent as argument for this function. */
 -
 -void silc_server_packet_send_dest(SilcServer server,
 -                                SilcSocketConnection sock, 
 -                                SilcPacketType type, 
 -                                SilcPacketFlags flags,
 -                                void *dst_id,
 -                                SilcIdType dst_id_type,
 -                                unsigned char *data, 
 -                                unsigned int data_len,
 -                                int force_send)
 -{
 -  SilcPacketContext packetdata;
 -  SilcCipher cipher = NULL;
 -  SilcHmac hmac = NULL;
 -  unsigned char *hmac_key = NULL;
 -  unsigned int hmac_key_len = 0;
 -  unsigned char mac[32];
 -  unsigned int mac_len = 0;
 -  unsigned char *dst_id_data = NULL;
 -  unsigned int dst_id_len = 0;
 -
 -  SILC_LOG_DEBUG(("Sending packet, type %d", type));
 +/* Sends disconnect message to remote connection and disconnects the 
 +   connection. */
  
 -  /* Get data used in the packet sending, keys and stuff */
 -  switch(sock->type) {
 -  case SILC_SOCKET_TYPE_CLIENT:
 -    if (sock->user_data) {
 -      cipher = ((SilcClientList *)sock->user_data)->send_key;
 -      hmac = ((SilcClientList *)sock->user_data)->hmac;
 -      if (hmac) {
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
 -      hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
 -      }
 -    }
 -    break;
 -  case SILC_SOCKET_TYPE_SERVER:
 -  case SILC_SOCKET_TYPE_ROUTER:
 -    if (sock->user_data) {
 -      cipher = ((SilcServerList *)sock->user_data)->send_key;
 -      hmac = ((SilcServerList *)sock->user_data)->hmac;
 -      if (hmac) {
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
 -      hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
 -      }
 -    }
 -    break;
 -  default:
 -    if (sock->user_data) {
 -      /* We don't know what type of connection this is thus it must
 -       be in authentication phase. */
 -      cipher = ((SilcIDListUnknown *)sock->user_data)->send_key;
 -      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
 -      if (hmac) {
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key;
 -      hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len;
 -      }
 -    }
 -    break;
 -  }
 +void silc_server_disconnect_remote(SilcServer server,
 +                                 SilcSocketConnection sock,
 +                                 const char *fmt, ...)
 +{
 +  va_list ap;
 +  unsigned char buf[4096];
  
 -  if (dst_id) {
 -    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
 -    dst_id_len = silc_id_get_len(dst_id_type);
 -  }
 +  if (!sock)
 +    return;
  
 -  /* Set the packet context pointers */
 -  packetdata.type = type;
 -  packetdata.flags = flags;
 -  packetdata.src_id = silc_id_id2str(server->id, server->id_type);
 -  packetdata.src_id_len = SILC_ID_SERVER_LEN;
 -  packetdata.src_id_type = server->id_type;
 -  packetdata.dst_id = dst_id_data;
 -  packetdata.dst_id_len = dst_id_len;
 -  packetdata.dst_id_type = dst_id_type;
 -  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;
 +  memset(buf, 0, sizeof(buf));
 +  va_start(ap, fmt);
 +  vsprintf(buf, fmt, ap);
 +  va_end(ap);
  
 -  /* Prepare outgoing data buffer for packet sending */
 -  silc_server_packet_send_prepare(server, sock, 
 -                                SILC_PACKET_HEADER_LEN +
 -                                packetdata.src_id_len + 
 -                                packetdata.dst_id_len,
 -                                packetdata.padlen,
 -                                data_len);
 +  SILC_LOG_DEBUG(("Disconnecting remote host"));
  
 -  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
 +  /* Notify remote end that the conversation is over. The notify message
 +     is tried to be sent immediately. */
 +  silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,  
 +                        buf, strlen(buf), TRUE);
  
 -  packetdata.buffer = sock->outbuf;
 +  /* Mark the connection to be disconnected */
 +  SILC_SET_DISCONNECTED(sock);
 +  silc_server_close_connection(server, sock);
 +}
  
 -  /* Put the data to the buffer */
 -  if (data && data_len)
 -    silc_buffer_put(sock->outbuf, data, data_len);
 +typedef struct {
 +  SilcServer server;
 +  SilcClientEntry client;
 +} *FreeClientInternal;
  
 -  /* Create the outgoing packet */
 -  silc_packet_assemble(&packetdata);
 +SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
 +{
 +  FreeClientInternal i = (FreeClientInternal)context;
  
 -  /* Compute MAC of the packet */
 -  if (hmac) {
 -    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
 -                          hmac_key, hmac_key_len, mac);
 -    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
 -    memset(mac, 0, sizeof(mac));
 -  }
 +  silc_idlist_del_data(i->client);
 +  silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
 +  silc_free(i);
 +}
  
 -  /* Encrypt the packet */
 -  if (cipher)
 -    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
 +/* Frees client data and notifies about client's signoff. */
  
 -  /* Pull MAC into the visible data area */
 -  if (hmac)
 -    silc_buffer_pull_tail(sock->outbuf, mac_len);
 +void silc_server_free_client_data(SilcServer server, 
 +                                SilcSocketConnection sock,
 +                                SilcClientEntry client, 
 +                                int notify,
 +                                const char *signoff)
 +{
 +  FreeClientInternal i = silc_calloc(1, sizeof(*i));
  
 -  SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len),
 -                 sock->outbuf->data, sock->outbuf->len);
 +  /* If there is pending outgoing data for the client then purge it
 +     to the network before removing the client entry. */
 +  silc_server_packet_queue_purge(server, sock);
  
 -  /* Now actually send the packet */
 -  silc_server_packet_send_real(server, sock, force_send);
 +  if (!client->id)
 +    return;
  
 -  if (packetdata.src_id)
 -    silc_free(packetdata.src_id);
 -  if (packetdata.dst_id)
 -    silc_free(packetdata.dst_id);
 +  /* Send SIGNOFF notify to routers. */
 +  if (notify && !server->standalone && server->router)
 +    silc_server_send_notify_signoff(server, server->router->connection,
 +                                  server->server_type == SILC_SERVER ?
 +                                  FALSE : TRUE, client->id, signoff);
 +    
 +  /* Remove client from all channels */
 +  if (notify)
 +    silc_server_remove_from_channels(server, NULL, client, 
 +                                   TRUE, (char *)signoff, TRUE);
 +  else
 +    silc_server_remove_from_channels(server, NULL, client, 
 +                                   FALSE, NULL, FALSE);
 +    
 +  /* Update statistics */
 +  server->stat.my_clients--;
 +  server->stat.clients--;
 +  if (server->server_type == SILC_ROUTER)
 +    server->stat.cell_clients--;
 +  SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
 +  SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
 +
 +  /* We will not delete the client entry right away. We will take it
 +     into history (for WHOWAS command) for 5 minutes */
 +  i->server = server;
 +  i->client = client;
 +  silc_schedule_task_add(server->schedule, 0, 
 +                       silc_server_free_client_data_timeout,
 +                       (void *)i, 300, 0,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 +  client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
 +  client->router = NULL;
 +  client->connection = NULL;
 +  client->mode = 0;
  }
  
 -/* 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)
 -{
 -  SilcCipher cipher = NULL;
 -  SilcHmac hmac = NULL;
 -  unsigned char *hmac_key = NULL;
 -  unsigned int hmac_key_len = 0;
 -  unsigned char mac[32];
 -  unsigned int mac_len = 0;
 +/* Frees user_data pointer from socket connection object. This also sends
 +   appropriate notify packets to the network to inform about leaving
 +   entities. */
  
 -  SILC_LOG_DEBUG(("Forwarding packet"));
 +void silc_server_free_sock_user_data(SilcServer server, 
 +                                   SilcSocketConnection sock,
 +                                   const char *signoff_message)
 +{
 +  SILC_LOG_DEBUG(("Start"));
  
 -  /* Get data used in the packet sending, keys and stuff */
 -  switch(sock->type) {
 +  switch (sock->type) {
    case SILC_SOCKET_TYPE_CLIENT:
 -    if (sock->user_data) {
 -      cipher = ((SilcClientList *)sock->user_data)->send_key;
 -      hmac = ((SilcClientList *)sock->user_data)->hmac;
 -      if (hmac) {
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
 -      hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
 -      }
 +    {
 +      SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
 +      silc_server_free_client_data(server, sock, user_data, TRUE, 
 +                                 signoff_message);
 +      break;
      }
 -    break;
    case SILC_SOCKET_TYPE_SERVER:
    case SILC_SOCKET_TYPE_ROUTER:
 -    if (sock->user_data) {
 -      cipher = ((SilcServerList *)sock->user_data)->send_key;
 -      hmac = ((SilcServerList *)sock->user_data)->hmac;
 -      if (hmac) {
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
 -      hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
 +    {
 +      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
 +      SilcServerEntry backup_router = NULL;
 +
 +      if (user_data->id)
 +      backup_router = silc_server_backup_get(server, user_data->id);
 +
 +      /* If this was our primary router connection then we're lost to
 +       the outside world. */
 +      if (server->router == user_data) {
 +      /* Check whether we have a backup router connection */
 +      if (!backup_router || backup_router == user_data) {
 +        silc_schedule_task_add(server->schedule, 0, 
 +                               silc_server_connect_to_router, 
 +                               server, 1, 0,
 +                               SILC_TASK_TIMEOUT,
 +                               SILC_TASK_PRI_NORMAL);
 +
 +        server->id_entry->router = NULL;
 +        server->router = NULL;
 +        server->standalone = TRUE;
 +        backup_router = NULL;
 +      } else {
 +        SILC_LOG_INFO(("New primary router is backup router %s",
 +                       backup_router->server_name));
 +        SILC_LOG_DEBUG(("New primary router is backup router %s",
 +                        backup_router->server_name));
 +        server->id_entry->router = backup_router;
 +        server->router = backup_router;
 +        server->router_connect = time(0);
 +        server->backup_primary = TRUE;
 +        if (server->server_type == SILC_BACKUP_ROUTER) {
 +          server->server_type = SILC_ROUTER;
 +
 +          /* We'll need to constantly try to reconnect to the primary
 +             router so that we'll see when it comes back online. */
 +          silc_server_backup_reconnect(server, sock->ip, sock->port,
 +                                       silc_server_backup_connected,
 +                                       NULL);
 +        }
 +
 +        /* Mark this connection as replaced */
 +        silc_server_backup_replaced_add(server, user_data->id, 
 +                                        backup_router);
 +      }
 +      } else if (backup_router) {
 +      SILC_LOG_INFO(("Enabling the use of backup router %s",
 +                     backup_router->server_name));
 +      SILC_LOG_DEBUG(("Enabling the use of backup router %s",
 +                      backup_router->server_name));
 +
 +      /* Mark this connection as replaced */
 +      silc_server_backup_replaced_add(server, user_data->id, 
 +                                      backup_router);
 +      }
 +
 +      if (!backup_router) {
 +      /* Free all client entries that this server owns as they will
 +         become invalid now as well. */
 +      if (user_data->id)
 +        silc_server_remove_clients_by_server(server, user_data, TRUE);
 +      if (server->server_type == SILC_SERVER)
 +        silc_server_remove_channels_by_server(server, user_data);
 +      } else {
 +      /* Update the client entries of this server to the new backup
 +         router. This also removes the clients that *really* was owned
 +         by the primary router and went down with the router.  */
 +      silc_server_update_clients_by_server(server, user_data, backup_router,
 +                                           TRUE, TRUE);
 +      silc_server_update_servers_by_server(server, user_data, backup_router);
 +      if (server->server_type == SILC_SERVER)
 +        silc_server_update_channels_by_server(server, user_data, 
 +                                              backup_router);
 +      }
 +
 +      /* Free the server entry */
 +      silc_server_backup_del(server, user_data);
 +      silc_server_backup_replaced_del(server, user_data);
 +      silc_idlist_del_data(user_data);
 +      if (!silc_idlist_del_server(server->local_list, user_data))
 +      silc_idlist_del_server(server->global_list, user_data);
 +      server->stat.my_servers--;
 +      server->stat.servers--;
 +      if (server->server_type == SILC_ROUTER)
 +      server->stat.cell_servers--;
 +
 +      if (backup_router) {
 +      /* Announce all of our stuff that was created about 5 minutes ago.
 +         The backup router knows all the other stuff already. */
 +      if (server->server_type == SILC_ROUTER)
 +        silc_server_announce_servers(server, FALSE, time(0) - 300,
 +                                     backup_router->connection);
 +
 +      /* Announce our clients and channels to the router */
 +      silc_server_announce_clients(server, time(0) - 300,
 +                                   backup_router->connection);
 +      silc_server_announce_channels(server, time(0) - 300,
 +                                    backup_router->connection);
        }
 +      break;
      }
 -    break;
    default:
 -    /* We won't forward to unknown destination - keys must exist with
 -       the destination before forwarding. */
 -    return;
 +    {
 +      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
 +
 +      silc_idlist_del_data(user_data);
 +      silc_free(user_data);
 +      break;
 +    }
    }
  
 -  /* Prepare outgoing data buffer for packet sending */
 -  silc_server_packet_send_prepare(server, sock, 0, 0, data_len);
 +  /* If any protocol is active cancel its execution */
 +  if (sock->protocol) {
 +    silc_protocol_cancel(sock->protocol, server->schedule);
 +    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +    silc_protocol_execute_final(sock->protocol, server->schedule);
 +    sock->protocol = NULL;
 +  }
  
 -  /* Mungle the packet flags and add the FORWARDED flag */
 -  if (data)
 -    data[2] |= (unsigned char)SILC_PACKET_FLAG_FORWARDED;
 +  sock->user_data = NULL;
 +}
  
 -  /* Put the data to the buffer */
 -  if (data && data_len)
 -    silc_buffer_put(sock->outbuf, data, data_len);
 +/* Removes client from all channels it has joined. This is used when client
 +   connection is disconnected. If the client on a channel is last, the
 +   channel is removed as well. This sends the SIGNOFF notify types. */
  
 -  /* Compute MAC of the packet */
 -  if (hmac) {
 -    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
 -                          hmac_key, hmac_key_len, mac);
 -    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
 -    memset(mac, 0, sizeof(mac));
 -  }
 +void silc_server_remove_from_channels(SilcServer server, 
 +                                    SilcSocketConnection sock,
 +                                    SilcClientEntry client,
 +                                    int notify,
 +                                    char *signoff_message,
 +                                    int keygen)
 +{
 +  SilcChannelEntry channel;
 +  SilcChannelClientEntry chl;
 +  SilcHashTableList htl;
 +  SilcBuffer clidp;
  
 -  /* Encrypt the packet */
 -  if (cipher)
 -    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
 +  SILC_LOG_DEBUG(("Start"));
  
 -  /* Pull MAC into the visible data area */
 -  if (hmac)
 -    silc_buffer_pull_tail(sock->outbuf, mac_len);
 +  if (!client || !client->id)
 +    return;
  
 -  SILC_LOG_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len),
 -                 sock->outbuf->data, sock->outbuf->len);
 +  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
  
 -  /* Now actually send the packet */
 -  silc_server_packet_send_real(server, sock, force_send);
 -}
 +  /* Remove the client from all channels. The client is removed from
 +     the channels' user list. */
 +  silc_hash_table_list(client->channels, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +    channel = chl->channel;
 +
 +    /* Remove channel from client's channel list */
 +    silc_hash_table_del(client->channels, channel);
 +
 +    /* Remove channel if there is no users anymore */
 +    if (server->server_type == SILC_ROUTER &&
 +      silc_hash_table_count(channel->user_list) < 2) {
 +      if (channel->rekey)
 +      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 +      if (silc_idlist_del_channel(server->local_list, channel))
 +      server->stat.my_channels--;
 +      else 
 +        silc_idlist_del_channel(server->global_list, channel);
 +      continue;
 +    }
  
 -/* This routine is used by the server to send packets to channel. The 
 -   packet sent with this function is distributed to all clients on
 -   the channel. Usually this is used to send notify messages to the
 -   channel, things like notify about new user joining to the channel. */
 +    /* Remove client from channel's client list */
 +    silc_hash_table_del(channel->user_list, chl->client);
 +
 +    /* If there is no global users on the channel anymore mark the channel
 +       as local channel. Do not check if the removed client is local client. */
 +    if (server->server_type != SILC_ROUTER && channel->global_users && 
 +      chl->client->router && !silc_server_channel_has_global(channel))
 +      channel->global_users = FALSE;
 +
 +    silc_free(chl);
 +    server->stat.my_chanclients--;
 +
 +    /* If there is not at least one local user on the channel then we don't
 +       need the channel entry anymore, we can remove it safely. */
 +    if (server->server_type != SILC_ROUTER &&
 +      !silc_server_channel_has_local(channel)) {
 +      /* Notify about leaving client if this channel has global users. */
 +      if (notify && channel->global_users)
 +      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
 +                                         SILC_NOTIFY_TYPE_SIGNOFF, 
 +                                         signoff_message ? 2 : 1,
 +                                         clidp->data, clidp->len,
 +                                         signoff_message, signoff_message ?
 +                                         strlen(signoff_message) : 0);
 +
 +      if (channel->rekey)
 +      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 +
 +      if (channel->founder_key) {
 +      /* The founder auth data exists, do not remove the channel entry */
 +      SilcChannelClientEntry chl2;
 +      SilcHashTableList htl2;
 +
 +      channel->disabled = TRUE;
 +
 +      silc_hash_table_list(channel->user_list, &htl2);
 +      while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
 +        silc_hash_table_del(chl2->client->channels, channel);
 +        silc_hash_table_del(channel->user_list, chl2->client);
 +        silc_free(chl2);
 +      }
 +      silc_hash_table_list_reset(&htl2);
 +      continue;
 +      }
  
 -void silc_server_packet_send_to_channel(SilcServer server,
 -                                      SilcChannelList *channel,
 -                                      unsigned char *data,
 -                                      unsigned int data_len,
 -                                      int force_send)
 -{
 -  int i;
 -  SilcSocketConnection sock = NULL;
 -  SilcPacketContext packetdata;
 -  SilcClientList *client = NULL;
 -  SilcServerList **routed = NULL;
 -  unsigned int routed_count = 0;
 -  unsigned char *hmac_key = NULL;
 -  unsigned int hmac_key_len = 0;
 -  unsigned char mac[32];
 -  unsigned int mac_len = 0;
 -  SilcCipher cipher;
 -  SilcHmac hmac;
 -  SilcBuffer payload;
 -
 -  SILC_LOG_DEBUG(("Sending packet to channel"));
 -
 -  /* Generate IV */
 -  for (i = 0; i < 16; i++)
 -    channel->iv[i] = silc_rng_get_byte(server->rng);
 -
 -  /* Encode the channel payload */
 -  payload = silc_channel_encode_payload(0, "", data_len, data, 
 -                                      16, channel->iv, server->rng);
 -  if (!payload)
 -    return;
 -  
 -  /* Encrypt payload of the packet. This is encrypted with the 
 -     channel key. */
 -  channel->channel_key->cipher->encrypt(channel->channel_key->context,
 -                                      payload->data, payload->data,
 -                                      payload->len - 16, /* -IV_LEN */
 -                                      channel->iv);
 -
 -  /* Set the packet context pointers. */
 -  packetdata.flags = 0;
 -  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
 -  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.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));
 -
 -  /* If there are global users in the channel we will send the message
 -     first to our router for further routing. */
 -  if (server->server_type == SILC_SERVER && !server->standalone &&
 -      channel->global_users) {
 -    SilcServerList *router;
 -
 -    /* Get data used in packet header encryption, keys and stuff. */
 -    router = server->id_entry->router;
 -    sock = (SilcSocketConnection)router->connection;
 -    cipher = router->send_key;
 -    hmac = router->hmac;
 -    mac_len = hmac->hash->hash->hash_len;
 -    hmac_key = router->hmac_key;
 -    hmac_key_len = router->hmac_key_len;
 -    
 -    SILC_LOG_DEBUG(("Sending packet to router for routing"));
 -
 -    packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
 -      packetdata.src_id_len + packetdata.dst_id_len;
 -
 -    /* Prepare outgoing data buffer for packet sending */
 -    silc_server_packet_send_prepare(server, sock, 
 -                                  SILC_PACKET_HEADER_LEN +
 -                                  packetdata.src_id_len + 
 -                                  packetdata.dst_id_len,
 -                                  packetdata.padlen,
 -                                  payload->len);
 -    packetdata.buffer = sock->outbuf;
 -
 -    /* Put the original packet into the buffer. */
 -    silc_buffer_put(sock->outbuf, payload->data, payload->len);
 -    
 -    /* Create the outgoing packet */
 -    silc_packet_assemble(&packetdata);
 -    
 -    /* Compute MAC of the packet. MAC is computed from the header,
 -       padding and the relayed packet. */
 -    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
 -                          hmac_key, hmac_key_len, mac);
 -    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
 -    memset(mac, 0, sizeof(mac));
 -
 -    /* Encrypt the header and padding of the packet. This is encrypted 
 -       with normal session key shared with the client. */
 -    silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
 -                      packetdata.src_id_len + packetdata.dst_id_len +
 -                      packetdata.padlen);
 -    
 -    /* Pull MAC into the visible data area */
 -    silc_buffer_pull_tail(sock->outbuf, mac_len);
 -    
 -    SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
 -                   sock->outbuf->data, sock->outbuf->len);
 +      /* Remove the channel entry */
 +      if (silc_idlist_del_channel(server->local_list, channel))
 +      server->stat.my_channels--;
 +      else 
 +        silc_idlist_del_channel(server->global_list, channel);
 +      continue;
 +    }
  
 -    /* Now actually send the packet */
 -    silc_server_packet_send_real(server, sock, force_send);
 +    /* Send notify to channel about client leaving SILC and thus
 +       the entire channel. */
 +    if (notify)
 +      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
 +                                       SILC_NOTIFY_TYPE_SIGNOFF, 
 +                                       signoff_message ? 2 : 1,
 +                                       clidp->data, clidp->len,
 +                                       signoff_message, signoff_message ?
 +                                       strlen(signoff_message) : 0);
 +
 +    if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
 +      /* Re-generate channel key */
 +      if (!silc_server_create_channel_key(server, channel, 0))
 +      goto out;
 +      
 +      /* Send the channel key to the channel. The key of course is not sent
 +       to the client who was removed from the channel. */
 +      silc_server_send_channel_key(server, client->connection, channel, 
 +                                 server->server_type == SILC_ROUTER ? 
 +                                 FALSE : !server->standalone);
 +    }
    }
  
 -  /* Send the message to clients on the channel's client list. */
 -  for (i = 0; i < channel->user_list_count; i++) {
 -    client = channel->user_list[i].client;
 + out:
 +  silc_hash_table_list_reset(&htl);
 +  silc_buffer_free(clidp);
 +}
  
 -    /* If client has router set it is not locally connected client and
 -       we will route the message to the router set in the client. */
 -    if (client && client->router && server->server_type == SILC_ROUTER) {
 -      int k;
 +/* 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. If `notify' is FALSE notify messages
 +   are not sent. */
  
 -      /* Check if we have sent the packet to this route already */
 -      for (k = 0; k < routed_count; k++)
 -      if (routed[k] == client->router)
 -        continue;
 +int silc_server_remove_from_one_channel(SilcServer server, 
 +                                      SilcSocketConnection sock,
 +                                      SilcChannelEntry channel,
 +                                      SilcClientEntry client,
 +                                      int notify)
 +{
 +  SilcChannelClientEntry chl;
 +  SilcBuffer clidp;
  
 -      /* Get data used in packet header encryption, keys and stuff. */
 -      sock = (SilcSocketConnection)client->router->connection;
 -      cipher = client->router->send_key;
 -      hmac = client->router->hmac;
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = client->router->hmac_key;
 -      hmac_key_len = client->router->hmac_key_len;
 -      
 -      packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
 -      packetdata.src_id_len + packetdata.dst_id_len;
 -
 -      /* Prepare outgoing data buffer for packet sending */
 -      silc_server_packet_send_prepare(server, sock, 
 -                                    SILC_PACKET_HEADER_LEN +
 -                                    packetdata.src_id_len + 
 -                                    packetdata.dst_id_len,
 -                                    packetdata.padlen,
 -                                    payload->len);
 -      packetdata.buffer = sock->outbuf;
 -
 -      /* Put the encrypted payload data into the buffer. */
 -      silc_buffer_put(sock->outbuf, payload->data, payload->len);
 -      
 -      /* Create the outgoing packet */
 -      silc_packet_assemble(&packetdata);
 -      
 -      /* Compute MAC of the packet. MAC is computed from the header,
 -       padding and the relayed packet. */
 -      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
 -                            hmac_key, hmac_key_len, mac);
 -      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
 -      memset(mac, 0, sizeof(mac));
 -
 -      /* Encrypt the header and padding of the packet. This is encrypted 
 -       with normal session key shared with the client. */
 -      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
 -                        packetdata.src_id_len + packetdata.dst_id_len +
 -                        packetdata.padlen);
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  /* Get the entry to the channel, if this client is not on the channel
 +     then return Ok. */
 +  if (!silc_hash_table_find(client->channels, channel, NULL, (void *)&chl))
 +    return TRUE;
 +
 +  /* Remove the client from the channel. The client is removed from
 +     the channel's user list. */
 +
 +  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +
 +  /* Remove channel from client's channel list */
 +  silc_hash_table_del(client->channels, chl->channel);
 +
 +  /* Remove channel if there is no users anymore */
 +  if (server->server_type == SILC_ROUTER &&
 +      silc_hash_table_count(channel->user_list) < 2) {
 +    if (channel->rekey)
 +      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 +    if (silc_idlist_del_channel(server->local_list, channel))
 +      server->stat.my_channels--;
 +    else 
 +      silc_idlist_del_channel(server->global_list, channel);
 +    silc_buffer_free(clidp);
 +    return FALSE;
 +  }
 +
 +  /* Remove client from channel's client list */
 +  silc_hash_table_del(channel->user_list, chl->client);
 +  
 +  /* If there is no global users on the channel anymore mark the channel
 +     as local channel. Do not check if the client is local client. */
 +  if (server->server_type != SILC_ROUTER && channel->global_users &&
 +      chl->client->router && !silc_server_channel_has_global(channel))
 +    channel->global_users = FALSE;
 +
 +  silc_free(chl);
 +  server->stat.my_chanclients--;
 +
 +  /* If there is not at least one local user on the channel then we don't
 +     need the channel entry anymore, we can remove it safely. */
 +  if (server->server_type != SILC_ROUTER &&
 +      !silc_server_channel_has_local(channel)) {
 +    /* Notify about leaving client if this channel has global users. */
 +    if (notify && channel->global_users)
 +      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
 +                                       SILC_NOTIFY_TYPE_LEAVE, 1,
 +                                       clidp->data, clidp->len);
 +    
 +    silc_buffer_free(clidp);
 +    
 +    if (channel->rekey)
 +      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 +
 +    if (channel->founder_key) {
 +      /* The founder auth data exists, do not remove the channel entry */
 +      SilcChannelClientEntry chl2;
 +      SilcHashTableList htl2;
        
 -      /* Pull MAC into the visible data area */
 -      silc_buffer_pull_tail(sock->outbuf, mac_len);
 +      channel->disabled = TRUE;
        
 -      SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
 -                     sock->outbuf->data, sock->outbuf->len);
 +      silc_hash_table_list(channel->user_list, &htl2);
 +      while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
 +      silc_hash_table_del(chl2->client->channels, channel);
 +      silc_hash_table_del(channel->user_list, chl2->client);
 +      silc_free(chl2);
 +      }
 +      silc_hash_table_list_reset(&htl2);
 +      return FALSE;
 +    }
  
 -      /* Now actually send the packet */
 -      silc_server_packet_send_real(server, sock, force_send);
 +    /* Remove the channel entry */
 +    if (silc_idlist_del_channel(server->local_list, channel))
 +      server->stat.my_channels--;
 +    else 
 +      silc_idlist_del_channel(server->global_list, channel);
 +    return FALSE;
 +  }
  
 -      /* We want to make sure that the packet is routed to same router
 -       only once. Mark this route as sent route. */
 -      k = routed_count;
 -      routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
 -      routed[k] = client->router;
 -      routed_count++;
 +  /* Send notify to channel about client leaving the channel */
 +  if (notify)
 +    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
 +                                     SILC_NOTIFY_TYPE_LEAVE, 1,
 +                                     clidp->data, clidp->len);
  
 -      continue;
 -    }
 +  silc_buffer_free(clidp);
 +  return TRUE;
 +}
  
 -    /* Send to locally connected client */
 -    if (client) {
 +/* Timeout callback. This is called if connection is idle or for some
 +   other reason is not responding within some period of time. This 
 +   disconnects the remote end. */
  
 -      /* XXX Check client's mode on the channel. */
 +SILC_TASK_CALLBACK(silc_server_timeout_remote)
 +{
 +  SilcServer server = (SilcServer)context;
 +  SilcSocketConnection sock = server->sockets[fd];
  
 -      /* Get data used in packet header encryption, keys and stuff. */
 -      sock = (SilcSocketConnection)client->connection;
 -      cipher = client->send_key;
 -      hmac = client->hmac;
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = client->hmac_key;
 -      hmac_key_len = client->hmac_key_len;
 -      
 -      packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
 -      packetdata.src_id_len + packetdata.dst_id_len;
 -
 -      /* Prepare outgoing data buffer for packet sending */
 -      silc_server_packet_send_prepare(server, sock, 
 -                                    SILC_PACKET_HEADER_LEN +
 -                                    packetdata.src_id_len + 
 -                                    packetdata.dst_id_len,
 -                                    packetdata.padlen,
 -                                    payload->len);
 -      packetdata.buffer = sock->outbuf;
 -
 -      /* Put the encrypted payload data into the buffer. */
 -      silc_buffer_put(sock->outbuf, payload->data, payload->len);
 -      
 -      /* Create the outgoing packet */
 -      silc_packet_assemble(&packetdata);
 -      
 -      /* Compute MAC of the packet. MAC is computed from the header,
 -       padding and the relayed packet. */
 -      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
 -                            hmac_key, hmac_key_len, mac);
 -      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
 -      memset(mac, 0, sizeof(mac));
 -
 -      /* Encrypt the header and padding of the packet. This is encrypted 
 -       with normal session key shared with the client. */
 -      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
 -                        packetdata.src_id_len + packetdata.dst_id_len +
 -                        packetdata.padlen);
 -      
 -      /* Pull MAC into the visible data area */
 -      silc_buffer_pull_tail(sock->outbuf, mac_len);
 -      
 -      SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
 -                     sock->outbuf->data, sock->outbuf->len);
 +  SILC_LOG_DEBUG(("Start"));
  
 -      /* Now actually send the packet */
 -      silc_server_packet_send_real(server, sock, force_send);
 -    }
 +  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) {
 +    silc_protocol_cancel(sock->protocol, server->schedule);
 +    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
 +    silc_protocol_execute_final(sock->protocol, server->schedule);
 +    sock->protocol = NULL;
 +    return;
    }
  
 -  if (routed_count)
 -    silc_free(routed);
 -  silc_free(packetdata.src_id);
 -  silc_free(packetdata.dst_id);
 -  silc_buffer_free(payload);
 +  if (sock->user_data)
 +    silc_server_free_sock_user_data(server, sock, NULL);
 +
 +  silc_server_disconnect_remote(server, sock, "Server closed connection: "
 +                              "Connection timeout");
  }
  
 -/* This routine is explicitly used to relay messages to some channel.
 -   Packets sent with this function we have received earlier and are
 -   totally encrypted. This just sends the packet to all clients on
 -   the channel. If the sender of the packet is someone on the channel 
 -   the message will not be sent to that client. The SILC Packet header
 -   is encrypted with the session key shared between us and the client.
 -   MAC is also computed before encrypting the header. Rest of the
 -   packet will be untouched. */
 -
 -void silc_server_packet_relay_to_channel(SilcServer server,
 -                                       SilcSocketConnection sender_sock,
 -                                       SilcChannelList *channel,
 -                                       void *sender, 
 -                                       SilcIdType sender_type,
 -                                       unsigned char *data,
 -                                       unsigned int data_len,
 -                                       int force_send)
 +/* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
 +   function may be used only by router. In real SILC network all channels
 +   are created by routers thus this function is never used by normal
 +   server. */
 +
 +SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
 +                                              SilcServerID *router_id,
 +                                              char *cipher, 
 +                                              char *hmac,
 +                                              char *channel_name,
 +                                              int broadcast)
  {
 -  int i, found = FALSE;
 -  SilcSocketConnection sock = NULL;
 -  SilcPacketContext packetdata;
 -  SilcClientList *client = NULL;
 -  SilcServerList **routed = NULL;
 -  unsigned int routed_count = 0;
 -  unsigned char *hmac_key = NULL;
 -  unsigned int hmac_key_len = 0;
 -  unsigned char mac[32];
 -  unsigned int mac_len = 0;
 -  SilcCipher cipher;
 -  SilcHmac hmac;
 -
 -  SILC_LOG_DEBUG(("Relaying packet to channel"));
 -
 -  SILC_LOG_HEXDUMP(("XXX %d", data_len), data, data_len);
 -
 -  /* Set the packet context pointers. */
 -  packetdata.flags = 0;
 -  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
 -  packetdata.src_id = silc_id_id2str(sender, sender_type);
 -  packetdata.src_id_len = silc_id_get_len(sender_type);
 -  packetdata.src_id_type = sender_type;
 -  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));
 -
 -  /* If there are global users in the channel we will send the message
 -     first to our router for further routing. */
 -  if (server->server_type == SILC_SERVER && !server->standalone &&
 -      channel->global_users) {
 -    SilcServerList *router;
 -
 -    router = server->id_entry->router;
 -
 -    /* Check that the sender is not our router. */
 -    if (sender_sock != (SilcSocketConnection)router->connection) {
 -
 -      /* Get data used in packet header encryption, keys and stuff. */
 -      sock = (SilcSocketConnection)router->connection;
 -      cipher = router->send_key;
 -      hmac = router->hmac;
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = router->hmac_key;
 -      hmac_key_len = router->hmac_key_len;
 -      
 -      SILC_LOG_DEBUG(("Sending packet to router for routing"));
 -      
 -      packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
 -      packetdata.src_id_len + packetdata.dst_id_len;
 -      
 -      /* Prepare outgoing data buffer for packet sending */
 -      silc_server_packet_send_prepare(server, sock, 
 -                                    SILC_PACKET_HEADER_LEN +
 -                                    packetdata.src_id_len + 
 -                                    packetdata.dst_id_len,
 -                                    packetdata.padlen,
 -                                    data_len);
 -      packetdata.buffer = sock->outbuf;
 -      
 -      /* Put the original packet into the buffer. */
 -      silc_buffer_put(sock->outbuf, data, data_len);
 -      
 -      /* Create the outgoing packet */
 -      silc_packet_assemble(&packetdata);
 -      
 -      /* Compute MAC of the packet. MAC is computed from the header,
 -       padding and the relayed packet. */
 -      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
 -                            hmac_key, hmac_key_len, mac);
 -      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
 -      memset(mac, 0, sizeof(mac));
 -      
 -      /* Encrypt the header and padding of the packet. This is encrypted 
 -       with normal session key shared with the client. */
 -      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
 -                        packetdata.src_id_len + packetdata.dst_id_len +
 -                        packetdata.padlen);
 -      
 -      /* Pull MAC into the visible data area */
 -      silc_buffer_pull_tail(sock->outbuf, mac_len);
 -      
 -      SILC_LOG_HEXDUMP(("Channel 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);
 -    }
 -  }
 +  SilcChannelID *channel_id;
 +  SilcChannelEntry entry;
 +  SilcCipher key;
 +  SilcHmac newhmac;
  
 -  /* Send the message to clients on the channel's client list. */
 -  for (i = 0; i < channel->user_list_count; i++) {
 -    client = channel->user_list[i].client;
 +  SILC_LOG_DEBUG(("Creating new channel"));
  
 -    if (client) {
 +  if (!cipher)
 +    cipher = SILC_DEFAULT_CIPHER;
 +  if (!hmac)
 +    hmac = SILC_DEFAULT_HMAC;
  
 -      /* If sender is one on the channel do not send it the packet. */
 -      if (!found && !SILC_ID_CLIENT_COMPARE(client->id, sender)) {
 -      found = TRUE;
 -      continue;
 -      }
 +  /* Allocate cipher */
 +  if (!silc_cipher_alloc(cipher, &key))
 +    return NULL;
  
 -      /* If the client has set router it means that it is not locally
 -       connected client and we won't send message to those in this
 -       function (they will be routed separately by the caller). */
 -      if (server->server_type == SILC_ROUTER && client->router) {
 -      int k;
 +  /* Allocate hmac */
 +  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
 +    silc_cipher_free(key);
 +    return NULL;
 +  }
  
 -      /* Sender maybe server as well so we want to make sure that
 -         we won't send the message to the server it came from. */
 -      if (!found && !SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
 -        found = TRUE;
 -        continue;
 -      }
 +  channel_name = strdup(channel_name);
  
 -      /* Check if we have sent the packet to this route already */
 -      for (k = 0; k < routed_count; k++)
 -        if (routed[k] == client->router)
 -          continue;
 -      
 -      /* Get data used in packet header encryption, keys and stuff. */
 -      sock = (SilcSocketConnection)client->router->connection;
 -      cipher = client->router->send_key;
 -      hmac = client->router->hmac;
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = client->router->hmac_key;
 -      hmac_key_len = client->router->hmac_key_len;
 -      
 -      packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
 -        packetdata.src_id_len + packetdata.dst_id_len;
 -      
 -      /* Prepare outgoing data buffer for packet sending */
 -      silc_server_packet_send_prepare(server, sock, 
 -                                      SILC_PACKET_HEADER_LEN +
 -                                      packetdata.src_id_len + 
 -                                      packetdata.dst_id_len,
 -                                      packetdata.padlen,
 -                                      data_len);
 -      packetdata.buffer = sock->outbuf;
 -      
 -      /* Put the original packet into the buffer. */
 -      silc_buffer_put(sock->outbuf, data, data_len);
 -      
 -      /* Create the outgoing packet */
 -      silc_packet_assemble(&packetdata);
 -      
 -      /* Compute MAC of the packet. MAC is computed from the header,
 -         padding and the relayed packet. */
 -      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
 -                              hmac_key, hmac_key_len, mac);
 -      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
 -      memset(mac, 0, sizeof(mac));
 -      
 -      /* Encrypt the header and padding of the packet. This is encrypted 
 -         with normal session key shared with the client. */
 -      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
 -                          packetdata.src_id_len + packetdata.dst_id_len +
 -                          packetdata.padlen);
 -      
 -      /* Pull MAC into the visible data area */
 -      silc_buffer_pull_tail(sock->outbuf, mac_len);
 -      
 -      SILC_LOG_HEXDUMP(("Packet to channel, 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);
 -      
 -      /* We want to make sure that the packet is routed to same router
 -         only once. Mark this route as sent route. */
 -      k = routed_count;
 -      routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
 -      routed[k] = client->router;
 -      routed_count++;
 -      
 -      continue;
 -      }
 -      
 -      /* XXX Check client's mode on the channel. */
 +  /* Create the channel ID */
 +  if (!silc_id_create_channel_id(server, router_id, server->rng, 
 +                               &channel_id)) {
 +    silc_free(channel_name);
 +    silc_cipher_free(key);
 +    silc_hmac_free(newhmac);
 +    return NULL;
 +  }
  
 +  /* Create the channel */
 +  entry = silc_idlist_add_channel(server->local_list, channel_name, 
 +                                SILC_CHANNEL_MODE_NONE, channel_id, 
 +                                NULL, key, newhmac, 0);
 +  if (!entry) {
 +    silc_free(channel_name);
 +    silc_cipher_free(key);
 +    silc_hmac_free(newhmac);
 +    silc_free(channel_id);
 +    return NULL;
 +  }
  
 -      /* Get data used in packet header encryption, keys and stuff. */
 -      sock = (SilcSocketConnection)client->connection;
 -      cipher = client->send_key;
 -      hmac = client->hmac;
 -      mac_len = hmac->hash->hash->hash_len;
 -      hmac_key = client->hmac_key;
 -      hmac_key_len = client->hmac_key_len;
 -      
 -      SILC_LOG_DEBUG(("Sending packet to client %s", 
 -                    sock->hostname ? sock->hostname : sock->ip));
 -
 -      packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
 -      packetdata.src_id_len + packetdata.dst_id_len;
 -
 -      /* Prepare outgoing data buffer for packet sending */
 -      silc_server_packet_send_prepare(server, sock, 
 -                                    SILC_PACKET_HEADER_LEN +
 -                                    packetdata.src_id_len + 
 -                                    packetdata.dst_id_len,
 -                                    packetdata.padlen,
 -                                    data_len);
 -      packetdata.buffer = sock->outbuf;
 -
 -      /* Put the original packet into the buffer. */
 -      silc_buffer_put(sock->outbuf, data, data_len);
 -      
 -      /* Create the outgoing packet */
 -      silc_packet_assemble(&packetdata);
 -      
 -      /* Compute MAC of the packet. MAC is computed from the header,
 -       padding and the relayed packet. */
 -      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
 -                            hmac_key, hmac_key_len, mac);
 -      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
 -      memset(mac, 0, sizeof(mac));
 -
 -      /* Encrypt the header and padding of the packet. This is encrypted 
 -       with normal session key shared with the client. */
 -      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
 -                        packetdata.src_id_len + packetdata.dst_id_len +
 -                        packetdata.padlen);
 -      
 -      /* Pull MAC into the visible data area */
 -      silc_buffer_pull_tail(sock->outbuf, mac_len);
 -      
 -      SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
 -                     sock->outbuf->data, sock->outbuf->len);
 +  entry->cipher = strdup(cipher);
 +  entry->hmac_name = strdup(hmac);
  
 -      /* Now actually send the packet */
 -      silc_server_packet_send_real(server, sock, force_send);
 -    }
 +  /* Now create the actual key material */
 +  if (!silc_server_create_channel_key(server, entry, 
 +                                    silc_cipher_get_key_len(key) / 8)) {
 +    silc_idlist_del_channel(server->local_list, entry);
 +    return NULL;
    }
  
 -  silc_free(packetdata.src_id);
 -  silc_free(packetdata.dst_id);
 -}
 +  /* Notify other routers about the new channel. We send the packet
 +     to our primary route. */
 +  if (broadcast && server->standalone == FALSE)
 +    silc_server_send_new_channel(server, server->router->connection, TRUE, 
 +                               channel_name, entry->id, 
 +                               silc_id_get_len(entry->id, SILC_ID_CHANNEL),
 +                               entry->mode);
  
 -/* Relays received command reply packet to the correct destination. The
 -   destination must be one of our locally connected client or the packet
 -   will be ignored. This is called when server has forwarded one of
 -   client's command request to router and router has now replied to the 
 -   command. */
 +  server->stat.my_channels++;
  
 -void silc_server_packet_relay_command_reply(SilcServer server,
 -                                          SilcSocketConnection sock,
 -                                          SilcPacketContext *packet)
 -{
 -  SilcBuffer buffer = packet->buffer;
 -  SilcClientList *client;
 -  SilcClientID *id;
 -  SilcSocketConnection dst_sock;
 -  unsigned char mac[32];
 -  unsigned int mac_len = 0;
 +  return entry;
 +}
  
 -  SILC_LOG_DEBUG(("Start"));
 +/* Same as above but creates the channel with Channel ID `channel_id. */
  
 -  /* 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))
 -    goto out;
 +SilcChannelEntry 
 +silc_server_create_new_channel_with_id(SilcServer server, 
 +                                     char *cipher, 
 +                                     char *hmac,
 +                                     char *channel_name,
 +                                     SilcChannelID *channel_id,
 +                                     int broadcast)
 +{
 +  SilcChannelEntry entry;
 +  SilcCipher key;
 +  SilcHmac newhmac;
  
 -  /* Destination must be client */
 -  if (packet->dst_id_type != SILC_ID_CLIENT)
 -    goto out;
 +  SILC_LOG_DEBUG(("Creating new channel"));
  
 -  /* Execute command reply locally for the command */
 -  silc_server_command_reply_process(server, sock, buffer);
 +  if (!cipher)
 +    cipher = SILC_DEFAULT_CIPHER;
 +  if (!hmac)
 +    hmac = SILC_DEFAULT_HMAC;
  
 -  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
 +  /* Allocate cipher */
 +  if (!silc_cipher_alloc(cipher, &key))
 +    return NULL;
  
 -  /* Destination must be one of ours */
 -  client = silc_idlist_find_client_by_id(server->local_list->clients, id);
 -  if (!client) {
 -    silc_free(id);
 -    goto out;
 +  /* Allocate hmac */
 +  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
 +    silc_cipher_free(key);
 +    return NULL;
    }
  
 -  /* Relay the packet to the client */
 -  if (client->hmac)
 -    mac_len = client->hmac->hash->hash->hash_len;
 +  channel_name = strdup(channel_name);
  
 -  dst_sock = (SilcSocketConnection)client->connection;
 +  /* Create the channel */
 +  entry = silc_idlist_add_channel(server->local_list, channel_name, 
 +                                SILC_CHANNEL_MODE_NONE, channel_id, 
 +                                NULL, key, newhmac, 0);
 +  if (!entry) {
 +    silc_cipher_free(key);
 +    silc_hmac_free(newhmac);
 +    silc_free(channel_name);
 +    return NULL;
 +  }
  
 -  silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 -                 + packet->dst_id_len + packet->padlen);
 -  silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
 -  silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
 -  
 -  /* Compute new HMAC */
 -  if (client->hmac) {
 -    memset(mac, 0, sizeof(mac));
 -    silc_hmac_make_with_key(client->hmac, 
 -                          dst_sock->outbuf->data, 
 -                          dst_sock->outbuf->len,
 -                          client->hmac_key, 
 -                          client->hmac_key_len, 
 -                          mac);
 -    silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
 -    memset(mac, 0, sizeof(mac));
 +  /* Now create the actual key material */
 +  if (!silc_server_create_channel_key(server, entry, 
 +                                    silc_cipher_get_key_len(key) / 8)) {
 +    silc_idlist_del_channel(server->local_list, entry);
 +    return NULL;
    }
 -    
 -  /* Encrypt */
 -  if (client && client->send_key)
 -    silc_packet_encrypt(client->send_key, dst_sock->outbuf, buffer->len);
 -    
 -  if (client->hmac)
 -    silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
 -    
 -  /* Send the packet */
 -  silc_server_packet_send_real(server, dst_sock, FALSE);
  
 -  silc_free(id);
 +  /* Notify other routers about the new channel. We send the packet
 +     to our primary route. */
 +  if (broadcast && server->standalone == FALSE)
 +    silc_server_send_new_channel(server, server->router->connection, TRUE, 
 +                               channel_name, entry->id, 
 +                               silc_id_get_len(entry->id, SILC_ID_CHANNEL),
 +                               entry->mode);
  
 - out:
 -  silc_buffer_free(buffer);
 +  server->stat.my_channels++;
 +
 +  return entry;
  }
  
 -/* Closes connection to socket connection */
 +/* Channel's key re-key timeout callback. */
  
 -void silc_server_close_connection(SilcServer server,
 -                                SilcSocketConnection sock)
 +SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
  {
 +  SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
 +  SilcServer server = (SilcServer)rekey->context;
  
 -  SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
 -
 -  /* We won't listen for this connection anymore */
 -  silc_schedule_unset_listen_fd(sock->sock);
 +  rekey->task = NULL;
  
 -  /* Unregister all tasks */
 -  silc_task_unregister_by_fd(server->io_queue, sock->sock);
 -  silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
 +  if (!silc_server_create_channel_key(server, rekey->channel, rekey->key_len))
 +    return;
  
 -  /* Close the actual connection */
 -  silc_net_close_connection(sock->sock);
 -  server->sockets[sock->sock] = NULL;
 -  silc_socket_free(sock);
 +  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
  }
  
 -/* Sends disconnect message to remote connection and disconnects the 
 -   connection. */
 +/* Generates new channel key. This is used to create the initial channel key
 +   but also to re-generate new key for channel. If `key_len' is provided
 +   it is the bytes of the key length. */
  
 -void silc_server_disconnect_remote(SilcServer server,
 -                                 SilcSocketConnection sock,
 -                                 const char *fmt, ...)
 +bool silc_server_create_channel_key(SilcServer server, 
 +                                  SilcChannelEntry channel,
 +                                  uint32 key_len)
  {
 -  va_list ap;
 -  unsigned char buf[4096];
 +  int i;
 +  unsigned char channel_key[32], hash[32];
 +  uint32 len;
  
 -  memset(buf, 0, sizeof(buf));
 -  va_start(ap, fmt);
 -  vsprintf(buf, fmt, ap);
 -  va_end(ap);
 +  SILC_LOG_DEBUG(("Generating channel key"));
  
 -  SILC_LOG_DEBUG(("Disconnecting remote host"));
 +  if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
 +    SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
 +    return TRUE;
 +  }
  
 -  /* Notify remote end that the conversation is over. The notify message
 -     is tried to be sent immediately. */
 -  silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,  
 -                        buf, strlen(buf), TRUE);
 +  if (!channel->channel_key)
 +    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key)) {
 +      channel->channel_key = NULL;
 +      return FALSE;
 +    }
  
 -  /* Mark the connection to be disconnected */
 -  SILC_SET_DISCONNECTED(sock);
 -  silc_server_close_connection(server, sock);
 +  if (key_len)
 +    len = key_len;
 +  else if (channel->key_len)
 +    len = channel->key_len / 8;
 +  else
 +    len = silc_cipher_get_key_len(channel->channel_key) / 8;
 +
 +  /* Create channel key */
 +  for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
 +  
 +  /* Set the key */
 +  silc_cipher_set_key(channel->channel_key, channel_key, len * 8);
 +
 +  /* Remove old key if exists */
 +  if (channel->key) {
 +    memset(channel->key, 0, channel->key_len / 8);
 +    silc_free(channel->key);
 +  }
 +
 +  /* Save the key */
 +  channel->key_len = len * 8;
 +  channel->key = silc_calloc(len, sizeof(*channel->key));
 +  memcpy(channel->key, channel_key, len);
 +  memset(channel_key, 0, sizeof(channel_key));
 +
 +  /* Generate HMAC key from the channel key data and set it */
 +  if (!channel->hmac)
 +    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
 +  silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, len, hash);
 +  silc_hmac_set_key(channel->hmac, hash, 
 +                  silc_hash_len(silc_hmac_get_hash(channel->hmac)));
 +  memset(hash, 0, sizeof(hash));
 +
 +  if (server->server_type == SILC_ROUTER) {
 +    if (!channel->rekey)
 +      channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
 +    channel->rekey->context = (void *)server;
 +    channel->rekey->channel = channel;
 +    channel->rekey->key_len = key_len;
 +    if (channel->rekey->task)
 +      silc_schedule_task_del(server->schedule, channel->rekey->task);
 +
 +    channel->rekey->task = 
 +      silc_schedule_task_add(server->schedule, 0, 
 +                           silc_server_channel_key_rekey,
 +                           (void *)channel->rekey, 3600, 0,
 +                           SILC_TASK_TIMEOUT,
 +                           SILC_TASK_PRI_NORMAL);
 +  }
 +
 +  return TRUE;
  }
  
 -/* Free's user_data pointer from socket connection object. As this 
 -   pointer maybe anything we wil switch here to find the corrent
 -   data type and free it the way it needs to be free'd. */
 +/* Saves the channel key found in the encoded `key_payload' buffer. This 
 +   function is used when we receive Channel Key Payload and also when we're
 +   processing JOIN command reply. Returns entry to the channel. */
  
 -void silc_server_free_sock_user_data(SilcServer server, 
 -                                   SilcSocketConnection sock)
 +SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 +                                            SilcBuffer key_payload,
 +                                            SilcChannelEntry channel)
  {
 -  SILC_LOG_DEBUG(("Start"));
 -
 -#define LCC(x) server->local_list->client_cache[(x) - 32]
 -#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
 +  SilcChannelKeyPayload payload = NULL;
 +  SilcChannelID *id = NULL;
 +  unsigned char *tmp, hash[32];
 +  uint32 tmp_len;
 +  char *cipher;
  
 -  switch(sock->type) {
 -  case SILC_SOCKET_TYPE_CLIENT:
 -    {
 -      SilcClientList *user_data = (SilcClientList *)sock->user_data;
 +  SILC_LOG_DEBUG(("Start"));
  
 -      /* Remove client from all channels */
 -      silc_server_remove_from_channels(server, sock, user_data);
 +  /* Decode channel key payload */
 +  payload = silc_channel_key_payload_parse(key_payload->data, 
 +                                         key_payload->len);
 +  if (!payload) {
 +    SILC_LOG_ERROR(("Bad channel key payload received, dropped"));
 +    channel = NULL;
 +    goto out;
 +  }
  
 -      /* Clear ID cache */
 -      if (user_data->nickname && user_data->id)
 -      silc_idcache_del_by_id(LCC(user_data->nickname[0]),
 -                             LCCC(user_data->nickname[0]),
 -                             SILC_ID_CLIENT, user_data->id);
 +  /* Get the channel entry */
 +  if (!channel) {
  
 -      /* Free the client entry and everything in it */
 -      /* XXX must take some info to history before freeing */
 -      silc_idlist_del_client(&server->local_list->clients, user_data);
 -      break;
 +    /* Get channel ID */
 +    tmp = silc_channel_key_get_id(payload, &tmp_len);
 +    id = silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL);
 +    if (!id) {
 +      channel = NULL;
 +      goto out;
      }
 -  case SILC_SOCKET_TYPE_SERVER:
 -  case SILC_SOCKET_TYPE_ROUTER:
 -    {
  
 -      break;
 -    }
 -    break;
 -  default:
 -    {
 -      SilcIDListUnknown *user_data = (SilcIDListUnknown *)sock->user_data;
 -
 -      if (user_data->send_key)
 -      silc_cipher_free(user_data->send_key);
 -      if (user_data->receive_key)
 -      silc_cipher_free(user_data->receive_key);
 -      if (user_data->pkcs)
 -      silc_pkcs_free(user_data->pkcs);
 -      if (user_data->hmac) {
 -      silc_hmac_free(user_data->hmac);
 -      memset(user_data->hmac_key, 0, user_data->hmac_key_len);
 -      silc_free(user_data->hmac_key);
 +    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
 +    if (!channel) {
 +      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
 +      if (!channel) {
 +      SILC_LOG_ERROR(("Received key for non-existent channel %s",
 +                      silc_id_render(id, SILC_ID_CHANNEL)));
 +      goto out;
        }
 -      silc_free(user_data);
 -      break;
      }
    }
  
 -  sock->user_data = NULL;
 -#undef LCC
 -#undef LCCC
 +  tmp = silc_channel_key_get_key(payload, &tmp_len);
 +  if (!tmp) {
 +    channel = NULL;
 +    goto out;
 +  }
 +
 +  cipher = silc_channel_key_get_cipher(payload, NULL);
 +  if (!cipher) {
 +    channel = NULL;
 +    goto out;
 +  }
 +
 +  /* Remove old key if exists */
 +  if (channel->key) {
 +    memset(channel->key, 0, channel->key_len / 8);
 +    silc_free(channel->key);
 +    silc_cipher_free(channel->channel_key);
 +  }
 +
 +  /* Create new cipher */
 +  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
 +    channel->channel_key = NULL;
 +    channel = NULL;
 +    goto out;
 +  }
 +
 +  if (channel->cipher)
 +    silc_free(channel->cipher);
 +  channel->cipher = strdup(cipher);
 +
 +  /* Save the key */
 +  channel->key_len = tmp_len * 8;
 +  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
 +  memcpy(channel->key, tmp, tmp_len);
 +  silc_cipher_set_key(channel->channel_key, tmp, channel->key_len);
 +
 +  /* Generate HMAC key from the channel key data and set it */
 +  if (!channel->hmac)
 +    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
 +  silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash);
 +  silc_hmac_set_key(channel->hmac, hash, 
 +                  silc_hash_len(silc_hmac_get_hash(channel->hmac)));
 +
 +  memset(hash, 0, sizeof(hash));
 +  memset(tmp, 0, tmp_len);
 +
 +  if (server->server_type == SILC_ROUTER) {
 +    if (!channel->rekey)
 +      channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
 +    channel->rekey->context = (void *)server;
 +    channel->rekey->channel = channel;
 +    if (channel->rekey->task)
 +      silc_schedule_task_del(server->schedule, channel->rekey->task);
 +
 +    channel->rekey->task = 
 +      silc_schedule_task_add(server->schedule, 0, 
 +                           silc_server_channel_key_rekey,
 +                           (void *)channel->rekey, 3600, 0,
 +                           SILC_TASK_TIMEOUT,
 +                           SILC_TASK_PRI_NORMAL);
 +  }
 +
 + out:
 +  silc_free(id);
 +  if (payload)
 +    silc_channel_key_payload_free(payload);
 +
 +  return channel;
  }
  
 -/* Removes client from all channels it has joined. This is used when
 -   client connection is disconnected. If the client on a channel
 -   is last, the channel is removed as well. */
 +/* Heartbeat callback. This function is set as argument for the
 +   silc_socket_set_heartbeat function. The library will call this function
 +   at the set time interval. */
  
 -void silc_server_remove_from_channels(SilcServer server, 
 -                                    SilcSocketConnection sock,
 -                                    SilcClientList *client)
 +void silc_server_perform_heartbeat(SilcSocketConnection sock,
 +                                 void *hb_context)
  {
 -  int i, k;
 -  SilcChannelList *channel;
 +  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
  
 -#define LCC(x) server->local_list->channel_cache[(x) - 32]
 -#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
 +  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip));
  
 -  /* Remove the client from all channels. The client is removed from
 -     the channels' user list. */
 -  for (i = 0; i < client->channel_count; i++) {
 -    channel = client->channel[i];
 -    if (!channel)
 -      continue;
 +  /* Send the heartbeat */
 +  silc_server_send_heartbeat(hb->server, sock);
 +}
  
 -    /* Remove from channel */
 -    for (k = 0; k < channel->user_list_count; k++) {
 -      if (channel->user_list[k].client == client) {
 -
 -      /* If this client is last one on the channel the channel
 -         is removed all together. */
 -      if (channel->user_list_count == 1) {
 -        silc_idcache_del_by_id(LCC(channel->channel_name[0]),
 -                               LCCC(channel->channel_name[0]),
 -                               SILC_ID_CHANNEL, channel->id);
 -        silc_idlist_del_channel(&server->local_list->channels, channel);
 -        break;
 +/* Returns assembled of all servers in the given ID list. The packet's
 +   form is dictated by the New ID payload. */
 +
 +static void silc_server_announce_get_servers(SilcServer server,
 +                                           SilcServerEntry remote,
 +                                           SilcIDList id_list,
 +                                           SilcBuffer *servers,
 +                                           unsigned long creation_time)
 +{
 +  SilcIDCacheList list;
 +  SilcIDCacheEntry id_cache;
 +  SilcServerEntry entry;
 +  SilcBuffer idp;
 +
 +  /* Go through all clients in the list */
 +  if (silc_idcache_get_all(id_list->servers, &list)) {
 +    if (silc_idcache_list_first(list, &id_cache)) {
 +      while (id_cache) {
 +      entry = (SilcServerEntry)id_cache->context;
 +
 +      /* Do not announce the one we've sending our announcements and
 +         do not announce ourself. Also check the creation time if it's
 +         provided. */
 +      if ((entry == remote) || (entry == server->id_entry) ||
 +          (creation_time && entry->data.created < creation_time)) {
 +        if (!silc_idcache_list_next(list, &id_cache))
 +          break;
 +        continue;
        }
  
 -      channel->user_list[k].client = NULL;
 -      channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
 +      idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
  
 -      /* XXX */
 -      /* Send notify to channel about client leaving SILC and thus
 -         the entire channel. */
 -      silc_server_send_notify_to_channel(server, channel,
 -                                         "%s has left channel %s",
 -                                         client->nickname,
 -                                         channel->channel_name);
 +      *servers = silc_buffer_realloc(*servers, 
 +                                     (*servers ? 
 +                                      (*servers)->truelen + idp->len : 
 +                                      idp->len));
 +      silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
 +      silc_buffer_put(*servers, idp->data, idp->len);
 +      silc_buffer_pull(*servers, idp->len);
 +      silc_buffer_free(idp);
 +
 +      if (!silc_idcache_list_next(list, &id_cache))
 +        break;
        }
      }
 -  }
  
 -  if (client->channel_count)
 -    silc_free(client->channel);
 -  client->channel = NULL;
 -#undef LCC
 -#undef LCCC
 +    silc_idcache_list_free(list);
 +  }
  }
  
 -/* Timeout callback. This is called if connection is idle or for some
 -   other reason is not responding within some period of time. This 
 -   disconnects the remote end. */
 -
 -SILC_TASK_CALLBACK(silc_server_timeout_remote)
 +static SilcBuffer 
 +silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
  {
 -  SilcServer server = (SilcServer)context;
 -  SilcSocketConnection sock = server->sockets[fd];
 +  va_list ap;
 +  SilcBuffer p;
  
 -  silc_server_disconnect_remote(server, sock, 
 -                              "Server closed connection: "
 -                              "Connection timeout");
 +  va_start(ap, argc);
 +  p = silc_notify_payload_encode(notify, argc, ap);
 +  va_end(ap);
 + 
 +  return p;
  }
  
 -/* Internal routine used to send (relay, route) private messages to some
 -   destination. This is used to by normal server to send the message to
 -   its primary route and router uses this to send it to any route it
 -   wants. If the private message key does not exist then the message
 -   is re-encrypted, otherwise we just pass it along. */
 -static void 
 -silc_server_private_message_send_internal(SilcServer server,
 -                                        SilcSocketConnection dst_sock,
 -                                        SilcServerList *router,
 -                                        SilcPacketContext *packet)
 +/* This function is used by router to announce existing servers to our
 +   primary router when we've connected to it. If `creation_time' is non-zero
 +   then only the servers that has been created after the `creation_time'
 +   will be announced. */
 +
 +void silc_server_announce_servers(SilcServer server, bool global,
 +                                unsigned long creation_time,
 +                                SilcSocketConnection remote)
  {
 -  SilcBuffer buffer = packet->buffer;
 +  SilcBuffer servers = NULL;
 +
 +  SILC_LOG_DEBUG(("Announcing servers"));
 +
 +  /* Get servers in local list */
 +  silc_server_announce_get_servers(server, remote->user_data,
 +                                 server->local_list, &servers,
 +                                 creation_time);
 +
 +  if (global)
 +    /* Get servers in global list */
 +    silc_server_announce_get_servers(server, remote->user_data,
 +                                   server->global_list, &servers,
 +                                   creation_time);
 +
 +  if (servers) {
 +    silc_buffer_push(servers, servers->data - servers->head);
 +    SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
  
 -  /* Send and re-encrypt if private messge key does not exist */
 -  if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
 -    unsigned char mac[32];
 -    unsigned int mac_len = 0;
 -    
 -    if (router->hmac)
 -      mac_len = router->hmac->hash->hash->hash_len;
 -    
 -    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 -                   + packet->dst_id_len + packet->padlen);
 -    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
 -    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
 -    
 -    /* Compute new HMAC */
 -    if (router->hmac) {
 -      mac_len = router->hmac->hash->hash->hash_len;
 -      memset(mac, 0, sizeof(mac));
 -      silc_hmac_make_with_key(router->hmac, 
 -                            dst_sock->outbuf->data, 
 -                            dst_sock->outbuf->len,
 -                            router->hmac_key, 
 -                            router->hmac_key_len, 
 -                            mac);
 -      silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
 -      memset(mac, 0, sizeof(mac));
 -    }
 -    
 -    silc_packet_encrypt(router->send_key, dst_sock->outbuf, buffer->len);
 -    
 -    if (router->hmac)
 -      silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
 -    
      /* Send the packet */
 -    silc_server_packet_send_real(server, dst_sock, FALSE);
 +    silc_server_packet_send(server, remote,
 +                          SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
 +                          servers->data, servers->len, TRUE);
  
 -  } else {
 -    /* Key exist so just send it */
 -    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 -                   + packet->dst_id_len + packet->padlen);
 -    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
 -    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
 -    silc_server_packet_send_real(server, dst_sock, FALSE);
 +    silc_buffer_free(servers);
    }
  }
  
 -/* Internal routine to send the received private message packet to
 -   our locally connected client. */
 -static void
 -silc_server_private_message_send_local(SilcServer server,
 -                                     SilcSocketConnection dst_sock,
 -                                     SilcClientList *client,
 -                                     SilcPacketContext *packet)
 -{
 -  SilcBuffer buffer = packet->buffer;
 +/* Returns assembled packet of all clients in the given ID list. The
 +   packet's form is dictated by the New ID Payload. */
  
 -  /* Re-encrypt packet if needed */
 -  if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
 -    unsigned char mac[32];
 -    unsigned int mac_len = 0;
 +static void silc_server_announce_get_clients(SilcServer server,
 +                                           SilcIDList id_list,
 +                                           SilcBuffer *clients,
 +                                           SilcBuffer *umodes,
 +                                           unsigned long creation_time)
 +{
 +  SilcIDCacheList list;
 +  SilcIDCacheEntry id_cache;
 +  SilcClientEntry client;
 +  SilcBuffer idp;
 +  SilcBuffer tmp;
 +  unsigned char mode[4];
 +
 +  /* Go through all clients in the list */
 +  if (silc_idcache_get_all(id_list->clients, &list)) {
 +    if (silc_idcache_list_first(list, &id_cache)) {
 +      while (id_cache) {
 +      client = (SilcClientEntry)id_cache->context;
 +
 +      if (creation_time && client->data.created < creation_time) {
 +        if (!silc_idcache_list_next(list, &id_cache))
 +          break;
 +        continue;
 +      }
  
 -    if (client->hmac)
 -      mac_len = client->hmac->hash->hash->hash_len;
 -    
 -    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 -                   + packet->dst_id_len + packet->padlen);
 -    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
 -    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
 -    
 -    /* Compute new HMAC */
 -    if (client->hmac) {
 -      memset(mac, 0, sizeof(mac));
 -      silc_hmac_make_with_key(client->hmac, 
 -                            dst_sock->outbuf->data, 
 -                            dst_sock->outbuf->len,
 -                            client->hmac_key, 
 -                            client->hmac_key_len, 
 -                            mac);
 -      silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
 -      memset(mac, 0, sizeof(mac));
 +      idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 +
 +      *clients = silc_buffer_realloc(*clients, 
 +                                     (*clients ? 
 +                                      (*clients)->truelen + idp->len : 
 +                                      idp->len));
 +      silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
 +      silc_buffer_put(*clients, idp->data, idp->len);
 +      silc_buffer_pull(*clients, idp->len);
 +
 +      SILC_PUT32_MSB(client->mode, mode);
 +      tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
 +                                               2, idp->data, idp->len,
 +                                               mode, 4);
 +      *umodes = silc_buffer_realloc(*umodes, 
 +                                    (*umodes ? 
 +                                     (*umodes)->truelen + tmp->len :  
 +                                     tmp->len));
 +      silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
 +      silc_buffer_put(*umodes, tmp->data, tmp->len);
 +      silc_buffer_pull(*umodes, tmp->len);
 +      silc_buffer_free(tmp);
 +
 +      silc_buffer_free(idp);
 +
 +      if (!silc_idcache_list_next(list, &id_cache))
 +        break;
 +      }
      }
 -    
 -    /* Encrypt */
 -    if (client && client->send_key)
 -      silc_packet_encrypt(client->send_key, dst_sock->outbuf, 
 -                        buffer->len);
 -    
 -    if (client->hmac)
 -      silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
 -    
 -    /* Send the packet */
 -    silc_server_packet_send_real(server, dst_sock, FALSE);
 -  } else {
 -    /* Key exist so just send it */
 -    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
 -                   + packet->dst_id_len + packet->padlen);
 -    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
 -    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
 -    silc_server_packet_send_real(server, dst_sock, FALSE);
 +
 +    silc_idcache_list_free(list);
    }
  }
  
 -/* Received private message. This resolves the destination of the message 
 -   and sends the packet. This is used by both server and router.  If the
 -   destination is our locally connected client this sends the packet to
 -   the client. This may also send the message for further routing if
 -   the destination is not in our server (or router). */
 +/* This function is used to announce our existing clients to our router
 +   when we've connected to it. If `creation_time' is non-zero then only
 +   the clients that has been created after the `creation_time' will be
 +   announced. */
  
 -void silc_server_private_message(SilcServer server,
 -                               SilcSocketConnection sock,
 -                               SilcPacketContext *packet)
 +void silc_server_announce_clients(SilcServer server,
 +                                unsigned long creation_time,
 +                                SilcSocketConnection remote)
  {
 -  SilcBuffer buffer = packet->buffer;
 -  SilcClientID *id;
 -  SilcServerList *router;
 -  SilcSocketConnection dst_sock;
 -  SilcClientList *client;
 +  SilcBuffer clients = NULL;
 +  SilcBuffer umodes = NULL;
  
 -  SILC_LOG_DEBUG(("Start"));
 +  SILC_LOG_DEBUG(("Announcing clients"));
  
 -  if (!packet->dst_id) {
 -    SILC_LOG_DEBUG(("Bad Client ID in private message packet"));
 -    goto err;
 -  }
 +  /* Get clients in local list */
 +  silc_server_announce_get_clients(server, server->local_list,
 +                                 &clients, &umodes, creation_time);
  
 -  /* Decode destination Client ID */
 -  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
 -  if (!id) {
 -    SILC_LOG_DEBUG(("Could not decode destination Client ID"));
 -    goto err;
 -  }
 +  /* As router we announce our global list as well */
 +  if (server->server_type == SILC_ROUTER)
 +    silc_server_announce_get_clients(server, server->global_list,
 +                                   &clients, &umodes, creation_time);
  
 -  /* If the destination belongs to our server we don't have to route
 -     the message anywhere but to send it to the local destination. */
 -  /* XXX: Should use local cache to search but the current idcache system
 -     is so sucky that it cannot be used... it MUST be rewritten! Using
 -     this search is probably faster than if we'd use here the current
 -     idcache system. */
 -  client =  silc_idlist_find_client_by_id(server->local_list->clients, id);
 -  if (client) {
 -    /* It exists, now deliver the message to the destination */
 -    dst_sock = (SilcSocketConnection)client->connection;
 +  if (clients) {
 +    silc_buffer_push(clients, clients->data - clients->head);
 +    SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
  
 -    /* 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) {
 -      silc_server_private_message_send_internal(server, dst_sock,
 -                                              client->router, packet);
 -      goto out;
 -    }
 +    /* Send the packet */
 +    silc_server_packet_send(server, remote,
 +                          SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
 +                          clients->data, clients->len, TRUE);
  
 -    /* Seems that client really is directly connected to us */
 -    silc_server_private_message_send_local(server, dst_sock, client, packet);
 -    goto out;
 +    silc_buffer_free(clients);
    }
  
 -  /* Destination belongs to someone not in this server. If we are normal
 -     server our action is to send the packet to our router. */
 -  if (server->server_type == SILC_SERVER && !server->standalone) {
 -    router = server->id_entry->router;
 -    dst_sock = (SilcSocketConnection)router->connection;
 +  if (umodes) {
 +    silc_buffer_push(umodes, umodes->data - umodes->head);
 +    SILC_LOG_HEXDUMP(("umodes"), umodes->data, umodes->len);
  
 -    /* Send to primary route */
 -    silc_server_private_message_send_internal(server, dst_sock, router,
 -                                            packet);
 -    goto out;
 -  }
 +    /* Send the packet */
 +    silc_server_packet_send(server, remote,
 +                          SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
 +                          umodes->data, umodes->len, TRUE);
  
 -  /* We are router and we will perform route lookup for the destination 
 -     and send the message to the correct route. */
 -  if (server->server_type == SILC_ROUTER && !server->standalone) {
 +    silc_buffer_free(umodes);
 +  }
 +}
  
 -    /* If we don't have specific route for the destination we will send
 -       it to our primary route (default route). */
 -    router = silc_server_route_check(id->ip.s_addr, server->id->port);
 -    if (router) {
 -      dst_sock = (SilcSocketConnection)router->connection;
 -    } else {
 -      router = server->id_entry->router;
 -      dst_sock = (SilcSocketConnection)router->connection;
 -    }
 +/* Returns channel's topic for announcing it */
  
 -    /* Send packet */
 -    silc_server_private_message_send_internal(server, dst_sock, 
 -                                            router, packet);
 -    goto out;
 +void silc_server_announce_get_channel_topic(SilcServer server,
 +                                          SilcChannelEntry channel,
 +                                          SilcBuffer *topic)
 +{
 +  SilcBuffer chidp;
 +
 +  if (channel->topic) {
 +    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 +    *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
 +                                              chidp->data, chidp->len,
 +                                              channel->topic, 
 +                                              strlen(channel->topic));
 +    silc_buffer_free(chidp);
    }
 -
 - err:
 -  silc_server_send_error(server, sock, 
 -                       "No such nickname: Private message not sent");
 - out:
 -  silc_buffer_free(buffer);
  }
  
 -SilcChannelList *silc_find_channel(SilcServer server, SilcChannelID *id)
 +/* Returns assembled packets for channel users of the `channel'. */
 +
 +void silc_server_announce_get_channel_users(SilcServer server,
 +                                          SilcChannelEntry channel,
 +                                          SilcBuffer *channel_users,
 +                                          SilcBuffer *channel_users_modes)
  {
 -  int i;
 -  SilcIDCache *id_cache;
 +  SilcChannelClientEntry chl;
 +  SilcHashTableList htl;
 +  SilcBuffer chidp, clidp;
 +  SilcBuffer tmp;
 +  int len;
 +  unsigned char mode[4];
  
 -#define LCC(x) server->local_list->channel_cache[(x)]
 -#define LCCC(x) server->local_list->channel_cache_count[(x)]
 +  SILC_LOG_DEBUG(("Start"));
  
 -  for (i = 0; i < 96; i++) {
 -    if (LCC(i) == NULL)
 -      continue;
 -    if (silc_idcache_find_by_id(LCC(i), LCCC(i), (void *)id, 
 -                              SILC_ID_CHANNEL, &id_cache))
 -      return (SilcChannelList *)id_cache->context;
 +  /* Now find all users on the channel */
 +  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 +  silc_hash_table_list(channel->user_list, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +    clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
 +
 +    /* JOIN Notify */
 +    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_JOIN, 2, 
 +                                           clidp->data, clidp->len,
 +                                           chidp->data, chidp->len);
 +    len = tmp->len;
 +    *channel_users = 
 +      silc_buffer_realloc(*channel_users, 
 +                        (*channel_users ? 
 +                         (*channel_users)->truelen + len : len));
 +    silc_buffer_pull_tail(*channel_users, 
 +                        ((*channel_users)->end - 
 +                         (*channel_users)->data));
 +    
 +    silc_buffer_put(*channel_users, tmp->data, tmp->len);
 +    silc_buffer_pull(*channel_users, len);
 +    silc_buffer_free(tmp);
 +
 +    /* CUMODE notify for mode change on the channel */
 +    SILC_PUT32_MSB(chl->mode, mode);
 +    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE, 
 +                                           3, clidp->data, clidp->len,
 +                                           mode, 4,
 +                                           clidp->data, clidp->len);
 +    len = tmp->len;
 +    *channel_users_modes = 
 +      silc_buffer_realloc(*channel_users_modes, 
 +                        (*channel_users_modes ? 
 +                         (*channel_users_modes)->truelen + len : len));
 +    silc_buffer_pull_tail(*channel_users_modes, 
 +                        ((*channel_users_modes)->end - 
 +                         (*channel_users_modes)->data));
 +    
 +    silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
 +    silc_buffer_pull(*channel_users_modes, len);
 +    silc_buffer_free(tmp);
 +
 +    silc_buffer_free(clidp);
    }
 -  
 -  return NULL;
 -#undef LCC
 -#undef LCCC
 +  silc_hash_table_list_reset(&htl);
 +  silc_buffer_free(chidp);
  }
  
 -/* Process received channel message. */
 -
 -void silc_server_channel_message(SilcServer server,
 -                               SilcSocketConnection sock,
 -                               SilcPacketContext *packet)
 +/* Returns assembled packets for all channels and users on those channels
 +   from the given ID List. The packets are in the form dictated by the
 +   New Channel and New Channel User payloads. */
 +
 +void silc_server_announce_get_channels(SilcServer server,
 +                                     SilcIDList id_list,
 +                                     SilcBuffer *channels,
 +                                     SilcBuffer *channel_users,
 +                                     SilcBuffer **channel_users_modes,
 +                                     uint32 *channel_users_modes_c,
 +                                     SilcBuffer **channel_topics,
 +                                     SilcChannelID ***channel_ids,
 +                                     unsigned long creation_time)
  {
 -  SilcChannelList *channel = NULL;
 -  SilcChannelID *id = NULL;
 -  SilcClientID *sender;
 -  SilcBuffer buffer = packet->buffer;
 +  SilcIDCacheList list;
 +  SilcIDCacheEntry id_cache;
 +  SilcChannelEntry channel;
 +  unsigned char *cid;
 +  uint32 id_len;
 +  uint16 name_len;
 +  int len;
 +  int i = *channel_users_modes_c;
 +  bool announce;
  
 -  SILC_LOG_DEBUG(("Processing channel message"));
 -  
 -  /* Check MAC */
 -  if (!silc_server_packet_check_mac(server, sock, buffer))
 -    goto out;
 +  SILC_LOG_DEBUG(("Start"));
  
 -  /* Sanity checks */
 -  if (packet->dst_id_type != SILC_ID_CHANNEL) {
 -    SILC_LOG_ERROR(("Received bad message for channel, dropped"));
 -    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
 -    goto out;
 -  }
 +  /* Go through all channels in the list */
 +  if (silc_idcache_get_all(id_list->channels, &list)) {
 +    if (silc_idcache_list_first(list, &id_cache)) {
 +      while (id_cache) {
 +      channel = (SilcChannelEntry)id_cache->context;
 +
 +      if (creation_time && channel->created < creation_time)
 +        announce = FALSE;
 +      else
 +        announce = TRUE;
 +
 +      cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 +      id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
 +      name_len = strlen(channel->channel_name);
 +
 +      if (announce) {
 +        len = 4 + name_len + id_len + 4;
 +        *channels = 
 +          silc_buffer_realloc(*channels, 
 +                              (*channels ? (*channels)->truelen + 
 +                               len : len));
 +        silc_buffer_pull_tail(*channels, 
 +                              ((*channels)->end - (*channels)->data));
 +        silc_buffer_format(*channels,
 +                           SILC_STR_UI_SHORT(name_len),
 +                           SILC_STR_UI_XNSTRING(channel->channel_name, 
 +                                                name_len),
 +                           SILC_STR_UI_SHORT(id_len),
 +                           SILC_STR_UI_XNSTRING(cid, id_len),
 +                           SILC_STR_UI_INT(channel->mode),
 +                           SILC_STR_END);
 +        silc_buffer_pull(*channels, len);
 +      }
  
 -  /* Send to local clients */
 -  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
 -  channel = silc_find_channel(server, id);
 -  if (!channel) {
 -    SILC_LOG_DEBUG(("Could not find channel"));
 -    goto out;
 -  }
 +      /* Channel user modes */
 +      *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)[i]);
 +      (*channel_ids)[i] = channel->id;
 +
 +      /* Channel's topic */
 +      *channel_topics = silc_realloc(*channel_topics,
 +                                     sizeof(**channel_topics) * (i + 1));
 +      (*channel_topics)[i] = NULL;
 +      silc_server_announce_get_channel_topic(server, channel,
 +                                             &(*channel_topics)[i]);
 +      i++;
 +
 +      if (!silc_idcache_list_next(list, &id_cache))
 +        break;
 +      }
  
 -  /* Distribute the packet to our local clients. This will send the
 -     packet for further routing as well, if needed. */
 -  sender = silc_id_str2id(packet->src_id, packet->src_id_type);
 -  silc_server_packet_relay_to_channel(server, sock, channel, sender,
 -                                    packet->src_id_type,
 -                                    packet->buffer->data,
 -                                    packet->buffer->len, FALSE);
 +      *channel_users_modes_c += i;
 +    }
  
 - out:
 -  silc_buffer_free(buffer);
 +    silc_idcache_list_free(list);
 +  }
  }
  
 -/* Received channel key packet. We distribute the key to all of our locally
 -   connected clients on the channel. Router ignores the packet. */
 +/* This function is used to announce our existing channels to our router
 +   when we've connected to it. This also announces the users on the
 +   channels to the router. If the `creation_time' is non-zero only the
 +   channels that was created after the `creation_time' are announced.
 +   Note that the channel users are still announced even if the `creation_time'
 +   was provided. */
  
 -void silc_server_channel_key(SilcServer server,
 -                           SilcSocketConnection sock,
 -                           SilcPacketContext *packet)
 +void silc_server_announce_channels(SilcServer server,
 +                                 unsigned long creation_time,
 +                                 SilcSocketConnection remote)
  {
 -  SilcBuffer buffer = packet->buffer;
 -  SilcChannelKeyPayload payload = NULL;
 -  SilcChannelID *id = NULL;
 -  SilcChannelList *channel;
 -  SilcClientList *client;
 -  unsigned char *key;
 -  unsigned int key_len;
 -  char *cipher;
 -  int i;
 -
 -  if (server->server_type == SILC_ROUTER)
 -    goto out;
 +  SilcBuffer channels = NULL, channel_users = NULL;
 +  SilcBuffer *channel_users_modes = NULL;
 +  SilcBuffer *channel_topics = 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_c,
 +                                  &channel_topics,
 +                                  &channel_ids, creation_time);
 +
 +  /* Get channels and channel users in global list */
 +  if (server->server_type != SILC_SERVER)
 +    silc_server_announce_get_channels(server, server->global_list,
 +                                    &channels, &channel_users,
 +                                    &channel_users_modes,
 +                                    &channel_users_modes_c,
 +                                    &channel_topics,
 +                                    &channel_ids, creation_time);
 +
 +  if (channels) {
 +    silc_buffer_push(channels, channels->data - channels->head);
 +    SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
  
 -  if (packet->src_id_type != SILC_ID_SERVER &&
 -      sock->type != SILC_SOCKET_TYPE_ROUTER)
 -    goto out;
 +    /* Send the packet */
 +    silc_server_packet_send(server, remote,
 +                          SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
 +                          channels->data, channels->len,
 +                          FALSE);
  
 -  /* Decode channel key payload */
 -  payload = silc_channel_key_parse_payload(buffer);
 -  if (!payload) {
 -    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
 -    SILC_LOG_DEBUG(("Bad channel key payload, dropped"));
 +    silc_buffer_free(channels);
    }
  
 -  /* Get channel ID */
 -  id = silc_id_str2id(silc_channel_key_get_id(payload, NULL), SILC_ID_CHANNEL);
 -  if (!id)
 -    goto out;
 +  if (channel_users) {
 +    silc_buffer_push(channel_users, channel_users->data - channel_users->head);
 +    SILC_LOG_HEXDUMP(("channel users"), channel_users->data, 
 +                   channel_users->len);
  
 -  /* Get the channel entry */
 -  channel = silc_idlist_find_channel_by_id(server->local_list->channels, id);
 -  if (!channel) {
 -    SILC_LOG_ERROR(("Received key for non-existent channel"));
 -    SILC_LOG_DEBUG(("Received key for non-existent channel"));
 -    goto out;
 +    /* Send the packet */
 +    silc_server_packet_send(server, remote,
 +                          SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
 +                          channel_users->data, channel_users->len,
 +                          FALSE);
 +
 +    silc_buffer_free(channel_users);
    }
  
 -  /* Save the key for us as well */
 -  key = silc_channel_key_get_key(payload, &key_len);
 -  if (!key)
 -    goto out;
 -  cipher = silc_channel_key_get_cipher(payload, NULL);;
 -  if (!cipher)
 -    goto out;
 -  channel->key_len = key_len;
 -  channel->key = silc_calloc(key_len, sizeof(unsigned char));
 -  memcpy(channel->key, key, key_len);
 -  silc_cipher_alloc(cipher, &channel->channel_key);
 -  channel->channel_key->cipher->set_key(channel->channel_key->context, 
 -                                      key, key_len);
 +  if (channel_users_modes) {
 +    int i;
 +
 +    for (i = 0; i < channel_users_modes_c; i++) {
 +      if (!channel_users_modes[i])
 +        continue;
 +      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, remote,
 +                                 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);
 +  }
 +
 +  if (channel_topics) {
 +    int i;
  
 -  /* Distribute the key to all clients on the channel */
 -  for (i = 0; i < channel->user_list_count; i++) {
 -    client = channel->user_list[i].client;
 +    for (i = 0; i < channel_users_modes_c; i++) {
 +      if (!channel_topics[i])
 +      continue;
  
 -    if (client)
 -      silc_server_packet_send_dest(server, client->connection,
 -                                 SILC_PACKET_CHANNEL_KEY, 0,
 -                                 client->id, SILC_ID_CLIENT,
 -                                 buffer->data, buffer->len, FALSE);
 +      silc_buffer_push(channel_topics[i], 
 +                     channel_topics[i]->data - 
 +                     channel_topics[i]->head);
 +      SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data, 
 +                     channel_topics[i]->len);
 +      silc_server_packet_send_dest(server, remote,
 +                                 SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
 +                                 channel_ids[i], SILC_ID_CHANNEL,
 +                                 channel_topics[i]->data, 
 +                                 channel_topics[i]->len,
 +                                 FALSE);
 +      silc_buffer_free(channel_topics[i]);
 +    }
 +    silc_free(channel_topics);
    }
  
 - out:
 -  if (id)
 -    silc_free(id);
 -  if (payload)
 -    silc_channel_key_free_payload(payload);
 -  silc_buffer_free(buffer);
 +  silc_free(channel_ids);
  }
  
 -/* Sends error message. Error messages may or may not have any 
 -   implications. */
 +/* Failure timeout callback. If this is called then we will immediately
 +   process the received failure. We always process the failure with timeout
 +   since we do not want to blindly trust to received failure packets. 
 +   This won't be called (the timeout is cancelled) if the failure was
 +   bogus (it is bogus if remote does not close the connection after sending
 +   the failure). */
  
 -void silc_server_send_error(SilcServer server,
 -                          SilcSocketConnection sock,
 -                          const char *fmt, ...)
 +SILC_TASK_CALLBACK(silc_server_failure_callback)
  {
 -  va_list ap;
 -  unsigned char buf[4096];
 +  SilcServerFailureContext f = (SilcServerFailureContext)context;
  
 -  memset(buf, 0, sizeof(buf));
 -  va_start(ap, fmt);
 -  vsprintf(buf, fmt, ap);
 -  va_end(ap);
 +  if (f->sock->protocol) {
 +    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
 +    silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
 +  }
  
 -  silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
 -                        buf, strlen(buf), FALSE);
 +  silc_free(f);
  }
  
 -/* Sends notify message */
 +/* Assembles user list and users mode list from the `channel'. */
  
 -void silc_server_send_notify(SilcServer server,
 -                           SilcSocketConnection sock,
 -                           const char *fmt, ...)
 +void silc_server_get_users_on_channel(SilcServer server,
 +                                    SilcChannelEntry channel,
 +                                    SilcBuffer *user_list,
 +                                    SilcBuffer *mode_list,
 +                                    uint32 *user_count)
  {
 -  va_list ap;
 -  unsigned char buf[4096];
 -
 -  memset(buf, 0, sizeof(buf));
 -  va_start(ap, fmt);
 -  vsprintf(buf, fmt, ap);
 -  va_end(ap);
 -
 -  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0, 
 -                        buf, strlen(buf), FALSE);
 +  SilcChannelClientEntry chl;
 +  SilcHashTableList htl;
 +  SilcBuffer client_id_list;
 +  SilcBuffer client_mode_list;
 +  SilcBuffer idp;
 +  uint32 list_count = 0, len = 0;
 +
 +  silc_hash_table_list(channel->user_list, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl))
 +    len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
 +  silc_hash_table_list_reset(&htl);
 +
 +  client_id_list = silc_buffer_alloc(len);
 +  client_mode_list = 
 +    silc_buffer_alloc(4 * silc_hash_table_count(channel->user_list));
 +  silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
 +  silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
 +
 +  silc_hash_table_list(channel->user_list, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +    /* Client ID */
 +    idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
 +    silc_buffer_put(client_id_list, idp->data, idp->len);
 +    silc_buffer_pull(client_id_list, idp->len);
 +    silc_buffer_free(idp);
 +
 +    /* Client's mode on channel */
 +    SILC_PUT32_MSB(chl->mode, client_mode_list->data);
 +    silc_buffer_pull(client_mode_list, 4);
 +
 +    list_count++;
 +  }
 +  silc_hash_table_list_reset(&htl);
 +  silc_buffer_push(client_id_list, 
 +                 client_id_list->data - client_id_list->head);
 +  silc_buffer_push(client_mode_list, 
 +                 client_mode_list->data - client_mode_list->head);
 +
 +  *user_list = client_id_list;
 +  *mode_list = client_mode_list;
 +  *user_count = list_count;
  }
  
 -/* Sends notify message to a channel. The notify message sent is 
 -   distributed to all clients on the channel. */
 +/* Saves users and their modes to the `channel'. */
  
 -void silc_server_send_notify_to_channel(SilcServer server,
 -                                      SilcChannelList *channel,
 -                                      const char *fmt, ...)
 +void silc_server_save_users_on_channel(SilcServer server,
 +                                     SilcSocketConnection sock,
 +                                     SilcChannelEntry channel,
 +                                     SilcClientID *noadd,
 +                                     SilcBuffer user_list,
 +                                     SilcBuffer mode_list,
 +                                     uint32 user_count)
  {
 -  va_list ap;
 -  unsigned char buf[4096];
 -
 -  memset(buf, 0, sizeof(buf));
 -  va_start(ap, fmt);
 -  vsprintf(buf, fmt, ap);
 -  va_end(ap);
 -
 -  silc_server_packet_send_to_channel(server, channel, buf, 
 -                                   strlen(buf), FALSE);
 -}
 +  int i;
 +  uint16 idp_len;
 +  uint32 mode;
 +  SilcClientID *client_id;
 +  SilcClientEntry client;
 +  SilcIDCacheEntry cache;
 +  bool global;
  
 -/* Sends New ID Payload to remote end. The packet is used to distribute
 -   information about new registered clients, servers, channel etc. usually
 -   to routers so that they can keep these information up to date. 
 -   If the argument `broadcast' is TRUE then the packet is sent as
 -   broadcast packet. */
 -
 -void silc_server_send_new_id(SilcServer server,
 -                           SilcSocketConnection sock,
 -                           int broadcast,
 -                           void *id, SilcIdType id_type, 
 -                           unsigned int id_len)
 -{
 -  SilcBuffer packet;
 -  unsigned char *id_string;
 +  SILC_LOG_DEBUG(("Start"));
  
 -  id_string = silc_id_id2str(id, id_type);
 -  if (!id_string)
 -    return;
 +  for (i = 0; i < user_count; i++) {
 +    /* Client ID */
 +    SILC_GET16_MSB(idp_len, user_list->data + 2);
 +    idp_len += 4;
 +    client_id = silc_id_payload_parse_id(user_list->data, idp_len);
 +    silc_buffer_pull(user_list, idp_len);
 +    if (!client_id)
 +      continue;
  
 -  packet = silc_buffer_alloc(2 + 2 + id_len);
 -  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 -  silc_buffer_format(packet,
 -                   SILC_STR_UI_SHORT(id_type),
 -                   SILC_STR_UI_SHORT(id_len),
 -                   SILC_STR_UI_XNSTRING(id_string, id_len),
 -                   SILC_STR_END);
 +    /* Mode */
 +    SILC_GET32_MSB(mode, mode_list->data);
 +    silc_buffer_pull(mode_list, 4);
  
 -  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
 -                        broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
 -                        packet->data, packet->len, FALSE);
 -  silc_free(id_string);
 -  silc_buffer_free(packet);
 -}
 +    if (noadd && SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
 +      silc_free(client_id);
 +      continue;
 +    }
  
 -/* 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;
 +    global = FALSE;
 +    
 +    /* Check if we have this client cached already. */
 +    client = silc_idlist_find_client_by_id(server->local_list, client_id,
 +                                         server->server_type, &cache);
 +    if (!client) {
 +      client = silc_idlist_find_client_by_id(server->global_list, 
 +                                           client_id, server->server_type,
 +                                           &cache);
 +      global = TRUE;
 +    }
 +    if (!client) {
 +      /* If router did not find such Client ID in its lists then this must
 +       be bogus client or some router in the net is buggy. */
 +      if (server->server_type == SILC_ROUTER) {
 +      silc_free(client_id);
 +      continue;
 +      }
  
 -  oid = silc_id_id2str(old_id, old_id_type);
 -  if (!oid)
 -    return;
 +      /* We don't have that client anywhere, add it. The client is added
 +       to global list since server didn't have it in the lists so it must be 
 +       global. */
 +      client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
 +                                    silc_id_dup(client_id, SILC_ID_CLIENT), 
 +                                    sock->user_data, NULL, 0);
 +      if (!client) {
 +      SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
 +      silc_free(client_id);
 +      continue;
 +      }
  
 -  nid = silc_id_id2str(new_id, new_id_type);
 -  if (!nid)
 -    return;
 +      client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
 +    } else {
 +      /* Found, if it is from global list we'll assure that we won't
 +       expire it now that the entry is on channel. */
 +      if (global)
 +      cache->expire = 0;
 +    }
  
 -  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_free(client_id);
  
 -  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);
 +    if (!silc_server_client_on_channel(client, channel)) {
 +      /* Client was not on the channel, add it. */
 +      SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
 +      chl->client = client;
 +      chl->mode = mode;
 +      chl->channel = channel;
 +      silc_hash_table_add(channel->user_list, chl->client, chl);
 +      silc_hash_table_add(client->channels, chl->channel, chl);
 +    }
 +  }
  }
  
 -/* Creates new channel. */
 +/* Lookups route to the client indicated by the `id_data'. The connection
 +   object and internal data object is returned. Returns NULL if route
 +   could not be found to the client. If the `client_id' is specified then
 +   it is used and the `id_data' is ignored. */
  
 -SilcChannelList *silc_server_new_channel(SilcServer server, 
 -                                       SilcServerID *router_id,
 -                                       char *cipher, char *channel_name)
 +SilcSocketConnection silc_server_get_client_route(SilcServer server,
 +                                                unsigned char *id_data,
 +                                                uint32 id_len,
 +                                                SilcClientID *client_id,
 +                                                SilcIDListData *idata)
  {
 -  int i, channel_len;
 -  SilcChannelID *channel_id;
 -  SilcChannelList *entry;
 -  SilcCipher key;
 -  unsigned char channel_key[32], *id_string;
 -  SilcBuffer packet;
 +  SilcClientID *id;
 +  SilcClientEntry client;
  
 -  SILC_LOG_DEBUG(("Creating new channel"));
 +  SILC_LOG_DEBUG(("Start"));
  
 -#define LCC(x) server->local_list->channel_cache[(x) - 32]
 -#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
 +  /* Decode destination Client ID */
 +  if (!client_id) {
 +    id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
 +    if (!id) {
 +      SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
 +      return NULL;
 +    }
 +  } else {
 +    id = silc_id_dup(client_id, SILC_ID_CLIENT);
 +  }
  
 -  /* Create channel key */
 -  for (i = 0; i < 32; i++)
 -    channel_key[i] = silc_rng_get_byte(server->rng);
 +  /* If the destination belongs to our server we don't have to route
 +     the packet anywhere but to send it to the local destination. */
 +  client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
 +  if (client) {
 +    silc_free(id);
  
 -  if (!cipher)
 -    cipher = "twofish";
 +    /* 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 route
 +       to the client is the server who owns the client. So, we will send
 +       the packet to that server. */
 +      if (idata)
 +      *idata = (SilcIDListData)client->router;
 +      return client->router->connection;
 +    }
  
 -  /* Allocate keys */
 -  silc_cipher_alloc(cipher, &key);
 -  key->cipher->set_key(key->context, channel_key, 16);
 +    /* Seems that client really is directly connected to us */
 +    if (idata)
 +      *idata = (SilcIDListData)client;
 +    return client->connection;
 +  }
  
 -  /* Create the channel */
 -  silc_id_create_channel_id(router_id, server->rng, &channel_id);
 -  silc_idlist_add_channel(&server->local_list->channels, channel_name, 
 -                        SILC_CHANNEL_MODE_NONE, channel_id, NULL, /*XXX*/
 -                        key, &entry);
 -  LCCC(channel_name[0]) = silc_idcache_add(&LCC(channel_name[0]), 
 -                                         LCCC(channel_name[0]),
 -                                         channel_name, SILC_ID_CHANNEL, 
 -                                         channel_id, (void *)entry);
 -  entry->key = silc_calloc(16, sizeof(*entry->key));
 -  entry->key_len = 16;
 -  memcpy(entry->key, channel_key, 16);
 -  memset(channel_key, 0, sizeof(channel_key));
 +  /* Destination belongs to someone not in this server. If we are normal
 +     server our action is to send the packet to our router. */
 +  if (server->server_type != SILC_ROUTER && !server->standalone) {
 +    silc_free(id);
 +    if (idata)
 +      *idata = (SilcIDListData)server->router;
 +    return server->router->connection;
 +  }
  
 -  /* Notify other routers about the new channel. We send the packet
 -     to our primary route. */
 -  if (server->standalone == FALSE) {
 -    channel_len = strlen(channel_name);
 -    id_string = silc_id_id2str(entry->id, SILC_ID_CHANNEL);
 -    packet = silc_buffer_alloc(2 + SILC_ID_CHANNEL_LEN);
 -
 -    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 -    silc_buffer_format(packet,
 -                     SILC_STR_UI_SHORT(channel_len),
 -                     SILC_STR_UI_XNSTRING(channel_name, channel_len),
 -                     SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
 -                     SILC_STR_UI_XNSTRING(id_string, SILC_ID_CHANNEL_LEN),
 -                     SILC_STR_END);
 +  /* We are router and we will perform route lookup for the destination 
 +     and send the packet to fastest route. */
 +  if (server->server_type == SILC_ROUTER && !server->standalone) {
 +    /* Check first that the ID is valid */
 +    client = silc_idlist_find_client_by_id(server->global_list, id, 
 +                                         TRUE, NULL);
 +    if (client) {
 +      SilcSocketConnection dst_sock;
  
 -    /* Send the packet to our router. */
 -    silc_server_packet_send(server, (SilcSocketConnection) 
 -                          server->id_entry->router->connection,
 -                          SILC_PACKET_NEW_CHANNEL_USER, 0, 
 -                          packet->data, packet->len, TRUE);
 -    
 -    silc_free(id_string);
 -    silc_buffer_free(packet);
 +      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
 +
 +      silc_free(id);
 +      if (idata)
 +      *idata = (SilcIDListData)dst_sock->user_data;
 +      return dst_sock;
 +    }
    }
  
 -#undef LCC
 -#undef LCCC
 -  return entry;
 +  silc_free(id);
 +  return NULL;
  }
  
 -/* Create new client. This processes incoming NEW_CLIENT packet and creates
 -   Client ID for the client and adds it to lists and cache. */
 +/* Encodes and returns channel list of channels the `client' has joined.
 +   Secret channels are not put to the list. */
  
 -SilcClientList *silc_server_new_client(SilcServer server,
 -                                     SilcSocketConnection sock,
 -                                     SilcPacketContext *packet)
 +SilcBuffer silc_server_get_client_channel_list(SilcServer server,
 +                                             SilcClientEntry client)
  {
 -  SilcBuffer buffer = packet->buffer;
 -  SilcClientList *id_entry;
 -  char *username = NULL, *realname = NULL, *id_string;
 -  SilcBuffer reply;
 -
 -  SILC_LOG_DEBUG(("Creating new client"));
 -
 -  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
 -    return NULL;
 -
 -#define LCC(x) server->local_list->client_cache[(x) - 32]
 -#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
 +  SilcBuffer buffer = NULL;
 +  SilcChannelEntry channel;
 +  SilcChannelClientEntry chl;
 +  SilcHashTableList htl;
 +  unsigned char *cid;
 +  uint32 id_len;
 +  uint16 name_len;
 +  int len;
 +
 +  silc_hash_table_list(client->channels, &htl);
 +  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
 +    channel = chl->channel;
 +
 +    if (channel->mode & SILC_CHANNEL_MODE_SECRET ||
 +      channel->mode & SILC_CHANNEL_MODE_PRIVATE)
 +      continue;
  
 -  silc_buffer_unformat(buffer,
 -                     SILC_STR_UI16_STRING_ALLOC(&username),
 -                     SILC_STR_UI16_STRING_ALLOC(&realname),
 +    cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 +    id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
 +    name_len = strlen(channel->channel_name);
 +    
 +    len = 4 + name_len + id_len + 4;
 +    buffer = silc_buffer_realloc(buffer, 
 +                               (buffer ? (buffer)->truelen + len : len));
 +    silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
 +    silc_buffer_format(buffer,
 +                     SILC_STR_UI_SHORT(name_len),
 +                     SILC_STR_UI_XNSTRING(channel->channel_name, 
 +                                          name_len),
 +                     SILC_STR_UI_SHORT(id_len),
 +                     SILC_STR_UI_XNSTRING(cid, id_len),
 +                     SILC_STR_UI_INT(chl->mode), /* Client's mode */
                       SILC_STR_END);
 +    silc_buffer_pull(buffer, len);
 +    silc_free(cid);
 +  }
 +  silc_hash_table_list_reset(&htl);
  
 -  /* Set the pointers to the client list and create new client ID */
 -  id_entry = (SilcClientList *)sock->user_data;
 -  id_entry->nickname = strdup(username);
 -  id_entry->username = username;
 -  id_entry->userinfo = realname;
 -  silc_id_create_client_id(server->id, server->rng, server->md5hash,
 -                         username, &id_entry->id);
 -
 -  /* Add to client cache */
 -  LCCC(username[0]) = silc_idcache_add(&LCC(username[0]), 
 -                                     LCCC(username[0]),
 -                                     username, SILC_ID_CLIENT, 
 -                                     id_entry->id, (void *)id_entry);
 -
 -  /* Notify our router about new client on the SILC network */
 -  if (!server->standalone)
 -    silc_server_send_new_id(server, (SilcSocketConnection) 
 -                          server->id_entry->router->connection, 
 -                          server->server_type == SILC_SERVER ? TRUE : FALSE,
 -                          id_entry->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
 -  
 -  /* Send the new client ID to the client. */
 -  id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT);
 -  reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
 -  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
 -  silc_buffer_format(reply,
 -                   SILC_STR_UI_SHORT(SILC_ID_CLIENT),
 -                   SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
 -                   SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
 -                   SILC_STR_END);
 -  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
 -                        reply->data, reply->len, FALSE);
 -  silc_free(id_string);
 -  silc_buffer_free(reply);
 -  
 -  /* Send some nice info to the client */
 -  silc_server_send_notify(server, sock, 
 -                        "Welcome to the SILC Network %s@%s",
 -                        username, 
 -                        sock->hostname ? sock->hostname : sock->ip);
 -  silc_server_send_notify(server, sock,
 -                        "Your host is %s, running version %s",
 -                        server->config->server_info->server_name,
 -                        server_version);
 -  silc_server_send_notify(server, sock, 
 -                        "Your connection is secured with %s cipher, "
 -                        "key length %d bits",
 -                        id_entry->send_key->cipher->name,
 -                        id_entry->send_key->cipher->key_len);
 -  silc_server_send_notify(server, sock, 
 -                        "Your current nickname is %s",
 -                        id_entry->nickname);
 -
 -  /* XXX Send motd */
 -
 -#undef LCC
 -#undef LCCC
 -  return id_entry;
 +  if (buffer)
 +    silc_buffer_push(buffer, buffer->data - buffer->head);
 +
 +  return buffer;
  }
  
 -/* Create new server. This processes incoming NEW_SERVER packet and
 -   saves the received Server ID. The server is our locally connected
 -   server thus we save all the information and save it to local list. 
 -   This funtion can be used by both normal server and router server.
 -   If normal server uses this it means that its router has connected
 -   to the server.  If router uses this it means that one of the cell's
 -   servers is connected to the router. */
 +/* Finds client entry by Client ID and if it is not found then resolves
 +   it using WHOIS command. */
  
 -SilcServerList *silc_server_new_server(SilcServer server,
 -                                     SilcSocketConnection sock,
 -                                     SilcPacketContext *packet)
 +SilcClientEntry silc_server_get_client_resolve(SilcServer server,
 +                                             SilcClientID *client_id,
 +                                             bool *resolved)
  {
 -  SilcBuffer buffer = packet->buffer;
 -  SilcServerList *id_entry;
 -  unsigned char *server_name, *id_string;
 +  SilcClientEntry client;
  
 -  SILC_LOG_DEBUG(("Creating new server"));
 +  if (resolved)
 +    *resolved = FALSE;
  
 -  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
 -      sock->type != SILC_SOCKET_TYPE_ROUTER)
 +  client = silc_idlist_find_client_by_id(server->local_list, client_id,
 +                                       TRUE, NULL);
 +  if (!client) {
 +    client = silc_idlist_find_client_by_id(server->global_list, 
 +                                         client_id, TRUE, NULL);
 +    if (!client && server->server_type == SILC_ROUTER)
 +      return NULL;
 +  }
 +
 +  if (!client && server->standalone)
      return NULL;
  
 -#define LSC(x) server->local_list->server_cache[(x) - 32]
 -#define LSCC(x) server->local_list->server_cache_count[(x) - 32]
 +  if (!client || !client->nickname || !client->username) {
 +    SilcBuffer buffer, idp;
  
 -  silc_buffer_unformat(buffer,
 -                     SILC_STR_UI16_STRING_ALLOC(&id_string),
 -                     SILC_STR_UI16_STRING_ALLOC(&server_name),
 -                     SILC_STR_END);
 +    client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
 +    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
 +    client->resolve_cmd_ident = ++server->cmd_ident;
  
 -  /* Save ID and name */
 -  id_entry = (SilcServerList *)sock->user_data;
 -  id_entry->id = silc_id_str2id(id_string, SILC_ID_SERVER);
 -  id_entry->server_name = server_name;
 -  
 -  /* Add to server cache */
 -  LSCC(server_name[0]) = 
 -    silc_idcache_add(&LSC(server_name[0]), 
 -                   LSCC(server_name[0]),
 -                   server_name, SILC_ID_SERVER, 
 -                   id_entry->id, (void *)id_entry);
 -
 -  /* Distribute the information about new server in the SILC network
 -     to our router. If we are normal server we won't send anything
 -     since this connection must be our router connection. */
 -  if (server->server_type == SILC_ROUTER && !server->standalone)
 -    silc_server_send_new_id(server, server->id_entry->router->connection,
 -                          TRUE, id_entry->id, SILC_ID_SERVER, 
 -                          SILC_ID_SERVER_LEN);
 +    idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
 +    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
 +                                          server->cmd_ident, 1,
 +                                          3, idp->data, idp->len);
 +    silc_server_packet_send(server, client ? client->router->connection :
 +                          server->router->connection,
 +                          SILC_PACKET_COMMAND, 0,
 +                          buffer->data, buffer->len, FALSE);
 +    silc_buffer_free(idp);
 +    silc_buffer_free(buffer);
  
 -  silc_free(id_string);
 +    if (resolved)
 +      *resolved = TRUE;
 +
 +    return NULL;
 +  }
  
 -#undef LSC
 -#undef LSCC
 -  return id_entry;
 +  return client;
  }
  
 -/* Processes incoming New ID Payload. New ID Payload is used to distribute
 -   information about newly registered clients, servers and created 
 -   channels. */
 +/* A timeout callback for the re-key. We will be the initiator of the
 +   re-key protocol. */
  
 -void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
 -                      SilcPacketContext *packet)
 +SILC_TASK_CALLBACK(silc_server_rekey_callback)
  {
 -  SilcBuffer buffer = packet->buffer;
 -  SilcIdType id_type;
 -  unsigned char *id_string;
 -  void *id;
 -
 -  SILC_LOG_DEBUG(("Processing new ID"));
 -
 -  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
 -      server->server_type == SILC_SERVER)
 -    return;
 -
 -  silc_buffer_unformat(buffer,
 -                     SILC_STR_UI_SHORT(&id_type),
 -                     SILC_STR_UI16_STRING_ALLOC(&id_string),
 -                     SILC_STR_END);
 +  SilcSocketConnection sock = (SilcSocketConnection)context;
 +  SilcIDListData idata = (SilcIDListData)sock->user_data;
 +  SilcServer server = (SilcServer)idata->rekey->context;
 +  SilcProtocol protocol;
 +  SilcServerRekeyInternalContext *proto_ctx;
  
 -  /* Normal server cannot have other normal server connections */
 -  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
 -    goto out;
 +  SILC_LOG_DEBUG(("Start"));
  
 -  id = silc_id_str2id(id_string, id_type);
 -  if (!id)
 -    goto out;
 +  /* Allocate internal protocol context. This is sent as context
 +     to the protocol. */
 +  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 +  proto_ctx->server = (void *)server;
 +  proto_ctx->sock = sock;
 +  proto_ctx->responder = FALSE;
 +  proto_ctx->pfs = idata->rekey->pfs;
 +      
 +  /* Perform rekey protocol. Will call the final callback after the
 +     protocol is over. */
 +  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
 +                    &protocol, proto_ctx, silc_server_rekey_final);
 +  sock->protocol = protocol;
 +      
 +  /* Run the protocol */
 +  silc_protocol_execute(protocol, server->schedule, 0, 0);
  
 -  /* 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 */
 +  /* Re-register re-key timeout */
 +  silc_schedule_task_add(server->schedule, sock->sock, 
 +                       silc_server_rekey_callback,
 +                       context, idata->rekey->timeout, 0,
 +                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 +}
  
 -  switch(id_type) {
 -  case SILC_ID_CLIENT:
 -    {
 -      SilcClientList *idlist;
 -
 -      /* Add the client to our local list. We are router and we keep
 -       cell specific local database of all clients in the cell. */
 -      silc_idlist_add_client(&server->local_list->clients, NULL, NULL, NULL,
 -                           id, sock->user_data, NULL, NULL, 
 -                           NULL, NULL, &idlist);
 -      idlist->connection = sock;
 -    }
 -    break;
 +/* The final callback for the REKEY protocol. This will actually take the
 +   new key material into use. */
  
 -  case SILC_ID_SERVER:
 -    {
 -      SilcServerList *idlist;
 -
 -      /* Add the server to our local list. We are router and we keep
 -       cell specific local database of all servers in the cell. */
 -      silc_idlist_add_server(&server->local_list->servers, NULL, 0,
 -                           id, server->id_entry, NULL, NULL, 
 -                         NULL, NULL, &idlist);
 -      idlist->connection = sock;
 -    }
 -    break;
 +SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcServerRekeyInternalContext *ctx =
 +    (SilcServerRekeyInternalContext *)protocol->context;
 +  SilcServer server = (SilcServer)ctx->server;
 +  SilcSocketConnection sock = ctx->sock;
  
 -  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->channels, NULL, 0,
 -                          id, server->id_entry, NULL, NULL);
 -    break;
 +  SILC_LOG_DEBUG(("Start"));
  
 -  default:
 -    goto out;
 -    break;
 +  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
 +      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
 +    /* Error occured during protocol */
 +    SILC_LOG_ERROR(("Error occurred during rekey protocol"));
 +    silc_protocol_cancel(protocol, server->schedule);
 +    silc_protocol_free(protocol);
 +    sock->protocol = NULL;
 +    if (ctx->packet)
 +      silc_packet_context_free(ctx->packet);
 +    if (ctx->ske)
 +      silc_ske_free(ctx->ske);
 +    silc_free(ctx);
 +    return;
    }
  
 - out:
 -  silc_free(id_string);
 +  /* Purge the outgoing data queue to assure that all rekey packets really
 +     go to the network before we quit the protocol. */
 +  silc_server_packet_queue_purge(server, sock);
 +
 +  /* Cleanup */
 +  silc_protocol_free(protocol);
 +  sock->protocol = NULL;
 +  if (ctx->packet)
 +    silc_packet_context_free(ctx->packet);
 +  if (ctx->ske)
 +    silc_ske_free(ctx->ske);
 +  silc_free(ctx);
  }
index 4bcf0b115f09601b93a695aac91ffa397f0db79b,0000000000000000000000000000000000000000..6b3d876a8e74999f6cf0c7be9696d10614773128
mode 100644,000000..100644
--- /dev/null
@@@ -1,1240 -1,0 +1,1240 @@@
-   sock = silc_net_create_connection(server->config->listen_port->local_ip,
 +/*
 +
 +  server_backup.c 
 +
 +  Author: Pekka Riikonen <priikone@silcnet.org>
 +
 +  Copyright (C) 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
 +  the Free Software Foundation; version 2 of the License.
 +
 +  This program is distributed in the hope that it will be useful,
 +  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +  GNU General Public License for more details.
 +
 +*/
 +/* $Id$ */
 +
 +#include "serverincludes.h"
 +#include "server_internal.h"
 +
 +SILC_TASK_CALLBACK(silc_server_protocol_backup_done);
 +
 +/* Backup router */
 +typedef struct {
 +  SilcServerEntry server;
 +  SilcIDIP ip;
 +  uint16 port;
 +  bool local;
 +} SilcServerBackupEntry;
 +
 +/* Holds IP address and port of the primary router that was replaced
 +   by backup router. */
 +typedef struct {
 +  SilcIDIP ip;
 +  uint16 port;
 +  SilcServerEntry server;     /* Backup router that replaced the primary */
 +} SilcServerBackupReplaced;
 +
 +/* Backup context */
 +struct SilcServerBackupStruct {
 +  SilcServerBackupEntry *servers;
 +  uint32 servers_count;
 +  SilcServerBackupReplaced **replaced;
 +  uint32 replaced_count;
 +};
 +
 +typedef struct {
 +  uint8 session;
 +  bool connected;
 +  SilcServerEntry server_entry;
 +} SilcServerBackupProtocolSession;
 +
 +/* Backup resuming protocol context  */
 +typedef struct {
 +  SilcServer server;
 +  SilcSocketConnection sock;
 +  bool responder;
 +  uint8 type;
 +  uint8 session;
 +  SilcServerBackupProtocolSession *sessions;
 +  uint32 sessions_count;
 +  long start;
 +} *SilcServerBackupProtocolContext;
 +
 +/* Adds the `backup_server' to be one of our backup router. This can be
 +   called multiple times to set multiple backup routers. The `ip' and `port'
 +   is the IP and port that the `backup_router' will replace if the `ip'
 +   will become unresponsive. If `local' is TRUE then the `backup_server' is
 +   in the local cell, if FALSE it is in some other cell. */
 +
 +void silc_server_backup_add(SilcServer server, SilcServerEntry backup_server,
 +                          const char *ip, int port, bool local)
 +{
 +  int i;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (!ip)
 +    return;
 +
 +  if (!server->backup)
 +    server->backup = silc_calloc(1, sizeof(*server->backup));
 +
 +  for (i = 0; i < server->backup->servers_count; i++) {
 +    if (!server->backup->servers[i].server) {
 +      server->backup->servers[i].server = backup_server;
 +      server->backup->servers[i].local = local;
 +      memset(server->backup->servers[i].ip.data, 0,
 +           sizeof(server->backup->servers[i].ip.data));
 +      silc_net_addr2bin(ip, server->backup->servers[i].ip.data,
 +                      sizeof(server->backup->servers[i].ip.data));
 +      //server->backup->servers[i].port = port;
 +      return;
 +    }
 +  }
 +
 +  i = server->backup->servers_count;
 +  server->backup->servers = silc_realloc(server->backup->servers,
 +                                       sizeof(*server->backup->servers) *
 +                                       (i + 1));
 +  server->backup->servers[i].server = backup_server;
 +  server->backup->servers[i].local = local;
 +  memset(server->backup->servers[i].ip.data, 0,
 +       sizeof(server->backup->servers[i].ip.data));
 +  silc_net_addr2bin(ip, server->backup->servers[i].ip.data,
 +                  sizeof(server->backup->servers[i].ip.data));
 +  //server->backup->servers[i].port = server_id->port;
 +  server->backup->servers_count++;
 +}
 +
 +/* Returns backup router for IP and port in `replacing' or NULL if there
 +   does not exist backup router. */
 +
 +SilcServerEntry silc_server_backup_get(SilcServer server, 
 +                                     SilcServerID *server_id)
 +{
 +  int i;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (!server->backup)
 +    return NULL;
 +
 +  for (i = 0; i < server->backup->servers_count; i++) {
 +    SILC_LOG_HEXDUMP(("IP"), server_id->ip.data, 16);
 +    SILC_LOG_HEXDUMP(("IP"), server->backup->servers[i].ip.data, 16);
 +    if (server->backup->servers[i].server &&
 +      !memcmp(&server->backup->servers[i].ip, &server_id->ip.data,
 +              sizeof(server_id->ip.data)))
 +      return server->backup->servers[i].server;
 +  }
 +
 +  return NULL;
 +}
 +
 +/* Deletes the backup server `server_entry'. */
 +void silc_server_backup_del(SilcServer server, SilcServerEntry server_entry)
 +{
 +  int i;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (!server->backup)
 +    return ;
 +
 +  for (i = 0; i < server->backup->servers_count; i++) {
 +    if (server->backup->servers[i].server == server_entry) {
 +      server->backup->servers[i].server = NULL;
 +      return;
 +    }
 +  }
 +}
 +
 +/* Marks the IP address and port from the `server_id' as  being replaced
 +   by backup router indicated by the `server'. If the router connects at
 +   a later time we can check whether it has been replaced by an backup
 +   router. */
 +
 +void silc_server_backup_replaced_add(SilcServer server, 
 +                                   SilcServerID *server_id,
 +                                   SilcServerEntry server_entry)
 +{
 +  int i;
 +  SilcServerBackupReplaced *r = silc_calloc(1, sizeof(*r));;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (!server->backup)
 +    server->backup = silc_calloc(1, sizeof(*server->backup));
 +  if (!server->backup->replaced) {
 +    server->backup->replaced = 
 +      silc_calloc(1, sizeof(*server->backup->replaced));
 +    server->backup->replaced_count = 1;
 +  }
 +
 +  SILC_LOG_DEBUG(("********************************"));
 +  SILC_LOG_DEBUG(("Replaced added"));
 +
 +  memcpy(&r->ip, &server_id->ip, sizeof(server_id->ip));
 +  //r->port = server_id->port;
 +  r->server = server_entry;
 +
 +  for (i = 0; i < server->backup->replaced_count; i++) {
 +    if (!server->backup->replaced[i]) {
 +      server->backup->replaced[i] = r;
 +      return;
 +    }
 +  }
 +
 +  i = server->backup->replaced_count;
 +  server->backup->replaced = silc_realloc(server->backup->replaced,
 +                                        sizeof(*server->backup->replaced) *
 +                                        (i + 1));
 +  server->backup->replaced[i] = r;
 +  server->backup->replaced_count++;
 +}
 +
 +/* Checks whether the IP address and port from the `server_id' has been
 +   replaced by an backup router. If it has been then this returns TRUE
 +   and the bacup router entry to the `server' pointer if non-NULL. Returns
 +   FALSE if the router is not replaced by backup router. */
 +
 +bool silc_server_backup_replaced_get(SilcServer server,
 +                                   SilcServerID *server_id,
 +                                   SilcServerEntry *server_entry)
 +{
 +  int i;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  SILC_LOG_DEBUG(("*************************************"));
 +
 +  if (!server->backup || !server->backup->replaced)
 +    return FALSE;
 +
 +  for (i = 0; i < server->backup->replaced_count; i++) {
 +    if (!server->backup->replaced[i])
 +      continue;
 +    SILC_LOG_HEXDUMP(("IP"), server_id->ip.data, server_id->ip.data_len);
 +    SILC_LOG_HEXDUMP(("IP"), server->backup->replaced[i]->ip.data, 
 +                   server->backup->replaced[i]->ip.data_len);
 +    if (!memcmp(&server->backup->replaced[i]->ip, &server_id->ip.data,
 +              sizeof(server_id->ip.data))) {
 +      if (server_entry)
 +      *server_entry = server->backup->replaced[i]->server;
 +      SILC_LOG_DEBUG(("REPLACED"));
 +      return TRUE;
 +    }
 +  }
 +
 +  SILC_LOG_DEBUG(("NOT REPLACED"));
 +  return FALSE;
 +}
 +
 +/* Deletes a replaced host by the set `server_entry. */
 +
 +void silc_server_backup_replaced_del(SilcServer server,
 +                                   SilcServerEntry server_entry)
 +{
 +  int i;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (!server->backup || !server->backup->replaced)
 +    return;
 +
 +  for (i = 0; i < server->backup->replaced_count; i++) {
 +    if (!server->backup->replaced[i])
 +      continue;
 +    if (server->backup->replaced[i]->server == server_entry) {
 +      silc_free(server->backup->replaced[i]);
 +      server->backup->replaced[i] = NULL;
 +      return;
 +    }
 +  }
 +}
 +
 +/* Broadcast the received packet indicated by `packet' to all of our backup 
 +   routers. All router wide information is passed using broadcast packets. 
 +   That is why all backup routers need to get this data too. It is expected
 +   that the caller already knows that the `packet' is broadcast packet. */
 +
 +void silc_server_backup_broadcast(SilcServer server, 
 +                                SilcSocketConnection sender,
 +                                SilcPacketContext *packet)
 +{
 +  SilcServerEntry backup;
 +  SilcSocketConnection sock;
 +  SilcBuffer buffer;
 +  SilcIDListData idata;
 +  int i;
 +
 +  if (!server->backup || server->server_type != SILC_ROUTER)
 +    return;
 +
 +  SILC_LOG_DEBUG(("Broadcasting received packet to backup routers"));
 +
 +  buffer = packet->buffer;
 +  silc_buffer_push(buffer, buffer->data - buffer->head);
 +
 +  for (i = 0; i < server->backup->servers_count; i++) {
 +    backup = server->backup->servers[i].server;
 +
 +    if (!backup || backup->connection == sender || 
 +      server->backup->servers[i].local == FALSE)
 +      continue;
 +
 +    idata = (SilcIDListData)backup;
 +    sock = backup->connection;
 +
 +    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_send, idata->psn_send++, 
 +                      sock->outbuf, sock->outbuf->len);
 +
 +    SILC_LOG_HEXDUMP(("Broadcasted packet, len %d", sock->outbuf->len),
 +                   sock->outbuf->data, sock->outbuf->len);
 +
 +    /* Now actually send the packet */
 +    silc_server_packet_send_real(server, sock, FALSE);
 +  }
 +}
 +
 +/* A generic routine to send data to all backup routers. If the `sender'
 +   is provided it will indicate the original sender of the packet and the
 +   packet won't be resent to that entity. The `data' is the data that will
 +   be assembled to packet context before sending. The packet will be
 +   encrypted this function. If the `force_send' is TRUE the data is sent
 +   immediately and not put to queue. If `local' is TRUE then the packet
 +   will be sent only to local backup routers inside the cell. If false the
 +   packet can go from one cell to the other. This function has no effect
 +   if there are no any backup routers. */
 +
 +void silc_server_backup_send(SilcServer server,
 +                           SilcServerEntry sender,
 +                           SilcPacketType type,
 +                           SilcPacketFlags flags,
 +                           unsigned char *data,
 +                           uint32 data_len,
 +                           bool force_send,
 +                           bool local)
 +{
 +  SilcServerEntry backup;
 +  SilcSocketConnection sock;
 +  int i;
 +
 +  if (!server->backup || server->server_type != SILC_ROUTER)
 +    return;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  for (i = 0; i < server->backup->servers_count; i++) {
 +    backup = server->backup->servers[i].server;
 +    if (!backup)
 +      continue;
 +
 +    if (sender == backup)
 +      continue;
 +
 +    if (local && server->backup->servers[i].local == FALSE)
 +      continue;
 +
 +    sock = backup->connection;
 +    silc_server_packet_send(server, backup->connection, type, flags, 
 +                          data, data_len, force_send);
 +  }
 +}
 +
 +/* Same as silc_server_backup_send but sets a specific Destination ID to
 +   the packet. The Destination ID is indicated by the `dst_id' and the
 +   ID type `dst_id_type'. For example, packets destined to channels must
 +   be sent using this function. */
 +
 +void silc_server_backup_send_dest(SilcServer server,
 +                                SilcServerEntry sender,
 +                                SilcPacketType type,
 +                                SilcPacketFlags flags,
 +                                void *dst_id,
 +                                SilcIdType dst_id_type,
 +                                unsigned char *data,
 +                                uint32 data_len,
 +                                bool force_send,
 +                                bool local)
 +{
 +  SilcServerEntry backup;
 +  SilcSocketConnection sock;
 +  int i;
 +
 +  if (!server->backup || server->server_type != SILC_ROUTER)
 +    return;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  for (i = 0; i < server->backup->servers_count; i++) {
 +    backup = server->backup->servers[i].server;
 +    if (!backup)
 +      continue;
 +
 +    if (sender == backup)
 +      continue;
 +
 +    if (local && server->backup->servers[i].local == FALSE)
 +      continue;
 +
 +    sock = backup->connection;
 +    silc_server_packet_send_dest(server, backup->connection, type, flags, 
 +                               dst_id, dst_id_type, data, data_len, 
 +                               force_send);
 +  }
 +}
 +
 +/* Processes incoming RESUME_ROUTER packet. This can give the packet
 +   for processing to the protocol handler or allocate new protocol if
 +   start command is received. */
 +
 +void silc_server_backup_resume_router(SilcServer server, 
 +                                    SilcSocketConnection sock, 
 +                                    SilcPacketContext *packet)
 +{
 +  uint8 type, session;
 +  SilcServerBackupProtocolContext ctx;
 +  int i, ret;
 +
 +  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
 +      sock->type == SILC_SOCKET_TYPE_UNKNOWN)
 +    return;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  ret = silc_buffer_unformat(packet->buffer,
 +                           SILC_STR_UI_CHAR(&type),
 +                           SILC_STR_UI_CHAR(&session),
 +                           SILC_STR_END);
 +  if (ret < 0)
 +    return;
 +  
 +  /* Activate the protocol for this socket if necessary */
 +  if ((type == SILC_SERVER_BACKUP_RESUMED || 
 +      type == SILC_SERVER_BACKUP_RESUMED_GLOBAL) &&
 +      sock->type == SILC_SOCKET_TYPE_ROUTER && !sock->protocol && 
 +      ((SilcIDListData)sock->user_data)->status & 
 +      SILC_IDLIST_STATUS_DISABLED) {
 +    SilcServerEntry backup_router;
 +
 +    if (silc_server_backup_replaced_get(server, 
 +                                      ((SilcServerEntry)sock->
 +                                       user_data)->id, 
 +                                      &backup_router)) {
 +      SilcSocketConnection bsock = 
 +      (SilcSocketConnection)backup_router->connection;
 +      if (bsock->protocol && bsock->protocol->protocol &&
 +        bsock->protocol->protocol->type == SILC_PROTOCOL_SERVER_BACKUP) {
 +      sock->protocol = bsock->protocol;
 +      ctx = sock->protocol->context;
 +      ctx->sock = sock;
 +      }
 +    }
 +  }
 +
 +  /* If the backup resuming protocol is active then process the packet
 +     in the protocol. */
 +  if (sock->protocol && sock->protocol->protocol &&
 +      sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_BACKUP) {
 +    ctx = sock->protocol->context;
 +    ctx->type = type;
 +
 +    SILC_LOG_DEBUG(("********************************"));
 +    SILC_LOG_DEBUG(("Continuing protocol, type %d", type));
 +
 +    if (type != SILC_SERVER_BACKUP_RESUMED &&
 +      type != SILC_SERVER_BACKUP_RESUMED_GLOBAL) {
 +      for (i = 0; i < ctx->sessions_count; i++) {
 +      if (session == ctx->sessions[i].session) {
 +        ctx->session = session;
 +        silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 +        return;
 +      }
 +      }
 +    } else {
 +      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 +      return;
 +    }
 +
 +    SILC_LOG_DEBUG(("Bad resume router packet"));
 +    return;
 +  }
 +
 +  /* We don't have protocol active. If we are router and the packet is 
 +     coming from our primary router then lets check whether it means we've
 +     been replaced by an backup router in my cell. This is usually received
 +     immediately after we've connected to our primary router. */
 +
 +  if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
 +      server->router == sock->user_data &&
 +      type == SILC_SERVER_BACKUP_REPLACED) {
 +    /* We have been replaced by an backup router in our cell. We must
 +       mark our primary router connection disabled since we are not allowed
 +       to use it at this moment. */
 +    SilcIDListData idata = (SilcIDListData)sock->user_data;
 +
 +    SILC_LOG_INFO(("We are replaced by an backup router in this cell, will "
 +                 "wait until backup resuming protocol is executed"));
 +
 +    SILC_LOG_DEBUG(("We are replaced by an backup router in this cell"));
 +    idata->status |= SILC_IDLIST_STATUS_DISABLED;
 +    return;
 +  }
 +
 +  if (type == SILC_SERVER_BACKUP_START ||
 +      type == SILC_SERVER_BACKUP_START_GLOBAL) {
 +    /* We have received a start for resuming protocol. */
 +    SilcServerBackupProtocolContext proto_ctx;
 +
 +    proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 +    proto_ctx->server = server;
 +    proto_ctx->sock = sock;
 +    proto_ctx->responder = TRUE;
 +    proto_ctx->type = type;
 +    proto_ctx->session = session;
 +    proto_ctx->start = time(0);
 +
 +    SILC_LOG_DEBUG(("Starting backup resuming protocol as responder"));
 +
 +    /* Run the backup resuming protocol */
 +    silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
 +                      &sock->protocol, proto_ctx, 
 +                      silc_server_protocol_backup_done);
 +    silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 +  }
 +}
 +
 +/* Timeout task callback to connect to remote router */
 +
 +SILC_TASK_CALLBACK(silc_server_backup_connect_to_router)
 +{
 +  SilcServerConnection sconn = (SilcServerConnection)context;
 +  SilcServer server = sconn->server;
 +  int sock;
 +
 +  SILC_LOG_DEBUG(("Connecting to router %s:%d", sconn->remote_host,
 +                sconn->remote_port));
 +
 +  /* Connect to remote host */
-       SilcServerConfigSectionServerConnection *primary;
++  sock = silc_net_create_connection(server->config->server_info->server_ip,
 +                                  sconn->remote_port,
 +                                  sconn->remote_host);
 +  if (sock < 0) {
 +    silc_schedule_task_add(server->schedule, 0,
 +                         silc_server_backup_connect_to_router,
 +                         context, 5, 0, SILC_TASK_TIMEOUT, 
 +                         SILC_TASK_PRI_NORMAL);
 +    return;
 +  }
 +
 +  /* Continue with key exchange protocol */
 +  silc_server_start_key_exchange(server, sconn, sock);
 +}
 +
 +/* Constantly tries to reconnect to a primary router indicated by the
 +   `ip' and `port'. The `connected' callback will be called when the
 +   connection is created. */
 +
 +void silc_server_backup_reconnect(SilcServer server,
 +                                const char *ip, uint16 port,
 +                                SilcServerConnectRouterCallback callback,
 +                                void *context)
 +{
 +  SilcServerConnection sconn;
 +
 +  sconn = silc_calloc(1, sizeof(*sconn));
 +  sconn->server = server;
 +  sconn->remote_host = strdup(ip);
 +  sconn->remote_port = port;
 +  sconn->callback = callback;
 +  sconn->callback_context = context;
 +  silc_schedule_task_add(server->schedule, 0, 
 +                       silc_server_backup_connect_to_router,
 +                       sconn, 1, 0, SILC_TASK_TIMEOUT,
 +                       SILC_TASK_PRI_NORMAL);
 +}
 +
 +SILC_TASK_CALLBACK(silc_server_backup_connected_later)
 +{
 +  SilcServerBackupProtocolContext proto_ctx = 
 +    (SilcServerBackupProtocolContext)context;
 +  SilcServer server = proto_ctx->server;
 +  SilcSocketConnection sock = proto_ctx->sock;
 +
 +  SILC_LOG_DEBUG(("Starting backup resuming protocol as initiator"));
 +
 +  /* Run the backup resuming protocol */
 +  silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
 +                    &sock->protocol, proto_ctx, 
 +                    silc_server_protocol_backup_done);
 +  silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 +}
 +
 +/* Called when we've established connection back to our primary router
 +   when we've acting as backup router and have replaced the primary router
 +   in the cell. This function will start the backup resuming protocol. */
 +
 +void silc_server_backup_connected(SilcServer server,
 +                                SilcServerEntry server_entry,
 +                                void *context)
 +{
 +  SilcServerBackupProtocolContext proto_ctx;
 +  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
 +
 +  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 +  proto_ctx->server = server;
 +  proto_ctx->sock = sock;
 +  proto_ctx->responder = FALSE;
 +  proto_ctx->type = SILC_SERVER_BACKUP_START;
 +  proto_ctx->start = time(0);
 +
 +  /* Start through scheduler */
 +  silc_schedule_task_add(server->schedule, 0,
 +                       silc_server_backup_connected_later,
 +                       proto_ctx, 0, 1,
 +                       SILC_TASK_TIMEOUT,
 +                       SILC_TASK_PRI_NORMAL);
 +}
 +
 +/* Called when normal server has connected to its primary router after
 +   backup router has sent the START packet in reusming protocol. We will
 +   move the protocol context from the backup router connection to the
 +   primary router. */
 +
 +static void silc_server_backup_connect_primary(SilcServer server,
 +                                             SilcServerEntry server_entry,
 +                                             void *context)
 +{
 +  SilcSocketConnection backup_router = (SilcSocketConnection)context;
 +  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
 +  SilcIDListData idata = (SilcIDListData)server_entry;
 +  SilcServerBackupProtocolContext ctx = 
 +    (SilcServerBackupProtocolContext)backup_router->protocol->context;
 +  SilcBuffer buffer;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  SILC_LOG_DEBUG(("********************************"));
 +  SILC_LOG_DEBUG(("Sending CONNECTED packet, session %d", ctx->session));
 +
 +  /* Send the CONNECTED packet back to the backup router. */
 +  buffer = silc_buffer_alloc(2);
 +  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
 +  silc_buffer_format(buffer,
 +                   SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_CONNECTED),
 +                   SILC_STR_UI_CHAR(ctx->session),
 +                   SILC_STR_END);
 +  silc_server_packet_send(server, backup_router, 
 +                        SILC_PACKET_RESUME_ROUTER, 0, 
 +                        buffer->data, buffer->len, FALSE);
 +  silc_buffer_free(buffer);
 +
 +  /* The primary connection is disabled until it sends the RESUMED packet
 +     to us. */
 +  idata->status |= SILC_IDLIST_STATUS_DISABLED;
 +
 +  /* Move this protocol context from this backup router connection to
 +     the primary router connection since it will send the subsequent
 +     packets in this protocol. We don't talk with backup router 
 +     anymore. */
 +  sock->protocol = backup_router->protocol;
 +  ctx->sock = (SilcSocketConnection)server_entry->connection;
 +  backup_router->protocol = NULL;
 +}
 +
 +SILC_TASK_CALLBACK(silc_server_backup_send_resumed)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcServerBackupProtocolContext ctx = protocol->context;
 +  SilcServer server = ctx->server;
 +  SilcBuffer packet;
 +  int i;
 +
 +  for (i = 0; i < ctx->sessions_count; i++)
 +    if (ctx->sessions[i].server_entry == ctx->sock->user_data)
 +      ctx->session = ctx->sessions[i].session;
 +  
 +  /* We've received all the CONNECTED packets and now we'll send the
 +     ENDING packet to the new primary router. */
 +  packet = silc_buffer_alloc(2);
 +  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 +  silc_buffer_format(packet,
 +                   SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_ENDING),
 +                   SILC_STR_UI_CHAR(ctx->session),
 +                   SILC_STR_END);
 +  silc_server_packet_send(server, ctx->sock, 
 +                        SILC_PACKET_RESUME_ROUTER, 0, 
 +                        packet->data, packet->len, FALSE);
 +  silc_buffer_free(packet);
 +  
 +  protocol->state = SILC_PROTOCOL_STATE_END;
 +}
 +
 +/* Resume protocol with RESUME_ROUTER packet: 
 +
 +   SILC_PACKET_RESUME_ROUTER:
 +
 +   <uint8 type> <uint8 Session ID>
 +
 +   <type>          = the protocol opcode
 +   <Session ID>    = Identifier for this packet and any subsequent reply
 +                     packets must include this identifier.
 +
 +   Types:
 +
 +     1    = To router: Comensing backup resuming protocol. This will
 +            indicate that the sender is backup router acting as primary
 +            and the receiver is primary router that has been replaced by
 +          the backup router.
 +
 +          To server. Comensing backup resuming protocol. This will
 +          indicate that the sender is backup router and the receiver
 +          must reconnect to the real primary router of the cell.
 +
 +     2    = To Router: Comesning backup resuming protocol in another
 +            cell.  The receiver will connect to its primary router 
 +          (the router that is now online again) but will not use
 +          the link.  If the receiver is not configured to connect
 +          to any router it does as locally configured.  The sender
 +          is always backup router.
 +
 +          To server: this is never sent to server.
 +
 +     3    = To backup router: Sender is normal server or router and it
 +            tells to backup router that they have connected to the
 +          primary router.  Backup router never sends this type.
 +
 +     4    = To router: Ending backup resuming protocol. This is sent
 +            to the real primary router to tell that it can take over
 +          the task as being primary router.
 +
 +          To server: same as sending for router.
 +
 +          Backup router sends this also to the primary route but only
 +          after it has sent them to normal servers and has purged all
 +          traffic coming from normal servers.
 +
 +     5    = To router: Sender is the real primary router after it has
 +            received type 4 from backup router. To tell that it is again
 +          primary router of the cell.
 +
 +     20   = To router: This is sent only when router is connecting to
 +            another router and has been replaced by an backup router.
 +          The sender knows that the connectee has been replaced.
 +
 + */
 +
 +/* Backup resuming protocol. This protocol is executed when the primary
 +   router wants to resume its position as being primary router. */
 +
 +SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcServerBackupProtocolContext ctx = protocol->context;
 +  SilcServer server = ctx->server;
 +  SilcBuffer packet;
 +  SilcIDCacheList list;
 +  SilcIDCacheEntry id_cache;
 +  SilcServerEntry server_entry;
 +  int i;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
 +    protocol->state = SILC_PROTOCOL_STATE_START;
 +
 +  SILC_LOG_DEBUG(("State=%d", protocol->state));
 +
 +  switch(protocol->state) {
 +  case SILC_PROTOCOL_STATE_START:
 +    if (ctx->responder == FALSE) {
 +      /* Initiator of the protocol. We are backup router */
 +
 +      packet = silc_buffer_alloc(2);
 +      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 +
 +      SILC_LOG_DEBUG(("********************************"));
 +      SILC_LOG_DEBUG(("Sending START packets"));
 +
 +      /* Send the START packet to primary router and normal servers. */
 +      if (silc_idcache_get_all(server->local_list->servers, &list)) {
 +      if (silc_idcache_list_first(list, &id_cache)) {
 +        while (id_cache) {
 +          server_entry = (SilcServerEntry)id_cache->context;
 +          if (!server_entry || (server_entry == server->id_entry) || 
 +              !server_entry->connection || !server_entry->data.send_key ||
 +              (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)) {
 +            if (!silc_idcache_list_next(list, &id_cache))
 +              break;
 +            else
 +              continue;
 +          }
 +
 +          ctx->sessions = silc_realloc(ctx->sessions,
 +                                       sizeof(*ctx->sessions) *
 +                                       (ctx->sessions_count + 1));
 +          ctx->sessions[ctx->sessions_count].session = ctx->sessions_count;
 +          ctx->sessions[ctx->sessions_count].connected = FALSE;
 +          ctx->sessions[ctx->sessions_count].server_entry = server_entry;
 +
 +          SILC_LOG_DEBUG(("********************************"));
 +          SILC_LOG_DEBUG(("START (local) for session %d", 
 +                          ctx->sessions_count));
 +
 +          /* This connection is performing this protocol too now */
 +          ((SilcSocketConnection)server_entry->connection)->protocol =
 +            protocol;
 +
 +          if (server_entry->server_type == SILC_ROUTER)
 +            packet->data[0] = SILC_SERVER_BACKUP_START;
 +          else
 +            packet->data[0] = SILC_SERVER_BACKUP_START_GLOBAL;
 +          packet->data[1] = ctx->sessions_count;
 +          silc_server_packet_send(server, server_entry->connection,
 +                                  SILC_PACKET_RESUME_ROUTER, 0, 
 +                                  packet->data, packet->len, FALSE);
 +          ctx->sessions_count++;
 +
 +          if (!silc_idcache_list_next(list, &id_cache))
 +            break;
 +        }
 +      }
 +
 +      silc_idcache_list_free(list);
 +      }
 +
 +      if (silc_idcache_get_all(server->global_list->servers, &list)) {
 +      if (silc_idcache_list_first(list, &id_cache)) {
 +        while (id_cache) {
 +          server_entry = (SilcServerEntry)id_cache->context;
 +          if (!server_entry || (server_entry == server->id_entry) || 
 +              !server_entry->connection || !server_entry->data.send_key ||
 +              (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)) {
 +            if (!silc_idcache_list_next(list, &id_cache))
 +              break;
 +            else
 +              continue;
 +          }
 +
 +          ctx->sessions = silc_realloc(ctx->sessions,
 +                                       sizeof(*ctx->sessions) *
 +                                       (ctx->sessions_count + 1));
 +          ctx->sessions[ctx->sessions_count].session = ctx->sessions_count;
 +          ctx->sessions[ctx->sessions_count].connected = FALSE;
 +          ctx->sessions[ctx->sessions_count].server_entry = server_entry;
 +
 +          SILC_LOG_DEBUG(("********************************"));
 +          SILC_LOG_DEBUG(("START (global) for session %d", 
 +                          ctx->sessions_count));
 +
 +          /* This connection is performing this protocol too now */
 +          ((SilcSocketConnection)server_entry->connection)->protocol =
 +            protocol;
 +
 +          if (server_entry->server_type == SILC_ROUTER)
 +            packet->data[0] = SILC_SERVER_BACKUP_START;
 +          else
 +            packet->data[0] = SILC_SERVER_BACKUP_START_GLOBAL;
 +          packet->data[1] = ctx->sessions_count;
 +          silc_server_packet_send(server, server_entry->connection,
 +                                  SILC_PACKET_RESUME_ROUTER, 0, 
 +                                  packet->data, packet->len, FALSE);
 +          ctx->sessions_count++;
 +
 +          if (!silc_idcache_list_next(list, &id_cache))
 +            break;
 +        }
 +      }
 +
 +      silc_idcache_list_free(list);
 +      }
 +
 +      silc_buffer_free(packet);
 +
 +      /* Announce all of our information */
 +      silc_server_announce_servers(server, TRUE, 0, ctx->sock);
 +      silc_server_announce_clients(server, 0, ctx->sock);
 +      silc_server_announce_channels(server, 0, ctx->sock);
 +
 +      protocol->state++;
 +    } else {
 +      /* Responder of the protocol. */
++      SilcServerConfigSectionRouter *primary;
 +
 +      /* We should have received START or START_GLOBAL packet */
 +      if (ctx->type != SILC_SERVER_BACKUP_START &&
 +        ctx->type != SILC_SERVER_BACKUP_START_GLOBAL) {
 +      SILC_LOG_DEBUG(("Bad resume router packet"));
 +      break;
 +      }
 +
 +      SILC_LOG_DEBUG(("********************************"));
 +      SILC_LOG_DEBUG(("Received START packet, reconnecting to router"));
 +
 +      /* Connect to the primary router that was down that is now supposed
 +       to be back online. We send the CONNECTED packet after we've
 +       established the connection to the primary router. */
 +      primary = silc_server_config_get_primary_router(server->config);
 +      if (primary && server->backup_primary) {
 +      silc_server_backup_reconnect(server,
 +                                   primary->host, primary->port,
 +                                   silc_server_backup_connect_primary,
 +                                   ctx->sock);
 +      } else {
 +      /* Nowhere to connect just return the CONNECTED packet */
 +
 +      SILC_LOG_DEBUG(("********************************"));
 +      SILC_LOG_DEBUG(("Sending CONNECTED packet, session %d", ctx->session));
 +      
 +      /* Send the CONNECTED packet back to the backup router. */
 +      packet = silc_buffer_alloc(2);
 +      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 +      silc_buffer_format(packet,
 +                         SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_CONNECTED),
 +                         SILC_STR_UI_CHAR(ctx->session),
 +                         SILC_STR_END);
 +      silc_server_packet_send(server, ctx->sock, 
 +                              SILC_PACKET_RESUME_ROUTER, 0, 
 +                              packet->data, packet->len, FALSE);
 +      silc_buffer_free(packet);
 +      }
 +
 +      if (server->server_type == SILC_ROUTER &&
 +        (!server->router || 
 +         server->router->data.status & SILC_IDLIST_STATUS_DISABLED))
 +      protocol->state++;
 +      else
 +      protocol->state = SILC_PROTOCOL_STATE_END;
 +
 +      ctx->sessions = silc_realloc(ctx->sessions,
 +                                 sizeof(*ctx->sessions) *
 +                                 (ctx->sessions_count + 1));
 +      ctx->sessions[ctx->sessions_count].session = ctx->session;
 +      ctx->sessions_count++;
 +    }
 +    break;
 +
 +  case 2:
 +    if (ctx->responder == FALSE) {
 +      /* Initiator */
 +
 +      /* We should have received CONNECTED packet */
 +      if (ctx->type != SILC_SERVER_BACKUP_CONNECTED) {
 +      SILC_LOG_DEBUG(("Bad resume router packet"));
 +      break;
 +      }
 +
 +      SILC_LOG_DEBUG(("********************************"));
 +      SILC_LOG_DEBUG(("Received CONNECTED packet, session %d", ctx->session));
 +
 +      for (i = 0; i < ctx->sessions_count; i++) {
 +      if (ctx->sessions[i].session == ctx->session) {
 +        ctx->sessions[i].connected = TRUE;
 +        break;
 +      }
 +      }
 +
 +      for (i = 0; i < ctx->sessions_count; i++) {
 +      if (!ctx->sessions[i].connected)
 +        return;
 +      }
 +
 +      SILC_LOG_DEBUG(("********************************"));
 +      SILC_LOG_DEBUG(("Sending ENDING packet to primary"));
 +
 +      /* Send with a timeout */
 +      silc_schedule_task_add(server->schedule, 0, 
 +                           silc_server_backup_send_resumed,
 +                           protocol, 1, 0, SILC_TASK_TIMEOUT,
 +                           SILC_TASK_PRI_NORMAL);
 +      return;
 +    } else {
 +      /* Responder */
 +
 +      /* We should have been received ENDING packet */
 +      if (ctx->type != SILC_SERVER_BACKUP_ENDING) {
 +      SILC_LOG_DEBUG(("Bad resume router packet"));
 +      break;
 +      }
 +
 +      SILC_LOG_DEBUG(("********************************"));
 +      SILC_LOG_DEBUG(("Received ENDING packet, sending RESUMED packets"));
 +
 +      /* This state is received by the primary router but also servers
 +       and perhaps other routers so check that if we are the primary
 +       router of the cell then start sending RESUMED packets.  If we
 +       are normal server or one of those other routers then procede
 +       to next state. */
 +      if (server->router &&
 +        !(server->router->data.status & SILC_IDLIST_STATUS_DISABLED) &&
 +        silc_server_config_is_primary_route(server->config)) {
 +      /* We'll wait for RESUMED packet */
 +      protocol->state = SILC_PROTOCOL_STATE_END;
 +      break;
 +      }
 +
 +      /* Switch announced informations to our primary router of using the
 +       backup router. */
 +      silc_server_update_servers_by_server(server, ctx->sock->user_data, 
 +                                         server->router);
 +      silc_server_update_clients_by_server(server, ctx->sock->user_data,
 +                                         server->router, TRUE, FALSE);
 +      if (server->server_type == SILC_SERVER)
 +      silc_server_update_channels_by_server(server, ctx->sock->user_data, 
 +                                            server->router);
 +
 +      packet = silc_buffer_alloc(2);
 +      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 +
 +      /* We are the primary router, start sending RESUMED packets. */
 +      if (silc_idcache_get_all(server->local_list->servers, &list)) {
 +      if (silc_idcache_list_first(list, &id_cache)) {
 +        while (id_cache) {
 +          server_entry = (SilcServerEntry)id_cache->context;
 +          if (!server_entry || (server_entry == server->id_entry) || 
 +              !server_entry->connection || !server_entry->data.send_key) {
 +            if (!silc_idcache_list_next(list, &id_cache))
 +              break;
 +            else
 +              continue;
 +          }
 +
 +          SILC_LOG_DEBUG(("********************************"));
 +          SILC_LOG_DEBUG(("RESUMED packet (local)"));
 +
 +          server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
 +
 +          /* This connection is performing this protocol too now */
 +          ((SilcSocketConnection)server_entry->connection)->protocol =
 +            protocol;
 +
 +          if (server_entry->server_type == SILC_ROUTER)
 +            packet->data[0] = SILC_SERVER_BACKUP_RESUMED;
 +          else
 +            packet->data[0] = SILC_SERVER_BACKUP_RESUMED_GLOBAL;
 +          silc_server_packet_send(server, server_entry->connection,
 +                                  SILC_PACKET_RESUME_ROUTER, 0, 
 +                                  packet->data, packet->len, FALSE);
 +
 +          if (!silc_idcache_list_next(list, &id_cache))
 +            break;
 +        }
 +      }
 +
 +      silc_idcache_list_free(list);
 +      }
 +
 +      if (silc_idcache_get_all(server->global_list->servers, &list)) {
 +      if (silc_idcache_list_first(list, &id_cache)) {
 +        while (id_cache) {
 +          server_entry = (SilcServerEntry)id_cache->context;
 +          if (!server_entry || (server_entry == server->id_entry) || 
 +              !server_entry->connection || !server_entry->data.send_key) {
 +            if (!silc_idcache_list_next(list, &id_cache))
 +              break;
 +            else
 +              continue;
 +          }
 +
 +          SILC_LOG_DEBUG(("********************************"));
 +          SILC_LOG_DEBUG(("RESUMED packet (global)"));
 +
 +          server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
 +
 +          /* This connection is performing this protocol too now */
 +          ((SilcSocketConnection)server_entry->connection)->protocol =
 +            protocol;
 +
 +          if (server_entry->server_type == SILC_ROUTER)
 +            packet->data[0] = SILC_SERVER_BACKUP_RESUMED;
 +          else
 +            packet->data[0] = SILC_SERVER_BACKUP_RESUMED_GLOBAL;
 +          silc_server_packet_send(server, server_entry->connection,
 +                                  SILC_PACKET_RESUME_ROUTER, 0, 
 +                                  packet->data, packet->len, FALSE);
 +
 +          if (!silc_idcache_list_next(list, &id_cache))
 +            break;
 +        }
 +      }
 +
 +      silc_idcache_list_free(list);
 +      }
 +
 +      silc_buffer_free(packet);
 +
 +      SILC_LOG_INFO(("We are now the primary router of our cell again"));
 +
 +      /* For us this is the end of this protocol. */
 +      if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +      else
 +      silc_protocol_free(protocol);
 +    }
 +    break;
 +
 +  case SILC_PROTOCOL_STATE_END:
 +    {
 +      SilcIDListData idata;
 +      SilcServerEntry router, backup_router;
 +
 +      /* We should have been received RESUMED packet from our primary
 +       router. */
 +      if (ctx->type != SILC_SERVER_BACKUP_RESUMED &&
 +        ctx->type != SILC_SERVER_BACKUP_RESUMED_GLOBAL) {
 +      SILC_LOG_DEBUG(("Bad resume router packet"));
 +      break;
 +      }
 +
 +      SILC_LOG_DEBUG(("********************************"));
 +      SILC_LOG_DEBUG(("Received RESUMED packet"));
 +
 +      /* We have now new primary router. All traffic goes there from now on. */
 +      if (server->backup_router)
 +      server->server_type = SILC_BACKUP_ROUTER;
 +
 +      router = (SilcServerEntry)ctx->sock->user_data;
 +      if (silc_server_backup_replaced_get(server, router->id, 
 +                                        &backup_router)) {
 +
 +      if (backup_router == server->router) {
 +        server->id_entry->router = router;
 +        server->router = router;
 +        SILC_LOG_INFO(("Switching back to primary router %s",
 +                       server->router->server_name));
 +        SILC_LOG_DEBUG(("Switching back to primary router %s",
 +                        server->router->server_name));
 +        idata = (SilcIDListData)server->router;
 +        idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
 +      } else {
 +        SILC_LOG_INFO(("Resuming the use of router %s",
 +                       router->server_name));
 +        SILC_LOG_DEBUG(("Resuming the use of router %s",
 +                        router->server_name));
 +        idata = (SilcIDListData)router;
 +        idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
 +      }
 +
 +      /* Update the client entries of the backup router to the new 
 +         router */
 +      silc_server_update_servers_by_server(server, backup_router, router);
 +      silc_server_update_clients_by_server(server, backup_router,
 +                                           router, TRUE, FALSE);
 +      if (server->server_type == SILC_SERVER)
 +        silc_server_update_channels_by_server(server, backup_router, router);
 +      silc_server_backup_replaced_del(server, backup_router);
 +      silc_server_backup_add(server, backup_router, 
 +                             ctx->sock->ip, ctx->sock->port,
 +                             backup_router->server_type != SILC_ROUTER ?
 +                             TRUE : FALSE);
 +
 +      /* Announce all of our information to the router. */
 +      if (server->server_type == SILC_ROUTER)
 +        silc_server_announce_servers(server, FALSE, 0, router->connection);
 +
 +      /* Announce our clients and channels to the router */
 +      silc_server_announce_clients(server, 0, router->connection);
 +      silc_server_announce_channels(server, 0, router->connection);
 +      }
 +
 +      /* Protocol has ended, call the final callback */
 +      if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +      else
 +      silc_protocol_free(protocol);
 +    }
 +    break;
 +
 +  case SILC_PROTOCOL_STATE_ERROR:
 +    /* Protocol has ended, call the final callback */
 +    if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +    else
 +      silc_protocol_free(protocol);
 +    break;
 +
 +  case SILC_PROTOCOL_STATE_FAILURE:
 +    /* Protocol has ended, call the final callback */
 +    if (protocol->final_callback)
 +      silc_protocol_execute_final(protocol, server->schedule);
 +    else
 +      silc_protocol_free(protocol);
 +    break;
 +
 +  case SILC_PROTOCOL_STATE_UNKNOWN:
 +    break;
 +  }
 +}
 +
 +SILC_TASK_CALLBACK(silc_server_protocol_backup_done)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcServerBackupProtocolContext ctx = protocol->context;
 +  SilcServer server = ctx->server;
 +  SilcServerEntry server_entry;
 +  SilcSocketConnection sock;
 +  SilcIDCacheList list;
 +  SilcIDCacheEntry id_cache;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
 +      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
 +    SILC_LOG_ERROR(("Error occurred during backup router resuming protcool"));
 +  }
 +
 +  /* Remove this protocol from all server entries that has it */
 +  if (silc_idcache_get_all(server->local_list->servers, &list)) {
 +    if (silc_idcache_list_first(list, &id_cache)) {
 +      while (id_cache) {
 +      server_entry = (SilcServerEntry)id_cache->context;
 +      sock = (SilcSocketConnection)server_entry->connection;
 +
 +      if (sock->protocol == protocol) {
 +        sock->protocol = NULL;
 +
 +        if (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)
 +          server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
 +      }
 +      
 +      if (!silc_idcache_list_next(list, &id_cache))
 +        break;
 +      }
 +    }
 +    silc_idcache_list_free(list);
 +  }
 +
 +  if (silc_idcache_get_all(server->global_list->servers, &list)) {
 +    if (silc_idcache_list_first(list, &id_cache)) {
 +      while (id_cache) {
 +      server_entry = (SilcServerEntry)id_cache->context;
 +      sock = (SilcSocketConnection)server_entry->connection;
 +
 +      if (sock->protocol == protocol) {
 +        sock->protocol = NULL;
 +
 +        if (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)
 +          server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
 +      }
 +      
 +      if (!silc_idcache_list_next(list, &id_cache))
 +        break;
 +      }
 +    }
 +    silc_idcache_list_free(list);
 +  }
 +
 +  if (ctx->sock->protocol)
 +    ctx->sock->protocol = NULL;
 +  silc_protocol_free(protocol);
 +  silc_free(ctx->sessions);
 +  silc_free(ctx);
 +}
index 5a8f731d0b0414570a6701e05e2026a239dd21ba,2f617c2621c8676c31b8513a41c081c924461391..e51039d619484e970556ec1c4765f1d4c85226fe
  
    serverconfig.c
  
--  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
++  Author: Johnny Mnemonic <johnny@themnemonic.org>
  
--  Copyright (C) 1997 - 2000 Pekka Riikonen
++  Copyright (C) 1997 - 2002 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
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
--  
++
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  
  */
 -/*
 - * $Id$
 - * $Log$
 - * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
 - *    Importet from internal CVS/Added Log headers.
 - *
 - *
 - */
 +/* $Id$ */
  
  #include "serverincludes.h"
  #include "server_internal.h"
  
- SilcServerConfigSection silc_server_config_sections[] = {
-   { "[Cipher]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER, 4 },
-   { "[PKCS]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 1 },
-   { "[Hash]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION, 4 },
-   { "[hmac]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_HMAC, 3 },
-   { "[ServerKeys]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS, 2 },
-   { "[ServerInfo]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO, 4 },
-   { "[AdminInfo]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO, 4 },
-   { "[ListenPort]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT, 3 },
-   { "[Identity]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY, 2 },
-   { "[Logging]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING, 3 },
-   { "[ConnectionClass]", 
-     SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS, 4 },
-   { "[ClientConnection]", 
-     SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION, 5 },
-   { "[ServerConnection]", 
-     SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION, 6 },
-   { "[RouterConnection]", 
-     SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION, 7 },
-   { "[AdminConnection]", 
-     SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION, 5 },
-   { "[DenyConnection]", 
-     SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION, 3 },
-   { "[motd]", 
-     SILC_CONFIG_SERVER_SECTION_TYPE_MOTD, 1 },
-   { "[pid]",
-     SILC_CONFIG_SERVER_SECTION_TYPE_PID, 1},
-   
-   { NULL, SILC_CONFIG_SERVER_SECTION_TYPE_NONE, 0 }
- };
 -/*  XXX
 -   All possible configuration sections for SILC server. 
--
- /* Allocates a new configuration object, opens configuration file and
-    parses the file. The parsed data is returned to the newly allocated
-    configuration object. */
 -   <Cipher>
 -
 -       Format:
 -
 -       +<Cipher name>:<SIM path>
 -
 -   <PKCS>
 -
 -       Format:
 -
 -       +<PKCS name>:<key length>
 -
 -   <HashFunction>
 -
 -       Format:
 -
 -       +<Hash function name>:<SIM path>
 -
 -   <ServerInfo>
 -
 -       This section is used to set the server informations.
 -
 -       Format:
 -
 -       +<Server DNS name>:<Server IP>:<Geographic location>:<Port>
 -
 -   <AdminInfo>
 -
 -       This section is used to set the server's administrative information.
 -
 -       Format:
 -
 -       +<Location>:<Server type>:<Admin's name>:<Admin's email address>
 -
 -   <ListenPort>
 -
 -       This section is used to set ports the server is listenning.
 -
 -       Format:
 -
 -       +<Local IP/UNIX socket path>:<Remote IP>:<Port>
 -
 -   <Logging>
 -
 -       This section is used to set various logging files, their paths
 -       and maximum sizes. All the other directives except those defined
 -       below are ignored in this section. Log files are purged after they
 -       reach the maximum set byte size.
 -
 -       Format:
 -
 -       +infologfile:<path>:<max byte size>
 -       +errorlogfile:<path>:<max byte size>
 -
 -   <ConnectionClass>
 -
 -       This section is used to define connection classes. These can be
 -       used to optimize the server and the connections.
 -
 -       Format:
 -
 -       +<Class number>:<Ping freq>:<Connect freq>:<Max links>
 -
 -   <ClientAuth>
 -
 -       This section is used to define client authentications.
 -
 -       Format:
 -
 -       +<Remote address or name>:<auth method>:<password/cert/key/???>:<Port>:<Class>
 -
 -   <AdminAuth>
 -
 -       This section is used to define the server's administration 
 -       authentications.
 -
 -       Format:
 -
 -       +<Hostname>:<auth method>:<password/cert/key/???>:<Nickname hash>:<Class>
 -
 -   <ServerConnection>
++#define SILC_CONFIG_SERVER_AUTH_METH_PASSWD "passwd"
++#define SILC_CONFIG_SERVER_AUTH_METH_PUBKEY "pubkey"
  
- SilcServerConfig silc_server_config_alloc(char *filename)
- {
-   SilcServerConfig new;
-   SilcBuffer buffer;
-   SilcServerConfigParse config_parse;
 -       This section is used to define the server connections to this
 -       server/router. Only routers can have normal server connections.
 -       Normal servers leave this section epmty. The remote server cannot be
 -       older than specified Version ID.
--
-   SILC_LOG_DEBUG(("Allocating new configuration object"));
 -       Format:
 -
 -       +<Remote address or name>:<auth method>:<password/key/???>:<Port>:<Version ID>:<Class>
 -
 -   <RouterConnection>
 -
 -       This section is used to define the router connections to this
 -       server/router. Both normal server and router can have router
 -       connections. Normal server usually has only one connection while
 -       a router can have multiple. The remote server cannot be older than
 -       specified Version ID.
 -
 -       Format:
 -
 -       +<Remote address or name>:<auth method>:<password/key/???>:<Port>:<Version ID>:<Class>
 -
 -   <DenyConnection>
 -
 -       This section is used to deny specific connections to your server. This
 -       can be used to deny both clients and servers.
 -
 -       Format:
 -
 -       +<Remote address or name or nickname>:<Time interval>:<Comment>:<Port>
 -
 -   <RedirectClient>
 -
 -       This section is used to set the alternate servers that clients will be
 -       redirected to when our server is full.
 -
 -       Format:
 -
 -       +<Remote address or name>:<Port>
++#if 0
++#define SERVER_CONFIG_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
++#else
++#define SERVER_CONFIG_DEBUG(fmt)
++#endif
  
-   new = silc_calloc(1, sizeof(*new));
-   if (!new) {
-     fprintf(stderr, "Could not allocate new configuration object");
-     return NULL;
 -*/
 -SilcConfigServerSection silc_config_server_sections[] = {
 -  { "[Cipher]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER, 4 },
 -  { "[PKCS]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 2 },
 -  { "[HashFunction]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION, 4 },
 -  { "[ServerInfo]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO, 4 },
 -  { "[AdminInfo]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO, 4 },
 -  { "[ListenPort]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT, 3 },
 -  { "[Logging]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING, 3 },
 -  { "[ConnectionClass]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS, 4 },
 -  { "[ClientConnection]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION, 5 },
 -  { "[ServerConnection]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION, 6 },
 -  { "[RouterConnection]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION, 6 },
 -  { "[AdminConnection]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION, 5 },
 -  { "[DenyConnection]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION, 4 },
 -  { "[RedirectClient]", 
 -    SILC_CONFIG_SERVER_SECTION_TYPE_REDIRECT_CLIENT, 2 },
 -  
 -  { NULL, SILC_CONFIG_SERVER_SECTION_TYPE_NONE, 0 }
 -};
++/* auto-declare needed variables for the common list parsing */
++#define SILC_SERVER_CONFIG_SECTION_INIT(__type__)                     \
++  SilcServerConfig config = (SilcServerConfig) context;                       \
++  __type__ *findtmp, *tmp = (__type__ *) config->tmp;                 \
++  int got_errno = 0
++
++/* append the tmp field to the specified list */
++#define SILC_SERVER_CONFIG_LIST_APPENDTMP(__list__)                   \
++  if (!__list__)                                                      \
++    __list__ = tmp;                                                   \
++  else {                                                              \
++    for (findtmp = __list__; findtmp->next; findtmp = findtmp->next); \
++    findtmp->next = tmp;                                              \
 +  }
  
-   new->filename = filename;
-   SILC_LOG_DEBUG(("Loading config data from `%s'", filename));
-   /* Open configuration file and parse it */
-   config_parse = NULL;
-   buffer = NULL;
-   silc_config_open(filename, &buffer);
-   if (!buffer)
-     goto fail;
-   if ((silc_server_config_parse(new, buffer, &config_parse)) == FALSE)
-     goto fail;
-   if ((silc_server_config_parse_lines(new, config_parse)) == FALSE)
-     goto fail;
-   silc_buffer_free(buffer);
-   return new;
-  fail:
-   silc_buffer_free(buffer);
-   silc_free(new);
-   return NULL;
- }
 -/* Allocates a new configuration object, opens configuration file and
 -   parses the file. The parsed data is returned to the newly allocated
 -   configuration object. */
++/* loops all elements in a list and provides a di struct pointer of the
++ * specified type containing the current element */
++#define SILC_SERVER_CONFIG_LIST_DESTROY(__type__, __list__)           \
++  for (tmp = (void *) __list__; tmp;) {                                       \
++    __type__ *di = (__type__ *) tmp;                                  \
++    tmp = (void *) di->next;
  
- /* Free's a configuration object. */
- void silc_server_config_free(SilcServerConfig config)
 -SilcConfigServer silc_config_server_alloc(char *filename)
++/* free an authdata according to its auth method */
++static void my_free_authdata(SilcAuthMethod auth_meth, void *auth_data)
  {
-   if (config) {
-     silc_free(config->filename);
-     silc_free(config->server_keys);
-     silc_free(config->server_info);
-     silc_free(config->admin_info);
-     silc_free(config->listen_port);
-     silc_free(config->identity);
-     silc_free(config->conn_class);
-     silc_free(config->clients);
-     silc_free(config->admins);
-     silc_free(config->servers);
-     silc_free(config->routers);
-     silc_free(config->denied);
-     silc_free(config->motd);
-     silc_free(config->pidfile);
-     silc_free(config);
 -  SilcConfigServer new;
 -  SilcBuffer buffer;
 -  SilcConfigServerParse config_parse;
 -
 -  SILC_LOG_DEBUG(("Allocating new configuration object"));
 -
 -  new = silc_calloc(1, sizeof(*new));
 -  if (!new) {
 -    fprintf(stderr, "Could not allocate new configuration object");
 -    return NULL;
++  if (auth_meth == SILC_AUTH_PASSWORD) {
++    silc_free(auth_data);
++  } else if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
++    silc_pkcs_public_key_free((SilcPublicKey) auth_data);
    }
 -
 -  new->filename = filename;
 -
 -  /* Open configuration file and parse it */
 -  config_parse = NULL;
 -  buffer = NULL;
 -  silc_config_open(filename, &buffer);
 -  if (!buffer)
 -    goto fail;
 -  if ((silc_config_server_parse(new, buffer, &config_parse)) == FALSE)
 -    goto fail;
 -  if ((silc_config_server_parse_lines(new, config_parse)) == FALSE)
 -    goto fail;
 -
 -  silc_free(buffer);
 -
 -  return new;
 -
 - fail:
 -  silc_free(new);
 -  return NULL;
  }
  
- /* Parses the the buffer and returns the parsed lines into return_config
-    argument. The return_config argument doesn't have to be initialized 
-    before calling this. It will be initialized during the parsing. The
-    buffer sent as argument can be safely free'd after this function has
-    succesfully returned. */
 -/* Free's a configuration object. */
--
- int silc_server_config_parse(SilcServerConfig config, SilcBuffer buffer, 
-                            SilcServerConfigParse *return_config)
 -void silc_config_server_free(SilcConfigServer config)
++/* parse an authdata according to its auth method */
++static bool my_parse_authdata(SilcAuthMethod auth_meth, char *p, uint32 line,
++                            void **auth_data, uint32 *auth_data_len)
  {
-   int i, begin, linenum;
-   char line[1024], *cp;
-   SilcServerConfigSection *cptr = NULL;
-   SilcServerConfigParse parse = *return_config, first = NULL;
-   SILC_LOG_DEBUG(("Parsing configuration file"));
-   begin = 0;
-   linenum = 0;
-   while((begin = silc_gets(line, sizeof(line),
-                          buffer->data, buffer->len, begin)) != EOF) {
-     cp = line;
-     linenum++;
-     /* Check for bad line */
-     if (silc_check_line(cp))
-       continue;
-     /* Remove tabs and whitespaces from the line */
-     if (strchr(cp, '\t')) {
-       i = 0;
-       while(strchr(cp + i, '\t')) {
-       *strchr(cp + i, '\t') = ' ';
-       i++;
-       }
-     }
-     for (i = 0; i < strlen(cp); i++) {
-       if (cp[i] != ' ') {
-       if (i)
-         cp++;
-       break;
-       }
-       cp++;
-     }
-     /* Parse line */
-     switch(cp[0]) {
-     case '[':
-       /*
-        * Start of a section
-        */
-       /* Remove new line sign */
-       if (strchr(cp, '\n'))
-       *strchr(cp, '\n') = '\0';
-       
-       /* Check for matching sections */
-       for (cptr = silc_server_config_sections; cptr->section; cptr++)
-       if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
-         break;
-       if (!cptr->section) {
-       fprintf(stderr, "%s:%d: Unknown section `%s'\n", 
-                       config->filename, linenum, cp);
 -  if (config) {
 -    silc_free(config->filename);
 -    silc_free(config->server_info);
 -    silc_free(config->admin_info);
 -    silc_free(config->listen_port);
 -    silc_free(config->conn_class);
 -    silc_free(config->clients);
 -    silc_free(config->admins);
 -    silc_free(config->servers);
 -    silc_free(config->routers);
 -    silc_free(config->denied);
 -    silc_free(config->redirect);
 -    silc_free(config);
++  if (auth_meth == SILC_AUTH_PASSWORD) {
++    /* p is a plain text password */
++    *auth_data = (void *) strdup(p);
++    *auth_data_len = (uint32) strlen(p);
++  } else if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
++    /* p is a public key */
++    SilcPublicKey public_key;
++
++    if (!silc_pkcs_load_public_key(p, &public_key, SILC_PKCS_FILE_PEM))
++      if (!silc_pkcs_load_public_key(p, &public_key, SILC_PKCS_FILE_BIN)) {
++      fprintf(stderr, "\nError while parsing config file at line %lu: "
++              "Could not load public key file!\n", line);
 +      return FALSE;
 +      }
-       break;
-     default:
-       /*
-        * Start of a configuration line
-        */
-       if (cptr->type != SILC_CONFIG_SERVER_SECTION_TYPE_NONE) {
-       
-       if (strchr(cp, '\n'))
-           *strchr(cp, '\n') = ':';
-       if (parse == NULL) {
-         parse = silc_calloc(1, sizeof(*parse));
-         parse->line = NULL;
-         parse->section = NULL;
-         parse->next = NULL;
-         parse->prev = NULL;
-       } else {
-         if (parse->next == NULL) {
-           parse->next = silc_calloc(1, sizeof(*parse->next));
-           parse->next->line = NULL;
-           parse->next->section = NULL;
-           parse->next->next = NULL;
-           parse->next->prev = parse;
-           parse = parse->next;
-         }
-       }
-       
-       if (first == NULL)
-         first = parse;
-       /* Add the line to parsing structure for further parsing. */
-       if (parse) {
-         parse->section = cptr;
-         parse->line = silc_buffer_alloc(strlen(cp) + 1);
-         parse->linenum = linenum;
-         silc_buffer_pull_tail(parse->line, strlen(cp));
-         silc_buffer_put(parse->line, cp, strlen(cp));
-       }
-       }
-       break;
-     }
++    *auth_data = (void *) public_key;
++    *auth_data_len = 0;
++  } else {
++    fprintf(stderr, "\nError while parsing config file at line %lu: Specify "
++              "the AuthMethod before specifying the AuthData.\n", line);
++    return FALSE;
    }
-   
-   /* Set the return_config argument to its first value so that further
-      parsing can be started from the first line. */
-   *return_config = first;
 +  return TRUE;
  }
  
- /* Parses the lines earlier read from configuration file. The config object
-    must not be initialized, it will be initialized in this function. The
-    parse_config argument is uninitialized automatically during this
-    function. */
 -/* Parses the the buffer and returns the parsed lines into return_config
 -   argument. The return_config argument doesn't have to be initialized 
 -   before calling this. It will be initialized during the parsing. The
 -   buffer sent as argument can be safely free'd after this function has
 -   succesfully returned. */
++/* Callbacks */
  
- int silc_server_config_parse_lines(SilcServerConfig config, 
-                                  SilcServerConfigParse parse_config)
 -int silc_config_server_parse(SilcConfigServer config, SilcBuffer buffer, 
 -                           SilcConfigServerParse *return_config)
++SILC_CONFIG_CALLBACK(fetch_generic)
  {
-   int ret, check = FALSE;
-   uint32 checkmask;
-   char *tmp;
-   SilcServerConfigParse pc = parse_config;
-   SilcBuffer line;
-   SILC_LOG_DEBUG(("Parsing configuration lines"));
-   
-   if (!config)
-     return FALSE;
-   
-   checkmask = 0;
-   while(pc) {
-     check = FALSE;
-     line = pc->line;
-     /* Get number of tokens in line */
-     ret = silc_config_check_num_token(line);
-     if (ret < pc->section->maxfields) {
-       /* Bad line */
-       fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
-             config->filename, pc->linenum, ret, 
-             pc->section->maxfields);
-       break;
-     }
-     /* Parse the line */
-     switch(pc->section->type) {
-     case SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->cipher);
-       /* Get cipher name */
-       ret = silc_config_get_token(line, &config->cipher->alg_name);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Cipher name not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       /* Get module name */
-       config->cipher->sim_name = NULL;
-       ret = silc_config_get_token(line, &config->cipher->sim_name);
-       if (ret < 0)
-       break;
-       /* Get key length */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Cipher key length not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       config->cipher->key_len = atoi(tmp);
-       silc_free(tmp);
-       /* Get block length */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Cipher block length not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       config->cipher->block_len = atoi(tmp);
-       silc_free(tmp);
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_PKCS:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->pkcs);
-       /* Get PKCS name */
-       ret = silc_config_get_token(line, &config->pkcs->alg_name);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: PKCS name not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->hash_func);
-       /* Get Hash function name */
-       ret = silc_config_get_token(line, &config->hash_func->alg_name);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Hash function name not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       
-       /* Get Hash function module name */
-       config->hash_func->sim_name = NULL;
-       ret = silc_config_get_token(line, &config->hash_func->sim_name);
-       if (ret < 0)
-       break;
-       /* Get block length */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Hash function block length not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       config->hash_func->block_len = atoi(tmp);
-       silc_free(tmp);
-       /* Get hash length */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Hash function hash length not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       config->hash_func->key_len = atoi(tmp);
-       silc_free(tmp);
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_HMAC:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->hmac);
-       /* Get HMAC name */
-       ret = silc_config_get_token(line, &config->hmac->alg_name);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: HMAC name not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       /* Get hash name */
-       ret = silc_config_get_token(line, &config->hmac->sim_name);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Hash function name not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       
-       /* Get MAC length */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: HMAC's MAC length not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       config->hmac->key_len = atoi(tmp);
-       silc_free(tmp);
 -  int i, begin;
 -  unsigned int linenum;
 -  char line[1024], *cp;
 -  SilcConfigServerSection *cptr = NULL;
 -  SilcConfigServerParse parse = *return_config, first = NULL;
--
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
 -  SILC_LOG_DEBUG(("Parsing configuration file"));
--
-     case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS:
 -  begin = 0;
 -  linenum = 0;
 -  while((begin = silc_gets(line, sizeof(line), 
 -                         buffer->data, buffer->len, begin)) != EOF) {
 -    cp = line;
 -    linenum++;
--
-       if (!config->server_keys)
-       config->server_keys = silc_calloc(1, sizeof(*config->server_keys));
 -    /* Check for bad line */
 -    if (silc_check_line(cp))
 -      continue;
--
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Public key name not defined\n",
-               config->filename, pc->linenum);
-       break;
 -    /* Remove tabs and whitespaces from the line */
 -    if (strchr(cp, '\t')) {
 -      i = 0;
 -      while(strchr(cp + i, '\t')) {
 -      *strchr(cp + i, '\t') = ' ';
 -      i++;
--      }
-       
-       if (!silc_pkcs_load_public_key(tmp, &config->server_keys->public_key, 
-                                    SILC_PKCS_FILE_PEM))
-       if (!silc_pkcs_load_public_key(tmp, &config->server_keys->public_key, 
-                                      SILC_PKCS_FILE_BIN)) {
-         fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
-       silc_free(tmp);
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Private key name not defined\n",
-               config->filename, pc->linenum);
 -    }
 -    for (i = 0; i < strlen(cp); i++) {
 -      if (cp[i] != ' ') {
 -      if (i)
 -        cp++;
--      break;
--      }
-       
-       if (!silc_pkcs_load_private_key(tmp, &config->server_keys->private_key, 
-                                    SILC_PKCS_FILE_BIN))
-       if (!silc_pkcs_load_private_key(tmp, 
-                                       &config->server_keys->private_key, 
-                                       SILC_PKCS_FILE_PEM)) {
-         fprintf(stderr, "%s:%d: Could not load private key file `%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
-       silc_free(tmp);
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO:
-       if (!config->server_info)
-       config->server_info = silc_calloc(1, sizeof(*config->server_info));
 -      cp++;
 -    }
--
-       /* Get server name */
-       ret = silc_config_get_token(line, &config->server_info->server_name);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       /* Server name not defined */
 -    /* Parse line */
 -    switch(cp[0]) {
 -    case '[':
 -      /*
 -       * Start of a section
 -       */
--
-       }
 -      /* Remove new line sign */
 -      if (strchr(cp, '\n'))
 -      *strchr(cp, '\n') = '\0';
--      
-       /* Get server IP */
-       ret = silc_config_get_token(line, &config->server_info->server_ip);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       /* Server IP not defined */
-       }
-       /* Get server location */
-       ret = silc_config_get_token(line, &config->server_info->location);
-       if (ret < 0)
-       break;
-       /* Get server port */
-       /* XXX: Need port here??? */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       /* Port not defined */
-       }
-       config->server_info->port = atoi(tmp);
-       silc_free(tmp);
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO:
-       if (!config->admin_info)
-       config->admin_info = silc_calloc(1, sizeof(*config->admin_info));
-       /* Get location */
-       ret = silc_config_get_token(line, &config->admin_info->location);
-       if (ret < 0)
-       break;
-       /* Get server type */
-       ret = silc_config_get_token(line, &config->admin_info->server_type);
-       if (ret < 0)
-       break;
-       /* Get admins name */
-       ret = silc_config_get_token(line, &config->admin_info->admin_name);
-       if (ret < 0)
-       break;
-       /* Get admins email address */
-       ret = silc_config_get_token(line, &config->admin_info->admin_email);
-       if (ret < 0)
-       break;
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->listen_port);
-       /* Get local IP */
-       ret = silc_config_get_token(line, &config->listen_port->local_ip);
-       if (ret < 0)
-       break;
-       /* Get listener IP */
-       ret = silc_config_get_token(line, &config->listen_port->listener_ip);
-       if (ret < 0)
-       break;
-       /* Get port */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       /* Any port */
-       config->listen_port->port = 0;
-       } else {
-       config->listen_port->port = atoi(tmp);
-       silc_free(tmp);
-       }
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY:
-       if (!config->identity)
-         config->identity = silc_calloc(1, sizeof(*config->identity));
-       /* Get user */
-       ret = silc_config_get_token(line, &config->identity->user);
-       if (ret < 0)
-         break;
-       /* Get group */
-       ret = silc_config_get_token(line, &config->identity->group);
-       if (ret < 0)
-         break;
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-     case SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->conn_class);
-       /* Get class number */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       /* Class number not defined */
-       }
-       config->conn_class->class = atoi(tmp);
-       silc_free(tmp);
-       /* Get ping frequency */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       config->conn_class->ping_freq = atoi(tmp);
-       silc_free(tmp);
-       /* Get connect frequency */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       config->conn_class->connect_freq = atoi(tmp);
-       silc_free(tmp);
-       /* Get max links */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       config->conn_class->max_links = atoi(tmp);
-       silc_free(tmp);
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->logging);
-       /* Get log section type and check it */
-       ret = silc_config_get_token(line, &config->logging->logtype);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Log file section not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       if (strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_INFO)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_WARNING)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_ERROR)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_FATAL)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LO_QUICK)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LO_FDELAY)) {
-       fprintf(stderr, "%s:%d: Unknown log file section '%s'\n",
-               config->filename, pc->linenum, config->logging->logtype);
-       break;
-       }
-       /* Get log filename */
-       ret = silc_config_get_token(line, &config->logging->filename);
-       if (ret < 0)
-       break;
-       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Log file name not defined\n",
-               config->filename, pc->linenum);
-       break;
-       }
-       /* Get max byte size */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret) {
-       config->logging->maxsize = atoi(tmp);
-       silc_free(tmp);
-       }
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->clients);
-       /* Get host */
-       ret = silc_config_get_token(line, &config->clients->host);
-       if (ret < 0)
-       break;
-       if (ret == 0)
-       /* Any host */
-       config->clients->host = strdup("*");
-       /* Get authentication method */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->clients->auth_meth = SILC_AUTH_PASSWORD;
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->clients->auth_meth = SILC_AUTH_PUBLIC_KEY;
-       silc_free(tmp);
-       }
-       /* Get authentication data */
-       ret = silc_config_get_token(line, (char **)&config->clients->auth_data);
-       if (ret < 0)
-       break;
-       if (config->clients->auth_meth == SILC_AUTH_PASSWORD) {
-       config->clients->auth_data_len = strlen(config->clients->auth_data);
-       } else if (config->clients->auth_meth == SILC_AUTH_PUBLIC_KEY) {
-       /* Get the public key */
-       SilcPublicKey public_key;
-       if (!silc_pkcs_load_public_key(config->clients->auth_data,
-                                      &public_key, SILC_PKCS_FILE_PEM))
-         if (!silc_pkcs_load_public_key(config->clients->auth_data,
-                                        &public_key, SILC_PKCS_FILE_BIN)) {
-           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                   config->filename, pc->linenum, 
-                   (char *)config->clients->auth_data);
-           break;
-         }
-       silc_free(config->clients->auth_data);
-       config->clients->auth_data = (void *)public_key;
-       config->clients->auth_data_len = 0;
-       }
-       /* Get port */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret) {
-       config->clients->port = atoi(tmp);
-       silc_free(tmp);
-       }
-       /* Get class number */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret) {
-       config->clients->class = atoi(tmp);
-       silc_free(tmp);
-       }
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION:
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->servers);
-       /* Get host */
-       ret = silc_config_get_token(line, &config->servers->host);
-       if (ret < 0)
-       break;
-       if (ret == 0)
-       /* Any host */
-       config->servers->host = strdup("*");
-       /* Get authentication method */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
 -      /* Check for matching sections */
 -      for (cptr = silc_config_server_sections; cptr->section; cptr++)
 -      if (!strcmp(cp, cptr->section))
--        break;
-       }
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->servers->auth_meth = SILC_AUTH_PASSWORD;
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->servers->auth_meth = SILC_AUTH_PUBLIC_KEY;
-       silc_free(tmp);
-       }
-       /* Get authentication data */
-       ret = silc_config_get_token(line, (char **)&config->servers->auth_data);
-       if (ret < 0)
-       break;
-       if (config->servers->auth_meth == SILC_AUTH_PASSWORD) {
-       config->servers->auth_data_len = strlen(config->servers->auth_data);
-       } else if (config->servers->auth_meth == SILC_AUTH_PUBLIC_KEY) {
-       /* Get the public key */
-       SilcPublicKey public_key;
-       if (!silc_pkcs_load_public_key(config->servers->auth_data,
-                                      &public_key, SILC_PKCS_FILE_PEM))
-         if (!silc_pkcs_load_public_key(config->servers->auth_data,
-                                        &public_key, SILC_PKCS_FILE_BIN)) {
-           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                   config->filename, pc->linenum, 
-                   (char *)config->servers->auth_data);
-           break;
-         }
-       silc_free(config->servers->auth_data);
-       config->servers->auth_data = (void *)public_key;
-       config->servers->auth_data_len = 0;
-       }
-       /* Get port */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret) {
-       config->servers->port = atoi(tmp);
-       silc_free(tmp);
-       }
-       /* Get version */
-       ret = silc_config_get_token(line, &config->servers->version);
-       if (ret < 0)
-       break;
++  SilcServerConfig config = (SilcServerConfig) context;
  
-       /* Get class number */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret) {
-       config->servers->class = atoi(tmp);
-       silc_free(tmp);
 -      if (!cptr->section) {
 -      fprintf(stderr, "%s:%d: Unknown section `%s'\n", 
 -                      config->filename, linenum, cp);
 -      return FALSE;
--      }
++  if (!strcmp(name, "modulepath")) {
++    if (config->module_path) return SILC_CONFIG_EDOUBLE;
++    /* dup it only if non-empty, otherwise point it to NULL */
++    config->module_path = (*(char *)val ? strdup((char *) val) : NULL);
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++}
  
-       /* Check whether this connection is backup router connection */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret != -1) {
-       config->servers->backup_router = atoi(tmp);
-       if (config->servers->backup_router != 0)
-         config->servers->backup_router = TRUE;
-       silc_free(tmp);
 -      break;
 -    default:
 -      /*
 -       * Start of a configuration line
 -       */
 -
 -      if (cptr->type != SILC_CONFIG_SERVER_SECTION_TYPE_NONE) {
 -      
 -      if (strchr(cp, '\n'))
 -          *strchr(cp, '\n') = ':';
 -
 -      if (parse == NULL) {
 -        parse = silc_calloc(1, sizeof(*parse));
 -        parse->line = NULL;
 -        parse->section = NULL;
 -        parse->next = NULL;
 -        parse->prev = NULL;
 -      } else {
 -        if (parse->next == NULL) {
 -          parse->next = silc_calloc(1, sizeof(*parse->next));
 -          parse->next->line = NULL;
 -          parse->next->section = NULL;
 -          parse->next->next = NULL;
 -          parse->next->prev = parse;
 -          parse = parse->next;
 -        }
 -      }
 -      
 -      if (first == NULL)
 -        first = parse;
 -
 -      /* Add the line to parsing structure for further parsing. */
 -      if (parse) {
 -        parse->section = cptr;
 -        parse->line = silc_buffer_alloc(strlen(cp) + 1);
 -        parse->linenum = linenum;
 -        silc_buffer_pull_tail(parse->line, strlen(cp));
 -        silc_buffer_put(parse->line, cp, strlen(cp));
 -      }
--      }
 -      break;
++SILC_CONFIG_CALLBACK(fetch_cipher)
++{
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionCipher);
++
++  SERVER_CONFIG_DEBUG(("Received CIPHER type=%d name=\"%s\" (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    /* check the temporary struct's fields */
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (!tmp->name) {
++      got_errno = SILC_CONFIG_EMISSFIELDS;
++      goto got_err;
+     }
++    /* the temporary struct is ok, append it to the list */
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->cipher);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionCipher *) config->tmp;
+   }
 -  
 -  /* Set the return_config argument to its first value so that further
 -     parsing can be started from the first line. */
 -  *return_config = first;
  
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
 -  return TRUE;
++  /* Identify and save this value */
++  if (!strcmp(name, "name")) {
++    if (tmp->name) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->name = strdup((char *) val);
++  }
++  else if (!strcmp(name, "module")) { /* can be empty */
++    if (tmp->module) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    /* dup it only if non-empty, otherwise point it to NULL */
++    tmp->module = (*(char *)val ? strdup((char *) val) : NULL);
++  }
++  else if (!strcmp(name, "key_length"))
++    tmp->key_length = *(uint32 *)val;
++  else if (!strcmp(name, "block_length"))
++    tmp->block_length = *(uint32 *)val;
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->name);
++  silc_free(tmp->module);
++  silc_free(tmp);
++  config->tmp = NULL;
++  return got_errno;
+ }
  
-     case SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION:
 -/* Parses the lines earlier read from configuration file. The config object
 -   must not be initialized, it will be initialized in this function. The
 -   parse_config argument is uninitialized automatically during this
 -   function. */
 -
 -int silc_config_server_parse_lines(SilcConfigServer config, 
 -                                 SilcConfigServerParse parse_config)
++SILC_CONFIG_CALLBACK(fetch_hash)
+ {
 -  int ret, check = FALSE;
 -  unsigned int checkmask;
 -  char *tmp;
 -  SilcConfigServerParse pc = parse_config;
 -  SilcBuffer line;
 -
 -  SILC_LOG_DEBUG(("Parsing configuration lines"));
 -  
 -  if (!config)
 -    return FALSE;
 -  
 -  checkmask = 0;
 -  while(pc) {
 -    check = FALSE;
 -    line = pc->line;
 -
 -    /* Get number of tokens in line */
 -    ret = silc_config_check_num_token(line);
 -    if (ret != pc->section->maxfields) {
 -      /* Bad line */
 -      fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
 -            config->filename, pc->linenum, ret, 
 -            pc->section->maxfields);
 -      break;
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionHash);
++
++  SERVER_CONFIG_DEBUG(("Received HASH type=%d name=%s (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    /* check the temporary struct's fields */
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (!tmp->name || (tmp->block_length == 0) || (tmp->digest_length == 0)) {
++      got_errno = SILC_CONFIG_EMISSFIELDS;
++      goto got_err;
+     }
++    /* the temporary struct in tmp is ok */
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->hash);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionHash *) config->tmp;
++  }
  
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->routers);
 -    /* Parse the line */
 -    switch(pc->section->type) {
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER:
 -
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->cipher);
 -
 -      /* Get cipher name */
 -      ret = silc_config_get_token(line, &config->cipher->alg_name);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: Cipher name not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -
 -      /* Get module name */
 -      config->cipher->sim_name = NULL;
 -      ret = silc_config_get_token(line, &config->cipher->sim_name);
 -      if (ret < 0)
 -      break;
 -
 -      /* Get block length */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: Cipher block length not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -      config->cipher->block_len = atoi(tmp);
 -      silc_free(tmp);
 -
 -      /* Get key length */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: Cipher key length not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -      config->cipher->key_len = atoi(tmp);
 -      silc_free(tmp);
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_PKCS:
 -
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->pkcs);
 -
 -      /* Get PKCS name */
 -      ret = silc_config_get_token(line, &config->pkcs->alg_name);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: PKCS name not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -
 -      /* Get key length */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: PKCS key length not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -      config->pkcs->key_len = atoi(tmp);
 -      silc_free(tmp);
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION:
 -
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->hash_func);
 -
 -      /* Get Hash function name */
 -      ret = silc_config_get_token(line, &config->hash_func->alg_name);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: Hash function name not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -      
 -      /* Get Hash function module name */
 -      config->hash_func->sim_name = NULL;
 -      ret = silc_config_get_token(line, &config->hash_func->sim_name);
 -      if (ret < 0)
 -      break;
 -
 -      /* Get block length */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: Hash function block length not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -      config->hash_func->block_len = atoi(tmp);
 -      silc_free(tmp);
 -
 -      /* Get hash length */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: Hash function hash length not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -      config->hash_func->key_len = atoi(tmp);
 -      silc_free(tmp);
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO:
 -
 -      if (!config->server_info)
 -      config->server_info = silc_calloc(1, sizeof(*config->server_info));
 -
 -      /* Get server name */
 -      ret = silc_config_get_token(line, &config->server_info->server_name);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      /* Server name not defined */
 -
 -      }
 -      
 -      /* Get server IP */
 -      ret = silc_config_get_token(line, &config->server_info->server_ip);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      /* Server IP not defined */
 -
 -      }
 -
 -      /* Get server location */
 -      ret = silc_config_get_token(line, &config->server_info->location);
 -      if (ret < 0)
 -      break;
 -
 -      /* Get server port */
 -      /* XXX: Need port here??? */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      /* Port not defined */
 -
 -      }
 -      config->server_info->port = atoi(tmp);
 -      silc_free(tmp);
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO:
 -
 -      if (!config->admin_info)
 -      config->admin_info = silc_calloc(1, sizeof(*config->admin_info));
 -
 -      /* Get server type */
 -      ret = silc_config_get_token(line, &config->admin_info->server_type);
 -      if (ret < 0)
 -      break;
 -
 -      /* Get admins name */
 -      ret = silc_config_get_token(line, &config->admin_info->admin_name);
 -      if (ret < 0)
 -      break;
 -
 -      /* Get admins email address */
 -      ret = silc_config_get_token(line, &config->admin_info->admin_email);
 -      if (ret < 0)
 -      break;
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT:
 -
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->listen_port);
 -
 -      /* Get host */
 -      ret = silc_config_get_token(line, &config->listen_port->host);
 -      if (ret < 0)
 -      break;
 -
 -      /* Get remote IP */
 -      ret = silc_config_get_token(line, &config->listen_port->remote_ip);
 -      if (ret < 0)
 -      break;
 -
 -      /* Get port */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      /* Any port */
 -      config->listen_port->port = 0;
 -      } else {
 -      config->listen_port->port = atoi(tmp);
 -      silc_free(tmp);
 -      }
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS:
 -
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->conn_class);
 -
 -      /* Get class number */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      /* Class number not defined */
 -
 -      }
 -      config->conn_class->class = atoi(tmp);
 -      silc_free(tmp);
 -
 -      /* Get ping frequency */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      config->conn_class->ping_freq = atoi(tmp);
 -      silc_free(tmp);
 -
 -      /* Get connect frequency */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      config->conn_class->connect_freq = atoi(tmp);
 -      silc_free(tmp);
 -
 -      /* Get max links */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      config->conn_class->max_links = atoi(tmp);
 -      silc_free(tmp);
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING:
 -
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->logging);
 -
 -      /* Get log section type and check it */
 -      ret = silc_config_get_token(line, &config->logging->logtype);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: Log file section not defined\n", 
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -      if (strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_INFO)
 -        && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_WARNING)
 -        && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_ERROR)
 -        && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_FATAL)) {
 -      fprintf(stderr, "%s:%d: Unknown log file section '%s'\n",
 -              config->filename, pc->linenum, config->logging->logtype);
 -      break;
 -      }
 -
 -      /* Get log filename */
 -      ret = silc_config_get_token(line, &config->logging->filename);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      fprintf(stderr, "%s:%d: Log file name not defined\n",
 -              config->filename, pc->linenum);
 -      break;
 -      }
 -
 -      /* Get max byte size */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret) {
 -      config->logging->maxsize = atoi(tmp);
 -      silc_free(tmp);
 -      }
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION:
 -
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->clients);
 -
 -      /* Get host */
 -      ret = silc_config_get_token(line, &config->clients->host);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0)
 -      /* Any host */
 -      config->clients->host = strdup("*");
 -
 -      /* Get authentication method */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret) {
 -      if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
 -          strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
 -        fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
 -                config->filename, pc->linenum, tmp);
 -        break;
 -      }
 -
 -      if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
 -        config->clients->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
 -
 -      if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
 -        config->clients->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
 -
 -      silc_free(tmp);
 -      }
 -
 -      /* Get authentication data */
 -      ret = silc_config_get_token(line, &config->clients->auth_data);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0)
 -      /* Any host */
 -      config->clients->host = strdup("*");
 -
 -      /* Get port */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret == 0) {
 -      config->clients->port = atoi(tmp);
 -      silc_free(tmp);
 -      }
 -
 -      /* Get class number */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret) {
 -      config->clients->class = atoi(tmp);
 -      silc_free(tmp);
 -      }
 -
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION:
 -
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->servers);
++  /* Identify and save this value */
++  if (!strcmp(name, "name")) {
++    if (tmp->name) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->name = strdup((char *) val);
++  }
++  else if (!strcmp(name, "module")) { /* can be empty */
++    if (tmp->module) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    /* dup it only if non-empty, otherwise point it to NULL */
++    tmp->module = (*(char *)val ? strdup((char *) val) : NULL);
++  }
++  else if (!strcmp(name, "block_length"))
++    tmp->block_length = *(int *)val;
++  else if (!strcmp(name, "digest_length"))
++    tmp->digest_length = *(int *)val;
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->name);
++  silc_free(tmp->module);
++  silc_free(tmp);
++  config->tmp = NULL;
++  return got_errno;
++}
  
--      /* Get host */
-       ret = silc_config_get_token(line, &config->routers->host);
 -      ret = silc_config_get_token(line, &config->servers->host);
--      if (ret < 0)
--      break;
 -      if (ret == 0)
 -      /* Any host */
 -      config->servers->host = strdup("*");
++SILC_CONFIG_CALLBACK(fetch_hmac)
++{
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionHmac);
++
++  SERVER_CONFIG_DEBUG(("Received HMAC type=%d name=\"%s\" (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    /* check the temporary struct's fields */
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (!tmp->name || !tmp->hash || (tmp->mac_length == 0)) {
++      got_errno = SILC_CONFIG_EMISSFIELDS;
++      goto got_err;
++    }
++    /* the temporary struct is ok, append it to the list */
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->hmac);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionHmac *) config->tmp;
++  }
  
--      /* Get authentication method */
--      ret = silc_config_get_token(line, &tmp);
--      if (ret < 0)
--      break;
--      if (ret) {
--      if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
--          strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
--        fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
--                config->filename, pc->linenum, tmp);
--        break;
--      }
++  /* Identify and save this value */
++  if (!strcmp(name, "name")) {
++    if (tmp->name) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->name = strdup((char *) val);
++  }
++  else if (!strcmp(name, "hash")) {
++    if (tmp->hash) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->hash = strdup((char *) val);
++  }
++  else if (!strcmp(name, "mac_length"))
++    tmp->mac_length = *(int *)val;
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->name);
++  silc_free(tmp->hash);
++  silc_free(tmp);
++  config->tmp = NULL;
++  return got_errno;
++}
  
--      if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->routers->auth_meth = SILC_AUTH_PASSWORD;
 -        config->servers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
++SILC_CONFIG_CALLBACK(fetch_pkcs)
++{
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionPkcs);
++
++  SERVER_CONFIG_DEBUG(("Received PKCS type=%d name=\"%s\" (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    /* check the temporary struct's fields */
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (!tmp->name) {
++      got_errno = SILC_CONFIG_EMISSFIELDS;
++      goto got_err;
++    }
++    /* the temporary struct is ok, append it to the list */
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->pkcs);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionPkcs *) config->tmp;
++  }
  
--      if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->routers->auth_meth = SILC_AUTH_PUBLIC_KEY;
 -        config->servers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
++  /* Identify and save this value */
++  if (!strcmp(name, "name")) {
++    if (tmp->name) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->name = strdup((char *) val);
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->name);
++  silc_free(tmp);
++  config->tmp = NULL;
++  return got_errno;
++}
  
--      silc_free(tmp);
--      }
++SILC_CONFIG_CALLBACK(fetch_serverinfo)
++{
++  SilcServerConfig config = (SilcServerConfig) context;
++  SilcServerConfigSectionServerInfo *server_info = config->server_info;
  
--      /* Get authentication data */
-       ret = silc_config_get_token(line, (char **)&config->routers->auth_data);
 -      ret = silc_config_get_token(line, &config->servers->auth_data);
--      if (ret < 0)
--      break;
++  /* if there isn't the struct alloc it */
++  if (!server_info) {
++    config->server_info = server_info = (SilcServerConfigSectionServerInfo *)
++              silc_calloc(1, sizeof(*server_info));
++  }
  
-       if (config->routers->auth_meth == SILC_AUTH_PASSWORD) {
-       config->routers->auth_data_len = strlen(config->routers->auth_data);
-       } else if (config->routers->auth_meth == SILC_AUTH_PUBLIC_KEY) {
-       /* Get the public key */
-       SilcPublicKey public_key;
-       if (!silc_pkcs_load_public_key(config->routers->auth_data,
-                                      &public_key, SILC_PKCS_FILE_PEM))
-         if (!silc_pkcs_load_public_key(config->routers->auth_data,
-                                        &public_key, SILC_PKCS_FILE_BIN)) {
-           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                   config->filename, pc->linenum, 
-                   (char *)config->routers->auth_data);
-           break;
-         }
-       silc_free(config->routers->auth_data);
-       config->routers->auth_data = (void *)public_key;
-       config->routers->auth_data_len = 0;
 -      /* Get port */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret) {
 -      config->servers->port = atoi(tmp);
 -      silc_free(tmp);
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    /* check for mandatory inputs */
++    return SILC_CONFIG_OK;
++  }
++  if (!strcmp(name, "hostname")) {
++    if (server_info->server_name) return SILC_CONFIG_EDOUBLE;
++    server_info->server_name = strdup((char *) val);
++  }
++  else if (!strcmp(name, "ip")) {
++    if (server_info->server_ip) return SILC_CONFIG_EDOUBLE;
++    server_info->server_ip = strdup((char *) val);
++  }
++  else if (!strcmp(name, "port")) {
++    int port = *(int *)val;
++    if ((port <= 0) || (port > 65535)) {
++      fprintf(stderr, "Invalid port number!\n");
++      return SILC_CONFIG_ESILENT;
++    }
++    server_info->port = (uint16) port;
++  }
++  else if (!strcmp(name, "servertype")) {
++    if (server_info->server_type) return SILC_CONFIG_EDOUBLE;
++    server_info->server_type = strdup((char *) val);
++  }
++  else if (!strcmp(name, "admin")) {
++    if (server_info->admin) return SILC_CONFIG_EDOUBLE;
++    server_info->admin = strdup((char *) val);
++  }
++  else if (!strcmp(name, "email")) {
++    if (server_info->email) return SILC_CONFIG_EDOUBLE;
++    server_info->email = strdup((char *) val);
++  }
++  else if (!strcmp(name, "location")) {
++    if (server_info->location) return SILC_CONFIG_EDOUBLE;
++    server_info->location = strdup((char *) val);
++  }
++  else if (!strcmp(name, "user")) {
++    if (server_info->user) return SILC_CONFIG_EDOUBLE;
++    server_info->user = strdup((char *) val);
++  }
++  else if (!strcmp(name, "group")) {
++    if (server_info->group) return SILC_CONFIG_EDOUBLE;
++    server_info->group = strdup((char *) val);
++  }
++  else if (!strcmp(name, "motdfile")) {
++    if (server_info->motd_file) return SILC_CONFIG_EDOUBLE;
++    server_info->motd_file = strdup((char *) val);
++  }
++  else if (!strcmp(name, "pidfile")) {
++    if (server_info->pid_file) return SILC_CONFIG_EDOUBLE;
++    server_info->pid_file = strdup((char *) val);
++  }
++  else if (!strcmp(name, "publickey")) {
++    char *tmp = (char *) val;
++
++    /* try to load specified file, if fail stop config parsing */
++    if (!silc_pkcs_load_public_key(tmp, &server_info->public_key,
++                                 SILC_PKCS_FILE_PEM))
++      if (!silc_pkcs_load_public_key(tmp, &server_info->public_key,
++                                   SILC_PKCS_FILE_BIN)) {
++      fprintf(stderr, "\nError: Could not load public key file.");
++      fprintf(stderr, "\n  line %lu: file \"%s\"\n", line, tmp);
++      return SILC_CONFIG_ESILENT;
        }
--
-       /* Get port */
 -      /* Get version */
 -      ret = silc_config_get_token(line, &config->servers->version);
 -      if (ret < 0)
 -      break;
 -
 -      /* Get class number */
--      ret = silc_config_get_token(line, &tmp);
--      if (ret < 0)
--      break;
--      if (ret) {
-       config->routers->port = atoi(tmp);
 -      config->servers->class = atoi(tmp);
--      silc_free(tmp);
++  }
++  else if (!strcmp(name, "privatekey")) {
++    char *tmp = (char *) val;
++
++    /* try to load specified file, if fail stop config parsing */
++    if (!silc_pkcs_load_private_key(tmp, &server_info->private_key,
++                                  SILC_PKCS_FILE_BIN))
++      if (!silc_pkcs_load_private_key(tmp, &server_info->private_key,
++                                    SILC_PKCS_FILE_PEM)) {
++      fprintf(stderr, "\nError: Could not load private key file.");
++      fprintf(stderr, "\n  line %lu: file \"%s\"\n", line, tmp);
++      return SILC_CONFIG_ESILENT;
        }
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++}
  
-       /* Get version */
-       ret = silc_config_get_token(line, &config->routers->version);
-       if (ret < 0)
-       break;
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
 -
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION:
++SILC_CONFIG_CALLBACK(fetch_logging)
++{
++  SilcServerConfig config = (SilcServerConfig) context;
++  SilcServerConfigSectionLogging *tmp =
++      (SilcServerConfigSectionLogging *) config->tmp;
++  int got_errno;
  
-       /* Get class number */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret < 0)
-       break;
-       if (ret) {
-       config->routers->class = atoi(tmp);
-       silc_free(tmp);
-       }
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->routers);
++  if (!strcmp(name, "quicklogs")) {
++    silc_log_quick = *(bool *)val;
++  }
++  else if (!strcmp(name, "flushdelay")) {
++    int flushdelay = *(int *)val;
++    if (flushdelay < 2) { /* this value was taken from silclog.h (min delay) */
++      fprintf(stderr, "Error: line %lu: invalid flushdelay value, use "
++              "quicklogs if you want real-time logging.\n", line);
++      return SILC_CONFIG_ESILENT;
++    }
++    silc_log_flushdelay = (long) flushdelay;
++  }
++#define FETCH_LOGGING_CHAN(__chan__, __member__)              \
++  else if (!strcmp(name, __chan__)) {                         \
++    if (!tmp) return SILC_CONFIG_OK;                          \
++    if (!tmp->file) {                                         \
++      got_errno = SILC_CONFIG_EMISSFIELDS; goto got_err;      \
++    }                                                         \
++    config->__member__ = tmp;                                 \
++    config->tmp = NULL;                                               \
++  }
++  FETCH_LOGGING_CHAN("info", logging_info)
++  FETCH_LOGGING_CHAN("warnings", logging_warnings)
++  FETCH_LOGGING_CHAN("errors", logging_errors)
++  FETCH_LOGGING_CHAN("fatals", logging_fatals)
++#undef FETCH_LOGGING_CHAN
++  else if (!strcmp(name, "file")) {
++    if (!tmp) { /* FIXME: what the fuck is this? */
++      config->tmp = silc_calloc(1, sizeof(*tmp));
++      tmp = (SilcServerConfigSectionLogging *) config->tmp;
++    }
++    if (tmp->file) {
++      got_errno = SILC_CONFIG_EMISSFIELDS; goto got_err;
++    }
++    tmp->file = strdup((char *) val);
++  }
++  else if (!strcmp(name, "size")) {
++    if (!tmp) {
++      config->tmp = silc_calloc(1, sizeof(*tmp));
++      tmp = (SilcServerConfigSectionLogging *) config->tmp;
++    }
++    tmp->maxsize = *(uint32 *) val;
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->file);
++  silc_free(tmp);
++  config->tmp = NULL;
++  return got_errno;
++}
  
-       /* Get whether we are initiator or not */
-       ret = silc_config_get_token(line, &tmp);
 -      /* Get host */
 -      ret = silc_config_get_token(line, &config->routers->host);
--      if (ret < 0)
--      break;
-       if (ret) {
-       config->routers->initiator = atoi(tmp);
-       if (config->routers->initiator != 0)
-         config->routers->initiator = TRUE;
-       silc_free(tmp);
-       }
 -      //      if (ret == 0)
 -      ///* Any host */
 -      //      config->routers->host = strdup("*");
++SILC_CONFIG_CALLBACK(fetch_client)
++{
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionClient);
++
++  SERVER_CONFIG_DEBUG(("Received CLIENT type=%d name=\"%s\" (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (tmp->auth_meth && !tmp->auth_data) {
++      fprintf(stderr, "\nError: line %lu: If you specify \"AuthMethod\" field "
++              "then you must also specify the \"AuthData\" field.\n", line);
++      got_errno = SILC_CONFIG_ESILENT;
++      goto got_err;
++    }
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->clients);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionClient *) config->tmp;
++  }
  
-       /* Get backup replace IP */
-       ret = silc_config_get_token(line, &config->routers->backup_replace_ip);
-       if (ret != -1)
-       config->routers->backup_router = TRUE;
-       if (config->routers->backup_router) {
-       /* Get backup replace port */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret != -1) {
-         config->routers->backup_replace_port = atoi(tmp);
-         silc_free(tmp);
-       }
-       
-       /* Check whether the backup connection is local */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret != -1) {
-         config->routers->backup_local = atoi(tmp);
-         if (config->routers->backup_local != 0)
-           config->routers->backup_local = TRUE;
-         silc_free(tmp);
 -      /* Get authentication method */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret) {
 -      if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
 -          strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
 -        fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
 -                config->filename, pc->linenum, tmp);
 -        break;
--      }
-       }
++  /* Identify and save this value */
++  if (!strcmp(name, "host")) { /* any host (*) accepted */
++    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->host = (*(char *)val ? strdup((char *) val) : NULL);
++  }
++  /* get authentication method */
++  else if (!strcmp(name, "authmethod")) {
++    if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
++      tmp->auth_meth = SILC_AUTH_PASSWORD;
++    else if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
++      tmp->auth_meth = SILC_AUTH_PUBLIC_KEY;
++    else {
++      got_errno = SILC_CONFIG_EINVALIDTEXT; goto got_err;
++    }
++  }
++  else if (!strcmp(name, "authdata")) {
++    if (!my_parse_authdata(tmp->auth_meth, (char *) val, line,
++                         &tmp->auth_data, &tmp->auth_data_len)) {
++      got_errno = SILC_CONFIG_ESILENT;
++      goto got_err; /* error outputted in my_parse_authdata */
++    }
++  }
++  else if (!strcmp(name, "port")) {
++    int port = *(int *)val;
++    if ((port <= 0) || (port > 65535)) {
++      fprintf(stderr, "Invalid port number!\n");
++      got_errno = SILC_CONFIG_ESILENT; goto got_err;
++    }
++    tmp->port = (uint16) port;
++  }
++  /* FIXME: Improvement: use a direct class struct pointer instead of num */
++  else if (!strcmp(name, "class")) {
++    /* XXX do nothing */
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->host);
++  my_free_authdata(tmp->auth_meth, tmp->auth_data);
++  silc_free(tmp);
++  return got_errno;
++}
  
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
 -      if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
 -        config->routers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
++SILC_CONFIG_CALLBACK(fetch_admin)
++{
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionAdmin);
++
++  SERVER_CONFIG_DEBUG(("Received CLIENT type=%d name=\"%s\" (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    /* check the temporary struct's fields */
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (!tmp->auth_meth) {
++      got_errno = SILC_CONFIG_EMISSFIELDS; goto got_err;
++    }
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->admins);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionAdmin *) config->tmp;
++  }
  
-     case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION:
 -      if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
 -        config->routers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
++  /* Identify and save this value */
++  if (!strcmp(name, "host")) { /* any host (*) accepted */
++    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->host = (*(char *)val ? strdup((char *) val) : NULL);
++  }
++  else if (!strcmp(name, "user")) {
++    if (tmp->user) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->user = (*(char *)val ? strdup((char *) val) : NULL);
++  }
++  else if (!strcmp(name, "nick")) {
++    if (tmp->nick) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->nick = (*(char *)val ? strdup((char *) val) : NULL);
++  }
++  /* get authentication method */
++  else if (!strcmp(name, "authmethod")) {
++    if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
++      tmp->auth_meth = SILC_AUTH_PASSWORD;
++    else if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
++      tmp->auth_meth = SILC_AUTH_PUBLIC_KEY;
++    else {
++      got_errno = SILC_CONFIG_EINVALIDTEXT; goto got_err;
++    }
++  }
++  else if (!strcmp(name, "authdata")) {
++    if (!my_parse_authdata(tmp->auth_meth, (char *) val, line,
++                         &tmp->auth_data, &tmp->auth_data_len)) {
++      got_errno = SILC_CONFIG_ESILENT;
++      goto got_err; /* error outputted in my_parse_authdata */
++    }
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->host);
++  silc_free(tmp->user);
++  silc_free(tmp->nick);
++  my_free_authdata(tmp->auth_meth, tmp->auth_data);
++  silc_free(tmp);
++  return got_errno;
++}
  
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->admins);
 -      silc_free(tmp);
 -      }
++SILC_CONFIG_CALLBACK(fetch_deny)
++{
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionDeny);
++
++  SERVER_CONFIG_DEBUG(("Received DENY type=%d name=\"%s\" (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    /* check the temporary struct's fields */
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (!tmp->reason) {
++      got_errno = SILC_CONFIG_EMISSFIELDS;
++      goto got_err;
++    }
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->denied);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionDeny *) config->tmp;
++  }
  
-       /* Get host */
-       ret = silc_config_get_token(line, &config->admins->host);
 -      /* Get authentication data */
 -      ret = silc_config_get_token(line, &config->routers->auth_data);
--      if (ret < 0)
--      break;
-       if (ret == 0)
-       /* Any host */
-       config->admins->host = strdup("*");
++  /* Identify and save this value */
++  if (!strcmp(name, "host")) { /* any host (*) accepted */
++    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->host = (*(char *)val ? strdup((char *) val) : strdup("*"));
++  }
++  else if (!strcmp(name, "port")) {
++    int port = *(int *)val;
++    if ((port <= 0) || (port > 65535)) {
++      fprintf(stderr, "Invalid port number!\n");
++      got_errno = SILC_CONFIG_ESILENT; goto got_err;
++    }
++    tmp->port = (uint16) port;
++  }
++  else if (!strcmp(name, "reason")) {
++    if (tmp->reason) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->reason = strdup((char *) val);
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->host);
++  silc_free(tmp->reason);
++  silc_free(tmp);
++  return got_errno;
++}
  
-       /* Get username */
-       ret = silc_config_get_token(line, &config->admins->username);
 -      /* Get port */
 -      ret = silc_config_get_token(line, &tmp);
--      if (ret < 0)
--      break;
-       if (ret == 0)
-       /* Any username */
-       config->admins->username = strdup("*");
 -      if (ret) {
 -      config->routers->port = atoi(tmp);
 -      silc_free(tmp);
 -      }
++SILC_CONFIG_CALLBACK(fetch_server)
++{
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionServer);
++
++  SERVER_CONFIG_DEBUG(("Received SERVER type=%d name=\"%s\" (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    /* check the temporary struct's fields */
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (!tmp->auth_meth || !tmp->version) {
++      got_errno = SILC_CONFIG_EMISSFIELDS;
++      goto got_err;
++    }
++    /* the temporary struct is ok, append it to the list */
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->servers);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionServer *) config->tmp;
++  }
  
-       /* Get nickname */
-       ret = silc_config_get_token(line, &config->admins->nickname);
 -      /* Get version */
 -      ret = silc_config_get_token(line, &config->routers->version);
--      if (ret < 0)
--      break;
-       if (ret == 0)
-       /* Any nickname */
-       config->admins->nickname = strdup("*");
++  /* Identify and save this value */
++  if (!strcmp(name, "host")) { /* any host (*) accepted */
++    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->host = (*(char *)val ? strdup((char *) val) : strdup("*"));
++  }
++  /* get authentication method */
++  else if (!strcmp(name, "authmethod")) {
++    if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
++      tmp->auth_meth = SILC_AUTH_PASSWORD;
++    else if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
++      tmp->auth_meth = SILC_AUTH_PUBLIC_KEY;
++    else {
++      got_errno = SILC_CONFIG_EINVALIDTEXT; goto got_err;
++    }
++  }
++  else if (!strcmp(name, "authdata")) {
++    if (!my_parse_authdata(tmp->auth_meth, (char *) val, line,
++                         &tmp->auth_data, &tmp->auth_data_len)) {
++      got_errno = SILC_CONFIG_ESILENT;
++      goto got_err; /* error outputted in my_parse_authdata */
++    }
++  }
++  else if (!strcmp(name, "port")) {
++    int port = *(int *)val;
++    if ((port <= 0) || (port > 65535)) {
++      fprintf(stderr, "Invalid port number!\n");
++      got_errno = SILC_CONFIG_ESILENT; goto got_err;
++    }
++    tmp->port = (uint16) port;
++  }
++  else if (!strcmp(name, "versionid")) {
++    if (tmp->version) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->version = strdup((char *) val);
++  }
++  /* FIXME: Improvement: use a direct class struct pointer instead of num */
++  else if (!strcmp(name, "class")) {
++    /* XXX do nothing */
++  }
++  else if (!strcmp(name, "backup")) {
++    tmp->backup_router = *(bool *)val;
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->host);
++  silc_free(tmp->version);
++  my_free_authdata(tmp->auth_meth, tmp->auth_data);
++  silc_free(tmp);
++  return got_errno;
++}
  
-       /* Get authentication method */
 -      /* Get class number */
--      ret = silc_config_get_token(line, &tmp);
--      if (ret < 0)
--      break;
--      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
 -      config->routers->class = atoi(tmp);
 -      silc_free(tmp);
 -      }
++SILC_CONFIG_CALLBACK(fetch_router)
++{
++  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionRouter);
++
++  SERVER_CONFIG_DEBUG(("Received ROUTER type=%d name=\"%s\" (val=%x)", type, name, context));
++  if (type == SILC_CONFIG_ARG_BLOCK) {
++    if (!tmp) /* empty sub-block? */
++      return SILC_CONFIG_OK;
++    if (!tmp->auth_meth || !tmp->version) {
++      got_errno = SILC_CONFIG_EMISSFIELDS;
++      goto got_err;
++    }
++    /* the temporary struct is ok, append it to the list */
++    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->routers);
++    config->tmp = NULL;
++    return SILC_CONFIG_OK;
++  }
++  /* if there isn't a temporary struct alloc one */
++  if (!tmp) {
++    config->tmp = silc_calloc(1, sizeof(*findtmp));
++    tmp = (SilcServerConfigSectionRouter *) config->tmp;
++  }
  
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->admins->auth_meth = SILC_AUTH_PASSWORD;
 -      check = TRUE;
 -      checkmask |= (1L << pc->section->type);
 -      break;
++  /* Identify and save this value */
++  if (!strcmp(name, "host")) {
++    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->host = strdup((char *) val);
++  }
++  else if (!strcmp(name, "authmethod")) {
++    if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
++      tmp->auth_meth = SILC_AUTH_PASSWORD;
++    else if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
++      tmp->auth_meth = SILC_AUTH_PUBLIC_KEY;
++    else {
++      got_errno = SILC_CONFIG_EINVALIDTEXT; goto got_err;
++    }
++  }
++  else if (!strcmp(name, "authdata")) {
++    if (!my_parse_authdata(tmp->auth_meth, (char *) val, line,
++                         &tmp->auth_data, &tmp->auth_data_len)) {
++      got_errno = SILC_CONFIG_ESILENT;
++      goto got_err; /* error outputted in my_parse_authdata */
++    }
++  }
++  else if (!strcmp(name, "port")) {
++    int port = *(int *)val;
++    if ((port <= 0) || (port > 65535)) {
++      fprintf(stderr, "Invalid port number!\n");
++      got_errno = SILC_CONFIG_ESILENT; goto got_err;
++    }
++    tmp->port = (uint16) port;
++  }
++  else if (!strcmp(name, "versionid")) {
++    if (tmp->version) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->version = strdup((char *) val);
++  }
++  /* FIXME: Improvement: use a direct class struct pointer instead of num */
++  else if (!strcmp(name, "class")) {
++    /* XXX do nothing */
++  }
++  else if (!strcmp(name, "initiator"))
++    tmp->initiator = *(bool *)val;
++  else if (!strcmp(name, "backuphost")) {
++    if (tmp->backup_replace_ip) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
++    tmp->backup_replace_ip = (*(char *)val ? strdup((char *) val) : strdup("*"));
++  }
++  else
++    return SILC_CONFIG_EINTERNAL;
++  return SILC_CONFIG_OK;
++
++ got_err:
++  silc_free(tmp->host);
++  silc_free(tmp->version);
++  silc_free(tmp->backup_replace_ip);
++  my_free_authdata(tmp->auth_meth, tmp->auth_data);
++  silc_free(tmp);
++  return got_errno;
++}
  
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->admins->auth_meth = SILC_AUTH_PUBLIC_KEY;
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION:
++/* known config options tables */
++static const SilcConfigTable table_general[] = {
++  { "modulepath",     SILC_CONFIG_ARG_STRE,   fetch_generic,  NULL },
++  { 0, 0, 0, 0 }
++};
  
-       silc_free(tmp);
-       }
 -      SILC_SERVER_CONFIG_LIST_ALLOC(config->admins);
++static const SilcConfigTable table_cipher[] = {
++  { "name",           SILC_CONFIG_ARG_STR,    fetch_cipher,   NULL },
++  { "module",         SILC_CONFIG_ARG_STRE,   fetch_cipher,   NULL },
++  { "key_length",     SILC_CONFIG_ARG_INT,    fetch_cipher,   NULL },
++  { "block_length",   SILC_CONFIG_ARG_INT,    fetch_cipher,   NULL },
++  { 0, 0, 0, 0 }
++};
  
-       /* Get authentication data */
-       ret = silc_config_get_token(line, (char **)&config->admins->auth_data);
 -      /* Get host */
 -      ret = silc_config_get_token(line, &config->admins->host);
--      if (ret < 0)
--      break;
 -      if (ret == 0)
 -      /* Any host */
 -      config->admins->host = strdup("*");
++static const SilcConfigTable table_hash[] = {
++  { "name",           SILC_CONFIG_ARG_STR,    fetch_hash,     NULL },
++  { "module",         SILC_CONFIG_ARG_STRE,   fetch_hash,     NULL },
++  { "block_length",   SILC_CONFIG_ARG_INT,    fetch_hash,     NULL },
++  { "digest_length",  SILC_CONFIG_ARG_INT,    fetch_hash,     NULL },
++  { 0, 0, 0, 0 }
++};
  
-       if (config->admins->auth_meth == SILC_AUTH_PASSWORD) {
-       config->admins->auth_data_len = strlen(config->admins->auth_data);
-       } else if (config->admins->auth_meth == SILC_AUTH_PUBLIC_KEY) {
-       /* Get the public key */
-       SilcPublicKey public_key;
-       if (!silc_pkcs_load_public_key(config->admins->auth_data,
-                                      &public_key, SILC_PKCS_FILE_PEM))
-         if (!silc_pkcs_load_public_key(config->admins->auth_data,
-                                        &public_key, SILC_PKCS_FILE_BIN)) {
-           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                   config->filename, pc->linenum, 
-                   (char *)config->admins->auth_data);
-           break;
-         }
-       silc_free(config->admins->auth_data);
-       config->admins->auth_data = (void *)public_key;
-       config->admins->auth_data_len = 0;
-       }
 -      /* Get authentication method */
 -      ret = silc_config_get_token(line, &tmp);
 -      if (ret < 0)
 -      break;
 -      if (ret) {
 -      if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
 -          strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
 -        fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
 -                config->filename, pc->linenum, tmp);
 -        break;
 -      }
++static const SilcConfigTable table_hmac[] = {
++  { "name",           SILC_CONFIG_ARG_STR,    fetch_hmac,     NULL },
++  { "hash",           SILC_CONFIG_ARG_STR,    fetch_hmac,     NULL },
++  { "mac_length",     SILC_CONFIG_ARG_INT,    fetch_hmac,     NULL },
++  { 0, 0, 0, 0 }
++};
  
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
 -      if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
 -        config->admins->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
++static const SilcConfigTable table_pkcs[] = {
++  { "name",           SILC_CONFIG_ARG_STR,    fetch_pkcs,     NULL },
++  { 0, 0, 0, 0 }
++};
  
-     case SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION:
 -      if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
 -        config->admins->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
++static const SilcConfigTable table_serverinfo[] = {
++  { "hostname",               SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "ip",             SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "port",           SILC_CONFIG_ARG_INT,    fetch_serverinfo, NULL},
++  { "servertype",     SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "location",               SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "admin",          SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "email",          SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "user",           SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "group",          SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "publickey",      SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "privatekey",     SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
++  { "motdfile",               SILC_CONFIG_ARG_STRE,   fetch_serverinfo, NULL},
++  { "pidfile",                SILC_CONFIG_ARG_STRE,   fetch_serverinfo, NULL},
++  { 0, 0, 0, 0 }
++};
  
-       SILC_SERVER_CONFIG_LIST_ALLOC(config->denied);
 -      silc_free(tmp);
 -      }
++static const SilcConfigTable table_logging_c[] = {
++  { "file",           SILC_CONFIG_ARG_STR,    fetch_logging,  NULL },
++  { "size",           SILC_CONFIG_ARG_SIZE,   fetch_logging,  NULL },
++/*{ "quicklog",               SILC_CONFIG_ARG_NONE,   fetch_logging,  NULL }, */
++  { 0, 0, 0, 0 }
++};
  
-       /* Get host */
-       ret = silc_config_get_token(line, &config->denied->host);
 -      /* Get authentication data */
 -      ret = silc_config_get_token(line, &config->admins->auth_data);
--      if (ret < 0)
--      break;
-       if (ret == 0) {
-       /* Any host */
-       config->denied->host = strdup("*");
-       fprintf(stderr, "warning: %s:%d: Denying all connections",
-               config->filename, pc->linenum);
-       }
++static const SilcConfigTable table_logging[] = {
++  { "quicklogs",      SILC_CONFIG_ARG_TOGGLE, fetch_logging,  NULL },
++  { "flushdelay",     SILC_CONFIG_ARG_INT,    fetch_logging,  NULL },
++  { "info",           SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
++  { "warnings",               SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
++  { "errors",         SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
++  { "fatals",         SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
++  { 0, 0, 0, 0 }
++};
  
-       /* Get port */
-       ret = silc_config_get_token(line, &tmp);
 -      /* Get nickname */
 -      ret = silc_config_get_token(line, &config->admins->nickname);
--      if (ret < 0)
--      break;
-       if (ret == 0) {
-       /* Any port */
-       config->denied->port = 0;
-       } else {
-       config->denied->port = atoi(tmp);
-       silc_free(tmp);
-       }
++/* still unsupported
++static const SilcConfigTable table_class[] = {
++  { "name",           SILC_CONFIG_ARG_STR,    fetch_class,    NULL },
++  { "ping",           SILC_CONFIG_ARG_INT,    fetch_class,    NULL },
++  { "connect",                SILC_CONFIG_ARG_INT,    fetch_class,    NULL },
++  { "links",          SILC_CONFIG_ARG_INT,    fetch_class,    NULL },
++  { 0, 0, 0, 0 }
++}; */
++
++static const SilcConfigTable table_client[] = {
++  { "host",           SILC_CONFIG_ARG_STRE,   fetch_client,   NULL },
++  { "authmethod",     SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
++  { "authdata",               SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
++  { "port",           SILC_CONFIG_ARG_INT,    fetch_client,   NULL },
++  { "class",          SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
++  { 0, 0, 0, 0 }
++};
  
-       /* Get comment */
-       ret = silc_config_get_token(line, &config->denied->comment);
 -      /* Get class number */
 -      ret = silc_config_get_token(line, &tmp);
--      if (ret < 0)
--      break;
 -      if (ret) {
 -      config->admins->class = atoi(tmp);
 -      silc_free(tmp);
 -      }
++static const SilcConfigTable table_admin[] = {
++  { "host",           SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
++  { "user",           SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
++  { "nick",           SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
++  { "authmethod",     SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
++  { "authdata",               SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
++  { "port",           SILC_CONFIG_ARG_INT,    fetch_admin,    NULL },
++  { "class",          SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
++  { 0, 0, 0, 0 }
++};
  
--      check = TRUE;
--      checkmask |= (1L << pc->section->type);
--      break;
++static const SilcConfigTable table_deny[] = {
++  { "host",           SILC_CONFIG_ARG_STRE,   fetch_deny,     NULL },
++  { "port",           SILC_CONFIG_ARG_INT,    fetch_deny,     NULL },
++  { "reason",         SILC_CONFIG_ARG_STR,    fetch_deny,     NULL },
++  { 0, 0, 0, 0 }
++};
  
-     case SILC_CONFIG_SERVER_SECTION_TYPE_MOTD:
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION:
 -      /* Not implemented yet */
 -      check = TRUE;
 -      break;
++static const SilcConfigTable table_serverconn[] = {
++  { "host",           SILC_CONFIG_ARG_STRE,   fetch_server,   NULL },
++  { "authmethod",     SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
++  { "authdata",               SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
++  { "port",           SILC_CONFIG_ARG_INT,    fetch_server,   NULL },
++  { "versionid",      SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
++  { "class",          SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
++  { "backup",         SILC_CONFIG_ARG_TOGGLE, fetch_server,   NULL },
++  { 0, 0, 0, 0 }
++};
  
-       if (!config->motd)
-       config->motd = silc_calloc(1, sizeof(*config->motd));
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_REDIRECT_CLIENT:
 -      /* Not implemented yet */
 -      check = TRUE;
 -      break;
++static const SilcConfigTable table_routerconn[] = {
++  { "host",           SILC_CONFIG_ARG_STRE,   fetch_router,   NULL },
++  { "authmethod",     SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
++  { "authdata",               SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
++  { "port",           SILC_CONFIG_ARG_INT,    fetch_router,   NULL },
++  { "versionid",      SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
++  { "class",          SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
++  { "initiator",      SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
++  { "backuphost",     SILC_CONFIG_ARG_STRE,   fetch_router,   NULL },
++  { "backupport",     SILC_CONFIG_ARG_INT,    fetch_router,   NULL },
++  { "localbackup",    SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
++  { 0, 0, 0, 0 }
++};
  
-       /* Get motd file */
-       ret = silc_config_get_token(line, &config->motd->motd_file);
-       if (ret < 0)
-       break;
 -    case SILC_CONFIG_SERVER_SECTION_TYPE_NONE:
 -    default:
 -      /* Error */
 -      break;
 -    }
++static const SilcConfigTable table_main[] = {
++  { "general",                SILC_CONFIG_ARG_BLOCK,  NULL,           table_general },
++  { "cipher",         SILC_CONFIG_ARG_BLOCK,  fetch_cipher,   table_cipher },
++  { "hash",           SILC_CONFIG_ARG_BLOCK,  fetch_hash,     table_hash },
++  { "hmac",           SILC_CONFIG_ARG_BLOCK,  fetch_hmac,     table_hmac },
++  { "pkcs",           SILC_CONFIG_ARG_BLOCK,  fetch_pkcs,     table_pkcs },
++  { "serverinfo",     SILC_CONFIG_ARG_BLOCK,  fetch_serverinfo, table_serverinfo },
++  { "logging",                SILC_CONFIG_ARG_BLOCK,  NULL,           table_logging },
++/*{ "class",          SILC_CONFIG_ARG_BLOCK,  fetch_class,    table_class }, */
++  { "client",         SILC_CONFIG_ARG_BLOCK,  fetch_client,   table_client },
++  { "admin",          SILC_CONFIG_ARG_BLOCK,  fetch_admin,    table_admin },
++  { "deny",           SILC_CONFIG_ARG_BLOCK,  fetch_deny,     table_deny },
++  { "serverconnection",       SILC_CONFIG_ARG_BLOCK,  fetch_server,   table_serverconn },
++  { "routerconnection",       SILC_CONFIG_ARG_BLOCK,  fetch_router,   table_routerconn },
++  { 0, 0, 0, 0 }
++};
  
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
 -    /* Check for error */
 -    if (check == FALSE) {
 -      /* Line could not be parsed */
 -      fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
--      break;
++/* Allocates a new configuration object, opens configuration file and
++ * parses it. The parsed data is returned to the newly allocated
++ * configuration object. */
 +
-     case SILC_CONFIG_SERVER_SECTION_TYPE_PID:
-        if (!config->pidfile)
-           config->pidfile = silc_calloc(1, sizeof(*config->pidfile));
-           
-        ret = silc_config_get_token(line, &config->pidfile->pid_file);
-        if (ret < 0)
-           break;
-           
-        check = TRUE;
-        checkmask |= (1L << pc->section->type);
-        break;
-     case SILC_CONFIG_SERVER_SECTION_TYPE_NONE:
-     default:
-       /* Error */
-       break;
-     }
++SilcServerConfig silc_server_config_alloc(char *filename)
++{
++  SilcServerConfig config;
++  SilcConfigEntity ent;
++  SilcConfigFile *file;
++  int ret;
++  SILC_LOG_DEBUG(("Loading config data from `%s'", filename));
 +
-     /* Check for error */
-     if (check == FALSE) {
-       /* Line could not be parsed */
-       fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
-       break;
++  /* alloc a config object */
++  config = (SilcServerConfig) silc_calloc(1, sizeof(*config));
++  /* obtain a config file object */
++  file = silc_config_open(filename);
++  if (!file) {
++    fprintf(stderr, "\nError: can't open config file `%s'\n", filename);
++    return NULL;
++  }
++  /* obtain a SilcConfig entity, we can use it to start the parsing */
++  ent = silc_config_init(file);
++  /* load the known configuration options, give our empty object as context */
++  silc_config_register_table(ent, table_main, (void *) config);
++  /* enter the main parsing loop.  When this returns, we have the parsing
++   * result and the object filled (or partially, in case of errors). */
++  ret = silc_config_main(ent);
++  SILC_LOG_DEBUG(("Parser returned [ret=%d]: %s", ret, silc_config_strerror(ret)));
++
++  /* Check if the parser returned errors */
++  if (ret) {
++    /* handle this special error return which asks to quietly return */
++    if (ret != SILC_CONFIG_ESILENT) {
++      char *linebuf, *filename = silc_config_get_filename(file);
++      uint32 line = silc_config_get_line(file);
++      fprintf(stderr, "\nError while parsing config file: %s.\n",
++              silc_config_strerror(ret));
++      linebuf = silc_config_read_line(file, line);
++      fprintf(stderr, "  file %s line %lu:  %s\n\n", filename, line, linebuf);
++      silc_free(linebuf);
      }
-     pc = pc->next;
++    return NULL;
 +  }
++  /* close (destroy) the file object */
++  silc_config_close(file);
  
 -    pc = pc->next;
 -    /* XXXX */
 -    //    silc_free(pc->prev);
 -    //    pc->prev = NULL;
 -  }
 -
--  if (check == FALSE)
-     return FALSE;
 -    return FALSE;;
--
--  /* Check that all mandatory sections really were found. If not, the server
--     cannot function and we return error. */
-   ret = silc_server_config_check_sections(checkmask);
 -  ret = silc_config_server_check_sections(checkmask);
--  if (ret == FALSE) {
--    /* XXX */
--
--  }
--  
--  /* Before returning all the lists in the config object must be set
--     to their first values (the last value is first here). */
--  while (config->cipher && config->cipher->prev)
--    config->cipher = config->cipher->prev;
--  while (config->pkcs && config->pkcs->prev)
--    config->pkcs = config->pkcs->prev;
--  while (config->hash_func && config->hash_func->prev)
--    config->hash_func = config->hash_func->prev;
-   while (config->hmac && config->hmac->prev)
-     config->hmac = config->hmac->prev;
--  while (config->listen_port && config->listen_port->prev)
--    config->listen_port = config->listen_port->prev;
--  while (config->logging && config->logging->prev)
--    config->logging = config->logging->prev;
--  while (config->conn_class && config->conn_class->prev)
--    config->conn_class = config->conn_class->prev;
--  while (config->clients && config->clients->prev)
--    config->clients = config->clients->prev;
--  while (config->servers && config->servers->prev)
--    config->servers = config->servers->prev;
-   while (config->admins && config->admins->prev)
-     config->admins = config->admins->prev;
--  while (config->routers && config->routers->prev)
--    config->routers = config->routers->prev;
--  
--  SILC_LOG_DEBUG(("Done"));
--  
--  return TRUE;
++  /* XXX FIXME: check for missing mandatory fields */
++  if (!config->server_info) {
++    fprintf(stderr, "\nError: Missing mandatory block `server_info'\n");
++    return NULL;
++  }
++  return config;
  }
  
--/* This function checks that the mask sent as argument includes all the 
--   sections that are mandatory in SILC server. */
++/* ... */
  
- int silc_server_config_check_sections(uint32 checkmask)
 -int silc_config_server_check_sections(unsigned int checkmask)
++void silc_server_config_destroy(SilcServerConfig config)
  {
--  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO))) {
--    
--    return FALSE;
++  void *tmp;
++  silc_free(config->module_path);
++
++  /* Destroy Logging channels */
++  if (config->logging_info)
++    silc_free(config->logging_info->file);
++  if (config->logging_warnings)
++    silc_free(config->logging_warnings->file);
++  if (config->logging_errors)
++    silc_free(config->logging_errors->file);
++  if (config->logging_fatals)
++    silc_free(config->logging_fatals->file);
++
++  /* Destroy the ServerInfo struct */
++  if (config->server_info) {
++    register SilcServerConfigSectionServerInfo *si = config->server_info;
++    silc_free(si->server_name);
++    silc_free(si->server_ip);
++    silc_free(si->server_type);
++    silc_free(si->location);
++    silc_free(si->admin);
++    silc_free(si->email);
++    silc_free(si->user);
++    silc_free(si->group);
++    silc_free(si->motd_file);
++    silc_free(si->pid_file);
    }
--  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO))) {
--    
--    return FALSE;
++
++  /* Now let's destroy the lists */
++
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionCipher,
++                                config->cipher)
++    silc_free(di->name);
++    silc_free(di->module);
++    silc_free(di);
    }
--  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT))) {
--    
--    return FALSE;
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionHash, config->hash)
++    silc_free(di->name);
++    silc_free(di->module);
++    silc_free(di);
    }
-   if (!(checkmask & 
-       (1L << SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION))) {
 -  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION))) {
--    
--    return FALSE;
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionHmac, config->hmac)
++    silc_free(di->name);
++    silc_free(di->hash);
++    silc_free(di);
    }
--  if (!(checkmask 
--      & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION))) {
--    
--    return FALSE;
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionPkcs, config->pkcs)
++    silc_free(di->name);
++    silc_free(di);
    }
-   if (!(checkmask
 -  if (!(checkmask 
--      & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION))) {
 -    
--    return FALSE;
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionClient,
++                                config->clients)
++    silc_free(di->host);
++    my_free_authdata(di->auth_meth, di->auth_data);
++    silc_free(di);
    }
--
--  return TRUE;
--}
--
--/* Sets log files where log messages is saved by the server. */
--
- void silc_server_config_setlogfiles(SilcServerConfig config, SilcSchedule sked)
 -void silc_config_server_setlogfiles(SilcConfigServer config)
--{
-   long tmp;
-   SilcServerConfigSectionLogging *log;
 -  SilcConfigServerSectionLogging *log;
 -  char *info, *warning, *error, *fatal;
 -  unsigned int info_size, warning_size, error_size, fatal_size;
--
--  SILC_LOG_DEBUG(("Setting configured log file names"));
 -
 -  /* Set default files before checking configuration */
 -  info = SILC_LOG_FILE_INFO;
 -  warning = SILC_LOG_FILE_WARNING;
 -  error = SILC_LOG_FILE_ERROR;
 -  fatal = SILC_LOG_FILE_FATAL;
 -  info_size = 0;
 -  warning_size = 0;
 -  error_size = 0;
 -  fatal_size = 0;
 -
--  log = config->logging;
-   while (log) {
-     /* Logging Files */
-     if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_INFO))
-       silc_log_set_file(SILC_LOG_INFO, log->filename, log->maxsize, sked);
-     if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_WARNING))
-       silc_log_set_file(SILC_LOG_WARNING, log->filename, log->maxsize, sked);
-     if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_ERROR))
-       silc_log_set_file(SILC_LOG_ERROR, log->filename, log->maxsize, sked);
-     if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_FATAL))
-       silc_log_set_file(SILC_LOG_FATAL, log->filename, log->maxsize, sked);
-     /* Logging Options */
-     if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LO_QUICK)) {
-       if (!strcasecmp(log->filename, "yes") ||
-         !strcasecmp(log->filename, "on"))
-          silc_log_quick = TRUE;
 -  while(log) {
 -    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_INFO)) {
 -      info = log->filename;
 -      info_size = log->maxsize;
--    }
-     if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LO_FDELAY)) {
-       tmp = atol(log->filename);
-       if (tmp > 0)
-         silc_log_flushdelay = tmp;
-       else {
-         fprintf(stderr, "config: invalid flushdelay value, use quicklogs if "
-               "you want real-time logging.\n");
-       exit(1);
-       }
 -    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_WARNING)) {
 -      warning = log->filename;
 -      warning_size = log->maxsize;
 -    }
 -    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_ERROR)) {
 -      error = log->filename;
 -      error_size = log->maxsize;
 -    }
 -    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_FATAL)) {
 -      fatal = log->filename;
 -      fatal_size = log->maxsize;
--    }
--
--    log = log->next;
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionAdmin, config->admins)
++    silc_free(di->host);
++    silc_free(di->user);
++    silc_free(di->nick);
++    my_free_authdata(di->auth_meth, di->auth_data);
++    silc_free(di);
++  }
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionDeny, config->denied)
++    silc_free(di->host);
++    silc_free(di->reason);
++    silc_free(di);
++  }
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionServer,
++                                config->servers)
++    silc_free(di->host);
++    silc_free(di->version);
++    my_free_authdata(di->auth_meth, di->auth_data);
++    silc_free(di);
++  }
++  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionRouter,
++                                config->routers)
++    silc_free(di->host);
++    silc_free(di->version);
++    silc_free(di->backup_replace_ip);
++    my_free_authdata(di->auth_meth, di->auth_data);
++    silc_free(di);
    }
 -
 -  silc_log_set_files(info, info_size, warning, warning_size,
 -                   error, error_size, fatal, fatal_size);
  }
  
  /* Registers configured ciphers. These can then be allocated by the
     server when needed. */
  
- bool silc_server_config_register_ciphers(SilcServerConfig config)
 -void silc_config_server_register_ciphers(SilcConfigServer config)
++bool silc_server_config_register_ciphers(SilcServer server)
  {
-   SilcServerConfigSectionAlg *alg;
 -  SilcConfigServerSectionAlg *alg;
--  SilcServer server = (SilcServer)config->server;
++  SilcServerConfig config = server->config;
++  SilcServerConfigSectionCipher *cipher = config->cipher;
++  char *module_path = config->module_path;
  
    SILC_LOG_DEBUG(("Registering configured ciphers"));
  
-   if (!config->cipher)
 -  alg = config->cipher;
 -  while(alg) {
 -
 -    if (!alg->sim_name) {
 -      /* Crypto module is supposed to be built in. Nothing to be done
 -       here except to test that the cipher really is built in. */
 -      SilcCipher tmp = NULL;
++  if (!cipher) /* any cipher in the config file? */
 +    return FALSE;
  
-   alg = config->cipher;
-   while(alg) {
-     if (!alg->sim_name) {
 -      if (silc_cipher_alloc(alg->alg_name, &tmp) == FALSE) {
 -      SILC_LOG_ERROR(("Unsupported cipher `%s'", alg->alg_name));
++  while (cipher) {
++    /* if there isn't a module_path OR there isn't a module sim name try to
++     * use buil-in functions */
++    if (!module_path || !cipher->module) {
 +      int i;
-       
 +      for (i = 0; silc_default_ciphers[i].name; i++)
-       if (!strcmp(silc_default_ciphers[i].name, alg->alg_name)) {
++      if (!strcmp(silc_default_ciphers[i].name, cipher->name)) {
 +        silc_cipher_register(&silc_default_ciphers[i]);
 +        break;
 +      }
-       
-       if (!silc_cipher_is_supported(alg->alg_name)) {
-       SILC_LOG_ERROR(("Unknown cipher `%s'", alg->alg_name));
++      if (!silc_cipher_is_supported(cipher->name)) {
++      SILC_LOG_ERROR(("Unknown cipher `%s'", cipher->name));
        silc_server_stop(server);
        exit(1);
        }
 -      silc_cipher_free(tmp);
 -
--#ifdef SILC_SIM
      } else {
++#ifdef SILC_SIM
        /* Load (try at least) the crypto SIM module */
--      SilcCipherObject cipher;
++      char buf[1023], *alg_name;
++      SilcCipherObject cipher_obj;
        SilcSimContext *sim;
-       char *alg_name;
  
--      memset(&cipher, 0, sizeof(cipher));
--      cipher.name = alg->alg_name;
--      cipher.block_len = alg->block_len;
--      cipher.key_len = alg->key_len * 8;
++      memset(&cipher_obj, 0, sizeof(cipher_obj));
++      cipher_obj.name = cipher->name;
++      cipher_obj.block_len = cipher->block_length;
++      cipher_obj.key_len = cipher->key_length * 8;
  
++      /* build the libname */
++      snprintf(buf, sizeof(buf), "%s/%s", config->module_path,
++              cipher->module);
        sim = silc_sim_alloc();
        sim->type = SILC_SIM_CIPHER;
--      sim->libname = alg->sim_name;
++      sim->libname = buf;
  
-       alg_name = strdup(alg->alg_name);
 -      if ((silc_sim_load(sim))) {
 -      cipher.set_key = 
 -        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
++      alg_name = strdup(cipher->name);
 +      if (strchr(alg_name, '-'))
 +      *strchr(alg_name, '-') = '\0';
 +
-       if ((silc_sim_load(sim))) {
-       cipher.set_key = 
-         silc_sim_getsym(sim, silc_sim_symname(alg_name, 
++      if (silc_sim_load(sim)) {
++      cipher_obj.set_key =
++        silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_SET_KEY));
--      SILC_LOG_DEBUG(("set_key=%p", cipher.set_key));
--      cipher.set_key_with_string = 
-         silc_sim_getsym(sim, silc_sim_symname(alg_name, 
 -        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
++      SILC_LOG_DEBUG(("set_key=%p", cipher_obj.set_key));
++      cipher_obj.set_key_with_string =
++        silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
--      SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string));
--      cipher.encrypt = 
 -        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
++      SILC_LOG_DEBUG(("set_key_with_string=%p", cipher_obj.set_key_with_string));
++      cipher_obj.encrypt =
 +        silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_ENCRYPT_CBC));
--      SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
--        cipher.decrypt = 
 -        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
++      SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher_obj.encrypt));
++        cipher_obj.decrypt =
 +        silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_DECRYPT_CBC));
--      SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt));
--        cipher.context_len = 
 -        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
++      SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher_obj.decrypt));
++        cipher_obj.context_len =
 +        silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_CONTEXT_LEN));
--      SILC_LOG_DEBUG(("context_len=%p", cipher.context_len));
++      SILC_LOG_DEBUG(("context_len=%p", cipher_obj.context_len));
  
 -      /* Put the SIM to the table of all SIM's in server */
 -      server->sim = silc_realloc(server->sim,
 -                                 sizeof(*server->sim) * 
 -                                 (server->sim_count + 1));
 -      server->sim[server->sim_count] = sim;
 -      server->sim_count++;
 +      /* Put the SIM to the list of all SIM's in server */
 +      silc_dlist_add(server->sim, sim);
 +
 +      silc_free(alg_name);
        } else {
        SILC_LOG_ERROR(("Error configuring ciphers"));
        silc_server_stop(server);
        }
  
        /* Register the cipher */
--      silc_cipher_register(&cipher);
--#endif
--    }
--
--    alg = alg->next;
--  }
-   return TRUE;
--}
--
--/* Registers configured PKCS's. */
 -/* XXX: This really doesn't do anything now since we have statically
 -   registered our PKCS's. This should be implemented when PKCS works
 -   as SIM's. This checks now only that the PKCS user requested is 
 -   really out there. */
--
- bool silc_server_config_register_pkcs(SilcServerConfig config)
 -void silc_config_server_register_pkcs(SilcConfigServer config)
--{
-   SilcServerConfigSectionAlg *alg = config->pkcs;
 -  SilcConfigServerSectionAlg *alg = config->pkcs;
--  SilcServer server = (SilcServer)config->server;
 -  SilcPKCS tmp = NULL;
--
--  SILC_LOG_DEBUG(("Registering configured PKCS"));
-   if (!config->pkcs)
-     return FALSE;
--
--  while(alg) {
-     int i;
-     
-     for (i = 0; silc_default_pkcs[i].name; i++)
-       if (!strcmp(silc_default_pkcs[i].name, alg->alg_name)) {
-       silc_pkcs_register(&silc_default_pkcs[i]);
-       break;
-       }
-       
-     if (!silc_pkcs_is_supported(alg->alg_name)) {
-       SILC_LOG_ERROR(("Unknown PKCS `%s'", alg->alg_name));
 -
 -    if (silc_pkcs_alloc(alg->alg_name, &tmp) == FALSE) {
 -      SILC_LOG_ERROR(("Unsupported PKCS `%s'", alg->alg_name));
++      silc_cipher_register(&cipher_obj);
++#else
++      SILC_LOG_ERROR(("Dynamic module support not compiled, "
++                      "can't load modules!"));
        silc_server_stop(server);
        exit(1);
++#endif
      }
-     alg = alg->next;
-   }
 -    silc_free(tmp);
++    cipher = cipher->next;
++  } /* while */
  
 -    alg = alg->next;
 -  }
 +  return TRUE;
  }
  
  /* Registers configured hash functions. These can then be allocated by the
     server when needed. */
  
- bool silc_server_config_register_hashfuncs(SilcServerConfig config)
 -void silc_config_server_register_hashfuncs(SilcConfigServer config)
++bool silc_server_config_register_hashfuncs(SilcServer server)
  {
-   SilcServerConfigSectionAlg *alg;
 -  SilcConfigServerSectionAlg *alg;
--  SilcServer server = (SilcServer)config->server;
++  SilcServerConfig config = server->config;
++  SilcServerConfigSectionHash *hash = config->hash;
++  char *module_path = config->module_path;
  
    SILC_LOG_DEBUG(("Registering configured hash functions"));
  
-   if (!config->hash_func)
 -  alg = config->hash_func;
 -  while(alg) {
 -
 -    if (!alg->sim_name) {
 -      /* Hash module is supposed to be built in. Nothing to be done
 -       here except to test that the hash function really is built in. */
 -      SilcHash tmp = NULL;
++  if (!hash) /* any hash func in the config file? */
 +    return FALSE;
  
-   alg = config->hash_func;
-   while(alg) {
-     if (!alg->sim_name) {
 -      if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) {
 -      SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->alg_name));
++  while (hash) {
++    /* if there isn't a module_path OR there isn't a module sim name try to
++     * use buil-in functions */
++    if (!module_path || !hash->module) {
 +      int i;
-       
 +      for (i = 0; silc_default_hash[i].name; i++)
-       if (!strcmp(silc_default_hash[i].name, alg->alg_name)) {
++      if (!strcmp(silc_default_hash[i].name, hash->name)) {
 +        silc_hash_register(&silc_default_hash[i]);
 +        break;
 +      }
-       
-       if (!silc_hash_is_supported(alg->alg_name)) {
-       SILC_LOG_ERROR(("Unknown hash funtion `%s'", alg->alg_name));
++      if (!silc_hash_is_supported(hash->name)) {
++      SILC_LOG_ERROR(("Unknown hash funtion `%s'", hash->name));
        silc_server_stop(server);
        exit(1);
        }
 -      silc_free(tmp);
--
--#ifdef SILC_SIM
      } else {
++#ifdef SILC_SIM
        /* Load (try at least) the hash SIM module */
--      SilcHashObject hash;
++      SilcHashObject hash_obj;
        SilcSimContext *sim;
  
--      memset(&hash, 0, sizeof(hash));
--      hash.name = alg->alg_name;
--      hash.block_len = alg->block_len;
--      hash.hash_len = alg->key_len;
++      memset(&hash_obj, 0, sizeof(hash_obj));
++      hash_obj.name = hash->name;
++      hash_obj.block_len = hash->block_length;
++      hash_obj.hash_len = hash->digest_length;
  
        sim = silc_sim_alloc();
        sim->type = SILC_SIM_HASH;
--      sim->libname = alg->sim_name;
++      sim->libname = hash->module;
  
        if ((silc_sim_load(sim))) {
--      hash.init = 
--        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
++      hash_obj.init =
++        silc_sim_getsym(sim, silc_sim_symname(hash->name,
                                                SILC_HASH_SIM_INIT));
--      SILC_LOG_DEBUG(("init=%p", hash.init));
--      hash.update = 
--        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
++      SILC_LOG_DEBUG(("init=%p", hash_obj.init));
++      hash_obj.update =
++        silc_sim_getsym(sim, silc_sim_symname(hash->name,
                                                SILC_HASH_SIM_UPDATE));
--      SILC_LOG_DEBUG(("update=%p", hash.update));
--        hash.final = 
--        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
++      SILC_LOG_DEBUG(("update=%p", hash_obj.update));
++        hash_obj.final =
++        silc_sim_getsym(sim, silc_sim_symname(hash->name,
                                                SILC_HASH_SIM_FINAL));
--      SILC_LOG_DEBUG(("final=%p", hash.final));
--        hash.context_len = 
--        silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
++      SILC_LOG_DEBUG(("final=%p", hash_obj.final));
++        hash_obj.context_len =
++        silc_sim_getsym(sim, silc_sim_symname(hash->name,
                                                SILC_HASH_SIM_CONTEXT_LEN));
--      SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
++      SILC_LOG_DEBUG(("context_len=%p", hash_obj.context_len));
  
        /* Put the SIM to the table of all SIM's in server */
 -      server->sim = silc_realloc(server->sim,
 -                                 sizeof(*server->sim) * 
 -                                 (server->sim_count + 1));
 -      server->sim[server->sim_count] = sim;
 -      server->sim_count++;
 +      silc_dlist_add(server->sim, sim);
        } else {
        SILC_LOG_ERROR(("Error configuring hash functions"));
        silc_server_stop(server);
        exit(1);
        }
  
 -      /* Register the cipher */
 -      silc_hash_register(&hash);
 +      /* Register the hash function */
-       silc_hash_register(&hash);
++      silc_hash_register(&hash_obj);
++#else
++      SILC_LOG_ERROR(("Dynamic module support not compiled, "
++                      "can't load modules!"));
++      silc_server_stop(server);
++      exit(1);
  #endif
      }
-     alg = alg->next;
-   }
++    hash = hash->next;
++  } /* while */
  
 -    alg = alg->next;
 -  }
 +  return TRUE;
  }
  
 -/* Returns client authentication information from server configuration
 -   by host (name or ip). */
 +/* Registers configure HMACs. These can then be allocated by the server
 +   when needed. */
  
- bool silc_server_config_register_hmacs(SilcServerConfig config)
 -SilcConfigServerSectionClientConnection *
 -silc_config_server_find_client_conn(SilcConfigServer config, 
 -                                  char *host, int port)
++bool silc_server_config_register_hmacs(SilcServer server)
  {
-   SilcServerConfigSectionAlg *alg;
-   SilcServer server = (SilcServer)config->server;
 -  int i;
 -  SilcConfigServerSectionClientConnection *client = NULL;
++  SilcServerConfig config = server->config;
++  SilcServerConfigSectionHmac *hmac = config->hmac;
 +
 +  SILC_LOG_DEBUG(("Registering configured HMACs"));
 +
-   if (!config->hmac)
++  if (!hmac)
 +    return FALSE;
 +
-   alg = config->hmac;
-   while(alg) {
-     SilcHmacObject hmac;
-     
-     if (!silc_hash_is_supported(alg->sim_name)) {
-       SILC_LOG_ERROR(("Unknown hash function `%s'", alg->sim_name));
++  while (hmac) {
++    SilcHmacObject hmac_obj;
++    if (!silc_hash_is_supported(hmac->hash)) {
++      SILC_LOG_ERROR(("Unknown hash function `%s'", hmac->hash));
 +      silc_server_stop(server);
 +      exit(1);
 +    }
-     
 +    /* Register the HMAC */
-     memset(&hmac, 0, sizeof(hmac));
-     hmac.name = alg->alg_name;
-     hmac.len = alg->key_len;
-     silc_hmac_register(&hmac);
++    memset(&hmac_obj, 0, sizeof(hmac_obj));
++    hmac_obj.name = hmac->name;
++    hmac_obj.len = hmac->mac_length;
++    silc_hmac_register(&hmac_obj);
 +
-     alg = alg->next;
-   }
++    hmac = hmac->next;
++  } /* while */
 +
 +  return TRUE;
 +}
 +
- /* Returns client authentication information from server configuration
-    by host (name or ip). If `port' is non-null then both name or IP and 
-    the port must match. */
++/* Registers configured PKCS's. */
 +
- SilcServerConfigSectionClientConnection *
- silc_server_config_find_client_conn(SilcServerConfig config, 
-                                   char *host, int port)
++bool silc_server_config_register_pkcs(SilcServer server)
 +{
-   int i;
-   SilcServerConfigSectionClientConnection *client = NULL;
-   bool match = FALSE;
++  SilcServerConfig config = server->config;
++  SilcServerConfigSectionPkcs *pkcs = config->pkcs;
 +
-   if (!host)
-     return NULL;
++  SILC_LOG_DEBUG(("Registering configured PKCS"));
++
++  if (!pkcs)
++    return FALSE;
++
++  while (pkcs) {
++    int i;
++    for (i = 0; silc_default_pkcs[i].name; i++)
++      if (!strcmp(silc_default_pkcs[i].name, pkcs->name)) {
++      silc_pkcs_register(&silc_default_pkcs[i]);
++      break;
++      }
++    if (!silc_pkcs_is_supported(pkcs->name)) {
++      SILC_LOG_ERROR(("Unknown PKCS `%s'", pkcs->name));
++      silc_server_stop(server);
++      exit(1);
++    }
++    pkcs = pkcs->next;
++  } /* while */
++
++  return TRUE;
++}
++
++/* Sets log files where log messages are saved by the server logger. */
++
++void silc_server_config_setlogfiles(SilcServerConfig config,
++                              SilcSchedule sked)
++{
++  SilcServerConfigSectionLogging *this;
++
++  SILC_LOG_DEBUG(("Setting configured log file names"));
 +
-   if (!config->clients)
++  if ((this = config->logging_info))
++    silc_log_set_file(SILC_LOG_INFO, this->file, this->maxsize, sked);
++  if ((this = config->logging_warnings))
++    silc_log_set_file(SILC_LOG_WARNING, this->file, this->maxsize, sked);
++  if ((this = config->logging_errors))
++    silc_log_set_file(SILC_LOG_ERROR, this->file, this->maxsize, sked);
++  if ((this = config->logging_fatals))
++    silc_log_set_file(SILC_LOG_FATAL, this->file, this->maxsize, sked);
++}
++
++/* Returns client authentication information from configuration file by host
++   (name or ip) */
++
++SilcServerConfigSectionClient *
++silc_server_config_find_client(SilcServerConfig config, char *host, int port)
++{
++  SilcServerConfigSectionClient *client;
++  if (!config || !port) {
++    SILC_LOG_WARNING(("Bogus: config_find_client(config=0x%08x, "
++                    "host=0x%08x \"%s\", port=%hu)",
++                    (uint32) config, (uint32) host, host, port));
++    return NULL;
++  }
+   if (!host)
      return NULL;
  
-   client = config->clients;
 -  if (!config->clients)
 -    return NULL;
++  for (client = config->clients; client; client = client->next) {
++    if (client->host && !silc_string_compare(client->host, host))
++      continue;
++    if (client->port && (client->port != port))
++      continue;
++    break;
++  }
++  /* if none matched, then client is already NULL */
++  return client;
++}
  
-   for (i = 0; client; i++) {
-     if (silc_string_compare(client->host, host))
-       match = TRUE;
 -  client = config->clients;
++/* Returns admin connection configuration by host, username and/or
++   nickname. */
  
-     if (port && client->port && client->port != port)
-       match = FALSE;
 -  for (i = 0; client; i++) {
 -    if (silc_string_compare(client->host, host))
 -      break;
 -    client = client->next;
++SilcServerConfigSectionAdmin *
++silc_server_config_find_admin(SilcServerConfig config,
++                            char *host, char *user, char *nick)
++{
++  SilcServerConfigSectionAdmin *admin;
 +
-     if (match)
-       break;
++  /* make sure we have a value for the matching parameters */
++  if (!host)
++    host = "*";
++  if (!user)
++    user = "*";
++  if (!nick)
++    nick = "*";
 +
-     client = client->next;
++  for (admin = config->admins; admin; admin = admin->next) {
++    if (admin->host && !silc_string_compare(admin->host, host))
++      continue;
++    if (admin->user && !silc_string_compare(admin->user, user))
++      continue;
++    if (admin->nick && !silc_string_compare(admin->nick, nick))
++      continue;
++    /* no checks failed -> this entry matches */
++    break;
    }
 -  if (!client)
++  /* if none matched, then admin is already NULL */
++  return admin;
++}
-   if (!client)
++/* Returns the denied connection configuration entry by host and port. */
 +
++SilcServerConfigSectionDeny *
++silc_server_config_find_denied(SilcServerConfig config,
++                             char *host, uint16 port)
++{
++  SilcServerConfigSectionDeny *deny;
++
++  /* make sure we have a value for the matching parameters */
++  if (!config || !port) {
++    SILC_LOG_WARNING(("Bogus: config_find_denied(config=0x%08x, "
++                    "host=0x%08x \"%s\", port=%hu)",
++                    (uint32) config, (uint32) host, host, port));
++    return NULL;
++  }
++  if (!host)
      return NULL;
  
--  return client;
++  for (deny = config->denied; deny; deny = deny->next) {
++    if (deny->host && !silc_string_compare(deny->host, host))
++      continue;
++    break;
++  }
++  /* if none matched, then deny is already NULL */
++  return deny;
  }
  
--/* Returns server connection info from server configuartion by host 
 -   (name or ip). */
++/* Returns server connection info from server configuartion by host
 +   (name or ip). If `port' is non-null then both name or IP and the port
 +   must match. */
  
- SilcServerConfigSectionServerConnection *
- silc_server_config_find_server_conn(SilcServerConfig config, 
 -SilcConfigServerSectionServerConnection *
 -silc_config_server_find_server_conn(SilcConfigServer config, 
++SilcServerConfigSectionServer *
++silc_server_config_find_server_conn(SilcServerConfig config,
                                    char *host, int port)
  {
    int i;
-   SilcServerConfigSectionServerConnection *serv = NULL;
 -  SilcConfigServerSectionServerConnection *serv = NULL;
++  SilcServerConfigSectionServer *serv = NULL;
 +  bool match = FALSE;
  
    if (!host)
      return NULL;
    serv = config->servers;
    for (i = 0; serv; i++) {
      if (silc_string_compare(serv->host, host))
 +      match = TRUE;
 +
 +    if (port && serv->port && serv->port != port)
 +      match = FALSE;
 +
 +    if (match)
        break;
 +
      serv = serv->next;
    }
  
    return serv;
  }
  
--/* Returns router connection info from server configuartion by
++/* Returns router connection info from server configuration by
     host (name or ip). */
  
- SilcServerConfigSectionServerConnection *
- silc_server_config_find_router_conn(SilcServerConfig config, 
 -SilcConfigServerSectionServerConnection *
 -silc_config_server_find_router_conn(SilcConfigServer config, 
++SilcServerConfigSectionRouter *
++silc_server_config_find_router_conn(SilcServerConfig config,
                                    char *host, int port)
  {
    int i;
-   SilcServerConfigSectionServerConnection *serv = NULL;
 -  SilcConfigServerSectionServerConnection *serv = NULL;
++  SilcServerConfigSectionRouter *serv = NULL;
 +  bool match = FALSE;
  
    if (!host)
      return NULL;
    serv = config->routers;
    for (i = 0; serv; i++) {
      if (silc_string_compare(serv->host, host))
 +      match = TRUE;
 +
 +    if (port && serv->port && serv->port != port)
 +      match = FALSE;
 +
 +    if (match)
        break;
 +
      serv = serv->next;
    }
  
    return serv;
  }
  
- /* Returns TRUE if configuartion for a router connection that we are 
 -/* Prints out example configuration file with default built in
 -   configuration values. */
++/* Returns TRUE if configuration for a router connection that we are
 +   initiating exists. */
  
 -void silc_config_server_print()
 +bool silc_server_config_is_primary_route(SilcServerConfig config)
  {
 -  char *buf;
 -
 -  buf = "\
 -#\n\
 -# Automatically generated example SILCd configuration file with default\n\
 -# built in values. Use this as a guide to configure your SILCd configuration\n\
 -# file for your system. For detailed description of different configuration\n\
 -# sections refer to silcd(8) manual page.\n\
 -#\n";
 -  /*
 -#<Cipher>
 -#+blowfish
 -#+twofish
 -#+rc5
 -#+rc6
 -#+3des
 -
 -#<HashFunction>
 -#+md5
 -#+sha1
 -
 -<ServerInfo>
 -+lassi.kuo.fi.ssh.com:10.2.1.6:Kuopio, Finland:1333
 -
 -<AdminInfo>
 -+Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
 -
 -<ListenPort>
 -+10.2.1.6:10.2.1.6:1333
 -
 -<Logging>
 -+infologfile:silcd.log:10000
 -#+warninglogfile:/var/log/silcd_warning.log:10000
 -#+errorlogfile:ERROR.log:10000
 -#+fatallogfile:/var/log/silcd_error.log:
 +  int i;
-   SilcServerConfigSectionServerConnection *serv = NULL;
++  SilcServerConfigSectionRouter *serv = NULL;
 +  bool found = FALSE;
  
 -<ConnectionClass>
 -              +1:100:100:100
 -                      +2:200:300:400
 +  serv = config->routers;
 +  for (i = 0; serv; i++) {
 +    if (serv->initiator == TRUE && serv->backup_router == FALSE) {
 +      found = TRUE;
 +      break;
 +    }
  
 -<ClientAuth>
 -+10.2.1.199:priikone:333:1
 +    serv = serv->next;
 +  }
  
 -<AdminAuth>
 -+10.2.1.199:priikone:priikone:1
 +  return found;
 +}
  
 -<ServerConnection>
 +/* Returns our primary connection configuration or NULL if we do not
 +   have primary router configured. */
  
- SilcServerConfigSectionServerConnection *
 -<RouterConnection>
++SilcServerConfigSectionRouter *
 +silc_server_config_get_primary_router(SilcServerConfig config)
 +{
 +  int i;
-   SilcServerConfigSectionServerConnection *serv = NULL;
++  SilcServerConfigSectionRouter *serv = NULL;
  
 -<DenyConnection>
 -<RedirectClient>
 -  */
 +  serv = config->routers;
 +  for (i = 0; serv; i++) {
 +    if (serv->initiator == TRUE && serv->backup_router == FALSE)
 +      return serv;
 +    serv = serv->next;
 +  }
  
 -  fprintf(stdout, "%s\n", buf);
 +  return NULL;
  }
- /* Returns Admin connection configuration by host, username and/or 
-    nickname. */
- SilcServerConfigSectionAdminConnection *
- silc_server_config_find_admin(SilcServerConfig config,
-                             char *host, char *username, char *nickname)
- {
-   SilcServerConfigSectionAdminConnection *admin = NULL;
-   int i;
-   if (!config->admins)
-     return NULL;
-   if (!host)
-     host = "*";
-   if (!username)
-     username = "*";
-   if (!nickname)
-     nickname = "*";
-   admin = config->admins;
-   for (i = 0; admin; i++) {
-     if (silc_string_compare(admin->host, host) &&
-       silc_string_compare(admin->username, username) &&
-       silc_string_compare(admin->nickname, nickname))
-       break;
-     admin = admin->next;
-   }
-   if (!admin)
-     return NULL;
-   return admin;
- }
- /* Returns the Denied connection configuration by host and port. */
- SilcServerConfigSectionDenyConnection *
- silc_server_config_denied_conn(SilcServerConfig config, char *host,
-                              int port)
- {
-   int i;
-   SilcServerConfigSectionDenyConnection *deny = NULL;
-   bool match = FALSE;
-   if (!host)
-     return NULL;
-   if (!config->denied)
-     return NULL;
-   deny = config->denied;
-   for (i = 0; deny; i++) {
-     if (silc_string_compare(deny->host, host))
-       match = TRUE;
-     if (port && deny->port && deny->port != port)
-       match = FALSE;
-     if (match)
-       break;
-     deny = deny->next;
-   }
-   if (!deny)
-     return NULL;
-   return deny;
- }
index 4bf80bd018110b8cecece8866c9116c887be309b,56ed10eb129e107081e2aad1f7fa0e4863798832..8be6d7cb7ccec9fec883c62321ae4fd72e781a51
@@@ -2,15 -2,15 +2,15 @@@
  
    serverconfig.h
  
--  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
++  Author: Johnny Mnemonic <johnny@themnemonic.org>
  
--  Copyright (C) 1997 - 2000 Pekka Riikonen
++  Copyright (C) 1997 - 2002 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
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
--  
++
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  #ifndef SERVERCONFIG_H
  #define SERVERCONFIG_H
  
--/* Holds information of configured algorithms */
- typedef struct SilcServerConfigSectionAlgStruct {
 -typedef struct SilcConfigServerSectionAlgStruct {
--  char *alg_name;
--  char *sim_name;
-   uint32 block_len;
-   uint32 key_len;
-   struct SilcServerConfigSectionAlgStruct *next;
-   struct SilcServerConfigSectionAlgStruct *prev;
 -  unsigned int block_len;
 -  unsigned int key_len;
 -  struct SilcConfigServerSectionAlgStruct *next;
 -  struct SilcConfigServerSectionAlgStruct *prev;
--#define SILC_CONFIG_SERVER_MODNAME "builtin"
- } SilcServerConfigSectionAlg;
- /* Holds server keys from config file */
- typedef struct {
-   SilcPublicKey public_key;
-   SilcPrivateKey private_key;
- } SilcServerConfigSectionServerKeys;
 -} SilcConfigServerSectionAlg;
--
--/* Holds server information from config file */
--typedef struct {
++typedef struct SilcServerConfigSectionCipherStruct {
++  char *name;
++  char *module;
++  uint32 key_length;
++  uint32 block_length;
++  struct SilcServerConfigSectionCipherStruct *next;
++} SilcServerConfigSectionCipher;
++
++typedef struct SilcServerConfigSectionHashStruct {
++  char *name;
++  char *module;
++  uint32 block_length;
++  uint32 digest_length;
++  struct SilcServerConfigSectionHashStruct *next;
++} SilcServerConfigSectionHash;
++
++typedef struct SilcServerConfigSectionHmacStruct {
++  char *name;
++  char *hash;
++  uint32 mac_length;
++  struct SilcServerConfigSectionHmacStruct *next;
++} SilcServerConfigSectionHmac;
++
++typedef struct SilcServerConfigSectionPkcsStruct {
++  char *name;
++  struct SilcServerConfigSectionPkcsStruct *next;
++} SilcServerConfigSectionPkcs;
++
++typedef struct SilcServerConfigSectionServerInfoStruct {
    char *server_name;
    char *server_ip;
--  char *location;
 -  unsigned short port;
 -} SilcConfigServerSectionServerInfo;
 -
 -/* Holds server's administrative information from config file */
 -typedef struct {
 -  char *server_type;
 -  char *admin_name;
 -  char *admin_email;
 -} SilcConfigServerSectionAdminInfo;
 -
 -/* Holds all the ports the server is listenning on */
 -typedef struct SilcConfigServerSectionListenPortStruct {
 -  char *host;
 -  char *remote_ip;
 -  unsigned short port;
 -  struct SilcConfigServerSectionListenPortStruct *next;
 -  struct SilcConfigServerSectionListenPortStruct *prev;
 -} SilcConfigServerSectionListenPort;
 -
 -/* Holds all the configured log files. */
 -typedef struct SilcConfigServerSectionLoggingStruct {
 -  char *logtype;
 -  char *filename;
 -  unsigned int maxsize;
 -  struct SilcConfigServerSectionLoggingStruct *next;
 -  struct SilcConfigServerSectionLoggingStruct *prev;
 -
 -/* Allowed <Logging> section types */
 -#define SILC_CONFIG_SERVER_LF_INFO "infologfile"
 -#define SILC_CONFIG_SERVER_LF_WARNING "warninglogfile"
 -#define SILC_CONFIG_SERVER_LF_ERROR "errorlogfile"
 -#define SILC_CONFIG_SERVER_LF_FATAL "fatalogfile"
 -} SilcConfigServerSectionLogging;
 +  uint16 port;
++  char *server_type;  /* E.g. "Test Server" */
++  char *location;     /* geographic location */
++  char *admin;                /* admin full name */
++  char *email;                /* admin's email address */
++  char *user;         /* userid the server should be runned at */
++  char *group;                /* ditto, but about groupid */
++  SilcPublicKey public_key;
++  SilcPrivateKey private_key;
++  char *motd_file;    /* path to text motd file (reading only) */
++  char *pid_file;     /* path to the pid file (for reading and writing) */
 +} SilcServerConfigSectionServerInfo;
 +
- /* Holds server's administrative information from config file */
- typedef struct {
-   char *location;
-   char *server_type;
-   char *admin_name;
-   char *admin_email;
- } SilcServerConfigSectionAdminInfo;
- /* Holds all the ports the server is listenning on */
- typedef struct SilcServerConfigSectionListenPortStruct {
-   char *local_ip;
-   char *listener_ip;
-   uint16 port;
-   struct SilcServerConfigSectionListenPortStruct *next;
-   struct SilcServerConfigSectionListenPortStruct *prev;
- } SilcServerConfigSectionListenPort;
- /* Holds server's execution identity, or the user and group which
-    to change from root when server starts */
- typedef struct {
-   char *user;
-   char *group;
- } SilcServerConfigSectionIdentity;
- /* Holds all the configured log files. */
 +typedef struct SilcServerConfigSectionLoggingStruct {
-   char *logtype;
-   char *filename;
++  char *file;
 +  uint32 maxsize;
-   struct SilcServerConfigSectionLoggingStruct *next;
-   struct SilcServerConfigSectionLoggingStruct *prev;
- /* Allowed <Logging> section types */
- #define SILC_CONFIG_SERVER_LF_INFO "infologfile"
- #define SILC_CONFIG_SERVER_LF_WARNING "warninglogfile"
- #define SILC_CONFIG_SERVER_LF_ERROR "errorlogfile"
- #define SILC_CONFIG_SERVER_LF_FATAL "fatallogfile"
- #define SILC_CONFIG_SERVER_LO_QUICK "quicklogs"
- #define SILC_CONFIG_SERVER_LO_FDELAY "flushdelay"
 +} SilcServerConfigSectionLogging;
  
  /* Holds all configured connection classes */
- typedef struct SilcServerConfigSectionConnectionClassStruct {
 -typedef struct SilcConfigServerSectionConnectionClassStruct {
 -  unsigned int class;
 -  unsigned int ping_freq;
 -  unsigned int connect_freq;
 -  unsigned int max_links;
 -  struct SilcConfigServerSectionConnectionClassStruct *next;
 -  struct SilcConfigServerSectionConnectionClassStruct *prev;
 -} SilcConfigServerSectionConnectionClass;
 -
 -#define SILC_CONFIG_SERVER_AUTH_METH_PASSWD "passwd"
 -#define SILC_CONFIG_SERVER_AUTH_METH_PUBKEY "pubkey"
++/* typedef struct SilcServerConfigSectionClassStruct {
 +  uint32 class;
 +  uint32 ping_freq;
 +  uint32 connect_freq;
 +  uint32 max_links;
-   struct SilcServerConfigSectionConnectionClassStruct *next;
-   struct SilcServerConfigSectionConnectionClassStruct *prev;
- } SilcServerConfigSectionConnectionClass;
- #define SILC_CONFIG_SERVER_AUTH_METH_PASSWD "passwd"
- #define SILC_CONFIG_SERVER_AUTH_METH_PUBKEY "pubkey"
++  struct SilcServerConfigSectionClassStruct *next;
++} SilcServerConfigSectionClass; */
  
  /* Holds all client authentication data from config file */
- typedef struct SilcServerConfigSectionClientConnectionStruct {
 -typedef struct SilcConfigServerSectionClientConnectionStruct {
++typedef struct SilcServerConfigSectionClientStruct {
    char *host;
 -  int auth_meth;
 -  char *auth_data;
 -  unsigned short port;
 -  unsigned int class;
 -  struct SilcConfigServerSectionClientConnectionStruct *next;
 -  struct SilcConfigServerSectionClientConnectionStruct *prev;
 -} SilcConfigServerSectionClientConnection;
 -
 -/* Hols all server's administrators authentication data from config file */
 -typedef struct SilcConfigServerSectionAdminConnectionStruct {
 +  SilcAuthMethod auth_meth;
 +  void *auth_data;
 +  uint32 auth_data_len;
 +  uint16 port;
 +  uint32 class;
-   struct SilcServerConfigSectionClientConnectionStruct *next;
-   struct SilcServerConfigSectionClientConnectionStruct *prev;
- } SilcServerConfigSectionClientConnection;
++  struct SilcServerConfigSectionClientStruct *next;
++} SilcServerConfigSectionClient;
++
++/* Holds all server's administrators authentication data from config file */
++typedef struct SilcServerConfigSectionAdminStruct {
+   char *host;
 -  int auth_meth;
 -  char *auth_data;
 -  char *nickname;
 -  unsigned int class;
 -  struct SilcConfigServerSectionAdminConnectionStruct *next;
 -  struct SilcConfigServerSectionAdminConnectionStruct *prev;
 -} SilcConfigServerSectionAdminConnection;
 -
 -/* Holds all configured server/router connections from config file */
 -typedef struct SilcConfigServerSectionServerConnectionStruct {
 -  char *host;
 -  int auth_meth;
 -  char *auth_data;
 -  unsigned short port;
 -  char *version;
 -  unsigned int class;
 -  struct SilcConfigServerSectionServerConnectionStruct *next;
 -  struct SilcConfigServerSectionServerConnectionStruct *prev;
 -} SilcConfigServerSectionServerConnection;
++  char *user;
++  char *nick;
++  SilcAuthMethod auth_meth;
++  void *auth_data;
++  uint32 auth_data_len;
++  struct SilcServerConfigSectionAdminStruct *next;
++} SilcServerConfigSectionAdmin;
+ /* Holds all configured denied connections from config file */
 -typedef struct {
++typedef struct SilcServerConfigSectionDenyStruct {
+   char *host;
 -  char *time;
 -  char *comment;
 -  unsigned short port;
 -} SilcConfigServerSectionDenyConnection;
++  uint16 port;
++  char *reason;
++  struct SilcServerConfigSectionDenyStruct *next;
++} SilcServerConfigSectionDeny;
  
- /* Hols all server's administrators authentication data from config file */
- typedef struct SilcServerConfigSectionAdminConnectionStruct {
 -/* Holds all client redirections from config file */
 -typedef struct {
++/* Holds all configured server connections from config file */
++typedef struct SilcServerConfigSectionServerStruct {
    char *host;
-   char *username;
-   char *nickname;
 -  unsigned short port;
 -} SilcConfigServerSectionRedirectClient;
 -
 -/* 
 -   SILC Server Config object. 
 -
 -   This object holds all the data parsed from the SILC server configuration
 -   file. This is mainly used at the initialization of the server.
 +  SilcAuthMethod auth_meth;
 +  void *auth_data;
 +  uint32 auth_data_len;
-   struct SilcServerConfigSectionAdminConnectionStruct *next;
-   struct SilcServerConfigSectionAdminConnectionStruct *prev;
- } SilcServerConfigSectionAdminConnection;
++  uint16 port;
++  char *version;
++  uint32 class;
++  bool backup_router;
++  struct SilcServerConfigSectionServerStruct *next;
++} SilcServerConfigSectionServer;
  
- /* Holds all configured server/router connections from config file */
- typedef struct SilcServerConfigSectionServerConnectionStruct {
 -*/
 -typedef struct {
 -  /* Pointer back to the server */
 -  void *server;
 -
 -  /* Filename of the configuration file */
 -  char *filename;
 -
 -  /* Configuration sections */
 -  SilcConfigServerSectionAlg *cipher;
 -  SilcConfigServerSectionAlg *pkcs;
 -  SilcConfigServerSectionAlg *hash_func;
 -  SilcConfigServerSectionServerInfo *server_info;
 -  SilcConfigServerSectionAdminInfo *admin_info;
 -  SilcConfigServerSectionListenPort *listen_port;
 -  SilcConfigServerSectionLogging *logging;
 -  SilcConfigServerSectionConnectionClass *conn_class;
 -  SilcConfigServerSectionClientConnection *clients;
 -  SilcConfigServerSectionServerConnection *servers;
 -  SilcConfigServerSectionServerConnection *routers;
 -  SilcConfigServerSectionAdminConnection *admins;
 -  SilcConfigServerSectionDenyConnection *denied;
 -  SilcConfigServerSectionRedirectClient *redirect;
 -} SilcConfigServerObject;
 -
 -typedef SilcConfigServerObject *SilcConfigServer;
 -
 -/* Configuration section type enumerations. */
 -typedef enum {
 -  SILC_CONFIG_SERVER_SECTION_TYPE_NONE = 0,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_PKCS,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION,
 -  SILC_CONFIG_SERVER_SECTION_TYPE_REDIRECT_CLIENT,
 -} SilcConfigServerSectionType;
 -
 -/* SILC Configuration Section structure. */
++/* Holds all configured router connections from config file */
++typedef struct SilcServerConfigSectionRouterStruct {
 +  char *host;
 +  SilcAuthMethod auth_meth;
 +  void *auth_data;
 +  uint32 auth_data_len;
 +  uint16 port;
 +  char *version;
 +  uint32 class;
 +  bool initiator;
 +  bool backup_router;
 +  char *backup_replace_ip;
 +  uint16 backup_replace_port;
 +  bool backup_local;
-   struct SilcServerConfigSectionServerConnectionStruct *next;
-   struct SilcServerConfigSectionServerConnectionStruct *prev;
- } SilcServerConfigSectionServerConnection;
- /* Holds all configured denied connections from config file */
- typedef struct SilcServerConfigSectionDenyConnectionStruct {
-   char *host;
-   char *comment;
-   uint16 port;
-   struct SilcServerConfigSectionDenyConnectionStruct *next;
-   struct SilcServerConfigSectionDenyConnectionStruct *prev;
- } SilcServerConfigSectionDenyConnection;
- /* Holds motd file */
- typedef struct {
-   char *motd_file;
- } SilcServerConfigSectionMotd;
- /* holds pid file */
- typedef struct {
-    char *pid_file;
- } SilcServerConfigSectionPid;
- /* 
-    SILC Server Config object. 
-    This object holds all the data parsed from the SILC server configuration
-    file. This is mainly used at the initialization of the server.
++  struct SilcServerConfigSectionRouterStruct *next;
++} SilcServerConfigSectionRouter;
 +
- */
++/* define the SilcServerConfig object */
  typedef struct {
-   /* Pointer back to the server */
-   void *server;
 -  const char *section;
 -  SilcConfigServerSectionType type;
 -  unsigned int maxfields;
 -} SilcConfigServerSection;
--
-   /* Filename of the configuration file */
-   char *filename;
 -/* LIst of all possible config sections in SILC server. */
 -extern SilcConfigServerSection silc_config_server_sections[];
--
-   /* Configuration sections */
-   SilcServerConfigSectionAlg *cipher;
-   SilcServerConfigSectionAlg *pkcs;
-   SilcServerConfigSectionAlg *hash_func;
-   SilcServerConfigSectionAlg *hmac;
-   SilcServerConfigSectionServerKeys *server_keys;
 -/* Structure used in parsing the configuration lines. The line is read
 -   from a file to this structure before parsing it further. */
 -typedef struct SilcConfigServerParseStruct {
 -  SilcBuffer line;
 -  unsigned int linenum;
 -  SilcConfigServerSection *section;
 -  struct SilcConfigServerParseStruct *next;
 -  struct SilcConfigServerParseStruct *prev;
 -} *SilcConfigServerParse;
 -
 -/* Macros */
 -
 -/* Allocates list entries for configuration sections. Used by all
 -   config sections as this is common. */
 -#define SILC_SERVER_CONFIG_LIST_ALLOC(x)              \
 -do {                                                  \
 -  if (!(x)) {                                         \
 -    (x) = silc_calloc(1, sizeof(*(x)));                       \
 -    (x)->next = NULL;                                 \
 -    (x)->prev = NULL;                                 \
 -  } else {                                            \
 -    if (!(x)->next) {                                 \
 -      (x)->next = silc_calloc(1, sizeof(*(x)->next)); \
 -      (x)->next->next = NULL;                         \
 -      (x)->next->prev = (x);                          \
 -      (x) = (x)->next;                                        \
 -    }                                                 \
 -  }                                                   \
 -} while(0)
++  void *tmp;
++  char *module_path;
++
++  SilcServerConfigSectionCipher *cipher;
++  SilcServerConfigSectionHash *hash;
++  SilcServerConfigSectionHmac *hmac;
++  SilcServerConfigSectionPkcs *pkcs;
++  SilcServerConfigSectionLogging *logging_info;
++  SilcServerConfigSectionLogging *logging_warnings;
++  SilcServerConfigSectionLogging *logging_errors;
++  SilcServerConfigSectionLogging *logging_fatals;
 +  SilcServerConfigSectionServerInfo *server_info;
-   SilcServerConfigSectionAdminInfo *admin_info;
-   SilcServerConfigSectionListenPort *listen_port;
-   SilcServerConfigSectionIdentity *identity;
-   SilcServerConfigSectionLogging *logging;
-   SilcServerConfigSectionConnectionClass *conn_class;
-   SilcServerConfigSectionClientConnection *clients;
-   SilcServerConfigSectionServerConnection *servers;
-   SilcServerConfigSectionServerConnection *routers;
-   SilcServerConfigSectionAdminConnection *admins;
-   SilcServerConfigSectionDenyConnection *denied;
-   SilcServerConfigSectionMotd *motd;
-   SilcServerConfigSectionPid *pidfile;
- } SilcServerConfigObject;
- typedef SilcServerConfigObject *SilcServerConfig;
++/*SilcServerConfigSectionClass *conn_class; */
++  SilcServerConfigSectionClient *clients;
++  SilcServerConfigSectionAdmin *admins;
++  SilcServerConfigSectionDeny *denied;
++  SilcServerConfigSectionServer *servers;
++  SilcServerConfigSectionRouter *routers;
++} *SilcServerConfig;
  
- /* Configuration section type enumerations. */
- typedef enum {
-   SILC_CONFIG_SERVER_SECTION_TYPE_NONE = 0,
-   SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER,
-   SILC_CONFIG_SERVER_SECTION_TYPE_PKCS,
-   SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION,
-   SILC_CONFIG_SERVER_SECTION_TYPE_HMAC,
-   SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS,
-   SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO,
-   SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO,
-   SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT,
-   SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY,
-   SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING,
-   SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS,
-   SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION,
-   SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION,
-   SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION,
-   SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION,
-   SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION,
-   SILC_CONFIG_SERVER_SECTION_TYPE_MOTD,
-   SILC_CONFIG_SERVER_SECTION_TYPE_PID,
- } SilcServerConfigSectionType;
+ /* Prototypes */
 -SilcConfigServer silc_config_server_alloc(char *filename);
 -void silc_config_server_free(SilcConfigServer config);
 -int silc_config_server_parse(SilcConfigServer config, SilcBuffer buffer,
 -                           SilcConfigServerParse *return_config);
 -int silc_config_server_parse_lines(SilcConfigServer config, 
 -                                 SilcConfigServerParse parse_config);
 -int silc_config_server_check_sections(unsigned int checkmask);
 -void silc_config_server_setlogfiles(SilcConfigServer config);
 -void silc_config_server_register_ciphers(SilcConfigServer config);
 -void silc_config_server_register_pkcs(SilcConfigServer config);
 -void silc_config_server_register_hashfuncs(SilcConfigServer config);
 -SilcConfigServerSectionClientConnection *
 -silc_config_server_find_client_conn(SilcConfigServer config, 
 -                                  char *host, int port);
 -SilcConfigServerSectionServerConnection *
 -silc_config_server_find_server_conn(SilcConfigServer config, 
 +
- /* SILC Configuration Section structure. */
- typedef struct {
-   const char *section;
-   SilcServerConfigSectionType type;
-   int maxfields;
- } SilcServerConfigSection;
++/* basic config operations */
++SilcServerConfig silc_server_config_alloc(char *filename);
++void silc_server_config_destroy(SilcServerConfig config);
 +
- /* LIst of all possible config sections in SILC server. */
- extern SilcServerConfigSection silc_server_config_sections[];
++/* algorithm registering and reset functions */
++bool silc_server_config_register_ciphers(SilcServer server);
++bool silc_server_config_register_hashfuncs(SilcServer server);
++bool silc_server_config_register_hmacs(SilcServer server);
++bool silc_server_config_register_pkcs(SilcServer server);
++void silc_server_config_setlogfiles(SilcServerConfig config, SilcSchedule sked);
 +
- /* Structure used in parsing the configuration lines. The line is read
-    from a file to this structure before parsing it further. */
- typedef struct SilcServerConfigParseStruct {
-   SilcBuffer line;
-   int linenum;
-   SilcServerConfigSection *section;
-   struct SilcServerConfigParseStruct *next;
-   struct SilcServerConfigParseStruct *prev;
- } *SilcServerConfigParse;
++/* run-time config access functions */
++SilcServerConfigSectionClient *
++silc_server_config_find_client(SilcServerConfig config, char *host, int port);
 +
- /* Macros */
++SilcServerConfigSectionAdmin *
++silc_server_config_find_admin(SilcServerConfig config,
++                            char *host, char *user, char *nick);
 +
- /* Allocates list entries for configuration sections. Used by all
-    config sections as this is common. */
- #define SILC_SERVER_CONFIG_LIST_ALLOC(x)              \
- do {                                                  \
-   if (!(x)) {                                         \
-     (x) = silc_calloc(1, sizeof(*(x)));                       \
-     (x)->next = NULL;                                 \
-     (x)->prev = NULL;                                 \
-   } else {                                            \
-     if (!(x)->next) {                                 \
-       (x)->next = silc_calloc(1, sizeof(*(x)->next)); \
-       (x)->next->next = NULL;                         \
-       (x)->next->prev = (x);                          \
-       (x) = (x)->next;                                        \
-     }                                                 \
-   }                                                   \
- } while(0)
++SilcServerConfigSectionDeny *
++silc_server_config_find_denied(SilcServerConfig config,
++                             char *host, uint16 port);
 +
- /* Prototypes */
- SilcServerConfig silc_server_config_alloc(char *filename);
- void silc_server_config_free(SilcServerConfig config);
- int silc_server_config_parse(SilcServerConfig config, SilcBuffer buffer,
-                            SilcServerConfigParse *return_config);
- int silc_server_config_parse_lines(SilcServerConfig config, 
-                                  SilcServerConfigParse parse_config);
- int silc_server_config_check_sections(uint32 checkmask);
- void silc_server_config_setlogfiles(SilcServerConfig config, SilcSchedule sked);
- bool silc_server_config_register_ciphers(SilcServerConfig config);
- bool silc_server_config_register_pkcs(SilcServerConfig config);
- bool silc_server_config_register_hashfuncs(SilcServerConfig config);
- bool silc_server_config_register_hmacs(SilcServerConfig config);
- SilcServerConfigSectionClientConnection *
- silc_server_config_find_client_conn(SilcServerConfig config, 
-                                   char *host, int port);
- SilcServerConfigSectionServerConnection *
- silc_server_config_find_server_conn(SilcServerConfig config, 
++/* Prototypes - OLD */
++SilcServerConfigSectionServer *
++silc_server_config_find_server_conn(SilcServerConfig config,
                                    char *host, int port);
- SilcServerConfigSectionServerConnection *
- silc_server_config_find_router_conn(SilcServerConfig config, 
 -SilcConfigServerSectionServerConnection *
 -silc_config_server_find_router_conn(SilcConfigServer config, 
++SilcServerConfigSectionRouter *
++silc_server_config_find_router_conn(SilcServerConfig config,
                                    char *host, int port);
 -void silc_config_server_print();
 +bool silc_server_config_is_primary_route(SilcServerConfig config);
- SilcServerConfigSectionServerConnection *
++SilcServerConfigSectionRouter *
 +silc_server_config_get_primary_router(SilcServerConfig config);
- SilcServerConfigSectionAdminConnection *
- silc_server_config_find_admin(SilcServerConfig config,
-                             char *host, char *username, char *nickname);
- SilcServerConfigSectionDenyConnection *
- silc_server_config_denied_conn(SilcServerConfig config, char *host,
-                              int port);
  
--#endif
++#endif        /* !SERVERCONFIG_H */
diff --combined apps/silcd/silcd.c
index 0eadeba52974a01a48ecd14c13e5a65b47b0223d,0de8874a5d8fe8353af8c4b64958b6ebb7162cca..cf3315a2cd126100a2611548ee6e1c271d797dda
  /*
  
    silcd.c
 -  
 -  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
  
 -  Copyright (C) 1997 - 2000 Pekka Riikonen
 +  Author: Pekka Riikonen <priikone@silcnet.org>
 +
 +  Copyright (C) 1997 - 2002 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
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 -  
 +
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  
  */
 -/* 
 +/*
   * Created: Wed Mar 19 00:17:12 1997
   *
   * This is the main program for the SILC daemon. This parses command
   * line arguments and creates the server object.
   */
 -/*
 - * $Id$
 - * $Log$
 - * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
 - *    Importet from internal CVS/Added Log headers.
 - *
 - *
 - */
 +/* $Id$ */
  
  #include "serverincludes.h"
  #include "server_internal.h"
  #include "version.h"
  
 +/* For now, we'll have this one server context global for this module. */
 +static SilcServer silcd;
 +
 +static void silc_usage();
 +static char *silc_server_create_identifier();
 +static int
 +silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
 +                          char *identifier,
 +                          SilcPublicKey *ret_pub_key,
 +                          SilcPrivateKey *ret_prv_key);
 +
  /* Long command line options */
 -static struct option long_opts[] = 
 +static struct option long_opts[] =
  {
    { "config-file", 1, NULL, 'f' },
 -  { "generate-config-file", 0, NULL, 'c' },
 +  { "debug", 1, NULL, 'd' },
    { "help", 0, NULL, 'h' },
 +  { "foreground", 0, NULL, 'F' },
    { "version", 0, NULL,'V' },
 +
 +  /* Key management options */
 +  { "create-key-pair", 1, NULL, 'C' },
 +  { "pkcs", 1, NULL, 10 },
 +  { "bits", 1, NULL, 11 },
 +  { "identifier", 1, NULL, 12 },
 +
    { NULL, 0, NULL, 0 }
  };
  
 +/* Command line option variables */
 +static bool opt_create_keypair = FALSE;
 +static char *opt_keypath = NULL;
 +static char *opt_pkcs = "rsa";
 +static char *opt_identifier = NULL;
 +static int opt_bits = 1024;
 +
  /* Prints out the usage of silc client */
  
 -void silc_usage()
 +static void silc_usage()
  {
 -  printf("Usage: silcd [options]\n");
 -  printf("Options:\n");
 -  printf("  -f  --config-file=FILE        Alternate configuration file\n");
 -  printf("  -c  --generate-config-file    Generate example configuration "
 -       "file\n");
 -  printf("  -h  --help                    Display this message\n");
 -  printf("  -V  --version                 Display version\n");
 +  printf("\
 +Usage: silcd [options]\n\
 +\n\
 +  Generic Options:\n\
 +  -f  --config-file=FILE        Alternate configuration file\n\
 +  -d  --debug=string            Enable debugging (Implies --foreground)\n\
 +  -h  --help                    Display this message\n\
 +  -F  --foreground              Dont fork\n\
 +  -V  --version                 Display version\n\
 +\n\
 +  Key Management Options:\n\
 +  -C, --create-key-pair=PATH    Create new public key pair\n\
 +      --pkcs=PKCS               Set the PKCS of the public key pair\n\
 +      --bits=VALUE              Set length of the public key pair\n\
 +      --identifier=IDENTIFIER   Public key identifier\n\
 +\n\
 +      The public key identifier may be of the following format:\n\
 +\n\
 +      UN=<username>, HN=<hostname or IP>, RN=<real name>, E=<email>,\n\
 +      O=<organization>, C=<country>\n\
 +\n\
 +      The UN and HN must be provided, the others are optional.  If the\n\
 +      --identifier option is not used an identifier will be created for\n\
 +      the public key automatically.\n\
 +\n\
 +      Example identifier: \"UN=foobar, HN=foo.bar.com, RN=Foo T. Bar, \n\
 +                           E=foo@bar.com, C=FI\"\n\
 +\n");
    exit(0);
  }
  
- static void silc_checkpid(SilcServer silcd)
 +/* Dies if a *valid* pid file exists already */
 +
-   if (silcd->config->pidfile && silcd->config->pidfile->pid_file) {
++static void silc_server_checkpid(SilcServer silcd)
 +{
-     buf = silc_file_readfile(silcd->config->pidfile->pid_file, &buf_len);
++  if (silcd->config->server_info->pid_file) {
 +    int oldpid;
 +    char *buf;
 +    uint32 buf_len;
 +
 +    SILC_LOG_DEBUG(("Checking for another silcd running"));
-       silcd->config->pidfile->pid_file);
++    buf = silc_file_readfile(silcd->config->server_info->pid_file, &buf_len);
 +    if (!buf)
 +      return;
 +    oldpid = atoi(buf);
 +    silc_free(buf);
 +    if (oldpid <= 0)
 +      return;
 +    kill(oldpid, SIGCHLD); /* this signal does nothing, check if alive */
 +    if (errno != ESRCH) {
 +      fprintf(stderr, "\nI detected another daemon running with the same pid file.\n");
 +      fprintf(stderr, "Please change the config file, or erase the %s\n",
++      silcd->config->server_info->pid_file);
 +      exit(1);
 +    }
 +  }
 +}
 +
 +static void got_hup(int z)
 +{
 +  /* First, reset all log files (they might have been deleted) */
 +  silc_log_reset_all();
 +  silc_log_flush_all();
 +}
 +
 +static void stop_server(int z)
 +{
 +  /* Stop scheduler, the program will stop eventually after noticing
 +     that the scheduler is down. */
 +  silc_schedule_stop(silcd->schedule); 
 +}
 +
  int main(int argc, char **argv)
  {
 -  int ret;
 -  int opt, option_index;
 +  int ret, opt, option_index;
    char *config_file = NULL;
 -  SilcServer silcd;
 +  bool foreground = FALSE;
 +  struct sigaction sa;
  
    /* Parse command line arguments */
    if (argc > 1) {
 -    while ((opt = getopt_long(argc, argv, "cf:hV",
 +    while ((opt = getopt_long(argc, argv, "cf:d:hFVC:",
                              long_opts, &option_index)) != EOF) {
 -      switch(opt) 
 +      switch(opt)
        {
        case 'h':
          silc_usage();
          break;
        case 'V':
          printf("SILCd Secure Internet Live Conferencing daemon, "
 -               "version %s\n", silc_version);
 -        printf("(c) 1997 - 2000 Pekka Riikonen "
 -               "<priikone@poseidon.pspt.fi>\n");
 +               "version %s (base: SILC Toolkit %s)\n",
 +                 silc_dist_version, silc_version);
 +        printf("(c) 1997 - 2001 Pekka Riikonen "
 +               "<priikone@silcnet.org>\n");
          exit(0);
          break;
 -      case 'c':
 -        /* Print out example configuration file */
 -        silc_config_server_print();
 -        exit(0);
 +      case 'd':
 +#ifdef SILC_DEBUG
 +        silc_debug = TRUE;
 +        silc_debug_hexdump = TRUE;
 +        silc_log_set_debug_string(optarg);
 +        foreground = TRUE;
 +        silc_log_quick = TRUE;
 +#else
 +        fprintf(stdout,
 +                "Run-time debugging is not enabled. To enable it recompile\n"
 +                "the server with --enable-debug configuration option.\n");
 +#endif
          break;
        case 'f':
          config_file = strdup(optarg);
          break;
 +      case 'F':
 +        foreground = TRUE;
 +        break;
 +
 +        /*
 +         * Key management options
 +         */
 +      case 'C':
 +        opt_create_keypair = TRUE;
 +        if (optarg)
 +          opt_keypath = strdup(optarg);
 +        break;
 +      case 10:
 +        if (optarg)
 +          opt_pkcs = strdup(optarg);
 +        break;
 +      case 11:
 +        if (optarg)
 +          opt_bits = atoi(optarg);
 +        break;
 +      case 12:
 +        if (optarg)
 +          opt_identifier = strdup(optarg);
 +        break;
 +
        default:
          silc_usage();
          break;
      }
    }
  
 +  if (opt_create_keypair == TRUE) {
 +    /* Create new key pair and exit */
 +    silc_cipher_register_default();
 +    silc_pkcs_register_default();
 +    silc_hash_register_default();
 +    silc_hmac_register_default();
 +    silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
 +                              opt_identifier, NULL, NULL);
 +    exit(0);
 +  }
 +
    /* Default configuration file */
    if (!config_file)
      config_file = strdup(SILC_SERVER_CONFIG_FILE);
      goto fail;
  
    /* Read configuration files */
 -  silcd->config = silc_config_server_alloc(config_file);
 +  silcd->config = silc_server_config_alloc(config_file);
    if (silcd->config == NULL)
      goto fail;
  
-   silc_checkpid(silcd);
 +  /* Check for another silcd running */
++  silc_server_checkpid(silcd);
 +
    /* Initialize the server */
    ret = silc_server_init(silcd);
    if (ret == FALSE)
      goto fail;
 -  
 +
 +  /* Ignore SIGPIPE */
 +  sa.sa_handler = SIG_IGN;
 +  sa.sa_flags = 0;
 +  sigemptyset(&sa.sa_mask);
 +  sigaction(SIGPIPE, &sa, NULL);
 +  sa.sa_handler = got_hup;
 +  sigaction(SIGHUP, &sa, NULL);
 +  sa.sa_handler = stop_server;
 +  sigaction(SIGTERM, &sa, NULL);
 +  sa.sa_handler = stop_server;
 +  sigaction(SIGINT, &sa, NULL);
 +
 +  /* Before running the server, fork to background. */
 +  if (!foreground)
 +    silc_server_daemonise(silcd);
 +
 +  /* If set, write pid to file */
-   if (silcd->config->pidfile && silcd->config->pidfile->pid_file) {
-     char buf[10];
-     unlink(silcd->config->pidfile->pid_file);
++  if (silcd->config->server_info->pid_file) {
++    char buf[10], *pidfile = silcd->config->server_info->pid_file;
++    unlink(pidfile);
 +    snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
-     silc_file_writefile(silcd->config->pidfile->pid_file, buf, strlen(buf));
++    silc_file_writefile(pidfile, buf, strlen(buf));
 +  }
 +
 +  /* Drop root. */
 +  silc_server_drop(silcd);
 +
    /* Run the server. When this returns the server has been stopped
       and we will exit. */
    silc_server_run(silcd);
    
 -  /* Stop the server. This probably has been done already but it
 -     doesn't hurt to do it here again. */
 +  /* Stop the server and free it. */
    silc_server_stop(silcd);
    silc_server_free(silcd);
 -  
 +
 +  /* Flush the logging system */
 +  silc_log_flush_all();
 +
    exit(0);
   fail:
    exit(1);
  }
 +
 +/* Returns identifier string for public key generation. */
 +
 +static char *silc_server_create_identifier()
 +{
 +  char *username = NULL, *realname = NULL;
 +  char hostname[256], email[256];
 +  
 +  /* Get realname */
 +  realname = silc_get_real_name();
 +
 +  /* Get hostname */
 +  memset(hostname, 0, sizeof(hostname));
 +  gethostname(hostname, sizeof(hostname));
 +
 +  /* Get username (mandatory) */
 +  username = silc_get_username();
 +  if (!username)
 +    return NULL;
 +
 +  /* Create default email address, whether it is right or not */
 +  snprintf(email, sizeof(email), "%s@%s", username, hostname);
 +
 +  return silc_pkcs_encode_identifier(username, hostname, realname, email,
 +                                   NULL, NULL);
 +}
 +
 +/* Creates new public key and private key pair. This is used only
 +   when user wants to create new key pair from command line. */
 +
 +static int 
 +silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
 +                          char *identifier, 
 +                          SilcPublicKey *ret_pub_key,
 +                          SilcPrivateKey *ret_prv_key)
 +{
 +  SilcPKCS pkcs;
 +  SilcPublicKey pub_key;
 +  SilcPrivateKey prv_key;
 +  SilcRng rng;
 +  unsigned char *key;
 +  uint32 key_len;
 +  char pkfile[256], prvfile[256];
 +
 +  if (!pkcs_name || !path)
 +    return FALSE;
 +
 +  if (!silc_pkcs_is_supported(pkcs_name)) {
 +    fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
 +    return FALSE;
 +  }
 +
 +  if (!bits)
 +    bits = 1024;
 +
 +  if (!identifier)
 +    identifier = silc_server_create_identifier();
 +
 +  rng = silc_rng_alloc();
 +  silc_rng_init(rng);
 +  silc_rng_global_init(rng);
 +
 +  snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
 +         SILC_SERVER_PUBLIC_KEY_NAME);
 +  snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
 +         SILC_SERVER_PRIVATE_KEY_NAME);
 +
 +  /* Generate keys */
 +  silc_pkcs_alloc(pkcs_name, &pkcs);
 +  pkcs->pkcs->init(pkcs->context, bits, rng);
 +
 +  /* Save public key into file */
 +  key = silc_pkcs_get_public_key(pkcs, &key_len);
 +  pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
 +                                     key, key_len);
 +  silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
 +  if (ret_pub_key)
 +    *ret_pub_key = pub_key;
 +  else
 +    silc_pkcs_public_key_free(pub_key);
 +
 +  memset(key, 0, sizeof(key_len));
 +  silc_free(key);
 +
 +  /* Save private key into file */
 +  key = silc_pkcs_get_private_key(pkcs, &key_len);
 +  prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
 +  silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
 +  if (ret_prv_key)
 +    *ret_prv_key = prv_key;
 +  else
 +    silc_pkcs_private_key_free(prv_key);
 +
 +  printf("Public key has been saved into `%s'\n", pkfile);
 +  printf("Private key has been saved into `%s'\n", prvfile);
 +
 +  memset(key, 0, sizeof(key_len));
 +  silc_free(key);
 +
 +  silc_rng_free(rng);
 +  silc_pkcs_free(pkcs);
 +
 +  return TRUE;
 +}
index d12a141b9d2574f34e2b0cb3188328a549106c41,0000000000000000000000000000000000000000..afe74b3e9b0820d0ae3598160d94328bc783377d
mode 100644,000000..100644
--- /dev/null
@@@ -1,241 -1,0 +1,348 @@@
- # configuration possibilities and may not actually give any sensible 
 +#
 +# Example configuration file.  Note that this attempts to present various
- # Configured ciphers.
++# configuration possibilities and may not actually give any sensible
 +# configuration.  For real life example see the examples/ directory.
 +#
 +
 +#
- # Format: <name>:<module path>:<key length>:<block length>
- #
- # If the cipher is builtin the <module path> maybe omitted.
- #
- [Cipher]
- aes-256-cbc:@MODULESDIR@/aes.sim.so:32:16
- aes-192-cbc:@MODULESDIR@/aes.sim.so:24:16
- aes-128-cbc:@MODULESDIR@/aes.sim.so:16:16
- twofish-256-cbc:@MODULESDIR@/twofish.sim.so:32:16
- twofish-192-cbc:@MODULESDIR@/twofish.sim.so:24:16
- twofish-128-cbc:@MODULESDIR@/twofish.sim.so:16:16
- mars-256-cbc:@MODULESDIR@/mars.sim.so:32:16
- mars-192-cbc:@MODULESDIR@/mars.sim.so:24:16
- mars-128-cbc:@MODULESDIR@/mars.sim.so:16:16
- none:@MODULESDIR@/none.sim.so:0:0
++# General configuration options
 +#
- # Configured hash functions.
- #
- # Format: <name>:<module path>:<block length>:<digest length>
- #
- # If the hash function is builtin the <module path> maybe omitted.
++General {
++      # This is the default path where to search modules
++      # You can comment it out to use builtin modules globally.
++      ModulePath = "@MODULESDIR@";
++};
 +
 +#
- [Hash]
- sha1::64:20
- md5::64:16
++# Configured ciphers
++#
++# The "Module" option can be either absolute or relative to the "ModulePath"
++# option.
++# If commented out forces using of built-in modules.
++#
++cipher {
++      name = "aes-256-cbc";
++      module = "aes.sim.so";
++      key_length = 32;
++      block_length = 16;
++};
++cipher {
++      name = "aes-192-cbc";
++      module = "aes.sim.so";
++      key_length = 24;
++      block_length = 16;
++};
++cipher {
++      name = "aes-128-cbc";
++      module = "aes.sim.so";
++      key_length = 16;
++      block_length = 16;
++};
++cipher {
++      name = "twofish-256-cbc";
++      module = "twofish.sim.so";
++      key_length = 32;
++      block_length = 16;
++};
++cipher {
++      name = "twofish-192-cbc";
++      module = "twofish.sim.so";
++      key_length = 24;
++      block_length = 16;
++};
++cipher {
++      name = "twofish-128-cbc";
++      module = "twofish.sim.so";
++      key_length = 16;
++      block_length = 16;
++};
++cipher {
++      name = "mars-256-cbc";
++      module = "mars.sim.so";
++      key_length = 32;
++      block_length = 16;
++};
++cipher {
++      name = "mars-192-cbc";
++      module = "mars.sim.so";
++      key_length = 24;
++      block_length = 16;
++};
++cipher {
++      name = "mars-128-cbc";
++      module = "mars.sim.so";
++      key_length = 16;
++      block_length = 16;
++};
++cipher {
++      name = "none";
++      module = "none.sim.so";
++};
++
 +#
- # configured to the [hash] section.
- #
- # Format: <name>:<hash name>:<mac length>
- #
- [hmac]
- hmac-sha1-96:sha1:12
- hmac-md5-96:md5:12
- hmac-sha1:sha1:20
- hmac-md5:md5:16
++# Configured hash functions
++#
++hash {
++      name = "sha1";
++      block_length = 64;
++      digest_length = 20;
++};
++hash {
++      name = "md5";
++      block_length = 64;
++      digest_length = 16;
++};
 +
 +#
 +# Configured HMAC functions. The hash function used in the HMAC must
- # Configured PKCS.
++# be configured in the hash section.
++#
++hmac {
++      name = "hmac-sha1-96";
++      hash = "sha1";
++      mac_length = 12;
++};
++hmac {
++      name = "hmac-md5-96";
++      hash = "md5";
++      mac_length = 12;
++};
++hmac {
++      name = "hmac-sha1";
++      hash = "sha1";
++      mac_length = 20;
++};
++hmac {
++      name = "hmac-md5";
++      hash = "md5";
++      mac_length = 16;
++};
 +
 +#
- # Format: <name>
- #
- [PKCS]
- rsa
++# Configured PKCS
 +#
- # Run SILC server as specific user and group. The server must be initially
- # run as root.
- #
- # Format: <user>:<group>
++PKCS { name = "rsa"; };
 +
 +#
- [Identity]
- nobody:nobody
++# Server information
 +#
- #
- # Server's administrative information.
- #
- # Format: <location>:<server type>:<admin's name>:<admin's email address>
- #
- [AdminInfo]
- Kuopio, Finland:Test Server:Pekka Riikonen:priikone@poseidon.pspt.fi
++ServerInfo {
++      #
++      # Server FQDN and IP address
++      #
++      hostname = "lassi.kuo.fi.ssh.com";
++      ip = "10.2.1.6";
++      port = 706;
 +
- #
- # Server information.
- #
- # Format: +<server FQDN>:<server IP>:<geographic location>:<port>
- #
- [ServerInfo]
- lassi.kuo.fi.ssh.com:10.2.1.6:Kuopio, Finland:706
++      #
++      # ServerType field specifies the purpose of this server
++      # This is only a descriptive field.
++      #
++      ServerType = "Test Server";
 +
- #
- # Server keys
- #
- # Format: +<public key>:<private key>
- #
- [ServerKeys]
- @ETCDIR@/silcd.pub:@ETCDIR@/silcd.prv
++      #
++      # Geographic location
++      #
++      Location = "Kuopio, Finland";
 +
- #
- # Listenning ports.
- #
- # Format: <local IP>:<Listener IP>:<port>
- #
- [ListenPort]
- 10.2.1.6:10.2.1.6:706
++      #
++      # Full admin name
++      #
++      Admin = "Pekka Riikonen";
 +
- # This section is used to set various logging files, their paths
- # and maximum sizes. There are only four defined channels allowed for
- # defining (see list below).
++      #
++      # Admin's email address
++      #
++      EMail = "priikone@poseidon.pspt.fi";
++
++      #
++      # Run SILC server as specific user and group. The server must be initially
++      # run as root.
++      #
++      User = "nobody";
++      Group = "nobody";
++
++      #
++      # Public and private keys
++      #
++      PublicKey = "@ETCDIR@/silcd.pub";
++      PrivateKey = "@ETCDIR@/silcd.prv";
++
++      #
++      # Motd file
++      #
++      # Specifies the text file displayed on client connection
++      #
++      #MotdFile = "@ETCDIR@/motd.txt";
++
++      #
++      # Pid file
++      #
++      PidFile = "@PIDFILE@";
++};
 +
 +#
 +# Log files.
 +#
- # are printed on the less important ones, thus setting the logging file
- # for "infologfile" will ensure logging for all channels, while setting
- # logging file for "errorlogfile" will ensure logging for channels
- # "error" and "fatal" only.
- # If a message can't find a valid output file it will be discarded, thus,
- # if you unset all files you will completely disable server logging (and
- # this is NOT recommended).
- # If maximum size is given, the logfile will be rotated to a logfile with
- # the ".old" extension added. Older logfiles are flushed.
- # There are also two options, quicklogs and flushdelay. Their values
- # must be enclosed in colons (:), see the format below.
- #
- # Format: quicklogs:<yes/no>:
- #         flushdelay:<seconds>:
- #         infologfile:<path>:<max byte size>
- #         warninglogile:<path>:<max byte size>
- #         errorlogile:<path>:<max byte size>
- #         fatallogile:<path>:<max byte size>
- #
- [Logging]
- quicklogs:no:
- flushdelay:300:
- infologfile:@LOGSDIR@/silcd.log:50000
- warninglogfile:@LOGSDIR@/silcd_warnings.log:50000
- #errorlogfile:@LOGSDIR@/silcd_errors.log:50000
- #fatallogfile:@LOGSDIR@/silcd_fatals.log:
- #
- # Connection classes.
- #
- # This section is used to define connection classes. These can be
- # used to optimize the server and the connections.#
++# This section is used to set various logging files, their paths, maximum
++# sizes and logging options.
++# There are only four defined channels allowed for defining (see below).
 +# The log channels have an importance value, and most important channels
- # Format: <class number>:<ping freq>:<connect freq>:<max links>
++# are redirected on the less important ones, thus setting a valid logging
++# file for "infologfile" will ensure logging for all channels, while setting
++# logging file for "errorlogfile" will ensure logging for channels "error"
++# and "fatal"
++#
++Logging {
++      #
++      # If QuickLogs is true, then the logging files will be updated
++      # real-time. This causes a bit more CPU and HDD activity, but
++      # reduces memory usage. (if unsure say true).
++      #
++      QuickLogs = false;
++
++      #
++      # (Only if QuickLogs is false)
++      # FlushDelay tells log files update delay in case you have chosen
++      # buffering output.
++      #
++      FlushDelay = 180;
++
++      Info {
++              File = "@LOGSDIR@/silcd.log";
++              Size = "50k";
++      };
++      Warnings {
++              File = "@LOGSDIR@/silcd_warnings.log";
++              Size = "50k";
++      };
++      Errors {
++              File = "@LOGSDIR@/silcd_errors.log";
++              Size = "50k";
++      };
++      Fatals {
++              File = "@LOGSDIR@/silcd_fatals.log";
++              Size = "50k";
++      };
++};
++
 +#
- [ConnectionClass]
- 1:100:100:100
- 2:200:300:400
++# Connection classes (UNSUPPORTED)
 +#
- # Format: <remote host>:<auth method>:<auth data>:<port>:<class>
++# This section is used to define connection classes. These can be
++# used to optimize the server and the connections.
++#
++#Class {
++#     Name = "norm";
++#     Ping = 100;
++#     Connect = 100;
++#     Links = 100;
++#};
 +
 +#
 +# Configured client connections.
 +#
- # The <auth data> is either passphrase or file path to the public key
- # file.
++# All fields except Class are optional.  Omitted fields are assumed
++# to be generic (e.g. if the "Host" field is omitted all hosts will match
++# this client class).
++#
++#Client {
++#     Host = "127.0.0.1";
++#     Port = 706;
++#     Class = "local";
++#};
++Client {
++      Port = 706;
++      Class = "norm";
++};
++
 +#
- [ClientConnection]
- :::706:1
++# Configured server administrator connections
 +#
- # Configured server administrator connections
++# The fields "Host", "User", and "Nick", are optional but you are encouraged
++# in using them to better identify your admins.
++# "AuthMethod" and "AuthData" fields are mandatory.  The "AuthMethod" field
++# can be either the special string "passwd" or "pubkey" to identify the type
++# of data specified by "AuthData".
++#
++Admin {
++      Host = "10.2.1.199";
++      User = "priikone";
++      Nick = "pekka";
++      AuthMethod = "passwd";
++      AuthData = "verysecret";
++};
 +
 +#
- # Format: <host>:<username>:<nickname>:<auth method>:<auth data>
++# Denied connections
 +#
- # The <auth data> is either passphrase or file path to the public key
- # file.
++# These connections are denied to connect to our server.
 +#
- [AdminConnection]
- 10.2.1.199:priikone:pekka:passwd:veryscret
++# The "Reason" field is mandatory, while the "Host" and "Port" fields can be
++# omitted to match everything.
 +#
- # If server connections are configured it means that our server is
- # router server.  Normal server must not configure server connections.
- # Thus, if your server is not router do not configure this section.  If
++#Deny {
++#     Host = "10.2.1.99";
++#     Port = 706;
++#     Reason = "Go away spammer";
++#};
++#Deny {
++#     Host = "10.3.*";
++#     Reason = "You are not welcome.";
++#};
 +
 +#
 +# Configured server connections.
 +#
- # Format: <remote host>:<auth method>:<auth data>:<port>:
- #         <version ID>:<class>:<backup connection>
- #
- # The <auth data> is either passphrase or file path to the public key
- # file. If the connection is backup connection then set the <backup 
- # connection> to value 1. For normal connections set it 0. If it is
- # set to value 1 then this server will be backup router.
- #
- [ServerConnection]
- 10.2.1.7:passwd:veryscret:706:1:1:0
- 10.2.1.17:passwd:veryscret13:706:1:1:1   # backup connection, that host
-                                          # will use this server as backup
-                                          # router.
++# If server connections are configured it means that this server is
++# router server.  Normal servers must not configure server connections.
++# Thus, if this server is not router do not configure this section.  If
 +# your server is router, this must be configured.
 +#
- # Configured router connections.
++# The "AuthData" option is either passphrase or file path to the public key
++# file. If the connection is backup connection then set the "Backup" option
++# to true. For normal connections set it false. If it is
++# set to true then this server will be backup router.
++#
++ServerConnection {
++      Host = "10.2.1.7";
++      AuthMethod = passwd;
++      AuthData = "verysecret";
++      Port = 706;
++      VersionID = 1;
++      Class = "norm";
++      Backup = false;
++};
 +
 +#
- # For normal server only one entry maybe configured to this section.  It
- # must be the router this server will be connected to.  For router server,
- # this sections includes all configured router connections.  The first
++# Configured router connections
 +#
- # Format: <remote host>:<auth method>:<auth data>:<port>:<version ID>:
- #         <class>:<initiator>:<backup replace IP>:<backup replace port>:
- #         <local backup>
++# For normal servers only one entry maybe configured to this section.  It
++# must be the router this server will be connected to.  For router servers,
++# this section includes all configured router connections.  The first
 +# configured connection is the primary route.
 +#
- # The <auth data> is either passphrase or file path to the public key
- # file. If you are the initiator of the connection then set the <initiator>
- # to value 1.  If you are the responder of the connection (waiting for 
- # incoming connection) then set it to 0.
- #
- # If the connection is backup router connection then set the <backup
- # replace IP> to the IP address of the router that the backup router will
++# The "AuthData" option is either passphrase or file path to the public key
++# file. If you are the initiator of the connection then set the "Initiator"
++# option to true.  If you are the responder of the connection (waiting for
++# incoming connection) then set it to false.
 +#
- # <backup replace port>.  For normal connection leave both empty. If this
- # backup router is in our cell then set the <local backup> to value 1.
- # If the backup router is in other cell then set it to value 0.
- #
- [RouterConnection]
- #10.2.1.100:passwd:veryverysecret:706:1:1:1
- #10.2.100.131:pubkey:/path/to/the/publickey:706:1:1:1
- #10.2.100.100:pubkey:/path/to/the/publickey:706:1:1:0:10.2.1.6:706:1
- #
- # Denied connections.
- #
- # These connections are denied to connect our server.
- #
- # Format: <remote host>:<port>:<comment>
- #
- [DenyConnection]
- #10.2.1.99:0:Your connection has been denied
- #
- # Message Of The Day
- #
- # specify the text file containing the motd:
- #
- #[motd]
- #@ETCDIR@/motd.txt
- #
- # Pid File
- #
- # specify the pidfile where it will be written:
- #
- [pid]
- @PIDFILE@
++# If the connection is backup router connection then set the "BackupHost"
++# option to the IP address of the router that the backup router will
 +# replace if it becomes unavailable.  Set also the router's port to the
++# "BackupPort" option.  For normal connection leave both commented. If this
++# backup router is in our cell then set the "LocalBackup" option to true.
++# If the backup router is in other cell then set it to false.
++#
++RouterConnection {
++      Host = "10.2.1.100";
++      AuthMethod = passwd;
++      AuthData = "verysecret";
++      Port = 706;
++      VersionID = 1;
++      Class = "norm";
++      Initiator = true;
++      #BackupHost = "10.2.1.6";
++      #BackupPort = 706;
++      #LocalBackup = true;
++};
index 19ae0b975b8ce0569f2c4dfc2951e7fd5f3108f1,0000000000000000000000000000000000000000..188ffa2b66a46e171b703db2a92162281b537d72
mode 100644,000000..100644
--- /dev/null
@@@ -1,86 -1,0 +1,634 @@@
-   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 +/*
 +
 +  silcconfig.c
 +
-   Copyright (C) 1997 - 2000 Pekka Riikonen
++  Author: Johnny Mnemonic <johnny@themnemonic.org>
 +
-   
++  Copyright (C) 1997 - 2002 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
 +  the Free Software Foundation; either version 2 of the License, or
 +  (at your option) any later version.
- /* Opens and reads a configuration file to a buffer. The read data is 
-    returned to the ret_buffer argument. */
++
 +  This program is distributed in the hope that it will be useful,
 +  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +  GNU General Public License for more details.
 +
 +*/
 +/* $Id$ */
 +
 +#include "silcincludes.h"
 +
- void silc_config_open(char *filename, SilcBuffer *ret_buffer)
++/* limit debug logging verbosity */
++#if 0
++#define SILC_CONFIG_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
++#else
++#define SILC_CONFIG_DEBUG(fmt)
++#endif
++
++/* this is the option struct and currently it is only used internally to
++ * the module and other structs. */
++typedef struct SilcConfigOptionStruct {
++  char *name;                 /* *lowercase* name of the option */
++  SilcConfigType type;                /* important: the type of the returned value */
++  SilcConfigCallback cb;      /* the value handler */
++  const SilcConfigTable *subtable; /* used if type is SILC_CONFIG_ARG_BLOCK */
++  void *context;              /* context passed to the callback function */
++  struct SilcConfigOptionStruct *next;
++} SilcConfigOption;
++
++/* unique for each config file (and included ones) */
++struct SilcConfigFileObject {
++  char *filename; /* the original filename opened */
++  int level;  /* parsing level, how many nested silc_config_main we have */
++  char *base; /* this is a fixed pointer to the base location */
++  char *p;    /* the Parser poitner */
++  uint32 len; /* fixed length of the whole file */
++  uint32 line;        /* current parsing line, strictly linked to p */
++  bool included; /* wether this file is main or included */
++};
++
++/* We need the entity to base our block-style parsing on */
++struct SilcConfigEntityObject {
++  SilcConfigOption *opts;     /* known options list */
++  SilcConfigFile *file;               /* parsing file object */
++};
++
++/* access error descriptions only with silc_config_strerror() */
++static char *errorstrs[] = {
++  "-OK",                                      /* SILC_CONFIG_OK */
++  "-SILENT",                                  /* SILC_CONFIG_ESILENT */
++  "Invalid syntax",                           /* SILC_CONFIG_EGENERIC */
++  "Internal error! Please report this bug",   /* SILC_CONFIG_EINTERNAL */
++  "Can't open specified file",                        /* SILC_CONFIG_ECANTOPEN */
++  "Expected open-brace '{'",                  /* SILC_CONFIG_EOPENBRACE */
++  "Missing close-brace '}'",                  /* SILC_CONFIG_ECLOSEBRACE */
++  "Invalid data type",                                /* SILC_CONFIG_ETYPE */
++  "Unknown option",                           /* SILC_CONFIG_EBADOPTION */
++  "Invalid text",                             /* SILC_CONFIG_EINVALIDTEXT */
++  "Double option specification",              /* SILC_CONFIG_EDOUBLE */
++  "Expected data but not found",              /* SILC_CONFIG_EEXPECTED */
++  "Expected '='",                             /* SILC_CONFIG_EEXPECTEDEQUAL */
++  "Unexpected data",                          /* SILC_CONFIG_EUNEXPECTED */
++  "Missing needed fields",                    /* SILC_CONFIG_EMISSFIELDS */
++  "Missing ';'",                              /* SILC_CONFIG_EMISSCOLON */
++};
++
++/* return string describing SilcConfig's error code */
++char *silc_config_strerror(int errnum)
++{
++  if ((errnum < 0) || (errnum >= sizeof(errorstrs)/sizeof(*errorstrs)) ||
++    (errorstrs[errnum] == NULL)) {
++    char *defret = "-INVALIDERROR";
++    return defret;
++  }
++  return errorstrs[errnum];
++}
++
++/* Begin of internal SilcConfig's text util functions */
++
++/* Points the first non-space character */
++static void my_trim_spaces(SilcConfigFile *file)
++{
++  register char *r = file->p;
++  while (isspace(*r))
++    if (*r++ == '\n') file->line++;
++  file->p = r;
++}
++/* Skips the current line until newline (lf or cr) */
++static void my_skip_line(SilcConfigFile *file)
++{
++  register char *r = file->p;
++  while (*r && (*r != '\n') && (*r != '\r')) r++;
++  file->p = (*r ? r + 1 : r);
++  file->line++;
++}
++/* Obtains a text token from the current position until first separator.
++ * a separator is any non alphanumeric character nor "_" or "-" */
++static char *my_next_token(SilcConfigFile *file, char *to)
++{
++  register char *o;
++  my_trim_spaces(file);
++  o = file->p;
++  while (isalnum(*o) || (*o == '_') || (*o == '-'))
++    *to++ = *o++;
++  *to = '\0';
++  file->p = o;
++  return to;
++}
++/* Obtains a string from the current position. The only difference from
++ * next_token() is that quoted-strings are also accepted */
++static char *my_get_string(SilcConfigFile *file, char *to)
++{
++  char *o;
++  my_trim_spaces(file);
++  o = file->p;
++  if (*o == '"') {
++    char *quot = strchr(++o, '"');
++    int len = quot - o;
++    if (!quot) { /* XXX FIXME: gotta do something here */
++      printf("Bullshit, missing matching \"");
++      exit(1);
++    }
++    if (len <= 0)
++      *to = '\0';
++    else {
++      strncpy(to, o, len);
++      to[len] = '\0';
++    }
++    /* update stream pointer */
++    file->p = quot + 1;
++    return to;
++  }
++  /* we don't need quote parsing, fall-back to token extractor */
++  my_next_token(file, to);
++  return to;
++};
++/* Skips all comment lines and spaces lines until first useful character */
++static void my_skip_comments(SilcConfigFile *file)
++{
++  while (1) {
++    my_trim_spaces(file);
++    if (*file->p != '#') return;
++    my_skip_line(file);
++  }
++}
++
++/* End of internal text functions
++ * Next section contains SilcConfig internal config utils */
 +
-   buffer = silc_file_readfile(filename, &filelen);
-   if (buffer == NULL)
-     return;
++/* find an option in the list by name and returns its pointer */
++static SilcConfigOption *silc_config_find_option(SilcConfigEntity ent,
++      const char *name)
++{
++  SilcConfigOption *tmp;
++  for (tmp = ent->opts; tmp; tmp = tmp->next) {
++    if (!strcasecmp(tmp->name, name))
++      return tmp;
++  }
++  return NULL;
++}
++/* ... */
++static void *silc_config_marshall(SilcConfigType type, const char *val)
++{
++  void *pt;
++  int val_int;
++  bool val_bool;
++  char *val_tmp;
++  uint32 val_size;
++
++  switch (type) {
++    case SILC_CONFIG_ARG_TOGGLE:
++      if (!strcasecmp(val, "yes") || !strcasecmp(val, "true") ||
++              !strcasecmp(val, "on") || !strcasecmp(val, "1")) {
++      val_bool = TRUE;
++      }
++      else if (!strcasecmp(val, "no") || !strcasecmp(val, "false") ||
++              !strcasecmp(val, "off") || !strcasecmp(val, "0")) {
++      val_bool = FALSE;
++      }
++      else
++      return NULL;
++      pt = silc_calloc(1, sizeof(val_bool));
++      *(bool *)pt = (bool) val_bool;
++      return pt;
++    case SILC_CONFIG_ARG_INT:
++      val_int = (int) strtol(val, &val_tmp, 0);
++      if (*val_tmp) /* error converting string */
++      return NULL;
++      pt = silc_calloc(1, sizeof(val_int));
++      *(int *)pt = val_int;
++      return pt;
++    case SILC_CONFIG_ARG_SIZE:
++      val_size = (uint32) strtol(val, &val_tmp, 0);
++      if (val == val_tmp)
++      return NULL; /* really wrong, there must be at least one digit */
++      /* Search for a designator */
++      switch (tolower(val_tmp[0])) {
++      case '\0': /* None */
++        break;
++      case 'k': /* Kilobytes */
++        val_size *= (uint32) 1024;
++        break;
++      case 'm': /* Megabytes */
++        val_size *= (uint32) (1024 * 1024);
++        break;
++      case 'g':
++        val_size *= (uint32) (1024 * 1024 * 1024);
++        break;
++      default:
++        return NULL;
++      }
++      /* the string must die here */
++      if (val_tmp[1])
++      return NULL;
++      pt = silc_calloc(1, sizeof(val_size));
++      *(uint32 *)pt = val_size;
++      return pt;
++    case SILC_CONFIG_ARG_STR: /* the only difference between STR and STRE is */
++      if (!val[0])          /* that STR cannot be empty, while STRE can.  */
++      return NULL;
++    case SILC_CONFIG_ARG_STRE:
++      pt = (void *) strdup(val);
++      return pt;
++    /* following types are not supposed to have a return value */
++    case SILC_CONFIG_ARG_BLOCK:
++    case SILC_CONFIG_ARG_NONE:
++      return NULL;
++    default:
++      return NULL;
++  }
++
++  return NULL;
++}
++
++/* End of internal functions */
++
++
++/* Tries to open the config file and returns a valid SilcConfigFile object
++ * or NULL if failed */
++
++SilcConfigFile *silc_config_open(char *configfile)
 +{
 +  char *buffer;
 +  uint32 filelen;
++  SilcConfigFile *ret;
 +
-   /* Buffer don't have EOF, but we'll need it. */
-   buffer[filelen] = EOF;
++  if (!(buffer = silc_file_readfile(configfile, &filelen)))
++    return NULL;
 +
-   *ret_buffer = silc_buffer_alloc(filelen + 1);
-   silc_buffer_pull_tail(*ret_buffer, filelen + 1);
-   silc_buffer_put(*ret_buffer, buffer, filelen + 1);
++  ret = (SilcConfigFile *) silc_calloc(1, sizeof(*ret));
++  ret->filename = strdup(configfile);
++  ret->base = ret->p = buffer;
++  ret->len = filelen;
++  ret->line = 1; /* line count, start from first line */
++  return ret;
++}
 +
-   SILC_LOG_DEBUG(("Config file `%s' opened", filename));
++/* Frees a file object */
 +
- /* Returns next token from a buffer to the dest argument. Returns the
-    length of the token. This is used to take tokens from a configuration
-    line. */
++void silc_config_close(SilcConfigFile *file)
++{
++  if (file) {
++    /* XXX FIXME: this check could probably be removed later */
++    uint32 my_len = (uint32) (strchr(file->base, EOF) - file->base);
++    SILC_CONFIG_DEBUG(("file=0x%x name=\"%s\" level=%d line=%lu", (uint32) file,
++                      file->filename, file->level, file->line));
++    if (my_len != file->len) {
++      fprintf(stderr, "FATAL ERROR: saved len and current len does not match!\n");
++      abort();
++    }
++    silc_free(file->filename);
++    memset(file->base, 'F', file->len);
++    silc_free(file->base);
++    memset(file, 'F', sizeof(*file));
++    silc_free(file);
++  }
 +}
 +
- int silc_config_get_token(SilcBuffer buffer, char **dest)
++/* initializes a SilcConfigEntity pointer allocation */
++
++SilcConfigEntity silc_config_init(SilcConfigFile *file)
++{
++  SilcConfigEntity ret;
++  if (!file)
++    return NULL;
++  SILC_CONFIG_DEBUG(("Allocating new config entity"));
++  ret = (SilcConfigEntity) silc_calloc(1, sizeof(*ret));
++  ret->file = file;
++  return ret;
++};
++
++/* Returns the original filename of the object file */
 +
-   if (strchr(buffer->data, ':')) {
-     len = strcspn(buffer->data, ":");
-     if (len) {
-       *dest = silc_calloc(len + 1, sizeof(char));
-       memcpy(*dest, buffer->data, len);
-     }
-     silc_buffer_pull(buffer, len + 1);
-     return len;
++char *silc_config_get_filename(SilcConfigFile *file)
 +{
++  if (file)
++    return file->filename;
++  return NULL;
++}
++
++/* Returns the current line that file parsing arrived at */
++
++uint32 silc_config_get_line(SilcConfigFile *file)
++{
++  if (file)
++    return file->line;
++  return 0;
++}
++
++/* Returns a pointer to the beginning of the requested line.  If the line
++ * was not found, NULL is returned */
++
++char *silc_config_read_line(SilcConfigFile *file, uint32 line)
++{
++  register char *p;
 +  int len;
++  char *ret, *endbuf;
 +
-   return -1;
++  if (!file || (line <= 0))
++    return NULL;
++  for (p = file->base; *p && (*p != EOF); p++) {
++    if (line <= 1)
++      goto found;
++    if (*p == '\n')
++      line--;
 +  }
++  return NULL;
 +
- /* Returns number of tokens in a buffer. */
++ found:
++  if ((endbuf = strchr(p, '\n'))) {
++    len = endbuf - p;
++    ret = silc_calloc(len, sizeof(*ret));
++    strncpy(ret, p, len);
++    ret[len] = '\0';
++  }
++  else {
++    ret = silc_calloc(strlen(p), sizeof(*ret));
++    strcpy(ret, p);
++  }
++  return ret;
 +}
 +
- int silc_config_check_num_token(SilcBuffer buffer)
++/* Convenience function to read the current parsed line */
 +
-   int len, len2, num;
++char *silc_config_read_current_line(SilcConfigFile *file)
 +{
-   if (strchr(buffer->data, ':')) {
-     len = 0;
-     num = 0;
-     while (strchr(buffer->data + len, ':')) {
-       num++;
-       len2 = strcspn(buffer->data + len, ":") + 1;
-       len += len2;
++  return silc_config_read_line(file, file->line);
++}
++
++/* (Private) destroy a SilcConfigEntity */
 +
-     return num;
++static void silc_config_destroy(SilcConfigEntity ent)
++{
++  SilcConfigOption *oldopt, *nextopt;
++  SILC_CONFIG_DEBUG(("Freeing config entity [ent=0x%x] [opts=0x%x]",
++                      (uint32) ent, (uint32) ent->opts));
++  for (oldopt = ent->opts; oldopt; oldopt = nextopt) {
++    nextopt = oldopt->next;
++    memset(oldopt->name, 'F', strlen(oldopt->name) + 1);
++    silc_free(oldopt->name);
++    memset(oldopt, 'F', sizeof(*oldopt));
++    silc_free(oldopt);
++  }
++  memset(ent, 'F', sizeof(*ent));
++  silc_free(ent);
++}
++
++/* Registers a new option in the specified entity */
++
++void silc_config_register(SilcConfigEntity ent, const char *name,
++                        SilcConfigType type, SilcConfigCallback cb,
++                        const SilcConfigTable *subtable, void *context)
++{
++  SilcConfigOption *newopt;
++  SILC_CONFIG_DEBUG(("Register new option=\"%s\" type=%u cb=0x%08x context=0x%08x",
++              name, type, (uint32) cb, (uint32) context));
++
++  if (!ent || !name)
++    return;
++  /* if we are registering a block, make sure there is a specified sub-table */
++  if ((type == SILC_CONFIG_ARG_BLOCK) && !subtable)
++    return;
++  /* refuse special tag */
++  if (!strcasecmp(name, "include"))
++    return;
++  if (silc_config_find_option(ent, name)) {
++    fprintf(stderr, "Internal Error: Option double registered\n");
++    abort();
++  }
++
++  /* allocate and append the new option */
++  newopt = (SilcConfigOption *) silc_calloc(1, sizeof(*newopt));
++  newopt->name = strdup(name);
++  newopt->type = type;
++  newopt->cb = cb;
++  newopt->subtable = subtable;
++  newopt->context = context;
++
++  if (!ent->opts)
++    ent->opts = newopt;
++  else {
++    SilcConfigOption *tmp;
++    for (tmp = ent->opts; tmp->next; tmp = tmp->next);
++    tmp->next = newopt;
++  }
++}
++
++/* Register a new option table in the specified config entity */
++
++void silc_config_register_table(SilcConfigEntity ent,
++                              const SilcConfigTable table[], void *context)
++{
++  int i;
++  if (!ent || !table) return;
++  SILC_CONFIG_DEBUG(("Registering table"));
++  /* FIXME: some potability checks needed */
++  for (i = 0; table[i].name; i++) {
++    silc_config_register(ent, table[i].name, table[i].type,
++                       table[i].callback, table[i].subtable, context);
++  }
++}
++
++/* ... */
++
++static int silc_config_main_internal(SilcConfigEntity ent)
++{
++  SilcConfigFile *file = ent->file;
++  char **p = &file->p;
++
++  /* loop throught statements */
++  while (1) {
++    char buf[255];
++    SilcConfigOption *thisopt;
++
++    /* makes it pointing to the next interesting char */
++    my_skip_comments(file);
++    /* got eof? */
++    if (**p == '\0' || **p == EOF) {
++      if (file->level > 1) /* cannot get eof in a sub-level! */
++      return SILC_CONFIG_EEXPECTED;
++      goto finish;
++    }
++    /* check if we completed this (sub) section (it doesn't matter if this
++     * is the main section) */
++    if (**p == '}') {
++      if (file->level < 2) /* can't be! must be at least one sub-block */
++      return SILC_CONFIG_EUNEXPECTED;
++      (*p)++;
++      goto finish;
++    }
++    //SILC_LOG_HEXDUMP(("Preparing lookup at line=%lu", file->line), *p, 16);
++
++    /* obtain the keyword */
++    my_next_token(file, buf);
++    SILC_CONFIG_DEBUG(("Looking up keyword=\"%s\" [line=%lu]", buf, file->line));
++
++    /* handle special directive */
++    if (!strcasecmp(buf, "include")) {
++      int ret;
++      SilcConfigFile *inc_file;
++      SilcConfigEntity inc_ent;
++
++      my_trim_spaces(file); /* prepare next char */
++
++      /* Now trying to include the specified file.  The included file will
++       * be allowed to include sub-files but it will preserve the block-level
++       * of the including block. Note that the included file won't be allowed
++       * to raise the block level of the including block. */
++
++      my_get_string(file, buf); /* get the filename */
++      SILC_LOG_DEBUG(("Including file \"%s\"", buf));
++      /* before getting on, check if this row is REALLY complete */
++      if (*(*p)++ != ';')
++      return SILC_CONFIG_EMISSCOLON;
++
++      /* open the file and start the parsing */
++      inc_file = silc_config_open(buf);
++      if (!inc_file) /* does it point a valid filename? */
++        return SILC_CONFIG_ECANTOPEN;
++      inc_file->included = TRUE;
++
++      /* create a new entity and hack it to use the same options */
++      inc_ent = silc_config_init(inc_file);
++      inc_ent->opts = ent->opts;
++      ret = silc_config_main(inc_ent);
++
++      /* Cleanup.
++       * If the included file returned an error, the application will probably
++       * want to output some kind of error message. Because of this, we can't
++       * destroy THIS file object. The hack works this way: The application
++       * expects to destroy the originally created object file, so we'll swap
++       * the original file with the included file. */
++      if (ret) {
++        SilcConfigFile tmp_file;
++        SILC_CONFIG_DEBUG(("SWAPPING FILE OBJECTS"));
++        memcpy(&tmp_file, inc_file, sizeof(tmp_file));
++        memcpy(inc_file, file, sizeof(tmp_file));
++        silc_config_close(inc_file);
++        memcpy(file, &tmp_file, sizeof(tmp_file));
++        return ret;
++      }
++      /* otherwise if no errors encoured, continue normally */
++      silc_config_close(inc_file);
++      continue; /* this one is handled */
 +    }
 +
-   return 0;
++    /* we have a registered option (it can also be a sub-block) */
++    thisopt = silc_config_find_option(ent, buf);
++    if (!thisopt)
++      return SILC_CONFIG_EBADOPTION;
++
++    my_trim_spaces(file); /* prepare next char */
++
++    /* option type is a block? */
++    if (thisopt->type == SILC_CONFIG_ARG_BLOCK) {
++      int ret;
++      SilcConfigEntity sub_ent;
++
++      SILC_CONFIG_DEBUG(("Entering sub-block"));
++      if (*(*p)++ != '{')
++      return SILC_CONFIG_EOPENBRACE;
++      /* build the new entity for this sub-block */
++      sub_ent = silc_config_init(ent->file);
++      /* use the previous specified table to describe this block's options */
++      silc_config_register_table(sub_ent, thisopt->subtable, thisopt->context);
++      /* run this block! */
++      ret = silc_config_main(sub_ent);
++      SILC_CONFIG_DEBUG(("Returned from sub-block [ret=%d]", ret));
++
++      if (ret) /* now check the result */
++      return ret;
++
++      /* now call block clean-up callback (if any) */
++      if (thisopt->cb) {
++      SILC_CONFIG_DEBUG(("Now calling clean-up callback (if any)"));
++      thisopt->cb(thisopt->type, thisopt->name, file->line,
++                  NULL, thisopt->context);
++      }
++      /* Do we want ';' to be mandatory after close brace? */
++      if (*(*p)++ != ';')
++      return SILC_CONFIG_EMISSCOLON;
++    }
++    else if (thisopt->type == SILC_CONFIG_ARG_NONE) {
++      /* before getting on, check if this row is REALLY complete */
++      if (*(*p)++ != ';')
++      return SILC_CONFIG_EMISSCOLON;
++      SILC_CONFIG_DEBUG(("Triggering callback for none"));
++      if (thisopt->cb) {
++      thisopt->cb(thisopt->type, thisopt->name, file->line,
++                  NULL, thisopt->context);
++      }
++    }
++    else {
++      void *pt;
++      int ret;
++
++      if (*(*p)++ != '=')
++      return SILC_CONFIG_EEXPECTEDEQUAL;
++
++      my_get_string(file, buf); /* get the option argument */
++      SILC_CONFIG_DEBUG(("With argument=\"%s\"", buf));
++
++      /* before getting on, check if this row is REALLY complete */
++      if (*(*p)++ != ';')
++      return SILC_CONFIG_EMISSCOLON;
++
++      /* convert the option argument to the right format */
++      pt = silc_config_marshall(thisopt->type, buf);
++      if (!pt)
++      return SILC_CONFIG_EINVALIDTEXT;
++      if (thisopt->cb) {
++      ret = thisopt->cb(thisopt->type, thisopt->name, file->line,
++                        pt, thisopt->context);
++      if (ret) {
++        SILC_CONFIG_DEBUG(("Callback refused the value [ret=%d]", ret));
++        return ret;
++      }
++      }
++      silc_free(pt);
++    }
++    continue;
++
++ finish:
++    break;
 +  }
 +
++  return SILC_CONFIG_OK;
++}
++
++/* ... */
++
++int silc_config_main(SilcConfigEntity ent)
++{
++  SilcConfigFile *file = ent->file;
++  int ret;
++
++  /* don't silently accept a NULL entity */
++  if (!ent) {
++    ret = SILC_CONFIG_EGENERIC;
++    goto main_cleanup;
++  }
++
++  /* call the real main and store the result */
++  file->level++;
++  SILC_CONFIG_DEBUG(("[Lev=%d] Entering config parsing core", file->level));
++  ret = silc_config_main_internal(ent);
++  SILC_CONFIG_DEBUG(("[Lev=%d] Quitting main [ret=%d]", file->level, ret));
++  if (!file->level) /* when swap happens, we could close a file twice */
++    goto main_end;
++  file->level--;
++
++  /* If this file was included don't destroy the options set because it is
++   * the same of the including block. Although if this entity is in a
++   * sub-block created inside the included file, this options set must be
++   * destroyed. */
++ main_cleanup:
++  if ((file->level != 0) || (file->included != TRUE))
++    silc_config_destroy(ent);
++
++ main_end:
++  return ret;
 +}
index 3153f256d72fc862daaf86ab3d2eb960afb828ca,0000000000000000000000000000000000000000..87db1cf84fdaec31326dd283d39c1e159a740284
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,447 @@@
-   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 +/*
 +
 +  silcconfig.h
 +
-   Copyright (C) 1997 - 2000 Pekka Riikonen
++  Author: Johnny Mnemonic <johnny@themnemonic.org>
 +
-   
++  Copyright (C) 1997 - 2002 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
 +  the Free Software Foundation; either version 2 of the License, or
 +  (at your option) any later version.
- void silc_config_open(char *filename, SilcBuffer *ret_buffer);
- int silc_config_get_token(SilcBuffer buffer, char **dest);
- int silc_config_check_num_token(SilcBuffer);
++
 +  This program is distributed in the hope that it will be useful,
 +  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +  GNU General Public License for more details.
 +
 +*/
 +
++/****h* silcutil/SilcConfigAPI
++ *
++ * DESCRIPTION
++ *
++ * The SILC Config util library is based on two main objects, SilcConfigFile
++ * (or File object) and SilcConfigEntity (or Entity).  The File objects are
++ * structs directly corresponding to the real files in the filesystem, while
++ * Entities are a little more abstract.
++ * An Entity is composed by delimited area on a File object (it can take the
++ * whole File object or just part of it), plus a group of known options.
++ *
++ * In order to parse this file, first you need to create a File object with
++ * the silc_config_open() function, and then you need to create the Entity
++ * with the silc_config_init() function.
++ * Now you can use the newly created Entity to register a group of expected
++ * known options and sub-blocks, and then you can call the main parsing loop
++ * with the silc_config_main() function.
++ * When silc_config_main() will return, if some error encoured the object file
++ * will point to the file that caused this error (this can be different from
++ * the originally opened file if it contained `Include' directives).  If no
++ * errors encoured then the File objects will still point to the original
++ * file.
++ * While silc_config_main() will take care of destroying Entities before
++ * returning, you need to take care that the File object you created is freed
++ * with the silc_config_close() function.
++ *
++ * The SILC Config library won't take care about storing the values contained
++ * in the config file.  You must take care about it with the callback
++ * functions.
++ *
++ * The config file syntax is pretty straightforward.  All lines starting
++ * with `#' will be skipped, while sub-blocks are delimited by braces (see
++ * the example below).
++ * Options with argument must have the `=' character between the option
++ * name and the value.  Simple words and numbers does not require quoting.
++ * There is a special built-in directive "Include" which allows you to include
++ * another config file in the point the directive is.  You can also Include
++ * inside a sub-block body, in this case when parsing the included config file
++ * it will be assumed that we are within this block, and the included file
++ * won't be allowed to close his root block.
++ *
++ * Example:
++ *
++ *    cipher {
++ *       name = aes-256-cbc;
++ *       module = "aes.sim.so";
++ *       key_length = 32;       # usually the default is just fine
++ *       block_length = 16;
++ *    };
++ *    Include "/etc/silc/hash_funcs.conf";
++ *
++ ***/
++
 +#ifndef SILCCONFIG_H
 +#define SILCCONFIG_H
 +
++/****d* silcutil/SilcConfigAPI/errno
++ *
++ * NAME
++ *
++ *    enum { ... } - describe a SILC Config error
++ *
++ * DESCRIPTION
++ *
++ *    The virtual integer `errno' is returned by the silc_config_main()
++ *    function and indicates what went wrong.
++ *    You can convert it to the corresponding error string with the function
++ *    silc_config_strerror().
++ *
++ * SOURCE
++ */
++enum {
++  SILC_CONFIG_OK,             /* OK */
++  SILC_CONFIG_ESILENT,                /* Error defined by callback function */
++  SILC_CONFIG_EGENERIC,               /* Invalid syntax */
++  SILC_CONFIG_EINTERNAL,      /* Internal Error (caused by developer) */
++  SILC_CONFIG_ECANTOPEN,      /* Can't open specified file */
++  SILC_CONFIG_EOPENBRACE,     /* Expected open-brace '{' */
++  SILC_CONFIG_ECLOSEBRACE,    /* Missing close-brace '}' */
++  SILC_CONFIG_ETYPE,          /* Invalid data type */
++  SILC_CONFIG_EBADOPTION,     /* Unknown option */
++  SILC_CONFIG_EINVALIDTEXT,   /* Invalid text */
++  SILC_CONFIG_EDOUBLE,                /* Double option specification */
++  SILC_CONFIG_EEXPECTED,      /* Expected data but not found */
++  SILC_CONFIG_EEXPECTEDEQUAL, /* Expected '=' */
++  SILC_CONFIG_EUNEXPECTED,    /* Unexpected data */
++  SILC_CONFIG_EMISSFIELDS,    /* Missing needed fields */
++  SILC_CONFIG_EMISSCOLON,     /* Missing ';' */
++};
++/***/
++
++/****d* silcutil/SilcConfigAPI/SilcConfigType
++ *
++ * NAME
++ *
++ *    typedef enum { ... } SilcConfigType;
++ *
++ * DESCRIPTION
++ *
++ *    This identifies the parameter type that an option has. This parameter
++ *    is very important because the callback's *val pointer points to a
++ *    memory location containing the previously specified data type.
++ *    For example, if you specified an option with an integer parameter
++ *    callback's *val will be a pointer to an integer.
++ *
++ * SOURCE
++ */
++typedef enum {
++  SILC_CONFIG_ARG_TOGGLE,     /* TOGGLE on,off; yes,no; true, false; */
++  SILC_CONFIG_ARG_INT,                /* callback wants an integer */
++  SILC_CONFIG_ARG_STR,                /* callback expects \0-terminated str */
++  SILC_CONFIG_ARG_STRE,               /* same as above, but can also be empty */
++  SILC_CONFIG_ARG_BLOCK,      /* this is a sub-block */
++  SILC_CONFIG_ARG_SIZE,               /* like int, but accepts suffixes kMG */
++  SILC_CONFIG_ARG_NONE,               /* does not expect any args */
++} SilcConfigType;
++/***/
++
++/****f* silcutil/SilcConfigAPI/SilcConfigCallback
++ *
++ * SYNOPSIS
++ *
++ *    typedef int (*SilcConfigCallback)(SilcConfigType type, const char *name,
++ *                                      uint32 line, void *val, void *context);
++ * DESCRIPTION
++ *
++ *    This is the callback prototype for the options handler.  The pointer
++ *    `val' points to a location of type described by `type'.  `name' points
++ *    to a null-terminated string with the name of the option which triggered
++ *    this callback, that is stated at line `line'.  `context' is the
++ *    user-specified context provided when this option was registered.
++ *
++ ***/
++typedef int (*SilcConfigCallback)(SilcConfigType type, const char *name,
++                                uint32 line, void *val, void *context);
++
++/****s* silcutil/SilcConfigAPI/SilcConfigTable
++ *
++ * SYNOPSIS
++ *
++ *    typedef struct { ... } SilcConfigTable;
++ *
++ * DESCRIPTION
++ *
++ *    SILC Config table defines an easy and quick way of registering options
++ *    in an entity. The function silc_config_register_table() will take as
++ *    argument a SilcConfigTable array terminated by a NULL struct, it is
++ *    important thus, that the `name' field of the terminating struct is set
++ *    to NULL.
++ *
++ *    char *name
++ *
++ *       The option name lowercase. The matching is always case-insensitive,
++ *       but for convention the option specification must always be lowercase.
++ *
++ *    SilcConfigType type
++ *
++ *       This specifies what kind of parameter this option expects.  The
++ *       special cases SILC_CONFIG_ARG_BLOCK tells SILC Config that this is
++ *       not a normal option but the name of a sub-block of the current
++ *       block (there is no limit to the number of nested blocks allowed).
++ *
++ *    SilcConfigCallback callback
++ *
++ *       Normally this is the value handler of the current option. If this
++ *       field is set to NULL then the value is silently discarded. Useful
++ *       for example to support deprecated options.
++ *
++ *    SilcConfigTable *subtable
++ *
++ *       If the `type' field is set to SILC_CONFIG_ARG_BLOCK, then this field
++ *       must point to a valid sub-table NULL-terminated array. If `type' is
++ *       something else, this valued is unused.
++ *
++ ***/
++typedef struct SilcConfigTableStruct {
++  char *name;
++  SilcConfigType type;
++  SilcConfigCallback callback;
++  const struct SilcConfigTableStruct *subtable;
++} SilcConfigTable;
++
++/****s* silcutil/SilcConfigAPI/SilcConfigFile
++ *
++ * SYNOPSIS
++ *
++ *    typedef struct { ... } SilcConfigFile;
++ *
++ * DESCRIPTION
++ *
++ *    A File object holds the data contained in a previously loaded file by
++ *    the silc_config_open() function.
++ *    This is an internally allocated struct and must be used only with the
++ *    helper functions.
++ *
++ ***/
++typedef struct SilcConfigFileObject SilcConfigFile;
++
++/****s* silcutil/SilcConfigAPI/SilcConfigEntity
++ *
++ * SYNOPSIS
++ *
++ *    typedef struct { ... } SilcConfigEntity;
++ *
++ * DESCRIPTION
++ *
++ *    The SILC Config is based on config entities.  An entity contains the
++ *    SilcConfigFile object we are parsing and the registered options.
++ *
++ ***/
++typedef struct SilcConfigEntityObject *SilcConfigEntity;
++
++/* Macros */
++
++/****d* silcutil/SilcConfigAPI/SILC_CONFIG_CALLBACK
++ *
++ * NAME
++ *
++ *    #define SILC_CONFIG_CALLBACK ...
++ *
++ * DESCRIPTION
++ *
++ *    Generic macro to define SilcConfigCallback functions. This defines a
++ *    static function with name `func' as a config callback function.
++ *
++ * SOURCE
++ */
++#define SILC_CONFIG_CALLBACK(func)                            \
++static int func(SilcConfigType type, const char *name,                \
++              uint32 line, void *val, void *context)
++/***/
++
 +/* Prototypes */
- #endif
 +
++/****f* silcutil/SilcConfigAPI/silc_config_open
++ *
++ * SYNOPSIS
++ *
++ *    SilcConfigFile *silc_config_open(char *configfile);
++ *
++ * DESCRIPTION
++ *
++ *    Tries to open the config file `configfile' and returns a valid File
++ *    object on success, or NULL on failure.
++ *    An File object created this way must be destroyed with the function
++ *    silc_config_close().
++ *
++ ***/
++SilcConfigFile *silc_config_open(char *configfile);
++
++/****f* silcutil/SilcConfigAPI/silc_config_close
++ *
++ * SYNOPSIS
++ *
++ *    void silc_config_close(SilcConfigFile *file);
++ *
++ * DESCRIPTION
++ *
++ *    Closes and frees the File object `file', which must have been returned
++ *    by a previous call to silc_config_open().  Otherwise, or if
++ *    this function has already been called before for the same File object,
++ *    undefined behaviour occurs.
++ *    If `file' is NULL, no operation is performed.
++ *
++ ***/
++void silc_config_close(SilcConfigFile *file);
++
++/****f* silcutil/SilcConfigAPI/silc_config_init
++ *
++ * SYNOPSIS
++ *
++ *    SilcConfigEntity silc_config_init(SilcConfigFile *file);
++ *
++ * DESCRIPTION
++ *
++ *    Creates an Entity pointing to the valid File object `file', which must
++ *    be returned by a previous call to silc_config_open(), otherwise NULL
++ *    is returned.
++ *    Entities will be automatically destroyed after the call to the
++ *    silc_config_main() function, because of this no uninit functions are
++ *    provided.
++ *
++ ***/
++SilcConfigEntity silc_config_init(SilcConfigFile *file);
++
++/****f* silcutil/SilcConfigAPI/silc_config_strerror
++ *
++ * SYNOPSIS
++ *
++ *    char *silc_config_strerror(int errnum);
++ *
++ * DESCRIPTION
++ *
++ *    The silc_config_strerror() function returns a string describing the
++ *    error code passed in the argument `errnum'.
++ *
++ ***/
++char *silc_config_strerror(int errnum);
++
++/****f* silcutil/SilcConfigAPI/silc_config_get_filename
++ *
++ * SYNOPSIS
++ *
++ *    char *silc_config_get_filename(SilcConfigFile *file);
++ *
++ * DESCRIPTION
++ *
++ *    Returns the original filename of the object file.
++ *    The returned pointer points to internally allocated storage and must
++ *    not be freed, modified or stored.
++ *
++ ***/
++char *silc_config_get_filename(SilcConfigFile *file);
++
++/****f* silcutil/SilcConfigAPI/silc_config_get_line
++ *
++ * SYNOPSIS
++ *
++ *    uint32 silc_config_get_line(SilcConfigFile *file);
++ *
++ * DESCRIPTION
++ *
++ *    Returns the current line that file parsing arrived at.
++ *
++ ***/
++uint32 silc_config_get_line(SilcConfigFile *file);
++
++/****f* silcutil/SilcConfigAPI/silc_config_read_line
++ *
++ * SYNOPSIS
++ *
++ *    char *silc_config_read_line(SilcConfigFile *file, uint32 line);
++ *
++ * DESCRIPTION
++ *
++ *    Returns a dynamically allocated null-terminated buffer containing the
++ *    line `line' of `file'.
++ *    The returned pointer must be freed when it's not needed any longer.
++ *
++ * SEE ALSO
++ *    silc_config_read_current_line
++ *
++ ***/
++char *silc_config_read_line(SilcConfigFile *file, uint32 line);
++
++/****f* silcutil/SilcConfigAPI/silc_config_read_current_line
++ *
++ * SYNOPSIS
++ *
++ *    char *silc_config_read_current_line(SilcConfigFile *file);
++ *
++ * DESCRIPTION
++ *
++ *    Returns a dynamically allocated buffer containing the line that the
++ *    parser stopped at.  This is a convenience function for
++ *    silc_config_read_line.
++ *    The returned pointer must be freed when it's not needed any longer.
++ *
++ ***/
++char *silc_config_read_current_line(SilcConfigFile *file);
++
++/****f* silcutil/SilcConfigAPI/silc_config_register
++ *
++ * SYNOPSIS
++ *
++ *    void silc_config_register(SilcConfigEntity ent, const char *name,
++ *                              SilcConfigType type, SilcConfigCallback cb,
++ *                              const SilcConfigTable *subtable,
++ *                              void *context);
++ *
++ * DESCRIPTION
++ *
++ *    Register option `name' in the entity `ent'. If `cb' is not NULL, it
++ *    will be called with the *val pointer pointing to an internally
++ *    allocated storage of type described by `type'.
++ *    If `type' is SILC_CONFIG_ARG_BLOCK, then `subtable' must be a valid
++ *    pointer to a SilcConfigTable array specified the options in the
++ *    sub-block.
++ *
++ * SEE ALSO
++ *    silc_config_register_table
++ *
++ ***/
++void silc_config_register(SilcConfigEntity ent, const char *name,
++                        SilcConfigType type, SilcConfigCallback cb,
++                        const SilcConfigTable *subtable, void *context);
++
++/****f* silcutil/SilcConfigAPI/silc_config_register_table
++ *
++ * SYNOPSIS
++ *
++ *    void silc_config_register_table(SilcConfigEntity ent,
++ *                                    const SilcConfigTable table[],
++ *                                    void *context);
++ *
++ * DESCRIPTION
++ *
++ *    Register the tableset of options `table' automatically in the entity
++ *    `ent'.  If defined in the table, the callback functions will be called
++ *    all with the same context `context'.
++ *    The `table' array must be terminated with an entry with the name field
++ *    set to NULL.
++ *
++ * SEE ALSO
++ *    SilcConfigTable
++ *
++ ***/
++void silc_config_register_table(SilcConfigEntity ent,
++                              const SilcConfigTable table[], void *context);
++
++/****f* silcutil/SilcConfigAPI/silc_config_main
++ *
++ * SYNOPSIS
++ *
++ *    int silc_config_main(SilcConfigEntity ent);
++ *
++ * DESCRIPTION
++ *
++ *    Enter the main parsing loop. When this function returns the parsing
++ *    is finished in the current block (and sub-blocks).
++ *    When this function exits, the entity is already destroyed, because
++ *    of this you should set it to NULL right after the function call.
++ *
++ ***/
++int silc_config_main(SilcConfigEntity ent);
++
++#endif        /* !SILCCONFIG_H */
diff --combined lib/silcutil/silclog.h
index 76f8f67c7a8c5a8520cafa1563d1d9a5a8bd1a01,0000000000000000000000000000000000000000..46db324914fa995737d8c07e6ad52b7fe9eb459c
mode 100644,000000..100644
--- /dev/null
@@@ -1,604 -1,0 +1,602 @@@
-  *    See the source code for SILC coding conventions about how choosing
 +/*
 +
 +  silclog.h
 +
 +  Author: Johnny Mnemonic <johnny@themnemonic.org>
 +
 +  Copyright (C) 1997 - 2002 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
 +  the Free Software Foundation; either version 2 of the License, or
 +  (at your option) any later version.
 +
 +  This program is distributed in the hope that it will be useful,
 +  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +  GNU General Public License for more details.
 +
 +*/
 +
 +/****h* silcutil/SilcLogAPI
 + *
 + * DESCRIPTION
 + *
 + * The SILC logging APIs provide a powerful and easy-to-use interface to
 + * the logging system and debugging output.
 + *
 + ***/
 +
 +#ifndef SILCLOG_H
 +#define SILCLOG_H
 +
 +/****d* silcutil/SilcLogAPI/SilcLogType
 + *
 + * NAME
 + *
 + *    typedef enum { ... } SilcLogType;
 + *
 + * DESCRIPTION
 + *
 + *    This is the main logging channel id. There are currently four known
 + *    logging channels (plus the debugging output channel), and they are
 + *    ordered by importance.
-   /* Total logging channels */
++ *    See the source code for SILC coding conventions about how to choose
 + *    the right output channel.
 + *
 + * SOURCE
 + */
 +typedef enum {
 +  /* Generic info channel file */
 +  SILC_LOG_INFO,
 +
 +  /* This should be used for warnings and non critical failures */
 +  SILC_LOG_WARNING,
 +
 +  /* Generic error and critical failures messages */
 +  SILC_LOG_ERROR,
 +
 +  /* Fatal messages (usually situations that will lead to a program crash */
 +  SILC_LOG_FATAL,
 +
-  *    print a formatted hexdump to stderr, and is commonly what you would it
-  *    like to be.  `file', `function', and `line' are the corresponding
++  /* Total number logging channels */
 +  SILC_LOG_MAX
 +} SilcLogType;
 +/***/
 +
 +/****f* silcutil/SilcLogAPI/SilcLogCb
 + *
 + * SYNOPSIS
 + *
 + *    typedef bool (*SilcLogCb)(SilcLogType type, char *message,
 + *                              void *context);
 + *
 + * DESCRIPTION
 + *
 + *    The logging custom callback function.  The `type' is the channel ID
 + *    that triggered the event, which allows you to use the same callback
 + *    function for multiple logging channels.
 + *    The `message' parameter points to a null-terminated buffer containing
 + *    the received message, while `context' is the caller-specified context.
 + *    The message must not be modified or freed by the callback function.
 + *
 + * SEE ALSO
 + *    silc_log_set_callback
 + *
 + ***/
 +typedef bool (*SilcLogCb)(SilcLogType type, char *message, void *context);
 +
 +/****f* silcutil/SilcLogAPI/SilcLogDebugCb
 + *
 + * SYNOPSIS
 + *
 + *    typedef bool (*SilcLogDebugCb)(char *file, char *function, int line,
 + *                                   char *message, void *context);
 + *
 + * DESCRIPTION
 + *
 + *    The debug logging callback function.  The default behaviour is to
 + *    output messages to stderr.  `file', `function', and `line' are the
 + *    corresponding offsets in the source files.  `message' points to a
 + *    null-terminated buffer containing the debugging message, and `context'
 + *    is the caller-specified context.
 + *    The message must not be modified or freed by the callback function.
 + *    If the function returns TRUE, SilcLog will assume the message as handled
 + *    and won't run its default handler.
 + *
 + * SEE ALSO
 + *    silc_debug, silc_log_set_debug_callbacks
 + *
 + ***/
 +typedef bool (*SilcLogDebugCb)(char *file, char *function, int line,
 +                             char *message, void *context);
 +
 +/****f* silcutil/SilcLogAPI/SilcLogHexdumpCb
 + *
 + * SYNOPSIS
 + *
 + *    typedef bool (*SilcDebugHexdumpCb)(char *file, char *function, int line,
 + *                                       unsigned char *data, uint32 data_len,
 + *                                       char *message, void *context;
 + *
 + * DESCRIPTION
 + *
 + *    The hexdump logging callback function.  The default behaviour is to
-  *    The standard behaviour is the same as SILC_LOG_INFO, but this macro
-  *    also depends on the global debugging macro SILC_DEBUG.
-  *    Undefining the global SILC_DEBUG define causes these functions to be
-  *    defined to an empty value, thus removing all logging calls from the
-  *    compiled program.
-  *
-  * SEE ALSO
-  *    SILC_LOG_INFO
++ *    print a formatted hexdump to stderr, and is commonly what you would
++ *    like it to be.  `file', `function', and `line' are the corresponding
 + *    offsets in the source files.  `data' is the begin of the buffer that
 + *    should be hexdumped, which is `data_len' bytes long.
 + *    The `message' parameter points to a null-terminated buffer containing
 + *    the received message, while `context' is the caller-specified context.
 + *    The message must not be modified or freed by the callback function.
 + *    If the function returns TRUE, SilcLog will assume the message as handled
 + *    and won't run its default handler.
 + *
 + * SEE ALSO
 + *    silc_debug_hexdump, silc_log_set_debug_callbacks
 + *
 + ***/
 +typedef bool (*SilcLogHexdumpCb)(char *file, char *function, int line,
 +                               unsigned char *data, uint32 data_len,
 +                               char *message, void *context);
 +
 +/* Global Variables */
 +
 +/****v* silcutil/SilcLogAPI/silc_log_quick
 + *
 + * NAME
 + *
 + *    bool silc_log_quick -- enable/disable fast logging output
 + *
 + * DESCRIPTION
 + *
 + *    SilcLog makes use of libc stream buffering output, which means that it
 + *    saves HD activity by buffering the logging messages and writing them
 + *    all together every some minutes (default is 5 minutes).
 + *    Setting this variable to TRUE will force SilcLog to write messages to the
 + *    filesystem as soon as they are received. This increases the CPU activity
 + *    notably on bigger servers, but reduces memory usage.
 + *    If you want to change the logging style on-the-fly, make sure to call
 + *    silc_log_flush_all() after setting this variable to TRUE.
 + *
 + ***/
 +extern DLLAPI bool silc_log_quick;
 +
 +/****v* silcutil/SilcLogAPI/silc_log_flushdelay
 + *
 + * NAME
 + *
 + *    long silc_log_flushdelay -- flushing time delay
 + *
 + * DESCRIPTION
 + *
 + *    Sets the logfiles flushing delay in seconds.  As for now, changing this
 + *    value AFTER logfiles initialization won't take effect until previous
 + *    delay time will expire; for example if you change from 300 seconds to
 + *    60 seconds you will have to wait up to 300 seconds for this change to
 + *    take effect.
 + *    This value must be greater than 2 seconds.
 + *
 + ***/
 +extern DLLAPI long silc_log_flushdelay;
 +
 +/****v* silcutil/SilcLogAPI/silc_debug
 + *
 + * NAME
 + *
 + *    bool silc_debug -- enable/disable debugging output
 + *
 + * DESCRIPTION
 + *
 + *    If silc_debug is set to FALSE, debugging functions won't procude any
 + *    output.  This is useful when for example you compile in the debugging
 + *    support but at a certain point you want to send the program in the
 + *    background.
 + *
 + * SEE ALSO
 + *    SILC_LOG_DEBUG
 + *
 + ***/
 +extern DLLAPI bool silc_debug;
 +
 +/****v* silcutil/SilcLogAPI/silc_debug_hexdump
 + *
 + * NAME
 + *
 + *    bool silc_debug_hexdump -- enable/disable debugging output
 + *
 + * DESCRIPTION
 + *
 + *    If silc_debug_hexdump is set to FALSE, debugging functions won't produce
 + *    any output.  This is useful when for example you compile in the debugging
 + *    support but at a certain point you want to send the program in the
 + *    background.
 + *
 + * SEE ALSO
 + *    SILC_LOG_HEXDUMP
 + *
 + ***/
 +extern DLLAPI bool silc_debug_hexdump;
 +
 +/* Macros */
 +
 +#ifdef WIN32
 +#define __FUNCTION__ ""
 +#endif
 +
 +/****d* silcutil/SilcLogAPI/SILC_LOG_INFO
 + *
 + * NAME
 + *
 + *    #define SILC_LOG_INFO(...)
 + *
 + * DESCRIPTION
 + *
 + *    This macro is a wrapper to the main logging function.
 + *    It supports variable argument list formatting, and *automatically*
 + *    appends newline at the end of the string.
 + *
 + * NOTES
 + *
 + *    This macro requires double parenthesis to ensure that the VA list
 + *    formatting would work correctly.
 + *
 + * EXAMPLE
 + *
 + *    SILC_LOG_INFO(("Today i feel %s", core->mood));
 + *
 + * SOURCE
 + */
 +#define SILC_LOG_INFO(fmt) silc_log_output(SILC_LOG_INFO, silc_format fmt)
 +/***/
 +
 +/****d* silcutil/SilcLogAPI/SILC_LOG_WARNING
 + *
 + * NAME
 + *
 + *    #define SILC_LOG_WARNING(...)
 + *
 + * DESCRIPTION
 + *
 + *    Wrapper to the WARNING logging channel.
 + *    Please see the SILC_LOG_INFO macro.
 + *
 + * SEE ALSO
 + *    SILC_LOG_INFO
 + *
 + * SOURCE
 + */
 +#define SILC_LOG_WARNING(fmt) silc_log_output(SILC_LOG_WARNING, silc_format fmt)
 +/***/
 +
 +/****d* silcutil/SilcLogAPI/SILC_LOG_ERROR
 + *
 + * NAME
 + *
 + *    #define SILC_LOG_ERROR(...)
 + *
 + * DESCRIPTION
 + *
 + *    Wrapper to the ERROR logging channel.
 + *    Please see the SILC_LOG_INFO macro.
 + *
 + * SEE ALSO
 + *    SILC_LOG_INFO
 + *
 + * SOURCE
 + */
 +#define SILC_LOG_ERROR(fmt) silc_log_output(SILC_LOG_ERROR, silc_format fmt)
 +/***/
 +
 +/****d* silcutil/SilcLogAPI/SILC_LOG_FATAL
 + *
 + * NAME
 + *
 + *    #define SILC_LOG_FATAL(...)
 + *
 + * DESCRIPTION
 + *
 + *    Wrapper to the FATAL logging channel.
 + *    Please see the SILC_LOG_INFO macro.
 + *
 + * SEE ALSO
 + *    SILC_LOG_INFO
 + *
 + * SOURCE
 + */
 +#define SILC_LOG_FATAL(fmt) silc_log_output(SILC_LOG_FATAL, silc_format fmt)
 +/***/
 +
 +/****d* silcutil/SilcLogAPI/SILC_LOG_DEBUG
 + *
 + * NAME
 + *
 + *    #define SILC_LOG_DEBUG(...)
 + *
 + * DESCRIPTION
 + *
 + *    This is a special wrapper to the debugging output (usually stderr).
-  *    The second parameter is a (char *) pointer to the beginning of the
-  *    memory section that should be hexdumped, and the third parameter is
-  *    the length of this memory section.
-  *    This macro is also affected by the global variable silc_debug_hexdump.
++ *    The standard behaviour is the same as SILC_LOG_INFO, with the difference
++ *    that this macro also depends on the global define SILC_DEBUG.
++ *    Undefining SILC_DEBUG causes these functions to be defined to an empty
++ *    value, thus removing all debug logging calls from the compiled
++ *    application.
++ *    This macro is also affected by the global variable silc_debug.
 + *
 + * SOURCE
 + */
 +#ifdef SILC_DEBUG
 +#define SILC_LOG_DEBUG(fmt) silc_log_output_debug(__FILE__, \
 +                              __FUNCTION__, \
 +                              __LINE__, \
 +                              silc_format fmt)
 +#else
 +#define SILC_LOG_DEBUG(fmt)
 +#endif        /* SILC_DEBUG */
 +/***/
 +
 +/****d* silcutil/SilcLogAPI/SILC_LOG_HEXDUMP
 + *
 + * NAME
 + *
 + *    #define SILC_LOG_HEXDUMP(...)
 + *
 + * DESCRIPTION
 + *
 + *    This is a special wrapper to the hexdump output function.  This macro
 + *    behaves slightly differently from other logging wrappers.
 + *    The first parameter, is composed by a group of parameters delimited by
 + *    parenthesis.
-  *    defined to an empty value, thus removing all logging calls from the
-  *    compiled program.
++ *    The second parameter is a `char *' pointer pointing to the beginning
++ *    of the memory section that should be hexdumped, and the third parameter
++ *    is the length of this memory section.
 + *    Undefining the global SILC_DEBUG define causes these functions to be
-  *    must not be freed, modified or stored.
++ *    defined to an empty value, thus removing all debug logging calls from
++ *    the compiled application.
++ *    This macro is also affected by the global variable silc_debug_hexdump.
 + *
 + * EXAMPLE
 + *
 + *    SILC_LOG_HEXDUMP(("Outgoing packet [%d], len %d", pckt->seq, pckt->len),
 + *                     pckt->data, pckt->datalen);
 + *
 + * SOURCE
 + */
 +#ifdef SILC_DEBUG
 +#define SILC_LOG_HEXDUMP(fmt, data, len) silc_log_output_hexdump(__FILE__, \
 +                              __FUNCTION__, \
 +                              __LINE__, \
 +                              (data), (len), \
 +                              silc_format fmt)
 +#else
 +#define SILC_LOG_HEXDUMP(fmt, data, len)
 +#endif        /* SILC_DEBUG */
 +/***/
 +
 +/* Prototypes */
 +
 +/****f* silcutil/SilcLogAPI/silc_log_output
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_output(SilcLogType type, char *string);
 + *
 + * DESCRIPTION
 + *
 + *    This is the main function for logging output. Please note that you
 + *    should rather use one of the logging wrapper macros.
 + *    If you really want to use this function, its usage is quite simple.
 + *    The `type' parameter identifies the channel to use, while the `string'
 + *    parameter must be a dynamic allocated (null-terminated) buffer, because
 + *    it will be freed at the end of this function, for internal reasons.
 + *    If there are registered callbacks for the specified type, this function
 + *    will first trigger those callbacks.  The callback functions must NOT
 + *    free or modify the original buffer.
 + *
 + * SEE ALSO
 + *    SILC_LOG_INFO, SILC_LOG_WARNING, SILC_LOG_ERROR, SILC_LOG_FATAL
 + *
 + ***/
 +void silc_log_output(SilcLogType type, char *string);
 +
 +/****f* silcutil/SilcLogAPI/silc_log_get_file
 + *
 + * SYNOPSIS
 + *
 + *    char *silc_log_get_file(SilcLogType type);
 + *
 + * DESCRIPTION
 + *
 + *    Returns the current logging file for the channel `type'.
 + *    If there has been an error during the opening of this channel, NULL
 + *    is returned, even if the file has been previously set with
 + *    silc_log_set_file().
 + *    The returned pointer points to internally allocated storage and must
-  *    SilcLogCb, silc_log_reset_callbacks
++ *    not be freed, modified or stored.
 + *
 + ***/
 +char *silc_log_get_file(SilcLogType type);
 +
 +/****f* silcutil/SilcLogAPI/silc_log_set_file
 + *
 + * SYNOPSIS
 + *
 + *    bool silc_log_set_file(SilcLogType type, char *filename, uint32 maxsize,
 + *                           SilcSchedule scheduler);
 + *
 + * DESCRIPTION
 + *
 + *    Sets `filename', which can be maximum `maxsize' bytes long, as the new
 + *    logging file for the channel `type'.  If you specify an illegal filename
 + *    a warning message is printed and FALSE is returned.  In this case
 + *    logging settings are not changed.
 + *    You can disable logging for a channel by specifying NULL filename, the
 + *    maxsize in this case is not important.
 + *    The `scheduler' parameter is needed by the internal logging to allow
 + *    buffered output and thus to save HD activity.
 + *
 + ***/
 +bool silc_log_set_file(SilcLogType type, char *filename, uint32 maxsize,
 +                     SilcSchedule scheduler);
 +
 +/****f* silcutil/SilcLogAPI/silc_log_set_callback
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_set_callback(SilcLogType type, SilcLogCb cb,
 + *                               void *context);
 + *
 + * DESCRIPTION
 + *
 + *    Set `cb' as the default callback function for the logging channel
 + *    `type'.  When SilcLog receives a message for this channel, it will
 + *    trigger the callback function.  If the callback function returns TRUE
 + *    SilcLog will assume the input as handled and won't run its default
 + *    handler.
 + *    You can disable/remove a callback by setting it to NULL or calling the
 + *    function silc_log_reset_callbacks.
 + *    If set, the callback function must be in the form described by
 + *    SilcLogCb.
 + *
 + * SEE ALSO
++ *    silc_log_reset_callbacks
 + *
 + ***/
 +void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context);
 +
 +/****f* silcutil/SilcLogAPI/silc_log_reset_callbacks
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_reset_callbacks();
 + *
 + * DESCRIPTION
 + *
 + *    Removes all logging callbacks for normal channels.  This function does
 + *    NOT remove callbacks for debugging channels (debug and hexdump), you
 + *    rather need to call silc_log_set_debug_callbacks() with NULL callbacks.
 + *
 + ***/
 +void silc_log_reset_callbacks();
 +
 +/****f* silcutil/SilcLogAPI/silc_log_flush_all
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_flush_all();
 + *
 + * DESCRIPTION
 + *
 + *    Forces flushing for all logging channels.  This should be called for
 + *    example after receiving special signals.
 + *
 + * SEE ALSO
 + *    silc_log_quick
 + *
 + ***/
 +void silc_log_flush_all();
 +
 +/****f* silcutil/SilcLogAPI/silc_log_reset_all
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_reset_all();
 + *
 + * DESCRIPTION
 + *
 + *    Forces all logging channels to close and reopen their streams.  Useful
 + *    for example after a SIGHUP signal.
 + *    Please note that this function could cause some warning messages if
 + *    some logging channel points to an illegal filename.
 + *
 + ***/
 +void silc_log_reset_all();
 +
 +/****f* silcutil/SilcLogAPI/silc_log_output_debug
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_output_debug(char *file, char *function,
 + *                               int line, char *string);
 + *
 + * DESCRIPTION
 + *
 + *    This is the main function for debug output.  Please note that you should
 + *    rather use the wrapper macro SILC_LOG_DEBUG.
 + *    If you want to use it anyway, the `file', `function', and `line' are the
 + *    corresponding offsets in the source files, while `string' must be a
 + *    dynamic allocated (null-terminated) buffer.
 + *
 + ***/
 +void silc_log_output_debug(char *file, char *function,
 +                         int line, char *string);
 +
 +/****f* silcutil/SilcLogAPI/silc_log_output_hexdump
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_output_hexdump(char *file, char *function,
 + *                                 int line, void *data_in,
 + *                                 uint32 len, char *string);
 + *
 + * DESCRIPTION
 + *
 + *    This is the main function for hexdump output.  Please note that you
 + *    should rather use the wrapper macro SILC_LOG_HEXDUMP.
 + *    If you want to use it anyway, the `file', `function', and `line' are the
 + *    corresponding offsets in the source files, `data_in' is the beginning
 + *    of the buffer you wish to hexdump, which is `len' bytes long.
 + *    `string' must be a dynamic allocated (null-terminated) buffer.
 + *
 + ***/
 +void silc_log_output_hexdump(char *file, char *function,
 +                           int line, void *data_in,
 +                           uint32 len, char *string);
 +
 +/****f* silcutil/SilcLogAPI/silc_log_set_debug_callbacks
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
 + *                                      void *debug_context,
 + *                                      SilcLogHexdumpCb hexdump_cb,
 + *                                      void *hexdump_context);
 + *
 + * DESCRIPTION
 + *
 + *    Sets `debug_cb' as the the default callback function for the debug
 + *    output, that will be called with the `debug_context' parameter.
 + *    When SilcLog receives a debug message, it will trigger the callback
 + *    function.  If the callback function returns TRUE SilcLog will assume
 + *    the input as handled and won't run its default handler.
 + *    `hexdump_cb' and `hexdump_context' works the same way, except that they
 + *    are referred to SILC_LOG_HEXDUMP requests.
 + *    You can disable/remove a callback by setting it to NULL.
 + *    If set, each callback function must be either in the form described by
 + *    SilcLogDebugCb or SilcLogHexdumpCb.
 + *
 + * SEE ALSO
 + *    SilcLogDebugCb,  SilcLogHexdumpCb
 + *
 + ***/
 +void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
 +                                void *debug_context,
 +                                SilcLogHexdumpCb hexdump_cb,
 +                                void *hexdump_context);
 +
 +/****f* silcutil/SilcLogAPI/silc_log_set_debug_string
 + *
 + * SYNOPSIS
 + *
 + *    void silc_log_set_debug_string(const char *debug_string);
 + *
 + * DESCRIPTION
 + *
 + *    Sets `debug_string' as the regexp string for filtering debugging
 + *    output.  The string is copied and it can be modified/destroyed after
 + *    this function call.
 + *
 + ***/
 +void silc_log_set_debug_string(const char *debug_string);
 +
 +#endif        /* !SILCLOG_H */