Moved silc_client_ch[u]mode[_char] to client library from silc/.
authorPekka Riikonen <priikone@silcnet.org>
Wed, 1 Nov 2000 21:44:27 +0000 (21:44 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 1 Nov 2000 21:44:27 +0000 (21:44 +0000)
NAMES command now shows users modes on joining with nickname

1  2 
CHANGES
apps/silc/clientutil.c
apps/silc/clientutil.h
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/command_reply.c

diff --cc CHANGES
index 08341773ba5b5141b53b024689856e92f10726c4,0000000000000000000000000000000000000000..bfec5a99a399d80324c42267e1b1c898eb663e70
mode 100644,000000..100644
--- /dev/null
+++ b/CHANGES
@@@ -1,614 -1,0 +1,622 @@@
 +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 321160d49055a73f705825eb99be4f0d331f2449,21043795c0249108bc680645c5583a99488b9461..210f50b02c5d2307f6a8bb4bf12a211bb97e6c38
@@@ -493,316 -441,4 +493,239 @@@ New pair of keys will be created.  Plea
    silc_math_primegen_uninit();
    silc_rng_free(rng);
    silc_pkcs_free(pkcs);
 +
 +  return TRUE;
 +}
 +
 +/* This checks stats for various SILC files and directories. First it 
 +   checks if ~/.silc directory exist and is owned by the correct user. If 
 +   it doesn't exist, it will create the directory. After that it checks if
 +   user's Public and Private key files exists and that they aren't expired.
 +   If they doesn't exist or they are expired, they will be (re)created
 +   after return. */
 +
 +int silc_client_check_silc_dir()
 +{
 +  char filename[256], file_public_key[256], file_private_key[256];
 +  char servfilename[256];
 +  char *identifier;
 +  struct stat st;
 +  struct passwd *pw;
 +  int firstime = FALSE;
 +  time_t curtime, modtime;
 +
 +  SILC_LOG_DEBUG(("Checking ~./silc directory"));
 +
 +  memset(filename, 0, sizeof(filename));
 +  memset(file_public_key, 0, sizeof(file_public_key));
 +  memset(file_private_key, 0, sizeof(file_private_key));
 +
 +  pw = getpwuid(getuid());
 +  if (!pw) {
 +    fprintf(stderr, "silc: %s\n", strerror(errno));
 +    return FALSE;
 +  }
 +
 +  identifier = silc_client_create_identifier();
 +
 +  /* We'll take home path from /etc/passwd file to be sure. */
 +  snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
 +  snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", 
 +         pw->pw_dir);
 +
 +  /*
 +   * Check ~/.silc directory
 +   */
 +  if ((stat(filename, &st)) == -1) {
 +    /* If dir doesn't exist */
 +    if (errno == ENOENT) {
 +      if (pw->pw_uid == geteuid()) {
 +      if ((mkdir(filename, 0755)) == -1) {
 +        fprintf(stderr, "Couldn't create `%s' directory\n", filename);
 +        return FALSE;
 +      }
 +
 +      /* Directory was created. First time running SILC */
 +      firstime = TRUE;
 +      } else {
 +      fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
 +              filename);
 +      return FALSE;
 +      }
 +    } else {
 +      fprintf(stderr, "%s\n", strerror(errno));
 +      return FALSE;
 +    }
 +  } else {
 +    
 +    /* Check the owner of the dir */
 +    if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
 +      fprintf(stderr, "You don't seem to own `%s' directory\n",
 +            filename);
 +      return FALSE;
 +    }
 +    
 +    /* Check the permissions of the dir */
 +    if ((st.st_mode & 0777) != 0755) {
 +      if ((chmod(filename, 0755)) == -1) {
 +      fprintf(stderr, "Permissions for `%s' directory must be 0755\n", 
 +              filename);
 +      return FALSE;
 +      }
 +    }
 +  }
 +
 +  /*
 +   * Check ~./silc/serverkeys directory
 +   */
 +  if ((stat(servfilename, &st)) == -1) {
 +    /* If dir doesn't exist */
 +    if (errno == ENOENT) {
 +      if (pw->pw_uid == geteuid()) {
 +      if ((mkdir(servfilename, 0755)) == -1) {
 +        fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
 +        return FALSE;
 +      }
 +      } else {
 +      fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
 +              servfilename);
 +      return FALSE;
 +      }
 +    } else {
 +      fprintf(stderr, "%s\n", strerror(errno));
 +      return FALSE;
 +    }
 +  }
 +  
 +  /*
 +   * Check Public and Private keys
 +   */
 +  snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s", 
 +         filename, SILC_CLIENT_PUBLIC_KEY_NAME);
 +  snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s", 
 +         filename, SILC_CLIENT_PRIVATE_KEY_NAME);
 +  
 +  /* If running SILC first time */
 +  if (firstime) {
 +    fprintf(stdout, "Running SILC for the first time\n");
 +    silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
 +                              SILC_CLIENT_DEF_PKCS_LEN,
 +                              file_public_key, file_private_key, 
 +                              identifier, NULL, NULL);
 +    return TRUE;
 +  }
 +  
 +  if ((stat(file_public_key, &st)) == -1) {
 +    /* If file doesn't exist */
 +    if (errno == ENOENT) {
 +      fprintf(stdout, "Your public key doesn't exist\n");
 +      silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
 +                                SILC_CLIENT_DEF_PKCS_LEN,
 +                                file_public_key, 
 +                                file_private_key, identifier, NULL, NULL);
 +    } else {
 +      fprintf(stderr, "%s\n", strerror(errno));
 +      return FALSE;
 +    }
 +  }
 +
 +  if ((stat(file_private_key, &st)) == -1) {
 +    /* If file doesn't exist */
 +    if (errno == ENOENT) {
 +      fprintf(stdout, "Your private key doesn't exist\n");
 +      silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
 +                                SILC_CLIENT_DEF_PKCS_LEN,
 +                                file_public_key, 
 +                                file_private_key, identifier, NULL, NULL);
 +    } else {
 +      fprintf(stderr, "%s\n", strerror(errno));
 +      return FALSE;
 +    }
 +  }
 +    
 +  /* Check the owner of the public key */
 +  if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
 +    fprintf(stderr, "You don't seem to own your public key!?\n");
 +    return FALSE;
 +  }
 +  
 +  /* Check the owner of the private key */
 +  if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
 +    fprintf(stderr, "You don't seem to own your private key!?\n");
 +    return FALSE;
 +  }
 +    
 +  /* Check the permissions for the private key */
 +  if ((st.st_mode & 0777) != 0600) {
 +    fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
 +          "Trying to change them ... ", file_private_key);
 +    if ((chmod(file_private_key, 0600)) == -1) {
 +      fprintf(stderr,
 +            "Failed to change permissions for private key file!\n" 
 +            "Permissions for your private key file must be 0600.\n");
 +      return FALSE;
 +    }
 +    fprintf(stderr, "Done.\n\n");
 +  }
 +
 +  /* See if the key has expired. */
 +  modtime = st.st_mtime;      /* last modified */
 +  curtime = time(0) - modtime;
 +    
 +  /* 86400 is seconds in a day. */
 +  if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
 +    fprintf(stdout, 
 +          "--------------------------------------------------\n"
 +          "Your private key has expired and needs to be\n" 
 +          "recreated.  This will be done automatically now.\n"
 +          "Your new key will expire in %d days from today.\n"
 +          "--------------------------------------------------\n",
 +          SILC_CLIENT_KEY_EXPIRES);
 +
 +    silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
 +                              SILC_CLIENT_DEF_PKCS_LEN,
 +                              file_public_key, 
 +                              file_private_key, identifier, NULL, NULL);
 +  }
 +  
 +  if (identifier)
 +    silc_free(identifier);
 +
 +  return TRUE;
 +}
 +
 +/* Loads public and private key from files. */
 +
 +int silc_client_load_keys(SilcClient client)
 +{
 +  char filename[256];
 +  struct passwd *pw;
 +
 +  SILC_LOG_DEBUG(("Loading public and private keys"));
 +
 +  pw = getpwuid(getuid());
 +  if (!pw)
 +    return FALSE;
 +
 +  memset(filename, 0, sizeof(filename));
 +  snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
 +         pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
 +
 +  if (silc_pkcs_load_private_key(filename, &client->private_key,
 +                               SILC_PKCS_FILE_BIN) == FALSE)
 +    if (silc_pkcs_load_private_key(filename, &client->private_key,
 +                                 SILC_PKCS_FILE_PEM) == FALSE)
 +      return FALSE;
 +
 +  memset(filename, 0, sizeof(filename));
 +  snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
 +         pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
 +
 +  if (silc_pkcs_load_public_key(filename, &client->public_key,
 +                              SILC_PKCS_FILE_PEM) == FALSE)
 +    if (silc_pkcs_load_public_key(filename, &client->public_key,
 +                                SILC_PKCS_FILE_BIN) == FALSE)
 +      return FALSE;
 +
 +  return TRUE;
  }
- /* Parses mode mask and returns the mode as string. */
- char *silc_client_chmode(unsigned int mode)
- {
-   char string[20];
-   if (!mode)
-     return NULL;
-   memset(string, 0, sizeof(string));
-   if (mode & SILC_CHANNEL_MODE_PRIVATE)
-     strncat(string, "p", 1);
-   if (mode & SILC_CHANNEL_MODE_SECRET)
-     strncat(string, "s", 1);
-   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
-     strncat(string, "k", 1);
-   if (mode & SILC_CHANNEL_MODE_INVITE)
-     strncat(string, "i", 1);
-   if (mode & SILC_CHANNEL_MODE_TOPIC)
-     strncat(string, "t", 1);
-   if (mode & SILC_CHANNEL_MODE_ULIMIT)
-     strncat(string, "l", 1);
-   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
-     strncat(string, "a", 1);
-   /* Rest of mode is ignored */
-   return strdup(string);
- }
- /* Parses channel user mode mask and returns te mode as string */
- char *silc_client_chumode(unsigned int mode)
- {
-   char string[4];
-   if (!mode)
-     return NULL;
-   memset(string, 0, sizeof(string));
-   if (mode & SILC_CHANNEL_UMODE_CHANFO)
-     strncat(string, "f", 1);
-   if (mode & SILC_CHANNEL_UMODE_CHANOP)
-     strncat(string, "o", 1);
-   return strdup(string);
- }
- /* Parses channel user mode and returns it as special mode character. */
- char *silc_client_chumode_char(unsigned int mode)
- {
-   char string[4];
-   if (!mode)
-     return NULL;
-   memset(string, 0, sizeof(string));
-   if (mode & SILC_CHANNEL_UMODE_CHANFO)
-     strncat(string, "*", 1);
-   if (mode & SILC_CHANNEL_UMODE_CHANOP)
-     strncat(string, "@", 1);
-   return strdup(string);
- }
index b682a1271a087df147c264f045936d6cfb46656f,e18cf2d71eef0a5c8f576d3057137e1a8403773c..38f0d9e857b7cfc88e28c0f6cd3c85f093c242cf
@@@ -35,16 -35,6 +35,13 @@@ char *silc_client_get_passphrase(const 
  void silc_client_list_ciphers();
  void silc_client_list_hash_funcs();
  void silc_client_list_pkcs();
 -void silc_client_create_key_pair(char *pkcs_name, int bits);
 +char *silc_client_create_identifier();
 +int silc_client_create_key_pair(char *pkcs_name, int bits,
 +                              char *public_key, char *private_key,
 +                              char *identifier, 
 +                              SilcPublicKey *ret_pub_key,
 +                              SilcPrivateKey *ret_prv_key);
 +int silc_client_check_silc_dir();
 +int silc_client_load_keys(SilcClient client);
- char *silc_client_chmode(unsigned int mode);
- char *silc_client_chumode(unsigned int mode);
- char *silc_client_chumode_char(unsigned int mode);
  
  #endif
index 4cb6fc7a5ec7abb7ab8060166a1215d039dd0c72,0000000000000000000000000000000000000000..44659ada59e5366baddab1912a6270fa5ddd8ea6
mode 100644,000000..100644
--- /dev/null
@@@ -1,2081 -1,0 +1,2158 @@@
 +/*
 +
 +  client.c
 +
 +  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +  Copyright (C) 1997 - 2000 Pekka Riikonen
 +
 +  This program is free software; you can redistribute it and/or modify
 +  it under the terms of the GNU General Public License as published by
 +  the Free Software Foundation; either version 2 of the License, or
 +  (at your option) any later version.
 +  
 +  This program is distributed in the hope that it will be useful,
 +  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +  GNU General Public License for more details.
 +
 +*/
 +/* $Id$ */
 +
 +#include "clientlibincludes.h"
 +
 +/* Static task callback prototypes */
 +SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
 +SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
 +SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
 +SILC_TASK_CALLBACK(silc_client_packet_process);
 +SILC_TASK_CALLBACK(silc_client_packet_parse_real);
 +
 +static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
 +static void silc_client_packet_parse_type(SilcClient client, 
 +                                        SilcSocketConnection sock,
 +                                        SilcPacketContext *packet);
 +
 +/* Allocates new client object. This has to be done before client may
 +   work. After calling this one must call silc_client_init to initialize
 +   the client. The `application' is application specific user data pointer
 +   and caller must free it. */
 +
 +SilcClient silc_client_alloc(SilcClientOperations *ops, void *application)
 +{
 +  SilcClient new_client;
 +
 +  new_client = silc_calloc(1, sizeof(*new_client));
 +  new_client->application = application;
 +  new_client->ops = ops;
 +
 +  return new_client;
 +}
 +
 +/* Free's client object */
 +
 +void silc_client_free(SilcClient client)
 +{
 +  if (client) {
 +    silc_free(client);
 +  }
 +}
 +
 +/* Initializes the client. This makes all the necessary steps to make
 +   the client ready to be run. One must call silc_client_run to run the
 +   client. */
 +
 +int silc_client_init(SilcClient client)
 +{
 +  SILC_LOG_DEBUG(("Initializing client"));
 +
 +  /* Initialize hash functions for client to use */
 +  silc_hash_alloc("md5", &client->md5hash);
 +  silc_hash_alloc("sha1", &client->sha1hash);
 +
 +  /* Initialize none cipher */
 +  silc_cipher_alloc("none", &client->none_cipher);
 +
 +  /* Initialize random number generator */
 +  client->rng = silc_rng_alloc();
 +  silc_rng_init(client->rng);
 +  silc_math_primegen_init(); /* XXX */
 +
 +  /* Register protocols */
 +  silc_client_protocols_register();
 +
 +  /* Initialize the scheduler */
 +  silc_schedule_init(&client->io_queue, &client->timeout_queue, 
 +                   &client->generic_queue, 5000);
 +
 +  return TRUE;
 +}
 +
 +/* Stops the client. This is called to stop the client and thus to stop
 +   the program. */
 +
 +void silc_client_stop(SilcClient client)
 +{
 +  SILC_LOG_DEBUG(("Stopping client"));
 +
 +  /* Stop the scheduler, although it might be already stopped. This
 +     doesn't hurt anyone. This removes all the tasks and task queues,
 +     as well. */
 +  silc_schedule_stop();
 +  silc_schedule_uninit();
 +
 +  silc_client_protocols_unregister();
 +
 +  SILC_LOG_DEBUG(("Client stopped"));
 +}
 +
 +/* Runs the client. */
 +
 +void silc_client_run(SilcClient client)
 +{
 +  SILC_LOG_DEBUG(("Running client"));
 +
 +  /* Start the scheduler, the heart of the SILC client. When this returns
 +     the program will be terminated. */
 +  silc_schedule();
 +}
 +
 +/* Allocates and adds new connection to the client. This adds the allocated
 +   connection to the connection table and returns a pointer to it. A client
 +   can have multiple connections to multiple servers. Every connection must
 +   be added to the client using this function. User data `context' may
 +   be sent as argument. */
 +
 +SilcClientConnection silc_client_add_connection(SilcClient client,
 +                                              char *hostname,
 +                                              int port,
 +                                              void *context)
 +{
 +  SilcClientConnection conn;
 +  int i;
 +
 +  conn = silc_calloc(1, sizeof(*conn));
 +
 +  /* Initialize ID caches */
 +  conn->client_cache = silc_idcache_alloc(0);
 +  conn->channel_cache = silc_idcache_alloc(0);
 +  conn->server_cache = silc_idcache_alloc(0);
 +  conn->client = client;
 +  conn->remote_host = strdup(hostname);
 +  conn->remote_port = port;
 +  conn->context = context;
 +
 +  /* Add the connection to connections table */
 +  for (i = 0; i < client->conns_count; i++)
 +    if (client->conns && !client->conns[i]) {
 +      client->conns[i] = conn;
 +      return conn;
 +    }
 +
 +  client->conns = silc_realloc(client->conns, sizeof(*client->conns)
 +                             * (client->conns_count + 1));
 +  client->conns[client->conns_count] = conn;
 +  client->conns_count++;
 +
 +  return conn;
 +}
 +
 +/* Removes connection from client. */
 +
 +void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 +{
 +  int i;
 +
 +  for (i = 0; i < client->conns_count; i++)
 +    if (client->conns[i] == conn) {
 +      silc_free(conn);
 +      client->conns[i] = NULL;
 +    }
 +}
 +
 +/* Internal context for connection process. This is needed as we
 +   doing asynchronous connecting. */
 +typedef struct {
 +  SilcClient client;
 +  SilcClientConnection conn;
 +  SilcTask task;
 +  int sock;
 +  char *host;
 +  int port;
 +  int tries;
 +} SilcClientInternalConnectContext;
 +
 +static int 
 +silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
 +{
 +  int sock;
 +
 +  /* XXX In the future we should give up this non-blocking connect all
 +     together and use threads instead. */
 +  /* Create connection to server asynchronously */
 +  sock = silc_net_create_connection_async(ctx->port, ctx->host);
 +  if (sock < 0)
 +    return -1;
 +
 +  /* Register task that will receive the async connect and will
 +     read the result. */
 +  ctx->task = silc_task_register(ctx->client->io_queue, sock, 
 +                               silc_client_connect_to_server_start,
 +                               (void *)ctx, 0, 0, 
 +                               SILC_TASK_FD,
 +                               SILC_TASK_PRI_NORMAL);
 +  silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
 +  silc_schedule_set_listen_fd(sock, ctx->task->iomask);
 +
 +  ctx->sock = sock;
 +
 +  return sock;
 +}
 +
 +/* Connects to remote server. This is the main routine used to connect
 +   to SILC server. Returns -1 on error and the created socket otherwise. 
 +   The `context' is user context that is saved into the SilcClientConnection
 +   that is created after the connection is created. */
 +
 +int silc_client_connect_to_server(SilcClient client, int port,
 +                                char *host, void *context)
 +{
 +  SilcClientInternalConnectContext *ctx;
 +  SilcClientConnection conn;
 +  int sock;
 +
 +  SILC_LOG_DEBUG(("Connecting to port %d of server %s",
 +                port, host));
 +
 +  conn = silc_client_add_connection(client, host, port, context);
 +
 +  client->ops->say(client, conn, 
 +                 "Connecting to port %d of server %s", port, host);
 +
 +  /* Allocate internal context for connection process. This is
 +     needed as we are doing async connecting. */
 +  ctx = silc_calloc(1, sizeof(*ctx));
 +  ctx->client = client;
 +  ctx->conn = conn;
 +  ctx->host = strdup(host);
 +  ctx->port = port;
 +  ctx->tries = 0;
 +
 +  /* Do the actual connecting process */
 +  sock = silc_client_connect_to_server_internal(ctx);
 +  if (sock == -1)
 +    silc_client_del_connection(client, conn);
 +  return sock;
 +}
 +
 +/* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
 +   key material between client and server.  This function can be called
 +   directly if application is performing its own connecting and does not
 +   use the connecting provided by this library. */
 +
 +int silc_client_start_key_exchange(SilcClient client,
 +                                 SilcClientConnection conn,
 +                                   int fd)
 +{
 +  SilcProtocol protocol;
 +  SilcClientKEInternalContext *proto_ctx;
 +  void *context;
 +
 +  /* Allocate new socket connection object */
 +  silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
 +  if (conn->sock == NULL) {
 +    client->ops->say(client, conn, 
 +                   "Error: Could not allocate connection socket");
 +    return FALSE;
 +  }
 +
 +  conn->nickname = strdup(client->username);
 +  conn->sock->hostname = conn->remote_host;
 +  conn->sock->port = conn->remote_port;
 +
 +  /* Allocate internal Key Exchange context. This is sent to the
 +     protocol as context. */
 +  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 +  proto_ctx->client = (void *)client;
 +  proto_ctx->sock = conn->sock;
 +  proto_ctx->rng = client->rng;
 +  proto_ctx->responder = FALSE;
 +
 +  /* Perform key exchange protocol. silc_client_connect_to_server_final
 +     will be called after the protocol is finished. */
 +  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
 +                    &protocol, (void *)proto_ctx,
 +                    silc_client_connect_to_server_second);
 +  if (!protocol) {
 +    client->ops->say(client, conn, 
 +                   "Error: Could not start authentication protocol");
 +    return FALSE;
 +  }
 +  conn->sock->protocol = protocol;
 +
 +  /* 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_CLIENT_SET_CONNECTION_FOR_OUTPUT,
 +     later when outgoing data is available. */
 +  context = (void *)client;
 +  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
 +
 +  /* Execute the protocol */
 +  protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
 +  return TRUE;
 +}
 +
 +/* Start of the connection to the remote server. This is called after
 +   succesful TCP/IP connection has been established to the remote host. */
 +
 +SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
 +{
 +  SilcClientInternalConnectContext *ctx =
 +    (SilcClientInternalConnectContext *)context;
 +  SilcClient client = ctx->client;
 +  SilcClientConnection conn = ctx->conn;
 +  int opt, opt_len = sizeof(opt);
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  /* Check the socket status as it might be in error */
 +  getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
 +  if (opt != 0) {
 +    if (ctx->tries < 2) {
 +      /* Connection failed but lets try again */
 +      client->ops->say(client, conn, "Could not connect to server %s: %s",
 +                     ctx->host, strerror(opt));
 +      client->ops->say(client, conn, 
 +                     "Connecting to port %d of server %s resumed", 
 +                     ctx->port, ctx->host);
 +
 +      /* Unregister old connection try */
 +      silc_schedule_unset_listen_fd(fd);
 +      silc_net_close_connection(fd);
 +      silc_task_unregister(client->io_queue, ctx->task);
 +
 +      /* Try again */
 +      silc_client_connect_to_server_internal(ctx);
 +      ctx->tries++;
 +    } else {
 +      /* Connection failed and we won't try anymore */
 +      client->ops->say(client, conn, "Could not connect to server %s: %s",
 +                     ctx->host, strerror(opt));
 +      silc_schedule_unset_listen_fd(fd);
 +      silc_net_close_connection(fd);
 +      silc_task_unregister(client->io_queue, ctx->task);
 +      silc_free(ctx);
 +
 +      /* Notify application of failure */
 +      client->ops->connect(client, conn, FALSE);
 +      silc_client_del_connection(client, conn);
 +    }
 +    return;
 +  }
 +
 +  silc_schedule_unset_listen_fd(fd);
 +  silc_task_unregister(client->io_queue, ctx->task);
 +  silc_free(ctx);
 +
 +  if (!silc_client_start_key_exchange(client, conn, fd)) {
 +    silc_net_close_connection(fd);
 +    client->ops->connect(client, conn, FALSE);
 +  }
 +}
 +
 +/* Second part of the connecting to the server. This executed 
 +   authentication protocol. */
 +
 +SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcClientKEInternalContext *ctx = 
 +    (SilcClientKEInternalContext *)protocol->context;
 +  SilcClient client = (SilcClient)ctx->client;
 +  SilcSocketConnection sock = NULL;
 +  SilcClientConnAuthInternalContext *proto_ctx;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
 +    /* Error occured during protocol */
 +    SILC_LOG_DEBUG(("Error during KE protocol"));
 +    silc_protocol_free(protocol);
 +    if (ctx->ske)
 +      silc_ske_free(ctx->ske);
 +    if (ctx->dest_id)
 +      silc_free(ctx->dest_id);
 +    ctx->sock->protocol = NULL;
 +
 +    /* Notify application of failure */
 +    client->ops->connect(client, ctx->sock->user_data, FALSE);
 +    silc_free(ctx);
 +    return;
 +  }
 +
 +  /* Allocate internal context for the authentication protocol. This
 +     is sent as context for the protocol. */
 +  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
 +  proto_ctx->client = (void *)client;
 +  proto_ctx->sock = sock = ctx->sock;
 +  proto_ctx->ske = ctx->ske;  /* Save SKE object from previous protocol */
 +  proto_ctx->dest_id_type = ctx->dest_id_type;
 +  proto_ctx->dest_id = ctx->dest_id;
 +
 +  /* Resolve the authentication method to be used in this connection */
 +  if (!client->ops->get_auth_method(client, sock->user_data, sock->hostname,
 +                                  sock->port, &proto_ctx->auth_meth,
 +                                  &proto_ctx->auth_data, 
 +                                  &proto_ctx->auth_data_len))
 +    {
 +      /* XXX do AUTH_REQUEST resolcing with server */
 +      proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
 +    }
 +
 +  /* Free old protocol as it is finished now */
 +  silc_protocol_free(protocol);
 +  if (ctx->packet)
 +    silc_buffer_free(ctx->packet);
 +  silc_free(ctx);
 +  /* silc_free(ctx->keymat....); */
 +  sock->protocol = NULL;
 +
 +  /* Allocate the authentication protocol. This is allocated here
 +     but we won't start it yet. We will be receiving party of this
 +     protocol thus we will wait that connecting party will make
 +     their first move. */
 +  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, 
 +                    &sock->protocol, (void *)proto_ctx, 
 +                    silc_client_connect_to_server_final);
 +
 +  /* Execute the protocol */
 +  sock->protocol->execute(client->timeout_queue, 0, sock->protocol, fd, 0, 0);
 +}
 +
 +/* Finalizes the connection to the remote SILC server. This is called
 +   after authentication protocol has been completed. This send our
 +   user information to the server to receive our client ID from
 +   server. */
 +
 +SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
 +{
 +  SilcProtocol protocol = (SilcProtocol)context;
 +  SilcClientConnAuthInternalContext *ctx = 
 +    (SilcClientConnAuthInternalContext *)protocol->context;
 +  SilcClient client = (SilcClient)ctx->client;
 +  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
 +  SilcBuffer packet;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
 +    /* Error occured during protocol */
 +    SILC_LOG_DEBUG(("Error during authentication protocol"));
 +    silc_protocol_free(protocol);
 +    if (ctx->auth_data)
 +      silc_free(ctx->auth_data);
 +    if (ctx->ske)
 +      silc_ske_free(ctx->ske);
 +    if (ctx->dest_id)
 +      silc_free(ctx->dest_id);
 +    conn->sock->protocol = NULL;
 +
 +    /* Notify application of failure */
 +    client->ops->connect(client, ctx->sock->user_data, FALSE);
 +    silc_free(ctx);
 +    return;
 +  }
 +
 +  /* Send NEW_CLIENT packet to the server. We will become registered
 +     to the SILC network after sending this packet and we will receive
 +     client ID from the server. */
 +  packet = silc_buffer_alloc(2 + 2 + strlen(client->username) + 
 +                           strlen(client->realname));
 +  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 +  silc_buffer_format(packet,
 +                   SILC_STR_UI_SHORT(strlen(client->username)),
 +                   SILC_STR_UI_XNSTRING(client->username,
 +                                        strlen(client->username)),
 +                   SILC_STR_UI_SHORT(strlen(client->realname)),
 +                   SILC_STR_UI_XNSTRING(client->realname,
 +                                        strlen(client->realname)),
 +                   SILC_STR_END);
 +
 +  /* Send the packet */
 +  silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
 +                        NULL, 0, NULL, NULL, 
 +                        packet->data, packet->len, TRUE);
 +  silc_buffer_free(packet);
 +
 +  /* Save remote ID. */
 +  conn->remote_id = ctx->dest_id;
 +  conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
 +  conn->remote_id_data_len = SILC_ID_SERVER_LEN;
 +
 +  /* Notify application of successful connection */
 +  client->ops->connect(client, conn, TRUE);
 +
 +  silc_protocol_free(protocol);
 +  if (ctx->auth_data)
 +    silc_free(ctx->auth_data);
 +  if (ctx->ske)
 +    silc_ske_free(ctx->ske);
 +  if (ctx->dest_id)
 +    silc_free(ctx->dest_id);
 +  silc_free(ctx);
 +  conn->sock->protocol = NULL;
 +}
 +
 +/* Internal routine that sends packet or marks packet to be sent. This
 +   is used directly only in special cases. Normal cases should use
 +   silc_server_packet_send. Returns < 0 on error. */
 +
 +static int silc_client_packet_send_real(SilcClient client,
 +                                      SilcSocketConnection sock,
 +                                      int force_send)
 +{
 +  int ret;
 +
 +  /* 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_client_packet_process. */
 +  SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(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;
 +}
 +
 +/* Packet processing callback. This is used to send and receive packets
 +   from network. This is generic task. */
 +
 +SILC_TASK_CALLBACK(silc_client_packet_process)
 +{
 +  SilcClient client = (SilcClient)context;
 +  SilcSocketConnection sock = NULL;
 +  SilcClientConnection conn;
 +  int ret;
 +
 +  SILC_LOG_DEBUG(("Processing packet"));
 +
 +  SILC_CLIENT_GET_SOCK(client, fd, sock);
 +  if (sock == NULL)
 +    return;
 +
 +  conn = (SilcClientConnection)sock->user_data;
 +
 +  /* Packet sending */
 +  if (type == SILC_TASK_WRITE) {
 +    SILC_LOG_DEBUG(("Writing data to connection"));
 +
 +    if (sock->outbuf->data - sock->outbuf->head)
 +      silc_buffer_push(sock->outbuf, 
 +                     sock->outbuf->data - sock->outbuf->head);
 +
 +    ret = silc_client_packet_send_real(client, sock, TRUE);
 +
 +    /* If returned -2 could not write to connection now, will do
 +       it later. */
 +    if (ret == -2)
 +      return;
 +    
 +    /* The packet has been sent and now it is time to set the connection
 +       back to only for input. When there is again some outgoing data 
 +       available for this connection it will be set for output as well. 
 +       This call clears the output setting and sets it only for input. */
 +    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(fd);
 +    SILC_UNSET_OUTBUF_PENDING(sock);
 +
 +    silc_buffer_clear(sock->outbuf);
 +    return;
 +  }
 +
 +  /* Packet receiving */
 +  if (type == SILC_TASK_READ) {
 +    SILC_LOG_DEBUG(("Reading data from connection"));
 +
 +    /* Read data from network */
 +    ret = silc_packet_receive(sock);
 +    if (ret < 0)
 +      return;
 +    
 +    /* EOF */
 +    if (ret == 0) {
 +      SILC_LOG_DEBUG(("Read EOF"));
 +
 +      /* If connection is disconnecting already we will finally
 +       close the connection */
 +      if (SILC_IS_DISCONNECTING(sock)) {
 +      client->ops->disconnect(client, conn);
 +      silc_client_close_connection(client, sock);
 +      return;
 +      }
 +      
 +      client->ops->say(client, conn, "Connection closed: premature EOF");
 +      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
 +      client->ops->disconnect(client, conn);
 +      silc_client_close_connection(client, sock);
 +      return;
 +    }
 +
 +    /* Process the packet. This will call the parser that will then
 +       decrypt and parse the packet. */
 +    silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
 +                              silc_client_packet_parse, client);
 +  }
 +}
 +
 +/* Parses whole packet, received earlier. */
 +
 +SILC_TASK_CALLBACK(silc_client_packet_parse_real)
 +{
 +  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
 +  SilcClient client = (SilcClient)parse_ctx->context;
 +  SilcPacketContext *packet = parse_ctx->packet;
 +  SilcBuffer buffer = packet->buffer;
 +  SilcSocketConnection sock = parse_ctx->sock;
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +  int ret;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  /* Decrypt the received packet */
 +  ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet);
 +  if (ret < 0)
 +    goto out;
 +
 +  if (ret == 0) {
 +    /* Parse the packet. Packet type is returned. */
 +    ret = silc_packet_parse(packet);
 +  } else {
 +    /* Parse the packet header in special way as this is "special"
 +       packet type. */
 +    ret = silc_packet_parse_special(packet);
 +  }
 +
 +  if (ret == SILC_PACKET_NONE)
 +    goto out;
 +
 +  /* Parse the incoming packet type */
 +  silc_client_packet_parse_type(client, sock, packet);
 +
 + out:
 +  silc_buffer_clear(buffer);
 +  if (packet->src_id)
 +    silc_free(packet->src_id);
 +  if (packet->dst_id)
 +    silc_free(packet->dst_id);
 +  silc_free(packet);
 +  silc_free(parse_ctx);
 +}
 +
 +/* Parser callback called by silc_packet_receive_process. Thie merely
 +   registers timeout that will handle the actual parsing when appropriate. */
 +
 +void silc_client_packet_parse(SilcPacketParserContext *parser_context)
 +{
 +  SilcClient client = (SilcClient)parser_context->context;
 +
 +  /* Parse the packet */
 +  silc_task_register(client->timeout_queue, parser_context->sock->sock, 
 +                   silc_client_packet_parse_real,
 +                   (void *)parser_context, 0, 1, 
 +                   SILC_TASK_TIMEOUT,
 +                   SILC_TASK_PRI_NORMAL);
 +}
 +  
 +/* Parses the packet type and calls what ever routines the packet type
 +   requires. This is done for all incoming packets. */
 +
 +void silc_client_packet_parse_type(SilcClient client, 
 +                                 SilcSocketConnection sock,
 +                                 SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcPacketType type = packet->type;
 +
 +  SILC_LOG_DEBUG(("Parsing packet type %d", type));
 +
 +  /* Parse the packet type */
 +  switch(type) {
 +  case SILC_PACKET_DISCONNECT:
 +    silc_client_disconnected_by_server(client, sock, buffer);
 +    break;
 +  case SILC_PACKET_SUCCESS:
 +    /*
 +     * Success received for something. For now we can have only
 +     * one protocol for connection executing at once hence this
 +     * success message is for whatever protocol is executing currently.
 +     */
 +    if (sock->protocol) {
 +      sock->protocol->execute(client->timeout_queue, 0,
 +                            sock->protocol, sock->sock, 0, 0);
 +    }
 +    break;
 +  case SILC_PACKET_FAILURE:
 +    /*
 +     * Failure received for some protocol. Set the protocol state to 
 +     * error and call the protocol callback. This fill cause error on
 +     * protocol and it will call the final callback.
 +     */
 +    if (sock->protocol) {
 +      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
 +      sock->protocol->execute(client->timeout_queue, 0,
 +                            sock->protocol, sock->sock, 0, 0);
 +
 +      /* XXX We have only two protocols currently thus we know what this
 +       failure indication is. */
 +      if (buffer->len >= 4) {
 +      unsigned int failure;
 +
 +      SILC_GET32_MSB(failure, buffer->data);
 +
 +      /* Notify application */
 +      client->ops->failure(client, sock->user_data, sock->protocol,
 +                           (void *)failure);
 +      }
 +    }
 +    break;
 +  case SILC_PACKET_REJECT:
 +    break;
 +
 +  case SILC_PACKET_NOTIFY:
 +    /*
 +     * Received notify message 
 +     */
 +    silc_client_notify_by_server(client, sock, packet);
 +    break;
 +
 +  case SILC_PACKET_ERROR:
 +    /*
 +     * Received error message
 +     */
 +    silc_client_error_by_server(client, sock, buffer);
 +    break;
 +
 +  case SILC_PACKET_CHANNEL_MESSAGE:
 +    /*
 +     * Received message to (from, actually) a channel
 +     */
 +    silc_client_channel_message(client, sock, packet);
 +    break;
 +  case SILC_PACKET_CHANNEL_KEY:
 +    /*
 +     * Received key for a channel. By receiving this key the client will be
 +     * able to talk to the channel it has just joined. This can also be
 +     * a new key for existing channel as keys expire peridiocally.
 +     */
 +    silc_client_receive_channel_key(client, sock, buffer);
 +    break;
 +
 +  case SILC_PACKET_PRIVATE_MESSAGE:
 +    /*
 +     * Received private message
 +     */
 +    silc_client_private_message(client, sock, packet);
 +    break;
 +  case SILC_PACKET_PRIVATE_MESSAGE_KEY:
 +    /*
 +     * Received private message key
 +     */
 +    break;
 +
 +  case SILC_PACKET_COMMAND_REPLY:
 +    /*
 +     * Recived reply for a command
 +     */
 +    silc_client_command_reply_process(client, sock, packet);
 +    break;
 +
 +  case SILC_PACKET_KEY_EXCHANGE:
 +    if (sock->protocol) {
 +      SilcClientKEInternalContext *proto_ctx = 
 +      (SilcClientKEInternalContext *)sock->protocol->context;
 +
 +      proto_ctx->packet = buffer;
 +      proto_ctx->dest_id_type = packet->src_id_type;
 +      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
 +
 +      /* Let the protocol handle the packet */
 +      sock->protocol->execute(client->timeout_queue, 0,
 +                            sock->protocol, sock->sock, 0, 0);
 +    } else {
 +      SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
 +                    "protocol active, packet dropped."));
 +
 +      /* XXX Trigger KE protocol?? Rekey actually! */
 +    }
 +    break;
 +
 +  case SILC_PACKET_KEY_EXCHANGE_1:
 +    if (sock->protocol) {
 +
 +    } else {
 +      SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
 +                    "protocol active, packet dropped."));
 +    }
 +    break;
 +  case SILC_PACKET_KEY_EXCHANGE_2:
 +    if (sock->protocol) {
 +      SilcClientKEInternalContext *proto_ctx = 
 +      (SilcClientKEInternalContext *)sock->protocol->context;
 +
 +      if (proto_ctx->packet)
 +      silc_buffer_free(proto_ctx->packet);
 +
 +      proto_ctx->packet = buffer;
 +      proto_ctx->dest_id_type = packet->src_id_type;
 +      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
 +
 +      /* Let the protocol handle the packet */
 +      sock->protocol->execute(client->timeout_queue, 0,
 +                            sock->protocol, sock->sock, 0, 0);
 +    } else {
 +      SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
 +                    "protocol active, packet dropped."));
 +    }
 +    break;
 +
 +  case SILC_PACKET_NEW_ID:
 +    {
 +      /*
 +       * Received new ID from server. This packet is received at
 +       * the connection to the server.  New ID is also received when 
 +       * user changes nickname but in that case the new ID is received
 +       * as command reply and not as this packet type.
 +       */
 +      SilcIDPayload idp;
 +
 +      idp = silc_id_payload_parse(buffer);
 +      if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT)
 +      break;
 +
 +      silc_client_receive_new_id(client, sock, idp);
 +      silc_id_payload_free(idp);
 +      break;
 +    }
 +
 +  default:
 +    SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
 +    break;
 +  }
 +}
 +
 +/* Sends packet. This doesn't actually send the packet instead it assembles
 +   it and marks it to be sent. However, if force_send is TRUE the packet
 +   is sent immediately. if dst_id, cipher and hmac are NULL those parameters
 +   will be derived from sock argument. Otherwise the valid arguments sent
 +   are used. */
 +
 +void silc_client_packet_send(SilcClient client, 
 +                           SilcSocketConnection sock,
 +                           SilcPacketType type, 
 +                           void *dst_id,
 +                           SilcIdType dst_id_type,
 +                           SilcCipher cipher,
 +                           SilcHmac hmac,
 +                           unsigned char *data, 
 +                           unsigned int data_len, 
 +                           int force_send)
 +{
 +  SilcPacketContext packetdata;
 +
 +  SILC_LOG_DEBUG(("Sending packet, type %d", type));
 +
 +  /* Get data used in the packet sending, keys and stuff */
 +  if ((!cipher || !hmac || !dst_id) && sock->user_data) {
 +    if (!cipher && ((SilcClientConnection)sock->user_data)->send_key)
 +      cipher = ((SilcClientConnection)sock->user_data)->send_key;
 +
 +    if (!hmac && ((SilcClientConnection)sock->user_data)->hmac)
 +      hmac = ((SilcClientConnection)sock->user_data)->hmac;
 +
 +    if (!dst_id && ((SilcClientConnection)sock->user_data)->remote_id) {
 +      dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
 +      dst_id_type = SILC_ID_SERVER;
 +    }
 +  }
 +
 +  /* Set the packet context pointers */
 +  packetdata.flags = 0;
 +  packetdata.type = type;
 +  if (((SilcClientConnection)sock->user_data)->local_id_data)
 +    packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
 +  else 
 +    packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
 +  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
 +  packetdata.src_id_type = SILC_ID_CLIENT;
 +  if (dst_id) {
 +    packetdata.dst_id = silc_id_id2str(dst_id, dst_id_type);
 +    packetdata.dst_id_len = silc_id_get_len(dst_id_type);
 +    packetdata.dst_id_type = dst_id_type;
 +  } else {
 +    packetdata.dst_id = NULL;
 +    packetdata.dst_id_len = 0;
 +    packetdata.dst_id_type = SILC_ID_NONE;
 +  }
 +  packetdata.rng = client->rng;
 +  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
 +    packetdata.src_id_len + packetdata.dst_id_len;
 +  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
 +
 +  /* 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);
 +
 +  /* Encrypt the packet */
 +  if (cipher)
 +    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
 +
 +  SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len),
 +                 sock->outbuf->data, sock->outbuf->len);
 +
 +  /* Now actually send the packet */
 +  silc_client_packet_send_real(client, sock, force_send);
 +}
 +
 +/* Sends packet to a channel. Packet to channel is always encrypted
 +   differently from "normal" packets. SILC header of the packet is 
 +   encrypted with the next receiver's key and the rest of the packet is
 +   encrypted with the channel specific key. Padding and HMAC is computed
 +   with the next receiver's key. */
 +
 +void silc_client_packet_send_to_channel(SilcClient client, 
 +                                      SilcSocketConnection sock,
 +                                      SilcChannelEntry channel,
 +                                      unsigned char *data, 
 +                                      unsigned int data_len, 
 +                                      int force_send)
 +{
 +  int i;
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +  SilcBuffer payload;
 +  SilcPacketContext packetdata;
 +  SilcCipher cipher;
 +  SilcHmac hmac;
 +  unsigned char *id_string;
 +
 +  SILC_LOG_DEBUG(("Sending packet to channel"));
 +
 +  if (!channel || !channel->key) {
 +    client->ops->say(client, conn, 
 +                   "Cannot talk to channel: key does not exist");
 +    return;
 +  }
 +
 +  /* Generate IV */
 +  if (!channel->iv)
 +    for (i = 0; i < 16; i++)
 +      channel->iv[i] = silc_rng_get_byte(client->rng);
 +  else
 +    silc_hash_make(client->md5hash, channel->iv, 16, channel->iv);
 +
 +  /* Encode the channel payload */
 +  payload = silc_channel_payload_encode(data_len, data, 16, channel->iv, 
 +                                      client->rng);
 +  if (!payload) {
 +    client->ops->say(client, conn, 
 +                   "Error: Could not create packet to be sent to channel");
 +    return;
 +  }
 +
 +  /* Get data used in packet header encryption, keys and stuff. Rest
 +     of the packet (the payload) is, however, encrypted with the 
 +     specified channel key. */
 +  cipher = conn->send_key;
 +  hmac = conn->hmac;
 +  id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 +
 +  /* Set the packet context pointers. The destination ID is always
 +     the Channel ID of the channel. Server and router will handle the
 +     distribution of the packet. */
 +  packetdata.flags = 0;
 +  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
 +  packetdata.src_id = conn->local_id_data;
 +  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
 +  packetdata.src_id_type = SILC_ID_CLIENT;
 +  packetdata.dst_id = id_string;
 +  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
 +  packetdata.dst_id_type = SILC_ID_CHANNEL;
 +  packetdata.rng = client->rng;
 +  packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
 +    packetdata.src_id_len + packetdata.dst_id_len;
 +  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
 +                                        packetdata.src_id_len +
 +                                        packetdata.dst_id_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,
 +                         payload->len);
 +
 +  packetdata.buffer = sock->outbuf;
 +
 +  /* Encrypt payload of the packet. This is encrypted with the channel key. */
 +  channel->channel_key->cipher->encrypt(channel->channel_key->context,
 +                                      payload->data, payload->data,
 +                                      payload->len - 16, /* -IV_LEN */
 +                                      channel->iv);
 +
 +  /* Put the actual encrypted payload data into the buffer. */
 +  silc_buffer_put(sock->outbuf, payload->data, payload->len);
 +
 +  /* Create the outgoing packet */
 +  silc_packet_assemble(&packetdata);
 +
 +  /* Encrypt the header and padding of the packet. This is encrypted 
 +     with normal session key shared with our server. */
 +  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
 +                    packetdata.src_id_len + packetdata.dst_id_len +
 +                    packetdata.padlen);
 +
 +  SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
 +                 sock->outbuf->data, sock->outbuf->len);
 +
 +  /* Now actually send the packet */
 +  silc_client_packet_send_real(client, sock, force_send);
 +  silc_buffer_free(payload);
 +  silc_free(id_string);
 +}
 +
 +/* Sends private message to remote client. If private message key has
 +   not been set with this client then the message will be encrypted using
 +   normal session keys. Private messages are special packets in SILC
 +   network hence we need this own function for them. This is similiar
 +   to silc_client_packet_send_to_channel except that we send private
 +   message. */
 +
 +void silc_client_packet_send_private_message(SilcClient client,
 +                                           SilcSocketConnection sock,
 +                                           SilcClientEntry client_entry,
 +                                           unsigned char *data, 
 +                                           unsigned int data_len, 
 +                                           int force_send)
 +{
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +  SilcBuffer buffer;
 +  SilcPacketContext packetdata;
 +  unsigned int nick_len;
 +  SilcCipher cipher;
 +  SilcHmac hmac;
 +
 +  SILC_LOG_DEBUG(("Sending private message"));
 +
 +  /* Create private message payload */
 +  nick_len = strlen(conn->nickname);
 +  buffer = silc_buffer_alloc(2 + nick_len + data_len);
 +  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
 +  silc_buffer_format(buffer,
 +                   SILC_STR_UI_SHORT(nick_len),
 +                   SILC_STR_UI_XNSTRING(conn->nickname,
 +                                        nick_len),
 +                   SILC_STR_UI_XNSTRING(data, data_len),
 +                   SILC_STR_END);
 +
 +  /* If we don't have private message specific key then private messages
 +     are just as any normal packet thus call normal packet sending.  If
 +     the key exist then the encryption process is a bit different and
 +     will be done in the rest of this function. */
 +  if (!client_entry->send_key) {
 +    silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
 +                          client_entry->id, SILC_ID_CLIENT, NULL, NULL,
 +                          buffer->data, buffer->len, force_send);
 +    goto out;
 +  }
 +
 +  /* We have private message specific key */
 +
 +  /* Get data used in the encryption */
 +  cipher = client_entry->send_key;
 +  hmac = conn->hmac;
 +
 +  /* Set the packet context pointers. */
 +  packetdata.flags = 0;
 +  packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
 +  packetdata.src_id = conn->local_id_data;
 +  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
 +  packetdata.src_id_type = SILC_ID_CLIENT;
 +  if (client_entry)
 +    packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
 +  else
 +    packetdata.dst_id = conn->local_id_data;
 +  packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
 +  packetdata.dst_id_type = SILC_ID_CLIENT;
 +  packetdata.rng = client->rng;
 +  packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
 +    packetdata.src_id_len + packetdata.dst_id_len;
 +  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
 +                                        packetdata.src_id_len +
 +                                        packetdata.dst_id_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,
 +                         buffer->len);
 +  
 +  packetdata.buffer = sock->outbuf;
 +
 +  /* Encrypt payload of the packet. Encrypt with private message specific
 +     key if it exist, otherwise with session key. */
 +  cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
 +                        buffer->len, cipher->iv);
 +      
 +  /* Put the actual encrypted payload data into the buffer. */
 +  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
 +
 +  /* Create the outgoing packet */
 +  silc_packet_assemble(&packetdata);
 +
 +  /* Encrypt the header and padding of the packet. */
 +  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
 +                    packetdata.src_id_len + packetdata.dst_id_len +
 +                    packetdata.padlen);
 +
 +  SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
 +                 sock->outbuf->data, sock->outbuf->len);
 +
 +  /* Now actually send the packet */
 +  silc_client_packet_send_real(client, sock, force_send);
 +  silc_free(packetdata.dst_id);
 +
 + out:
 +  silc_free(buffer);
 +}     
 +
 +/* Closes connection to remote end. Free's all allocated data except
 +   for some information such as nickname etc. that are valid at all time. */
 +
 +void silc_client_close_connection(SilcClient client,
 +                                SilcSocketConnection sock)
 +{
 +  SilcClientConnection conn;
 +
 +  /* We won't listen for this connection anymore */
 +  silc_schedule_unset_listen_fd(sock->sock);
 +
 +  /* Unregister all tasks */
 +  silc_task_unregister_by_fd(client->io_queue, sock->sock);
 +  silc_task_unregister_by_fd(client->timeout_queue, sock->sock);
 +
 +  /* Close the actual connection */
 +  silc_net_close_connection(sock->sock);
 +
 +  client->ops->say(client, sock->user_data,
 +                 "Closed connection to host %s", sock->hostname ?
 +                 sock->hostname : sock->ip);
 +
 +  /* Free everything */
 +  if (sock->user_data) {
 +    conn = (SilcClientConnection)sock->user_data;
 +
 +    /* XXX Free all client entries and channel entries. */
 +
 +    /* Clear ID caches */
 +    silc_idcache_del_all(conn->client_cache);
 +    silc_idcache_del_all(conn->channel_cache);
 +
 +    /* Free data */
 +    if (conn->remote_host)
 +      silc_free(conn->remote_host);
 +    if (conn->local_id)
 +      silc_free(conn->local_id);
 +    if (conn->local_id_data)
 +      silc_free(conn->local_id_data);
 +    if (conn->send_key)
 +      silc_cipher_free(conn->send_key);
 +    if (conn->receive_key)
 +      silc_cipher_free(conn->receive_key);
 +    if (conn->hmac)
 +      silc_hmac_free(conn->hmac);
 +    if (conn->hmac_key) {
 +      memset(conn->hmac_key, 0, conn->hmac_key_len);
 +      silc_free(conn->hmac_key);
 +    }
 +
 +    conn->sock = NULL;
 +    conn->remote_port = 0;
 +    conn->remote_type = 0;
 +    conn->send_key = NULL;
 +    conn->receive_key = NULL;
 +    conn->hmac = NULL;
 +    conn->hmac_key = NULL;
 +    conn->hmac_key_len = 0;
 +    conn->local_id = NULL;
 +    conn->local_id_data = NULL;
 +    conn->remote_host = NULL;
 +    conn->current_channel = NULL;
 +
 +    silc_client_del_connection(client, conn);
 +  }
 +
 +  if (sock->protocol) {
 +    silc_protocol_free(sock->protocol);
 +    sock->protocol = NULL;
 +  }
 +  silc_socket_free(sock);
 +}
 +
 +/* Called when we receive disconnection packet from server. This 
 +   closes our end properly and displays the reason of the disconnection
 +   on the screen. */
 +
 +void silc_client_disconnected_by_server(SilcClient client,
 +                                      SilcSocketConnection sock,
 +                                      SilcBuffer message)
 +{
 +  char *msg;
 +
 +  SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
 +
 +  msg = silc_calloc(message->len + 1, sizeof(char));
 +  memcpy(msg, message->data, message->len);
 +  client->ops->say(client, sock->user_data, msg);
 +  silc_free(msg);
 +
 +  SILC_SET_DISCONNECTED(sock);
 +  silc_client_close_connection(client, sock);
 +}
 +
 +/* Received error message from server. Display it on the screen. 
 +   We don't take any action what so ever of the error message. */
 +
 +void silc_client_error_by_server(SilcClient client,
 +                               SilcSocketConnection sock,
 +                               SilcBuffer message)
 +{
 +  char *msg;
 +
 +  msg = silc_calloc(message->len + 1, sizeof(char));
 +  memcpy(msg, message->data, message->len);
 +  client->ops->say(client, sock->user_data, msg);
 +  silc_free(msg);
 +}
 +
 +/* Called when notify is received and some async operation (such as command)
 +   is required before processing the notify message. This calls again the
 +   silc_client_notify_by_server and reprocesses the original notify packet. */
 +
 +static void silc_client_notify_by_server_pending(void *context)
 +{
 +  SilcPacketContext *p = (SilcPacketContext *)context;
 +  silc_client_notify_by_server(p->context, p->sock, p);
 +  if (p->src_id)
 +    silc_free(p->src_id);
 +  if (p->dst_id)
 +    silc_free(p->dst_id);
 +  silc_buffer_free(p->buffer);
 +  silc_free(p);
 +}
 +
 +/* Received notify message from server */
 +
 +void silc_client_notify_by_server(SilcClient client,
 +                                SilcSocketConnection sock,
 +                                SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +  SilcNotifyPayload payload;
 +  SilcNotifyType type;
 +  SilcArgumentPayload args;
 +  int i;
 +
 +  SilcClientID *client_id = NULL;
 +  SilcChannelID *channel_id = NULL;
 +  SilcClientEntry client_entry;
 +  SilcClientEntry client_entry2;
 +  SilcChannelEntry channel;
 +  SilcIDCacheEntry id_cache = NULL;
 +  unsigned char *tmp;
 +  unsigned int tmp_len, mode;
 +
 +  payload = silc_notify_payload_parse(buffer);
 +  type = silc_notify_get_type(payload);
 +  args = silc_notify_get_args(payload);
 +  if (!args)
 +    goto out;
 +
 +  switch(type) {
 +  case SILC_NOTIFY_TYPE_NONE:
 +    /* Notify application */
 +    client->ops->notify(client, conn, type, 
 +                      silc_argument_get_arg_type(args, 1, NULL));
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_INVITE:
 +    /* 
 +     * Someone invited me to a channel. Find Client and Channel entries
 +     * for the application.
 +     */
 +    
 +    /* 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);
 +
 +    /* Find Client entry and if not found query it */
 +    client_entry = silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
 +    if (!client_entry) {
 +      SilcPacketContext *p = silc_packet_context_dup(packet);
 +      p->context = (void *)client;
 +      p->sock = sock;
 +      silc_client_command_pending(SILC_COMMAND_WHOIS, 
 +                                silc_client_notify_by_server_pending, p);
 +      goto out;
 +    }
 +
 +    /* 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);
 +
 +    /* XXX Will ALWAYS fail because currently we don't have way to resolve
 +       channel information for channel that we're not joined to. */
 +    /* XXX ways to fix: use (extended) LIST command, or define the channel
 +       name to the notfy type when name resolving is not mandatory. */
 +    /* Find channel entry */
 +    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
 +                                   SILC_ID_CHANNEL, &id_cache))
 +      goto out;
 +
 +    channel = (SilcChannelEntry)id_cache->context;
 +
 +    /* Notify application */
 +    client->ops->notify(client, conn, type, client_entry, channel);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_JOIN:
 +    /*
 +     * Someone has joined to a channel. Get their ID and nickname and
 +     * cache them for later use.
 +     */
 +
 +    /* 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);
 +
 +    /* Find Client entry and if not found query it */
 +    client_entry = silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
 +    if (!client_entry) {
 +      SilcPacketContext *p = silc_packet_context_dup(packet);
 +      p->context = (void *)client;
 +      p->sock = sock;
 +      silc_client_command_pending(SILC_COMMAND_WHOIS, 
 +                                silc_client_notify_by_server_pending, p);
 +      goto out;
 +    }
 +
 +    /* Get channel entry */
 +    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
 +    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
 +                                   SILC_ID_CHANNEL, &id_cache))
 +      break;
 +
 +    channel = (SilcChannelEntry)id_cache->context;
 +
 +    /* Add client to channel */
 +    for (i = 0; i < channel->clients_count; i++) {
 +      if (channel->clients[i].client == NULL) {
 +      channel->clients[channel->clients_count].client = client_entry;
 +      channel->clients_count++;
 +      break;
 +      }
 +    }
 +
 +    if (i == channel->clients_count) {
 +      channel->clients = silc_realloc(channel->clients, 
 +                                    sizeof(*channel->clients) * 
 +                                    (channel->clients_count + 1));
 +      channel->clients[channel->clients_count].client = client_entry;
 +      channel->clients[channel->clients_count].mode = 0;
 +      channel->clients_count++;
 +    }
 +
 +    /* XXX add support for multiple same nicks on same channel. Check
 +       for them here */
 +
 +    /* Notify application. The channel entry is sent last as this notify
 +       is for channel but application don't know it from the arguments
 +       sent by server. */
 +    client->ops->notify(client, conn, type, client_entry, channel);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_LEAVE:
 +    /*
 +     * Someone has left a channel. We will remove it from the channel but
 +     * we'll keep it in the cache in case we'll need it later.
 +     */
 +    
 +    /* 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);
 +
 +    /* Find Client entry */
 +    client_entry = 
 +      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
 +    if (!client_entry)
 +      goto out;
 +
 +    /* Get channel entry */
 +    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
 +    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
 +                                   SILC_ID_CHANNEL, &id_cache))
 +      break;
 +
 +    channel = (SilcChannelEntry)id_cache->context;
 +
 +    /* Remove client from channel */
 +    for (i = 0; i < channel->clients_count; i++) {
 +      if (channel->clients[i].client == client_entry) {
 +      channel->clients[i].client = NULL;
 +      channel->clients_count--;
 +      break;
 +      }
 +    }
 +
 +    /* Notify application. The channel entry is sent last as this notify
 +       is for channel but application don't know it from the arguments
 +       sent by server. */
 +    client->ops->notify(client, conn, type, client_entry, channel);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_SIGNOFF:
 +    /*
 +     * Someone left SILC. We'll remove it from all channels and from cache.
 +     */
 +
 +    /* 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);
 +
 +    /* Find Client entry */
 +    client_entry = 
 +      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
 +    if (!client_entry)
 +      goto out;
 +
 +    /* Remove from all channels */
 +    silc_client_remove_from_channels(client, conn, client_entry);
 +
 +    /* Remove from cache */
 +    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
 +                         client_entry->id);
 +
 +    /* Notify application */
 +    client->ops->notify(client, conn, type, client_entry);
 +
 +    /* Free data */
 +    if (client_entry->nickname)
 +      silc_free(client_entry->nickname);
 +    if (client_entry->server)
 +      silc_free(client_entry->server);
 +    if (client_entry->id)
 +      silc_free(client_entry->id);
 +    if (client_entry->send_key)
 +      silc_cipher_free(client_entry->send_key);
 +    if (client_entry->receive_key)
 +      silc_cipher_free(client_entry->receive_key);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_TOPIC_SET:
 +    /*
 +     * Someone set the topic on a channel.
 +     */
 +
 +    /* 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);
 +
 +    /* Find Client entry */
 +    client_entry = 
 +      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
 +    if (!client_entry)
 +      goto out;
 +
 +    /* Get topic */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +
 +    /* Get channel entry */
 +    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
 +    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
 +                                   SILC_ID_CHANNEL, &id_cache))
 +      break;
 +
 +    channel = (SilcChannelEntry)id_cache->context;
 +
 +    /* Notify application. The channel entry is sent last as this notify
 +       is for channel but application don't know it from the arguments
 +       sent by server. */
 +    client->ops->notify(client, conn, type, client_entry, tmp, channel);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_NICK_CHANGE:
 +    /*
 +     * Someone changed their nickname. If we don't have entry for the new
 +     * ID we will query it and return here after it's done. After we've
 +     * returned we fetch the old entry and free it and notify the 
 +     * application.
 +     */
 +
 +    /* Get new Client ID */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +
 +    client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +
 +    /* Ignore my ID */
 +    if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
 +      break;
 +
 +    /* Find Client entry and if not found query it */
 +    client_entry2 = 
 +      silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
 +    if (!client_entry2) {
 +      SilcPacketContext *p = silc_packet_context_dup(packet);
 +      p->context = (void *)client;
 +      p->sock = sock;
 +      silc_client_command_pending(SILC_COMMAND_WHOIS, 
 +                                silc_client_notify_by_server_pending, p);
 +      goto out;
 +    }
 +
 +    /* Get old 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);
 +
 +    /* Find old Client entry */
 +    client_entry = 
 +      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
 +    if (!client_entry)
 +      goto out;
 +
 +    /* Remove the old from cache */
 +    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
 +                         client_entry->id);
 +
 +    /* Replace old ID entry with new one on all channels. */
 +    silc_client_replace_from_channels(client, conn, client_entry,
 +                                    client_entry2);
 +
 +    /* Notify application */
 +    client->ops->notify(client, conn, type, client_entry, client_entry2);
 +
 +    /* Free data */
 +    if (client_entry->nickname)
 +      silc_free(client_entry->nickname);
 +    if (client_entry->server)
 +      silc_free(client_entry->server);
 +    if (client_entry->id)
 +      silc_free(client_entry->id);
 +    if (client_entry->send_key)
 +      silc_cipher_free(client_entry->send_key);
 +    if (client_entry->receive_key)
 +      silc_cipher_free(client_entry->receive_key);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
 +    /*
 +     * Someone changed a channel mode
 +     */
 +
 +    /* 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);
 +
 +    /* Find Client entry */
 +    client_entry = 
 +      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
 +    if (!client_entry)
 +      goto out;
 +
 +    /* Get the mode */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +
 +    SILC_GET32_MSB(mode, tmp);
 +
 +    /* Get channel entry */
 +    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
 +    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
 +                                   SILC_ID_CHANNEL, &id_cache))
 +      break;
 +
 +    channel = (SilcChannelEntry)id_cache->context;
 +
 +    /* Save the new mode */
 +    channel->mode = mode;
 +
 +    /* Notify application. The channel entry is sent last as this notify
 +       is for channel but application don't know it from the arguments
 +       sent by server. */
 +    client->ops->notify(client, conn, type, client_entry, mode, channel);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
 +    /*
 +     * Someone changed user's mode on a channel
 +     */
 +
 +    /* 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);
 +
 +    /* Find Client entry */
 +    client_entry = 
 +      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
 +    if (!client_entry)
 +      goto out;
 +
 +    /* Get the mode */
 +    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +
 +    SILC_GET32_MSB(mode, tmp);
 +
 +    /* Get target Client ID */
 +    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +
 +    silc_free(client_id);
 +    client_id = silc_id_payload_parse_id(tmp, tmp_len);
 +
 +    /* Find target Client entry */
 +    client_entry2 = 
 +      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
 +    if (!client_entry2)
 +      goto out;
 +
 +    /* Get channel entry */
 +    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
 +    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
 +                                   SILC_ID_CHANNEL, &id_cache))
 +      break;
 +
 +    channel = (SilcChannelEntry)id_cache->context;
 +
 +    /* Save the mode */
 +    for (i = 0; i < channel->clients_count; i++) {
 +      if (channel->clients[i].client == client_entry2) {
 +      channel->clients[i].mode = mode;
 +      break;
 +      }
 +    }
 +
 +    /* Notify application. The channel entry is sent last as this notify
 +       is for channel but application don't know it from the arguments
 +       sent by server. */
 +    client->ops->notify(client, conn, type, client_entry, mode, 
 +                      client_entry2, channel);
 +    break;
 +
 +  case SILC_NOTIFY_TYPE_MOTD:
 +    /*
 +     * Received Message of the day
 +     */
 +
 +    /* Get motd */
 +    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
 +    if (!tmp)
 +      goto out;
 +    
 +    /* Notify application */
 +    client->ops->notify(client, conn, type, tmp);
 +    break;
 +    
 +  default:
 +    break;
 +  }
 +
 + out:
 +  silc_notify_payload_free(payload);
 +  if (client_id)
 +    silc_free(client_id);
 +  if (channel_id)
 +    silc_free(channel_id);
 +}
 +
 +/* Processes the received new Client ID from server. Old Client ID is
 +   deleted from cache and new one is added. */
 +
 +void silc_client_receive_new_id(SilcClient client,
 +                              SilcSocketConnection sock,
 +                              SilcIDPayload idp)
 +{
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +
 +  /* Delete old ID from ID cache */
 +  silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
 +  
 +  /* Save the new ID */
 +  if (conn->local_id)
 +    silc_free(conn->local_id);
 +  if (conn->local_id_data)
 +    silc_free(conn->local_id_data);
 +
 +  conn->local_id = silc_id_payload_get_id(idp);
 +  conn->local_id_data = silc_id_payload_get_data(idp);
 +  conn->local_id_data_len = silc_id_payload_get_len(idp);;
 +
 +  if (!conn->local_entry)
 +    conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
 +
 +  conn->local_entry->nickname = conn->nickname;
 +  if (!conn->local_entry->username) {
 +    conn->local_entry->username = 
 +      silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
 +                sizeof(conn->local_entry->username));
 +    sprintf(conn->local_entry->username, "%s@%s", client->username,
 +          client->hostname);
 +  }
 +  conn->local_entry->id = conn->local_id;
 +  
 +  /* Put it to the ID cache */
 +  silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
 +                 conn->local_id, (void *)conn->local_entry, TRUE);
 +}
 +
 +/* Processed received Channel ID for a channel. This is called when client
 +   joins to channel and server replies with channel ID. The ID is cached. */
 +
 +void silc_client_new_channel_id(SilcClient client,
 +                              SilcSocketConnection sock,
 +                              char *channel_name,
 +                              unsigned int mode, SilcIDPayload idp)
 +{
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +  SilcChannelEntry channel;
 +
 +  SILC_LOG_DEBUG(("New channel ID"));
 +
 +  channel = silc_calloc(1, sizeof(*channel));
 +  channel->channel_name = channel_name;
 +  channel->id = silc_id_payload_get_id(idp);
 +  channel->mode = mode;
 +  conn->current_channel = channel;
 +
 +  /* Put it to the ID cache */
 +  silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
 +                 (void *)channel->id, (void *)channel, TRUE);
 +}
 +
 +/* Processes received key for channel. The received key will be used
 +   to protect the traffic on the channel for now on. Client must receive
 +   the key to the channel before talking on the channel is possible. 
 +   This is the key that server has generated, this is not the channel
 +   private key, it is entirely local setting. */
 +
 +void silc_client_receive_channel_key(SilcClient client,
 +                                   SilcSocketConnection sock,
 +                                   SilcBuffer packet)
 +{
 +  unsigned char *id_string, *key, *cipher;
 +  unsigned int tmp_len;
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +  SilcChannelID *id;
 +  SilcIDCacheEntry id_cache = NULL;
 +  SilcChannelEntry channel;
 +  SilcChannelKeyPayload payload;
 +
 +  SILC_LOG_DEBUG(("Received key for channel"));
 +  
 +  payload = silc_channel_key_payload_parse(packet);
 +  if (!payload)
 +    return;
 +
 +  id_string = silc_channel_key_get_id(payload, &tmp_len);
 +  if (!id_string) {
 +    silc_channel_key_payload_free(payload);
 +    return;
 +  }
 +  id = silc_id_payload_parse_id(id_string, tmp_len);
 +
 +  /* Find channel. */
 +  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
 +                                 SILC_ID_CHANNEL, &id_cache))
 +    goto out;
 +    
 +  /* Save the key */
 +  key = silc_channel_key_get_key(payload, &tmp_len);
 +  cipher = silc_channel_key_get_cipher(payload, NULL);
 +
 +  channel = (SilcChannelEntry)id_cache->context;
 +  channel->key_len = tmp_len;
 +  channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
 +  memcpy(channel->key, key, tmp_len);
 +
 +  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
 +    client->ops->say(client, conn,
 +                   "Cannot talk to channel: unsupported cipher %s", cipher);
 +    goto out;
 +  }
 +  channel->channel_key->cipher->set_key(channel->channel_key->context, 
 +                                      key, tmp_len);
 +
 +  /* Client is now joined to the channel */
 +  channel->on_channel = TRUE;
 +
 + out:
 +  silc_free(id);
 +  silc_channel_key_payload_free(payload);
 +}
 +
 +/* Process received message to a channel (or from a channel, really). This
 +   decrypts the channel message with channel specific key and parses the
 +   channel payload. Finally it displays the message on the screen. */
 +
 +void silc_client_channel_message(SilcClient client, 
 +                               SilcSocketConnection sock, 
 +                               SilcPacketContext *packet)
 +{
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +  SilcBuffer buffer = packet->buffer;
 +  SilcChannelPayload payload = NULL;
 +  SilcChannelID *id = NULL;
 +  SilcChannelEntry channel;
 +  SilcIDCacheEntry id_cache = NULL;
 +  SilcClientID *client_id = NULL;
 +  int i;
 +  char *nickname;
 +
 +  /* Sanity checks */
 +  if (packet->dst_id_type != SILC_ID_CHANNEL)
 +    goto out;
 +
 +  client_id = silc_id_str2id(packet->src_id, SILC_ID_CLIENT);
 +  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
 +
 +  /* Find the channel entry from channels on this connection */
 +  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
 +                                 SILC_ID_CHANNEL, &id_cache))
 +    goto out;
 +
 +  channel = (SilcChannelEntry)id_cache->context;
 +
 +  /* Decrypt the channel message payload. Push the IV out of the way,
 +     since it is not encrypted (after pushing buffer->tail has the IV). */
 +  silc_buffer_push_tail(buffer, 16);
 +  channel->channel_key->cipher->decrypt(channel->channel_key->context,
 +                                      buffer->data, buffer->data,
 +                                      buffer->len, buffer->tail);
 +  silc_buffer_pull_tail(buffer, 16);
 +
 +  /* Parse the channel message payload */
 +  payload = silc_channel_payload_parse(buffer);
 +  if (!payload)
 +    goto out;
 +
 +  /* Find nickname */
 +  nickname = "[unknown]";
 +  for (i = 0; i < channel->clients_count; i++) {
 +    if (channel->clients[i].client && 
 +      !SILC_ID_CLIENT_COMPARE(channel->clients[i].client->id, client_id))
 +      nickname = channel->clients[i].client->nickname;
 +  }
 +
 +  /* Pass the message to application */
 +  client->ops->channel_message(client, conn, nickname,
 +                             channel->channel_name,
 +                             silc_channel_get_data(payload, NULL));
 +
 + out:
 +  if (id)
 +    silc_free(id);
 +  if (client_id)
 +    silc_free(client_id);
 +  if (payload)
 +    silc_channel_payload_free(payload);
 +}
 +
 +/* Private message received. This processes the private message and
 +   finally displays it on the screen. */
 +
 +void silc_client_private_message(SilcClient client, 
 +                               SilcSocketConnection sock, 
 +                               SilcPacketContext *packet)
 +{
 +  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 +  SilcBuffer buffer = packet->buffer;
 +  unsigned short nick_len;
 +  unsigned char *nickname, *message;
 +
 +  /* Get nickname */
 +  silc_buffer_unformat(buffer, 
 +                     SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
 +                     SILC_STR_END);
 +  silc_buffer_pull(buffer, 2 + nick_len);
 +     
 +  message = silc_calloc(buffer->len + 1, sizeof(char));
 +  memcpy(message, buffer->data, buffer->len);
 +
 +  /* Pass the private message to application */
 +  client->ops->private_message(client, conn, nickname, message);
 +
 +  /* See if we are away (gone). If we are away we will reply to the
 +     sender with the set away message. */
 +  if (conn->away && conn->away->away) {
 +    SilcClientID *remote_id;
 +    SilcClientEntry remote_client;
 +    SilcIDCacheEntry id_cache;
 +
 +    if (packet->src_id_type != SILC_ID_CLIENT)
 +      goto out;
 +
 +    remote_id = silc_id_str2id(packet->src_id, SILC_ID_CLIENT);
 +    if (!remote_id)
 +      goto out;
 +
 +    /* If it's me, ignore */
 +    if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
 +      goto out;
 +
 +    /* Check whether we know this client already */
 +    if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
 +                                   SILC_ID_CLIENT, &id_cache))
 +      {
 +      /* Allocate client entry */
 +      remote_client = silc_calloc(1, sizeof(*remote_client));
 +      remote_client->id = remote_id;
 +      silc_parse_nickname(nickname, &remote_client->nickname, 
 +                          &remote_client->server, &remote_client->num);
 +
 +      /* Save the client to cache */
 +      silc_idcache_add(conn->client_cache, remote_client->nickname,
 +                       SILC_ID_CLIENT, remote_client->id, remote_client, 
 +                       TRUE);
 +      } else {
 +      silc_free(remote_id);
 +      remote_client = (SilcClientEntry)id_cache->context;
 +      }
 +
 +    /* Send the away message */
 +    silc_client_packet_send_private_message(client, sock, remote_client,
 +                                          conn->away->away,
 +                                          strlen(conn->away->away), TRUE);
 +  }
 +
 + out:
 +  memset(message, 0, buffer->len);
 +  silc_free(message);
 +  silc_free(nickname);
 +}
 +
 +/* Removes a client entry from all channel it has joined. This really is
 +   a performance killer (client_entry should have pointers to channel 
 +   entry list). */
 +
 +void silc_client_remove_from_channels(SilcClient client,
 +                                    SilcClientConnection conn,
 +                                    SilcClientEntry client_entry)
 +{
 +  SilcIDCacheEntry id_cache;
 +  SilcIDCacheList list;
 +  SilcChannelEntry channel;
 +  int i;
 +
 +  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
 +                             SILC_ID_CHANNEL, &list))
 +    return;
 +
 +  silc_idcache_list_first(list, &id_cache);
 +  channel = (SilcChannelEntry)id_cache->context;
 +  
 +  while (channel) {
 +    
 +    /* Remove client from channel */
 +    for (i = 0; i < channel->clients_count; i++) {
 +      if (channel->clients[i].client == client_entry) {
 +      channel->clients[i].client = NULL;
 +      channel->clients_count--;
 +      break;
 +      }
 +    }
 +
 +    if (!silc_idcache_list_next(list, &id_cache))
 +      break;
 +    
 +    channel = (SilcChannelEntry)id_cache->context;
 +  }
 +
 +  silc_idcache_list_free(list);
 +}
 +
 +/* Replaces `old' client entries from all channels to `new' client entry.
 +   This can be called for example when nickname changes and old ID entry
 +   is replaced from ID cache with the new one. If the old ID entry is only
 +   updated, then this fucntion needs not to be called. */
 +
 +void silc_client_replace_from_channels(SilcClient client, 
 +                                     SilcClientConnection conn,
 +                                     SilcClientEntry old,
 +                                     SilcClientEntry new)
 +{
 +  SilcIDCacheEntry id_cache;
 +  SilcIDCacheList list;
 +  SilcChannelEntry channel;
 +  int i;
 +
 +  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
 +                             SILC_ID_CHANNEL, &list))
 +    return;
 +
 +  silc_idcache_list_first(list, &id_cache);
 +  channel = (SilcChannelEntry)id_cache->context;
 +  
 +  while (channel) {
 +    
 +    /* Remove client from channel */
 +    for (i = 0; i < channel->clients_count; i++) {
 +      if (channel->clients[i].client == old) {
 +      channel->clients[i].client = new;
 +      break;
 +      }
 +    }
 +
 +    if (!silc_idcache_list_next(list, &id_cache))
 +      break;
 +    
 +    channel = (SilcChannelEntry)id_cache->context;
 +  }
 +
 +  silc_idcache_list_free(list);
 +}
++
++/* Parses mode mask and returns the mode as string. */
++
++char *silc_client_chmode(unsigned int mode)
++{
++  char string[20];
++
++  if (!mode)
++    return NULL;
++
++  memset(string, 0, sizeof(string));
++
++  if (mode & SILC_CHANNEL_MODE_PRIVATE)
++    strncat(string, "p", 1);
++
++  if (mode & SILC_CHANNEL_MODE_SECRET)
++    strncat(string, "s", 1);
++
++  if (mode & SILC_CHANNEL_MODE_PRIVKEY)
++    strncat(string, "k", 1);
++
++  if (mode & SILC_CHANNEL_MODE_INVITE)
++    strncat(string, "i", 1);
++
++  if (mode & SILC_CHANNEL_MODE_TOPIC)
++    strncat(string, "t", 1);
++
++  if (mode & SILC_CHANNEL_MODE_ULIMIT)
++    strncat(string, "l", 1);
++
++  if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
++    strncat(string, "a", 1);
++
++  /* Rest of mode is ignored */
++
++  return strdup(string);
++}
++
++/* Parses channel user mode mask and returns te mode as string */
++
++char *silc_client_chumode(unsigned int mode)
++{
++  char string[4];
++
++  if (!mode)
++    return NULL;
++
++  memset(string, 0, sizeof(string));
++
++  if (mode & SILC_CHANNEL_UMODE_CHANFO)
++    strncat(string, "f", 1);
++
++  if (mode & SILC_CHANNEL_UMODE_CHANOP)
++    strncat(string, "o", 1);
++
++  return strdup(string);
++}
++
++/* Parses channel user mode and returns it as special mode character. */
++
++char *silc_client_chumode_char(unsigned int mode)
++{
++  char string[4];
++
++  if (!mode)
++    return NULL;
++
++  memset(string, 0, sizeof(string));
++
++  if (mode & SILC_CHANNEL_UMODE_CHANFO)
++    strncat(string, "*", 1);
++
++  if (mode & SILC_CHANNEL_UMODE_CHANOP)
++    strncat(string, "@", 1);
++
++  return strdup(string);
++}
index 84cfa3244dea8770a53de4cd7fa66bdaf2bc6946,661bd83a481604aad891dc11bc2d0263043af5ca..fdff610eee9e0ae4bcafa828d3540eeeafe0e81a
@@@ -278,14 -246,4 +278,18 @@@ void silc_client_receive_channel_key(Si
  void silc_client_channel_message(SilcClient client, 
                                 SilcSocketConnection sock, 
                                 SilcPacketContext *packet);
 +void silc_client_private_message(SilcClient client, 
 +                               SilcSocketConnection sock, 
 +                               SilcPacketContext *packet);
 +void silc_client_remove_from_channels(SilcClient client,
 +                                    SilcClientConnection conn,
 +                                    SilcClientEntry client_entry);
 +void silc_client_replace_from_channels(SilcClient client, 
 +                                     SilcClientConnection conn,
 +                                     SilcClientEntry old,
 +                                     SilcClientEntry new);
++char *silc_client_chmode(unsigned int mode);
++char *silc_client_chumode(unsigned int mode);
++char *silc_client_chumode_char(unsigned int mode);
++
  #endif
index 5ed0a3b1fe9935d3003ecd6ece3c66927e179ce0,0000000000000000000000000000000000000000..8b2f0c35273793f87cb2f0e0451fe0931b9ce8b1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1136 -1,0 +1,1145 @@@
-       snprintf(t, sizeof(t), " [%d]", c++);
-       strncat(channel->clients[k].client->nickname, t, strlen(t));
 +/*
 +
 +  command_reply.c
 +
 +  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 +
 +  Copyright (C) 1997 - 2000 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.
 +
 +*/
 +/*
 + * Command reply functions are "the otherside" of the command functions.
 + * Reply to a command sent by server is handled by these functions.
 + *
 + * The arguments received from server are also passed to the calling
 + * application through command_reply client operation.  The arguments are
 + * exactly same and in same order as the server sent it.  However, ID's are
 + * not sent to the application.  Instead, corresponding ID entry is sent
 + * to the application.  For example, instead of sending Client ID the 
 + * corresponding SilcClientEntry is sent to the application.  The case is
 + * same with for example Channel ID's.  This way application has all the
 + * necessary data already in hand without redundant searching.  If ID is
 + * received but ID entry does not exist, NULL is sent.
 + */
 +/* $Id$ */
 +
 +#include "clientlibincludes.h"
 +
 +/* Client command reply list. */
 +SilcClientCommandReply silc_command_reply_list[] =
 +{
 +  SILC_CLIENT_CMD_REPLY(whois, WHOIS),
 +  SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
 +  SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
 +  SILC_CLIENT_CMD_REPLY(nick, NICK),
 +  SILC_CLIENT_CMD_REPLY(list, LIST),
 +  SILC_CLIENT_CMD_REPLY(topic, TOPIC),
 +  SILC_CLIENT_CMD_REPLY(invite, INVITE),
 +  SILC_CLIENT_CMD_REPLY(quit, QUIT),
 +  SILC_CLIENT_CMD_REPLY(kill, KILL),
 +  SILC_CLIENT_CMD_REPLY(info, INFO),
 +  SILC_CLIENT_CMD_REPLY(connect, CONNECT),
 +  SILC_CLIENT_CMD_REPLY(ping, PING),
 +  SILC_CLIENT_CMD_REPLY(oper, OPER),
 +  SILC_CLIENT_CMD_REPLY(join, JOIN),
 +  SILC_CLIENT_CMD_REPLY(motd, MOTD),
 +  SILC_CLIENT_CMD_REPLY(umode, UMODE),
 +  SILC_CLIENT_CMD_REPLY(cmode, CMODE),
 +  SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
 +  SILC_CLIENT_CMD_REPLY(kick, KICK),
 +  SILC_CLIENT_CMD_REPLY(restart, RESTART),
 +  SILC_CLIENT_CMD_REPLY(close, CLOSE),
 +  SILC_CLIENT_CMD_REPLY(die, DIE),
 +  SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
 +  SILC_CLIENT_CMD_REPLY(leave, LEAVE),
 +  SILC_CLIENT_CMD_REPLY(names, NAMES),
 +
 +  { NULL, 0 },
 +};
 +
 +/* Status message structure. Messages are defined below. */
 +typedef struct {
 +  SilcCommandStatus status;
 +  char *message;
 +} SilcCommandStatusMessage;
 +
 +/* Status messages returned by the server */
 +#define STAT(x) SILC_STATUS_ERR_##x
 +const SilcCommandStatusMessage silc_command_status_messages[] = {
 +
 +  { STAT(NO_SUCH_NICK),      "No such nickname" },
 +  { STAT(NO_SUCH_CHANNEL),   "No such channel" },
 +  { STAT(NO_SUCH_SERVER),    "No such server" },
 +  { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
 +  { STAT(NO_RECIPIENT),      "No recipient given" },
 +  { STAT(UNKNOWN_COMMAND),   "Unknown command" },
 +  { STAT(WILDCARDS),         "Unknown command" },
 +  { STAT(NO_CLIENT_ID),      "No Client ID given" },
 +  { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
 +  { STAT(NO_SERVER_ID),      "No Server ID given" },
 +  { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
 +  { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
 +  { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
 +  { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
 +  { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
 +  { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
 +  { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
 +  { STAT(USER_ON_CHANNEL),   "User already on the channel" },
 +  { STAT(NOT_REGISTERED),    "You have not registered" },
 +  { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
 +  { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
 +  { STAT(PERM_DENIED),       "Your host is not among the privileged" },
 +  { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
 +  { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
 +  { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
 +  { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
 +  { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
 +  { STAT(UNKNOWN_MODE),    "Unknown mode" },
 +  { STAT(NOT_YOU),         "Cannot change mode for other users" },
 +  { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
 +  { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
 +  { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
 +  { STAT(BAD_NICKNAME),    "Bad nickname" },
 +  { STAT(BAD_CHANNEL),     "Bad channel name" },
 +  { STAT(AUTH_FAILED),     "Authentication failed" },
 +
 +  { 0, NULL }
 +};
 +
 +/* Command reply operation that is called at the end of all command replys. 
 +   Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
 +#define COMMAND_REPLY(args) cmd->client->ops->command_reply args
 +#define ARGS cmd->client, cmd->sock->user_data, \
 +             cmd->payload, TRUE, silc_command_get(cmd->payload), status
 +
 +/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
 +#define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
 +  cmd->sock->user_data, cmd->payload, FALSE, \
 +  silc_command_get(cmd->payload), status)
 +
 +/* Process received command reply. */
 +
 +void silc_client_command_reply_process(SilcClient client,
 +                                     SilcSocketConnection sock,
 +                                     SilcPacketContext *packet)
 +{
 +  SilcBuffer buffer = packet->buffer;
 +  SilcClientCommandReplyContext ctx;
 +  SilcCommandPayload payload;
 +
 +  /* Get command reply payload from packet */
 +  payload = silc_command_payload_parse(buffer);
 +  if (!payload) {
 +    /* Silently ignore bad reply packet */
 +    SILC_LOG_DEBUG(("Bad command reply packet"));
 +    return;
 +  }
 +  
 +  /* Allocate command reply context. This must be free'd by the
 +     command reply routine receiving it. */
 +  ctx = silc_calloc(1, sizeof(*ctx));
 +  ctx->client = client;
 +  ctx->sock = sock;
 +  ctx->payload = payload;
 +  ctx->args = silc_command_get_args(ctx->payload);
 +  ctx->packet = packet;
 +      
 +  /* Check for pending commands and mark to be exeucted */
 +  SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
 +  
 +  /* Execute command reply */
 +  SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
 +}
 +
 +/* Returns status message string */
 +
 +static char *
 +silc_client_command_status_message(SilcCommandStatus status)
 +{
 +  int i;
 +
 +  for (i = 0; silc_command_status_messages[i].message; i++) {
 +    if (silc_command_status_messages[i].status == status)
 +      break;
 +  }
 +
 +  if (silc_command_status_messages[i].message == NULL)
 +    return NULL;
 +
 +  return silc_command_status_messages[i].message;
 +}
 +
 +/* Free command reply context and its internals. */
 +
 +void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
 +{
 +  if (cmd) {
 +    silc_command_free_payload(cmd->payload);
 +    silc_free(cmd);
 +  }
 +}
 +
 +static void 
 +silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
 +                                    SilcCommandStatus status)
 +{
 +  char buf[256];
 +  int argc, len;
 +  unsigned char *id_data;
 +  char *nickname = NULL, *username = NULL;
 +  char *realname = NULL;
 +  SilcClientID *client_id;
 +  SilcIDCacheEntry id_cache = NULL;
 +  SilcClientEntry client_entry = NULL;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  
 +  memset(buf, 0, sizeof(buf));
 +  
 +  argc = silc_argument_get_arg_num(cmd->args);
 +
 +  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
 +  if (!id_data) {
 +    COMMAND_REPLY_ERROR;
 +    return;
 +  }
 +  
 +  client_id = silc_id_payload_parse_id(id_data, len);
 +  
 +  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
 +  if (nickname) {
 +    strncat(buf, nickname, len);
 +    strncat(buf, " is ", 4);
 +  }
 +  
 +  username = silc_argument_get_arg_type(cmd->args, 4, &len);
 +  if (username) {
 +    strncat(buf, username, len);
 +  }
 +  
 +  realname = silc_argument_get_arg_type(cmd->args, 5, &len);
 +  if (realname) {
 +    strncat(buf, " (", 2);
 +    strncat(buf, realname, len);
 +    strncat(buf, ")", 1);
 +  }
 +  
 +  /* Check if we have this client cached already. */
 +  if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
 +                                 SILC_ID_CLIENT, &id_cache)) {
 +    client_entry = silc_calloc(1, sizeof(*client_entry));
 +    client_entry->id = client_id;
 +    silc_parse_nickname(nickname, &client_entry->nickname, 
 +                      &client_entry->server, &client_entry->num);
 +    client_entry->username = strdup(username);
 +    
 +    /* Add client to cache */
 +    silc_idcache_add(conn->client_cache, client_entry->nickname,
 +                   SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
 +  } else {
 +    client_entry = (SilcClientEntry)id_cache->context;
 +    silc_free(client_id);
 +  }
 +
 +  if (!cmd->callback)
 +    cmd->client->ops->say(cmd->client, conn, "%s", buf);
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, client_entry, nickname, 
 +               username, realname, NULL, NULL));
 +}
 +
 +/* Received reply for WHOIS command. This maybe called several times
 +   for one WHOIS command as server may reply with list of results. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(whois)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  unsigned char *tmp;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +  SILC_GET16_MSB(status, tmp);
 +  if (status != SILC_STATUS_OK && 
 +      status != SILC_STATUS_LIST_START &&
 +      status != SILC_STATUS_LIST_ITEM &&
 +      status != SILC_STATUS_LIST_END) {
 +    if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
 +      /* Take nickname which may be provided */
 +      tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
 +      if (tmp)
 +      cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
 +               silc_client_command_status_message(status));
 +      else
 +      cmd->client->ops->say(cmd->client, conn, "%s",
 +               silc_client_command_status_message(status));
 +      COMMAND_REPLY_ERROR;
 +      goto out;
 +    } else {
 +      cmd->client->ops->say(cmd->client, conn,
 +             "%s", silc_client_command_status_message(status));
 +      COMMAND_REPLY_ERROR;
 +      goto out;
 +    }
 +  }
 +
 +  /* Display one whois reply */
 +  if (status == SILC_STATUS_OK) {
 +    silc_client_command_reply_whois_print(cmd, status);
 +  }
 +
 +  /* XXX list should not be displayed untill all items has been received. */
 +  if (status == SILC_STATUS_LIST_START) {
 +    silc_client_command_reply_whois_print(cmd, status);
 +  }
 +
 +  if (status == SILC_STATUS_LIST_ITEM) {
 +    silc_client_command_reply_whois_print(cmd, status);
 +  }
 +
 +  if (status == SILC_STATUS_LIST_END) {
 +    silc_client_command_reply_whois_print(cmd, status);
 +  }
 +
 +  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +/* Received reply for WHOWAS command. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(whowas)
 +{
 +
 +}
 +
 +/* Received reply for IDENTIFY command. This maybe called several times
 +   for one IDENTIFY command as server may reply with list of results. 
 +   This is totally silent and does not print anything on screen. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(identify)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcClientEntry client_entry;
 +  SilcCommandStatus status;
 +  unsigned char *tmp;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +  SILC_GET16_MSB(status, tmp);
 +  if (status != SILC_STATUS_OK) {
 +    if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
 +      /* Take nickname which may be provided */
 +      tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
 +      if (tmp)
 +      cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
 +               silc_client_command_status_message(status));
 +      else
 +      cmd->client->ops->say(cmd->client, conn, "%s",
 +               silc_client_command_status_message(status));
 +      COMMAND_REPLY_ERROR;
 +      goto out;
 +    } else {
 +      cmd->client->ops->say(cmd->client, conn,
 +             "%s", silc_client_command_status_message(status));
 +      COMMAND_REPLY_ERROR;
 +      goto out;
 +    }
 +  }
 +
 +  /* Display one whois reply */
 +  if (status == SILC_STATUS_OK) {
 +    unsigned int len;
 +    unsigned char *id_data;
 +    char *nickname;
 +    char *username;
 +
 +    id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
 +    nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
 +    username = silc_argument_get_arg_type(cmd->args, 4, NULL);
 +
 +    /* Allocate client entry */
 +    client_entry = silc_calloc(1, sizeof(*client_entry));
 +    client_entry->id = silc_id_payload_parse_id(id_data, len);
 +    if (nickname)
 +      silc_parse_nickname(nickname, &client_entry->nickname, 
 +                        &client_entry->server, &client_entry->num);
 +    if (username)
 +      client_entry->username = strdup(username);
 +
 +    /* Save received Client ID to ID cache */
 +    silc_idcache_add(conn->client_cache, client_entry->nickname,
 +                   SILC_ID_CLIENT, client_entry->id, client_entry, TRUE);
 +  }
 +
 +  if (status == SILC_STATUS_LIST_START) {
 +
 +  }
 +
 +  if (status == SILC_STATUS_LIST_END) {
 +
 +  }
 +
 +  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +/* Received reply for command NICK. If everything went without errors
 +   we just received our new Client ID. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(nick)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  SilcIDPayload idp;
 +  unsigned char *tmp;
 +  unsigned int argc, len;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
 +           silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  argc = silc_argument_get_arg_num(cmd->args);
 +  if (argc < 2 || argc > 2) {
 +    cmd->client->ops->say(cmd->client, conn, 
 +                        "Cannot set nickname: bad reply to command");
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Take received Client ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
 +  idp = silc_id_payload_parse_data(tmp, len);
 +  silc_client_receive_new_id(cmd->client, cmd->sock, idp);
 +    
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, conn->local_entry));
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(list)
 +{
 +}
 +
 +/* Received reply to topic command. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(topic)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  SilcChannelEntry channel;
 +  SilcChannelID *channel_id = NULL;
 +  SilcIDCacheEntry id_cache = NULL;
 +  unsigned char *tmp;
 +  char *topic;
 +  unsigned int argc, len;
 +
 +  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    silc_client_command_reply_free(cmd);
 +    return;
 +  }
 +
 +  argc = silc_argument_get_arg_num(cmd->args);
 +  if (argc < 1 || argc > 3) {
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Take Channel ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
 +  if (!tmp)
 +    goto out;
 +
 +  /* Take topic */
 +  topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
 +  if (!topic)
 +    goto out;
 +
 +  channel_id = silc_id_payload_parse_id(tmp, len);
 +
 +  /* Get the channel name */
 +  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
 +                                 SILC_ID_CHANNEL, &id_cache)) {
 +    silc_free(channel_id);
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +  
 +  channel = (SilcChannelEntry)id_cache->context;
 +
 +  cmd->client->ops->say(cmd->client, conn, 
 +                      "Topic on channel %s: %s", channel->channel_name,
 +                      topic);
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, channel, topic));
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +/* Received reply to invite command. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(invite)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  unsigned char *tmp;
 +
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +  SILC_GET16_MSB(status, tmp);
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    silc_client_command_reply_free(cmd);
 +    return;
 +  }
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS));
 +
 +  silc_client_command_reply_free(cmd);
 +}
 + 
 +SILC_CLIENT_CMD_REPLY_FUNC(quit)
 +{
 +}
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(kill)
 +{
 +}
 +
 +/* Received reply to INFO command. We receive the server ID and some
 +   information about the server user requested. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(info)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcClient client = cmd->client;
 +  SilcCommandStatus status;
 +  unsigned char *tmp;
 +
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +  SILC_GET16_MSB(status, tmp);
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    silc_client_command_reply_free(cmd);
 +    return;
 +  }
 +
 +  /* Get server ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +  if (!tmp)
 +    goto out;
 +
 +  /* XXX save server id */
 +
 +  /* Get server info */
 +  tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
 +  if (!tmp)
 +    goto out;
 +
 +  client->ops->say(cmd->client, conn, "Info: %s", tmp);
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, NULL, (char *)tmp));
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(connect)
 +{
 +}
 +
 +/* Received reply to PING command. The reply time is shown to user. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(ping)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  void *id;
 +  int i;
 +  time_t diff, curtime;
 +
 +  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  curtime = time(NULL);
 +  id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
 +
 +  for (i = 0; i < conn->ping_count; i++) {
 +    if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
 +      diff = curtime - conn->ping[i].start_time;
 +      cmd->client->ops->say(cmd->client, conn, 
 +                          "Ping reply from %s: %d second%s", 
 +                          conn->ping[i].dest_name, diff, 
 +                          diff == 1 ? "" : "s");
 +
 +      conn->ping[i].start_time = 0;
 +      silc_free(conn->ping[i].dest_id);
 +      conn->ping[i].dest_id = NULL;
 +      silc_free(conn->ping[i].dest_name);
 +      conn->ping[i].dest_name = NULL;
 +
 +      /* Notify application */
 +      COMMAND_REPLY((ARGS));
 +      break;
 +    }
 +  }
 +
 +  silc_free(id);
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(oper)
 +{
 +}
 +
 +/* Received reply for JOIN command. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(join)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcClient client = cmd->client;
 +  SilcCommandStatus status;
 +  SilcIDPayload idp;
 +  unsigned int argc, mode, len;
 +  char *topic, *tmp, *channel_name;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  argc = silc_argument_get_arg_num(cmd->args);
 +  if (argc < 3 || argc > 5) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "Cannot join channel: Bad reply packet");
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Get channel name */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +  if (!tmp) {
 +    cmd->client->ops->say(cmd->client, conn, 
 +                        "Cannot join channel: Bad reply packet");
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +  channel_name = strdup(tmp);
 +
 +  /* Get Channel ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
 +  if (!tmp) {
 +    cmd->client->ops->say(cmd->client, conn, 
 +                        "Cannot join channel: Bad reply packet");
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +  idp = silc_id_payload_parse_data(tmp, len);
 +
 +  /* Get channel mode */
 +  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
 +  if (tmp)
 +    SILC_GET32_MSB(mode, tmp);
 +  else
 +    mode = 0;
 +
 +  /* Get topic */
 +  topic = silc_argument_get_arg_type(cmd->args, 5, NULL);
 +
 +  /* Save received Channel ID */
 +  silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
 +                           mode, idp);
 +  silc_id_payload_free(idp);
 +
 +  if (topic)
 +    client->ops->say(cmd->client, conn, 
 +                   "Topic for %s: %s", channel_name, topic);
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
 +               NULL, NULL, topic));
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +/* Received reply for MOTD command */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(motd)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  unsigned int argc, i;
 +  unsigned char *tmp;
 +  char *motd = NULL, *cp, line[256];
 +
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +  SILC_GET16_MSB(status, tmp);
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    return;
 +  }
 +
 +  argc = silc_argument_get_arg_num(cmd->args);
 +  if (argc > 2) {
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  if (argc == 2) {
 +    motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +    if (!motd) {
 +      COMMAND_REPLY_ERROR;
 +      goto out;
 +    }
 +
 +    i = 0;
 +    cp = motd;
 +    while(cp[i] != 0) {
 +      if (cp[i++] == '\n') {
 +      memset(line, 0, sizeof(line));
 +      strncat(line, cp, i - 1);
 +      cp += i;
 +      
 +      if (i == 2)
 +        line[0] = ' ';
 +      
 +      cmd->client->ops->say(cmd->client, conn, "%s", line);
 +      
 +      if (!strlen(cp))
 +        break;
 +      i = 0;
 +      }
 +    }
 +  }
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, motd));
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(umode)
 +{
 +}
 +
 +/* Received reply for CMODE command. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(cmode)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  unsigned char *tmp;
 +
 +  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Get channel mode */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +  if (!tmp) {
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, tmp));
 +
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +/* Received reply for CUMODE command */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(cumode)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  SilcIDCacheEntry id_cache = NULL;
 +  SilcClientID *client_id;
 +  unsigned char *tmp, *id;
 +  unsigned int len;
 +  
 +  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +  
 +  /* Get channel mode */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
 +  if (!tmp) {
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Get Client ID */
 +  id = silc_argument_get_arg_type(cmd->args, 3, &len);
 +  if (!id) {
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +  client_id = silc_id_payload_parse_id(id, len);
 +  
 +  /* Get client entry */
 +  if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
 +                                 SILC_ID_CLIENT, &id_cache)) {
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
 +  silc_free(client_id);
 +  
 + out:
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(kick)
 +{
 +}
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(restart)
 +{
 +}
 + 
 +SILC_CLIENT_CMD_REPLY_FUNC(close)
 +{
 +}
 + 
 +SILC_CLIENT_CMD_REPLY_FUNC(die)
 +{
 +}
 + 
 +SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
 +{
 +}
 +
 +/* Reply to LEAVE command. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(leave)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  unsigned char *tmp;
 +
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +  SILC_GET16_MSB(status, tmp);
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    return;
 +  }
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS));
 +
 +  silc_client_command_reply_free(cmd);
 +}
 +
 +/* Reply to NAMES command. Received list of client names on the channel 
 +   we requested. */
 +
 +SILC_CLIENT_CMD_REPLY_FUNC(names)
 +{
 +  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 +  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 +  SilcCommandStatus status;
 +  SilcIDCacheEntry id_cache = NULL;
 +  SilcChannelEntry channel;
 +  SilcChannelID *channel_id = NULL;
 +  SilcBuffer client_id_list;
 +  SilcBuffer client_mode_list;
 +  unsigned char *tmp;
 +  char *name_list, *cp;
 +  int i, k, len1, len2, list_count = 0;
 +
 +  SILC_LOG_DEBUG(("Start"));
 +
 +  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
 +  SILC_GET16_MSB(status, tmp);
 +  if (status != SILC_STATUS_OK) {
 +    cmd->client->ops->say(cmd->client, conn,
 +           "%s", silc_client_command_status_message(status));
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Get channel ID */
 +  tmp = silc_argument_get_arg_type(cmd->args, 2, &len1);
 +  if (!tmp) {
 +    cmd->client->ops->say(cmd->client, conn, 
 +                        "Cannot Channel ID: Bad reply packet");
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +  channel_id = silc_id_payload_parse_id(tmp, len1);
 +
 +  /* Get the name list of the channel */
 +  name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
 +  if (!name_list) {
 +    cmd->client->ops->say(cmd->client, conn, 
 +                        "Cannot get user list: Bad reply packet");
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  /* Get Client ID list */
 +  tmp = silc_argument_get_arg_type(cmd->args, 4, &len2);
 +  if (!tmp) {
 +    cmd->client->ops->say(cmd->client, conn, 
 +                        "Cannot get user list: Bad reply packet");
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  client_id_list = silc_buffer_alloc(len2);
 +  silc_buffer_pull_tail(client_id_list, len2);
 +  silc_buffer_put(client_id_list, tmp, len2);
 +
 +  /* Get client mode list */
 +  tmp = silc_argument_get_arg_type(cmd->args, 5, &len2);
 +  if (!tmp) {
 +    cmd->client->ops->say(cmd->client, conn, 
 +                        "Cannot get user list: Bad reply packet");
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +
 +  client_mode_list = silc_buffer_alloc(len2);
 +  silc_buffer_pull_tail(client_mode_list, len2);
 +  silc_buffer_put(client_mode_list, tmp, len2);
 +
 +  /* Get the channel name */
 +  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
 +                                 SILC_ID_CHANNEL, &id_cache)) {
 +    COMMAND_REPLY_ERROR;
 +    goto out;
 +  }
 +  
 +  channel = (SilcChannelEntry)id_cache->context;
 +
 +  /* If there is pending command we know that user has called this command
 +     and we will handle the name list differently. */
 +  if (cmd->callback) {
 +    /* We will resolve all the necessary information about the people
 +       on the channel. Only after that will we display the user list. */
 +    for (i = 0; i < len1; i++) {
 +      /* XXX */
 +
 +    }
 +    silc_client_command_pending_del(SILC_COMMAND_NAMES);
 +  } else {
 +    /* there is no pending callback it means that this command reply
 +       has been received without calling the command, ie. server has sent
 +       the reply without getting the command from us first. This happens
 +       with SILC servers that sends NAMES reply after joining to a channel. */
 +
 +    /* Remove commas from list */
 +    for (i = 0; i < len1; i++)
 +      if (name_list[i] == ',') {
 +      name_list[i] = ' ';
 +      list_count++;
 +      }
 +    list_count++;
 +  }
 +
 +  /* Remove old client list from channel, if exists */
 +  if (channel->clients) {
 +    silc_free(channel->clients);
 +    channel->clients = NULL;
 +    channel->clients_count = 0;
 +  }
 +
 +  /* Allocate room for clients in the channel */
 +  channel->clients = silc_calloc(list_count, sizeof(*channel->clients));
 +
 +  /* Cache the received name list, client ID's and modes. This cache expires
 +     whenever server sends notify message to channel. It means two things;
 +     some user has joined or leaved the channel. */
 +  cp = name_list;
 +  for (i = 0; i < list_count; i++) {
 +    int nick_len = strcspn(name_list, " ");
 +    unsigned short idp_len;
 +    unsigned int mode;
 +    char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
 +    SilcClientID *client_id;
 +    SilcClientEntry client;
 +
 +    memcpy(nickname, name_list, nick_len);
 +    SILC_GET16_MSB(idp_len, client_id_list->data + 2);
 +    idp_len += 4;
 +    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
 +    silc_buffer_pull(client_id_list, idp_len);
 +    
 +    SILC_GET32_MSB(mode, client_mode_list->data);
 +    silc_buffer_pull(client_mode_list, 4);
 +
 +    /* Check if we have this client cached already. */
 +    if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
 +                                   SILC_ID_CLIENT, &id_cache)) {
 +      client = silc_calloc(1, sizeof(*client));
 +      client->id = client_id;
 +      silc_parse_nickname(nickname, &client->nickname, &client->server, 
 +                        &client->num);
 +      silc_free(nickname);
 +
 +      /* Add client to cache */
 +      silc_idcache_add(conn->client_cache, client->nickname, SILC_ID_CLIENT,
 +                     client_id, (void *)client, TRUE);
 +    } else {
 +      client = (SilcClientEntry)id_cache->context;
 +      silc_free(client_id);
 +      silc_free(nickname);
 +      id_cache = NULL;
 +    }
 +
 +    channel->clients[channel->clients_count].client = client;
 +    channel->clients[channel->clients_count].mode = mode;
 +    channel->clients_count++;
 +
 +    name_list += nick_len + 1;
 +  }
 +
 +  name_list = cp;
 +  for (i = 0; i < list_count; i++) {
 +    int c;
 +    int nick_len = strcspn(name_list, " ");
 +    char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
 +    memcpy(nickname, name_list, nick_len);
 +
 +    for (c = 0, k = 0; k < channel->clients_count; k++) {
 +      if (channel->clients[k].client && 
 +        !strncmp(channel->clients[k].client->nickname, 
 +                 nickname, strlen(channel->clients[k].client->nickname))) {
 +      char t[8];
 +      
 +      if (!c) {
 +        c++;
 +        continue;
 +      }
 +      
 +      memset(t, 0, sizeof(t));
 +      channel->clients[k].client->nickname = 
 +        silc_calloc(strlen(nickname) + 8, sizeof(*channel->clients[k].
 +                                                 client->nickname));
++      snprintf(t, sizeof(t), "[%d]", c++);
++      strncat(channel->clients[k].client->nickname, t, strlen(t));
 +      strncat(channel->clients[k].client->nickname, nickname, 
 +              strlen(nickname));
-     char *n = channel->clients[k].client->nickname;
 +      }
 +    }
 +
 +    silc_free(nickname);
 +  }
 +
 +  name_list = NULL;
 +  len1 = 0;
 +  for (k = 0; k < channel->clients_count; k++) {
-     name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 1));
++    char *m, *n = channel->clients[k].client->nickname;
 +    len2 = strlen(n);
 +    len1 += len2;
++
++    name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
++
++    m = silc_client_chumode_char(channel->clients[k].mode);
++    if (m) {
++      memcpy(name_list + (len1 - len2), m, strlen(m));
++      len1 += strlen(m);
++      silc_free(m);
++    }
++
 +    memcpy(name_list + (len1 - len2), n, len2);
 +    name_list[len1] = 0;
 +    
 +    if (k == channel->clients_count - 1)
 +      break;
 +    memcpy(name_list + len1, " ", 1);
 +    len1++;
 +  }
 +
 +  cmd->client->ops->say(cmd->client, conn,
 +                      "Users on %s: %s", channel->channel_name, name_list);
 +
 +  /* Notify application */
 +  COMMAND_REPLY((ARGS, channel, name_list, client_id_list->head,
 +               client_mode_list->head));
 +
 +  silc_free(name_list);
 +  silc_buffer_free(client_id_list);
 +  silc_buffer_free(client_mode_list);
 +
 + out:
 +  if (channel_id)
 +    silc_free(channel_id);
 +  silc_client_command_reply_free(cmd);
 +}