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 --combined 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.
diff --combined apps/silc/clientutil.c
index 321160d49055a73f705825eb99be4f0d331f2449,21043795c0249108bc680645c5583a99488b9461..210f50b02c5d2307f6a8bb4bf12a211bb97e6c38
    GNU General Public License for more details.
  
  */
 -/*
 - * $Id$
 - * $Log$
 - * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
 - *    Importet from internal CVS/Added Log headers.
 - *
 - *
 - */
 +/* $Id$ */
  
  #include "clientincludes.h"
  
 -/* Internal routine used to print lines to window. This can split the
 +/* Routine used to print lines to window. This can split the
     line neatly if a word would overlap the line. */
  
 -static void silc_print_to_window(WINDOW *win, char *message)
 +void silc_print_to_window(WINDOW *win, char *message)
  {
    int str_len, len;
  
    wrefresh(win);
  }
  
 -/* Prints a message with three star (*) sign before the actual message
 -   on the current output window. This is used to print command outputs
 -   and error messages. */
 -/* XXX Change to accept SilcClientWindow and use output window 
 -   from there (the pointer to the output window must be added to the
 -   SilcClientWindow object. */
 -
 -void silc_say(SilcClient client, char *msg, ...)
 -{
 -  va_list vp;
 -  char message[1024];
 -  
 -  memset(message, 0, sizeof(message));
 -  strncat(message, "\n***  ", 5);
 -
 -  va_start(vp, msg);
 -  vsprintf(message + 5, msg, vp);
 -  va_end(vp);
 -  
 -  /* Print the message */
 -  silc_print_to_window(client->screen->output_win[0], message);
 -}
 -
  /* Prints message to the screen. This is used to print the messages
     user is typed and message that came on channels. */
  
  void silc_print(SilcClient client, char *msg, ...)
  {
    va_list vp;
 -  char message[1024];
 +  char message[2048];
 +  SilcClientInternal app = client->application;
    
    memset(message, 0, sizeof(message));
    strncat(message, "\n ", 2);
    va_end(vp);
    
    /* Print the message */
 -  silc_print_to_window(client->screen->output_win[0], message);
 +  silc_print_to_window(app->screen->output_win[0], message);
  }
  
  /* Returns user's mail path */
@@@ -81,13 -110,8 +81,13 @@@ char *silc_get_mail_path(
  {
    char pathbuf[MAXPATHLEN];
    char *path;
 +
 +#ifndef _PATH_MAILDIR
 +#define _PATH_MAILDIR "/var/mail"
 +#endif
    
 -  if ((path = (char *)getenv("MAIL")) != 0) {
 +  path = getenv("MAIL");
 +  if (path) {
      strncpy(pathbuf, path, strlen(path));
    } else {
      strcpy(pathbuf, _PATH_MAILDIR);
@@@ -114,7 -138,7 +114,7 @@@ int silc_get_number_of_emails(
      fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
    } else {
      while((fscanf(tl, "%s", data)) != EOF) { 
 -      if(!strcmp(data, "Subject:"))
 +      if(!strcmp(data, "From:"))
        num++;
      }
      
@@@ -185,39 -209,48 +185,39 @@@ int silc_client_time_til_next_min(
    return 60 - min->tm_sec;
  }
  
 -/* Asks passphrase from user on the input line. */
 +/* Asks yes/no from user on the input line. Returns TRUE on "yes" and
 +   FALSE on "no". */
  
 -char *silc_client_ask_passphrase(SilcClient client)
 +int silc_client_ask_yes_no(SilcClient client, char *prompt)
  {
 -  char pass1[256], pass2[256];
 -  char *ret;
 -  int try = 3;
 -
 -  while(try) {
 -
 -    /* Print prompt */
 -    wattroff(client->screen->input_win, A_INVIS);
 -    silc_screen_input_print_prompt(client->screen, "Passphrase: ");
 -    wattron(client->screen->input_win, A_INVIS);
 -    
 -    /* Get string */
 -    memset(pass1, 0, sizeof(pass1));
 -    wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
 -    
 -    /* Print retype prompt */
 -    wattroff(client->screen->input_win, A_INVIS);
 -    silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
 -    wattron(client->screen->input_win, A_INVIS);
 -    
 -    /* Get string */
 -    memset(pass2, 0, sizeof(pass2));
 -    wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
 -
 -    if (!strncmp(pass1, pass2, strlen(pass2)))
 -      break;
 -
 -    try--;
 +  SilcClientInternal app = (SilcClientInternal)client->application;
 +  char answer[4];
 +  int ret;
 +
 + again:
 +  silc_screen_input_reset(app->screen);
 +
 +  /* Print prompt */
 +  wattroff(app->screen->input_win, A_INVIS);
 +  silc_screen_input_print_prompt(app->screen, prompt);
 +
 +  /* Get string */
 +  memset(answer, 0, sizeof(answer));
 +  echo();
 +  wgetnstr(app->screen->input_win, answer, sizeof(answer));
 +  if (!strncasecmp(answer, "yes", strlen(answer)) ||
 +      !strncasecmp(answer, "y", strlen(answer))) {
 +    ret = TRUE;
 +  } else if (!strncasecmp(answer, "no", strlen(answer)) ||
 +           !strncasecmp(answer, "n", strlen(answer))) {
 +    ret = FALSE;
 +  } else {
 +    silc_say(client, app->conn, "Type yes or no");
 +    goto again;
    }
 +  noecho();
  
 -  ret = silc_calloc(strlen(pass1), sizeof(char));
 -  memcpy(ret, pass1, strlen(pass1));
 -
 -  memset(pass1, 0, sizeof(pass1));
 -  memset(pass2, 0, sizeof(pass2));
 -
 -  wattroff(client->screen->input_win, A_INVIS);
 -  silc_screen_input_reset(client->screen);
 +  silc_screen_input_reset(app->screen);
  
    return ret;
  }
@@@ -253,7 -286,7 +253,7 @@@ char *silc_client_get_input(const char 
    fd = open("/dev/tty", O_RDONLY);
    if (fd < 0) {
      fprintf(stderr, "silc: %s\n", strerror(errno));
 -    exit(1);
 +    return NULL;
    }
  
    memset(input, 0, sizeof(input));
  
    if ((read(fd, input, sizeof(input))) < 0) {
      fprintf(stderr, "silc: %s\n", strerror(errno));
 -    exit(1);
 +    return NULL;
    }
  
    if (strlen(input) <= 1)
@@@ -290,7 -323,7 +290,7 @@@ char *silc_client_get_passphrase(const 
    fd = open("/dev/tty", O_RDONLY);
    if (fd < 0) {
      fprintf(stderr, "silc: %s\n", strerror(errno));
 -    exit(1);
 +    return NULL;
    }
  
    signal(SIGINT, SIG_IGN);
  
    if ((read(fd, input, sizeof(input))) < 0) {
      fprintf(stderr, "silc: %s\n", strerror(errno));
 -    exit(1);
 +    return NULL;
    }
  
    if (strlen(input) <= 1) {
  #endif
  }
  
 +/* Returns identifier string for public key generation. */
 +
 +char *silc_client_create_identifier()
 +{
 +  char *username = NULL, *realname = NULL;
 +  char hostname[256], email[256];
 +  
 +  /* Get realname */
 +  realname = silc_get_real_name();
 +
 +  /* Get hostname */
 +  memset(hostname, 0, sizeof(hostname));
 +  gethostname(hostname, sizeof(hostname));
 +
 +  /* Get username (mandatory) */
 +  username = silc_get_username();
 +  if (!username)
 +    return NULL;
 +
 +  /* Create default email address, whether it is right or not */
 +  snprintf(email, sizeof(email), "%s@%s", username, hostname);
 +
 +  return silc_pkcs_encode_identifier(username, hostname, realname, email,
 +                                   NULL, NULL);
 +}
 +
  /* Creates new public key and private key pair. This is used only
     when user wants to create new key pair from command line. */
  
 -void silc_client_create_key_pair(char *pkcs_name, int bits)
 +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)
  {
    SilcPKCS pkcs;
 +  SilcPublicKey pub_key;
 +  SilcPrivateKey prv_key;
    SilcRng rng;
    unsigned char *key;
    unsigned int key_len;
 +  char line[256];
    char *pkfile = NULL, *prvfile = NULL;
  
 -  printf("\
 +  if (!pkcs_name || !public_key || !private_key)
 +    printf("\
  New pair of keys will be created.  Please, answer to following questions.\n\
  ");
  
      }
    }
  
 +  if (!silc_pkcs_is_supported(pkcs_name)) {
 +    fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
 +    return FALSE;
 +  }
 +
    if (!bits) {
      char *length = NULL;
      length = 
        bits = atoi(length);
    }
  
 +  if (!identifier) {
 +    char *def = silc_client_create_identifier();
 +
 +    memset(line, 0, sizeof(line));
 +    if (def)
 +      snprintf(line, sizeof(line), "Identifier [%s]: ", def);
 +    else
 +      snprintf(line, sizeof(line),
 +             "Identifier (eg. UN=jon, HN=jon.dummy.com, "
 +             "RN=Jon Johnson, E=jon@dummy.com): ");
 +
 +    while (!identifier) {
 +      identifier = silc_client_get_input(line);
 +      if (!identifier && def)
 +      identifier = strdup(def);
 +    }
 +
 +    if (def)
 +      silc_free(def);
 +  }
 +
    rng = silc_rng_alloc();
    silc_rng_init(rng);
    silc_math_primegen_init();
  
 - again_pk:
 -  pkfile = silc_client_get_input("Public key filename: ");
 -  if (!pkfile) {
 -    printf("Public key filename must be defined\n");
 -    goto again_pk;
 +  if (!public_key) {
 +    memset(line, 0, sizeof(line));
 +    snprintf(line, sizeof(line), "Public key filename [%s] ", 
 +           SILC_CLIENT_PUBLIC_KEY_NAME);
 +    pkfile = silc_client_get_input(line);
 +    if (!pkfile)
 +      pkfile = SILC_CLIENT_PUBLIC_KEY_NAME;
 +  } else {
 +    pkfile = public_key;
    }
  
 - again_prv:
 -  prvfile = silc_client_get_input("Private key filename: ");
 -  if (!prvfile) {
 -    printf("Private key filename must be defined\n");
 -    goto again_prv;
 +  if (!private_key) {
 +    memset(line, 0, sizeof(line));
 +    snprintf(line, sizeof(line), "Public key filename [%s] ", 
 +           SILC_CLIENT_PRIVATE_KEY_NAME);
 +    prvfile = silc_client_get_input(line);
 +    if (!prvfile)
 +      prvfile = SILC_CLIENT_PRIVATE_KEY_NAME;
 +  } else {
 +    prvfile = private_key;
    }
  
    /* Generate keys */
    silc_pkcs_alloc(pkcs_name, &pkcs);
    pkcs->pkcs->init(pkcs->context, bits, rng);
  
 -  /* Save keys into file */
 +  /* Save public key into file */
    key = silc_pkcs_get_public_key(pkcs, &key_len);
 -  silc_pkcs_save_public_key(pkcs, pkfile, key, key_len);
 +  pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
 +                                     key, key_len);
 +  silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
 +  if (ret_pub_key)
 +    *ret_pub_key = pub_key;
 +
    memset(key, 0, sizeof(key_len));
    silc_free(key);
 +
 +  /* Save private key into file */
    key = silc_pkcs_get_private_key(pkcs, &key_len);
 -  silc_pkcs_save_private_key(pkcs, prvfile, key, key_len, "");
 +  prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
 +
 +  silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
 +  if (ret_prv_key)
 +    *ret_prv_key = prv_key;
 +
 +  printf("Public key has been save into `%s'.\n", pkfile);
 +  printf("Private key has been saved into `%s'.\n", prvfile);
 +  printf("Press <Enter> to continue...\n");
 +  getchar();
 +
    memset(key, 0, sizeof(key_len));
    silc_free(key);
  
    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);
- }
diff --combined apps/silc/clientutil.h
index b682a1271a087df147c264f045936d6cfb46656f,e18cf2d71eef0a5c8f576d3057137e1a8403773c..38f0d9e857b7cfc88e28c0f6cd3c85f093c242cf
  #define CLIENTUTIL_H
  
  /* Prototypes */
 -void silc_say(SilcClient client, char *msg, ...);
 +void silc_print_to_window(WINDOW *win, char *message);
  void silc_print(SilcClient client, char *msg, ...);
  char *silc_get_mail_path();
  int silc_get_number_of_emails();
  char *silc_get_username();
  char *silc_get_real_name();
  int silc_client_time_til_next_min();
 -char *silc_client_ask_passphrase(SilcClient client);
 +int silc_client_ask_yes_no(SilcClient client, char *prompt);
  char *silc_client_get_input(const char *prompt);
  char *silc_client_get_passphrase(const char *prompt);
  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
diff --combined lib/silcclient/client.c
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);
++}
diff --combined lib/silcclient/client.h
index 84cfa3244dea8770a53de4cd7fa66bdaf2bc6946,661bd83a481604aad891dc11bc2d0263043af5ca..fdff610eee9e0ae4bcafa828d3540eeeafe0e81a
  #ifndef CLIENT_H
  #define CLIENT_H
  
 -/* Window structure used in client to associate all the important
 -   connection (window) specific data to this structure. How the window
 -   actually appears on the screen in handeled by the silc_screen*
 -   routines in screen.c. */
 -typedef struct {
 +/* Forward declaration for client */
 +typedef struct SilcClientObject *SilcClient;
 +
 +/* Forward declaration for client connection */
 +typedef struct SilcClientConnectionObject *SilcClientConnection;
 +
 +#include "idlist.h"
 +#include "command.h"
 +#include "ops.h"
 +
 +/* Structure to hold ping time information. Every PING command will 
 +   add entry of this structure and is removed after reply to the ping
 +   as been received. */
 +typedef struct SilcClientPingStruct {
 +  time_t start_time;
 +  void *dest_id;
 +  char *dest_name;
 +} SilcClientPing;
 +
 +/* Structure to hold away messages set by user. This is mainly created
 +   for future extensions where away messages could be set according filters
 +   such as nickname and hostname. For now only one away message can 
 +   be set in one connection. */
 +typedef struct SilcClientAwayStruct {
 +  char *away;
 +  struct SilcClientAwayStruct *next;
 +} SilcClientAway;
 +
 +/* Connection structure used in client to associate all the important
 +   connection specific data to this structure. */
 +struct SilcClientConnectionObject {
    /*
     * Local data 
     */
    char *remote_host;
    int remote_port;
    int remote_type;
 +  char *remote_info;
  
 -  /* Remote client ID for this connection */
 -  SilcClientID *remote_id;
 +  /* Remote server ID for this connection */
 +  SilcServerID *remote_id;
  
 -  /* Remote local ID so that the above defined ID would not have
 +  /* Decoded remote ID so that the above defined ID would not have
       to be decoded for every packet. */
    unsigned char *remote_id_data;
    unsigned int remote_id_data_len;
@@@ -90,6 -63,7 +90,6 @@@
    /* Keys */
    SilcCipher send_key;
    SilcCipher receive_key;
 -  SilcPKCS public_key;
    SilcHmac hmac;
    unsigned char *hmac_key;
    unsigned int hmac_key_len;
    /* Client ID and Channel ID cache. Messages transmitted in SILC network
       are done using different unique ID's. These are the cache for
       thoses ID's used in the communication. */
 -  SilcIDCache *client_id_cache[96];
 -  unsigned int client_id_cache_count[96];
 -  SilcIDCache *channel_id_cache[96];
 -  unsigned int channel_id_cache_count[96];
 -  SilcIDCache *server_id_cache;
 -  unsigned int server_id_cache_count;
 -
 -  /* Current channel on window. All channel's are saved (allocated) into
 +  SilcIDCache client_cache;
 +  SilcIDCache channel_cache;
 +  SilcIDCache server_cache;
 +
 +  /* Current channel on window. All channels are saved (allocated) into
       the cache entries. */
    SilcChannelEntry current_channel;
  
       referencing (sock->user_data). */
    SilcSocketConnection sock;
  
 -  /* The actual physical screen. This data is handled by the
 -     screen handling routines. */
 -  void *screen;
 -} *SilcClientWindow;
 +  /* Requested pings. */
 +  SilcClientPing *ping;
 +  unsigned int ping_count;
 +
 +  /* Set away message */
 +  SilcClientAway *away;
 +
 +  /* Pointer back to the SilcClient. This object is passed to the application
 +     and the actual client object is accesible through this pointer. */
 +  SilcClient client;
 +
 +  /* User data context. Library does not touch this. */
 +  void *context;
 +};
 +
 +/* Main client structure. */
 +struct SilcClientObject {
 +  /*
 +   * Public data. All the following pointers must be set by the allocator
 +   * of this structure.
 +   */
  
 -typedef struct {
 +  /* Users's username, hostname and realname. */
    char *username;
 +  char *hostname;
    char *realname;
  
 +  /* Private and public key of the user. */
 +  SilcPKCS pkcs;
 +  SilcPublicKey public_key;
 +  SilcPrivateKey private_key;
 +
 +  /* Application specific user data pointer. Client library does not
 +     touch this. */
 +  void *application;
 +
 +  /*
 +   * Private data. Following pointers are used internally by the client
 +   * library and should be considered read-only fields.
 +   */
 +
 +  /* All client operations that are implemented in the application. */
 +  SilcClientOperations *ops;
 +
    /* SILC client task queues */
    SilcTaskQueue io_queue;
    SilcTaskQueue timeout_queue;
    SilcTaskQueue generic_queue;
  
 -  /* Input buffer that holds the characters user types. This is
 -     used only to store the typed chars for a while. */
 -  SilcBuffer input_buffer;
 -
 -  /* Table of windows in client. All the data, including connection
 -     specific data, is saved in here. */
 -  SilcClientWindow *windows;
 -  unsigned int windows_count;
 +  /* Table of connections in client. All the connection data is saved here. */
 +  SilcClientConnection *conns;
 +  unsigned int conns_count;
  
 -  /* Currently active window. This is pointer to the window table 
 -     defined above. This must never be free'd directly. */
 -  SilcClientWindow current_win;
 -
 -  /* The SILC client screen object */
 -  SilcScreen screen;
 -
 -  /* Generic cipher and hash objects */
 +  /* Generic cipher and hash objects. These can be used and referenced
 +     by the application as well. */
    SilcCipher none_cipher;
    SilcHash md5hash;
    SilcHash sha1hash;
    SilcHmac md5hmac;
    SilcHmac sha1hmac;
  
 -  /* Configuration object */
 -  SilcClientConfig config;
 -
 -  /* Random Number Generator */
 +  /* Random Number Generator. Application should use this as its primary
 +     random number generator. */
    SilcRng rng;
 -
 -#ifdef SILC_SIM
 -  /* SIM (SILC Module) table */
 -  SilcSimContext **sim;
 -  unsigned int sim_count;
 -#endif
 -} SilcClientObject;
 -
 -typedef SilcClientObject *SilcClient;
 +};
  
  /* Macros */
  
 -#ifndef CTRL
 -#define CTRL(x) ((x) & 0x1f)  /* Ctrl+x */
 -#endif
 -
  /* Registers generic task for file descriptor for reading from network and
     writing to network. As being generic task the actual task is allocated 
     only once and after that the same task applies to all registered fd's. */
@@@ -207,31 -174,32 +207,31 @@@ do {                                                            
  do {                                                  \
    int __i;                                            \
                                                        \
 -  for (__i = 0; __i < (__x)->windows_count; __i++)    \
 -    if ((__x)->windows[__i]->sock->sock == (__fd))    \
 +  for (__i = 0; __i < (__x)->conns_count; __i++)      \
 +    if ((__x)->conns[__i]->sock->sock == (__fd))      \
        break;                                          \
                                                        \
 -  if (__i >= (__x)->windows_count)                    \
 +  if (__i >= (__x)->conns_count)                      \
      (__sock) = NULL;                                  \
 - (__sock) = (__x)->windows[__i]->sock;                        \
 + (__sock) = (__x)->conns[__i]->sock;                  \
  } while(0)
  
 -/* Returns TRUE if windows is currently active window */
 -#define SILC_CLIENT_IS_CURRENT_WIN(__x, __win) ((__x)->current_win == (__win))
 -
  /* Prototypes */
 -int silc_client_alloc(SilcClient *new_client);
 +
 +SilcClient silc_client_alloc(SilcClientOperations *ops, void *application);
  void silc_client_free(SilcClient client);
  int silc_client_init(SilcClient client);
  void silc_client_stop(SilcClient client);
  void silc_client_run(SilcClient client);
 -void silc_client_parse_command_line(unsigned char *buffer, 
 -                                  unsigned char ***parsed,
 -                                  unsigned int **parsed_lens,
 -                                  unsigned int **parsed_types,
 -                                  unsigned int *parsed_num,
 -                                  unsigned int max_args);
 +SilcClientConnection silc_client_add_connection(SilcClient client,
 +                                              char *hostname,
 +                                              int port,
 +                                              void *context);
  int silc_client_connect_to_server(SilcClient client, int port,
 -                                char *host);
 +                                char *host, void *context);
 +int silc_client_start_key_exchange(SilcClient client,
 +                                 SilcClientConnection conn,
 +                                   int fd);
  void silc_client_packet_send(SilcClient client, 
                             SilcSocketConnection sock,
                             SilcPacketType type, 
@@@ -264,28 -232,18 +264,32 @@@ void silc_client_error_by_server(SilcCl
                                 SilcBuffer message);
  void silc_client_notify_by_server(SilcClient client,
                                  SilcSocketConnection sock,
 -                                SilcBuffer message);
 +                                SilcPacketContext *packet);
  void silc_client_receive_new_id(SilcClient client,
                                SilcSocketConnection sock,
 -                              unsigned char *id_string);
 +                              SilcIDPayload idp);
  void silc_client_new_channel_id(SilcClient client,
                                SilcSocketConnection sock,
                                char *channel_name,
 -                              unsigned char *id_string);
 +                              unsigned int mode, SilcIDPayload idp);
  void silc_client_receive_channel_key(SilcClient client,
                                     SilcSocketConnection sock,
                                     SilcBuffer packet);
  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);
 +}