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 --cc 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.
index 3b3a8b5cde781038304a90332b5747b2c7c69bf9,b9a0cc63b90f069216bf6bc42f1a58079f75f260..b4d5bcee37adf53669b7e5e77dde3ac515eb6dae
@@@ -956,3789 -429,183 +956,3789 @@@ silc_server_command_whois_process(SilcS
      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;
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);
 +}
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);
 +  }
 +}
index d5a287d53275d437c322328f1f6b95d31ea73fc5,60d491705041f334956f622a56042bcf5fedee93..a7a1b707d7d1a39000626dbf555ac8f20a52cd2d
@@@ -906,7 -477,16 +906,7 @@@ SILC_TASK_CALLBACK(silc_server_protocol
  
        /* 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) {
        
        /* 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) {
        
        /* 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;
index d033284d457599c0614e9386425bcbd3794b2d38,636b428fdeb2ff2c698d0e4904be130d5b5e9e7d..f0ed13f8e6b1950302bfdb8045d0f1e150ffcdb1
@@@ -113,46 -123,21 +113,42 @@@ 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_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 
  
    /* 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"));
  
    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. */
@@@ -509,150 -329,17 +503,150 @@@ void silc_server_stop(SilcServer server
    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)"));
  
@@@ -726,10 -525,8 +720,10 @@@ 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"));
  
@@@ -909,194 -677,83 +903,194 @@@ SILC_TASK_CALLBACK(silc_server_connect_
    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;
    }
  
@@@ -1357,84 -924,41 +1351,85 @@@ SILC_TASK_CALLBACK(silc_server_accept_n
    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:
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
@@@ -2,15 -2,15 +2,15 @@@
  
    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
  #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;
    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;
    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 */
index 0eadeba52974a01a48ecd14c13e5a65b47b0223d,0de8874a5d8fe8353af8c4b64958b6ebb7162cca..cf3315a2cd126100a2611548ee6e1c271d797dda
@@@ -100,53 -60,12 +100,53 @@@ Usage: silcd [options]\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) {
    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);
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 */
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 */