changeset 549:68032209609d

Irccd: initial custom IRC code
author David Demelier <markand@malikania.fr>
date Thu, 23 Nov 2017 22:45:12 +0100
parents a7c0eb100760
children 64cb98dd8e9d
files CMakeLists.txt extern/libircclient/CMakeLists.txt extern/libircclient/include/libirc_errors.h extern/libircclient/include/libirc_events.h extern/libircclient/include/libirc_options.h extern/libircclient/include/libirc_rfcnumeric.h extern/libircclient/include/libircclient.h extern/libircclient/src/colors.c extern/libircclient/src/dcc.c extern/libircclient/src/dcc.h extern/libircclient/src/errors.c extern/libircclient/src/libircclient.c extern/libircclient/src/params.h extern/libircclient/src/portable.c extern/libircclient/src/session.h extern/libircclient/src/sockets.c extern/libircclient/src/ssl.c extern/libircclient/src/utils.c irccd/main.cpp libirccd-js/irccd/js/util_jsapi.cpp libirccd-test/CMakeLists.txt libirccd-test/irccd/js_test.hpp libirccd-test/irccd/plugin_test.cpp libirccd-test/irccd/server-tester.cpp libirccd-test/irccd/server-tester.hpp libirccd/CMakeLists.txt libirccd/irccd/config.cpp libirccd/irccd/config.hpp libirccd/irccd/irc.cpp libirccd/irccd/irc.hpp libirccd/irccd/server.cpp libirccd/irccd/server.hpp libirccd/irccd/service.cpp tests/CMakeLists.txt tests/irc/CMakeLists.txt tests/irc/main.cpp tests/plugin-auth/main.cpp
diffstat 37 files changed, 1279 insertions(+), 7963 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed Nov 22 20:10:03 2017 +0100
+++ b/CMakeLists.txt	Thu Nov 23 22:45:12 2017 +0100
@@ -75,7 +75,6 @@
 include(cmake/IrccdOptions.cmake)
 include(cmake/IrccdSystem.cmake)
 
-add_subdirectory(extern/libircclient)
 add_subdirectory(extern/json)
 add_subdirectory(doc)
 add_subdirectory(libcommon)
--- a/extern/libircclient/CMakeLists.txt	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for irccd
-#
-# Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-# Libircclient bundled
-project(extern-libircclient)
-
-set(FLAGS "ENABLE_THREADS")
-
-# Portability checks
-include(CheckFunctionExists)
-
-check_function_exists(gethostbyname_r HAVE_GETHOSTBYNAME_R)
-check_function_exists(localtime_r HAVE_LOCALTIME_R)
-
-if (HAVE_GETHOSTBYNAME_R)
-    list(APPEND FLAGS "HAVE_GETHOSTBYNAME_R")
-endif ()
-if (HAVE_LOCALTIME_R)
-    list(APPEND FLAGS "HAVE_LOCALTIME_R")
-endif ()
-
-# SSL is optional
-if (HAVE_SSL)
-    list(APPEND INCLUDES ${OPENSSL_INCLUDE_DIR})
-    list(APPEND LIBRARIES ${OPENSSL_LIBRARIES})
-    list(APPEND FLAGS "ENABLE_SSL")
-endif ()
-
-# Enable or disable IPv6
-if (WITH_IPV6)
-    list(APPEND FLAGS "ENABLE_IPV6")
-endif ()
-
-if (WIN32)
-    list(APPEND LIBRARIES ws2_32)
-endif ()
-
-irccd_define_library(
-    LOCAL EXTERNAL
-    TARGET extern-ircclient
-    SOURCES src/libircclient.c
-    FLAGS ${FLAGS}
-    LIBRARIES ${LIBRARIES}
-    PUBLIC_INCLUDES "${extern-libircclient_SOURCE_DIR}/include"
-    LOCAL_INCLUDES ${INCLUDES}
-)
-
-set_target_properties(
-    extern-ircclient
-    PROPERTIES
-        PROJECT_LABEL libircclient
-        FOLDER extern
-)
--- a/extern/libircclient/include/libirc_errors.h	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,235 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-#ifndef INCLUDE_IRC_ERRORS_H
-#define INCLUDE_IRC_ERRORS_H
-
-#ifndef IN_INCLUDE_LIBIRC_H
-	#error This file should not be included directly, include just libircclient.h
-#endif
-
-
-/*! brief No error
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_OK			0
-
-
-/*! \brief Invalid argument
- * 
- * An invalid value was given for one of the arguments to a function. 
- * For example, supplying the NULL value for \a channel argument of 
- * irc_cmd_join() produces LIBIRC_ERR_INVAL error. You should fix the code.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_INVAL		1
-
-
-/*! \brief Could not resolve host.
- * 
- * The host name supplied for irc_connect() function could not be resolved
- * into valid IP address. Usually means that host name is invalid.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_RESOLV		2
-
-
-/*! \brief Could not create socket.
- * 
- * The new socket could not be created or made non-blocking. Usually means
- * that the server is out of resources, or (rarely :) a bug in libircclient.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_SOCKET		3
-
-
-/*! \brief Could not connect.
- * 
- * The socket could not connect to the IRC server, or to the destination DCC
- * part. Usually means that either the IRC server is down or its address is 
- * invalid. For DCC the reason usually is the firewall on your or destination
- * computer, which refuses DCC transfer.
- *
- * \sa irc_run irc_connect
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_CONNECT		4
-
-
-/*! \brief Connection closed by remote peer.
- * 
- * The IRC connection was closed by the IRC server (which could mean that an 
- * IRC operator just have banned you from the server :)), or the DCC connection
- * was closed by remote peer - for example, the other side just quits his mIrc.
- * Usually it is not an error.
- *
- * \sa irc_run irc_connect irc_dcc_callback_t
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_CLOSED		5
-
-
-/*! \brief Out of memory
- * 
- * There are two possible reasons for this error. First is that memory could 
- * not be allocated for libircclient use, and this error usually is fatal.
- * Second reason is that the command queue (which keeps command ready to be 
- * sent to the IRC server) is full, and could not accept more commands yet.
- * In this case you should just wait, and repeat the command later.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_NOMEM		6
-
-
-/*! \brief Could not accept new connection
- * 
- * A DCC chat/send connection from the remote peer could not be accepted.
- * Either the connection was just terminated before it is accepted, or there 
- * is a bug in libircclient.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_ACCEPT		7
-
-
-/*! \brief Could not send this
- * 
- * A \a filename supplied to irc_dcc_sendfile() could not be sent. Either is 
- * is not a file (a directory or a socket, for example), or it is not readable. *
- *
- * \sa LIBIRC_ERR_OPENFILE
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_NODCCSEND	9
-
-
-/*! \brief Could not read DCC file or socket
- * 
- * Either a DCC file could not be read (for example, was truncated during 
- * sending), or a DCC socket returns a read error, which usually means that
- * the network connection is terminated.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_READ			10
-
-
-/*! \brief Could not write DCC file or socket
- * 
- * Either a DCC file could not be written (for example, there is no free space
- * on disk), or a DCC socket returns a write error, which usually means that
- * the network connection is terminated.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_WRITE		11
-
-
-/*! \brief Invalid state
- * 
- * The function is called when it is not allowed to be called. For example,
- * irc_cmd_join() was called before the connection to IRC server succeed, and
- * ::event_connect is called.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_STATE		12
-
-
-/*! \brief Operation timed out
- * 
- * The DCC request is timed out. 
- * There is a timer for each DCC request, which tracks connecting, accepting
- * and non-accepted/declined DCC requests. For every request this timer
- * is currently 60 seconds. If the DCC request was not connected, accepted
- * or declined during this time, it will be terminated with this error.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_TIMEOUT		13
-
-
-/*! \brief Could not open file for DCC send
- * 
- * The file specified in irc_dcc_sendfile() could not be opened.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_OPENFILE		14
-
-
-/*! \brief IRC server connection terminated
- * 
- * The connection to the IRC server was terminated - possibly, by network 
- * error. Try to irc_connect() again.
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_TERMINATED	15
-
-
-/*! \brief IPv6 not supported
- * 
- * The function which requires IPv6 support was called, but the IPv6 support was not compiled
- * into the application
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_NOIPV6		16
-
-
-/*! \brief SSL not supported
- * 
- * The SSL connection was required but the library was not compiled with SSL support
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_SSL_NOT_SUPPORTED		17
-
-
-/*! \brief SSL initialization failed
- * 
- * The SSL connection was required but the library was not compiled with SSL support
- *
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_SSL_INIT_FAILED			18
-
-
-/*! \brief SSL connection failed
- * 
- * SSL handshare failed when attempting to connect to the server. Typically this means you're trying
- * to use SSL but attempting to connect to a non-SSL port.
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_CONNECT_SSL_FAILED		19
-
-
-/*! \brief SSL certificate verify failed
- * 
- * The server is using the self-signed certificate. Use LIBIRC_OPTION_SSL_NO_VERIFY option to connect to it.
- * \ingroup errorcodes
- */
-#define LIBIRC_ERR_SSL_CERT_VERIFY_FAILED	20
-
-
-// Internal max error value count.
-// If you added more errors, add them to errors.c too!
-#define LIBIRC_ERR_MAX			21
-
-#endif /* INCLUDE_IRC_ERRORS_H */
--- a/extern/libircclient/include/libirc_events.h	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,394 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-
-#ifndef INCLUDE_IRC_EVENTS_H
-#define INCLUDE_IRC_EVENTS_H
-
-
-#ifndef IN_INCLUDE_LIBIRC_H
-	#error This file should not be included directly, include just libircclient.h
-#endif
-
-
-
-/*!
- * \fn typedef void (*irc_event_callback_t) (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count)
- * \brief A most common event callback
- *
- * \param session the session, which generates an event
- * \param event   the text name of the event. Useful in case you use a single 
- *                event handler for several events simultaneously. 
- * \param origin  the originator of the event. See the note below.
- * \param params  a list of event params. Depending on the event nature, it 
- *                could have zero or more params. The actual number of params 
- *                is specified in count. None of the params can be NULL, but 
- *                'params' pointer itself could be NULL for some events.
- * \param count   the total number of params supplied.
- *
- * Every event generates a callback. This callback is generated by most events.
- * Depending on the event nature, it can provide zero or more params. For each
- * event, the number of provided params is fixed, and their meaning is 
- * described.
- *
- * Every event has origin, though the \a origin variable may be NULL, which 
- * means that event origin is unknown. The origin usually looks like 
- * nick!host\@ircserver, i.e. like tim!home\@irc.krasnogorsk.ru. Such origins
- * can not be used in IRC commands, and need to be stripped (i.e. host and 
- * server part should be cut off) before using. This can be done either 
- * explicitly, by calling irc_target_get_nick(), or implicitly for all the 
- * events - by setting the #LIBIRC_OPTION_STRIPNICKS option with irc_option_set().
- *
- * \ingroup events
- */
-typedef void (*irc_event_callback_t) (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count);
-
-
-/*!
- * \fn typedef void (*irc_eventcode_callback_t) (irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count)
- * \brief A numeric event callback
- *
- * \param session the session, which generates an event
- * \param event   the numeric code of the event. Useful in case you use a 
- *                single event handler for several events simultaneously. 
- * \param origin  the originator of the event. See the note below.
- * \param params  a list of event params. Depending on the event nature, it 
- *                could have zero or more params. The actual number of params 
- *                is specified in count. None of the params can be NULL, but 
- *                'params' pointer itself could be NULL for some events.
- * \param count   the total number of params supplied.
- *
- * Most times in reply to your actions the IRC server generates numeric 
- * callbacks. Most of them are error codes, and some of them mark list start
- * and list stop markers. Every code has its own set of params; for details
- * you can either experiment, or read RFC 1459.
- *
- * Every event has origin, though the \a origin variable may be NULL, which 
- * means that event origin is unknown. The origin usually looks like 
- * nick!host\@ircserver, i.e. like tim!home\@irc.krasnogorsk.ru. Such origins
- * can not be used in IRC commands, and need to be stripped (i.e. host and 
- * server part should be cut off) before using. This can be done either 
- * explicitly, by calling irc_target_get_nick(), or implicitly for all the 
- * events - by setting the #LIBIRC_OPTION_STRIPNICKS option with irc_option_set().
- *
- * \ingroup events
- */
-typedef void (*irc_eventcode_callback_t) (irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count);
-
-
-/*!
- * \fn typedef void (*irc_event_dcc_chat_t) (irc_session_t * session, const char * nick, const char * addr, irc_dcc_t dccid)
- * \brief A remote DCC CHAT request callback
- *
- * \param session the session, which generates an event
- * \param nick    the person who requested DCC CHAT with you.
- * \param addr    the person's IP address in decimal-dot notation.
- * \param dccid   an id associated with this request. Use it in calls to 
- *                irc_dcc_accept() or irc_dcc_decline().
- *
- * This callback is called when someone requests DCC CHAT with you. In respond
- * you should call either irc_dcc_accept() to accept chat request, or 
- * irc_dcc_decline() to decline chat request.
- *
- * \sa irc_dcc_accept or irc_dcc_decline
- * \ingroup events
- */
-typedef void (*irc_event_dcc_chat_t) (irc_session_t * session, const char * nick, const char * addr, irc_dcc_t dccid);
-
-
-/*!
- * \fn typedef void (*irc_event_dcc_send_t) (irc_session_t * session, const char * nick, const char * addr, const char * filename, unsigned long size, irc_dcc_t dccid)
- * \brief A remote DCC CHAT request callback
- *
- * \param session the session, which generates an event
- * \param nick    the person who requested DCC CHAT with you.
- * \param addr    the person's IP address in decimal-dot notation.
- * \param filename the sent filename.
- * \param size    the filename size.
- * \param dccid   an id associated with this request. Use it in calls to 
- *                irc_dcc_accept() or irc_dcc_decline().
- *
- * This callback is called when someone wants to send a file to you using 
- * DCC SEND. As with chat, in respond you should call either irc_dcc_accept() 
- * to accept this request and receive the file, or irc_dcc_decline() to 
- * decline this request.
- *
- * \sa irc_dcc_accept or irc_dcc_decline
- * \ingroup events
- */
-typedef void (*irc_event_dcc_send_t) (irc_session_t * session, const char * nick, const char * addr, const char * filename, unsigned long size, irc_dcc_t dccid);
-
-
-/*! \brief Event callbacks structure.
- *
- * All the communication with the IRC network is based on events. Generally
- * speaking, event is anything generated by someone else in the network,
- * or by the IRC server itself. "Someone sends you a message", "Someone
- * has joined the channel", "Someone has quits IRC" - all these messages
- * are events.
- *
- * Every event has its own event handler, which is called when the 
- * appropriate event is received. You don't have to define all the event
- * handlers; define only the handlers for the events you need to intercept.
- * 
- * Most event callbacks are the types of ::irc_event_callback_t. There are 
- * also events, which generate ::irc_eventcode_callback_t, 
- * ::irc_event_dcc_chat_t and ::irc_event_dcc_send_t callbacks.
- *
- * \ingroup events
- */
-typedef struct
-{
-	/*!
-	 * The "on_connect" event is triggered when the client successfully 
-	 * connects to the server, and could send commands to the server.
-     * No extra params supplied; \a params is 0.
-	 */
-	irc_event_callback_t	event_connect;
-
-	/*!
-	 * The "ping" event is triggered when the client receives PING requests.
-	 */
-	irc_event_callback_t	event_ping;
-
-	/*!
-	 * The "nick" event is triggered when the client receives a NICK message,
-	 * meaning that someone (including you) on a channel with the client has 
-	 * changed their nickname. 
-	 *
-	 * \param origin the person, who changes the nick. Note that it can be you!
-	 * \param params[0] mandatory, contains the new nick.
-	 */
-	irc_event_callback_t	event_nick;
-
-	/*!
-	 * The "quit" event is triggered upon receipt of a QUIT message, which
-     * means that someone on a channel with the client has disconnected.
-     *
-	 * \param origin the person, who is disconnected
-	 * \param params[0] optional, contains the reason message (user-specified).
-	 */
-	irc_event_callback_t	event_quit;
-
-	/*!
-	 * The "join" event is triggered upon receipt of a JOIN message, which
-     * means that someone has entered a channel that the client is on.
-	 *
-	 * \param origin the person, who joins the channel. By comparing it with 
-	 *               your own nickname, you can check whether your JOIN 
-	 *               command succeed.
-	 * \param params[0] mandatory, contains the channel name.
-	 */
-	irc_event_callback_t	event_join;
-
-	/*!
-	 * The "part" event is triggered upon receipt of a PART message, which
-     * means that someone has left a channel that the client is on.
-	 *
-	 * \param origin the person, who leaves the channel. By comparing it with 
-	 *               your own nickname, you can check whether your PART 
-	 *               command succeed.
-	 * \param params[0] mandatory, contains the channel name.
-	 * \param params[1] optional, contains the reason message (user-defined).
-	 */
-	irc_event_callback_t	event_part;
-           
-	/*!
-	 * The "mode" event is triggered upon receipt of a channel MODE message,
-	 * which means that someone on a channel with the client has changed the
-	 * channel's parameters.
-     *
-	 * \param origin the person, who changed the channel mode.
-	 * \param params[0] mandatory, contains the channel name.
-	 * \param params[1] mandatory, contains the changed channel mode, like 
-	 *        '+t', '-i' and so on.
-	 * \param params[2] optional, contains the mode argument (for example, a
-	 *      key for +k mode, or user who got the channel operator status for 
-	 *      +o mode)
-	 */
-	irc_event_callback_t	event_mode;
-
-	/*!
-	 * The "umode" event is triggered upon receipt of a user MODE message, 
-	 * which means that your user mode has been changed.
-     *
-	 * \param origin the person, who changed the channel mode.
-	 * \param params[0] mandatory, contains the user changed mode, like 
-	 *        '+t', '-i' and so on.
-	 */
-	irc_event_callback_t	event_umode;
-
-	/*!
-	 * The "topic" event is triggered upon receipt of a TOPIC message, which
-     * means that someone on a channel with the client has changed the 
-     * channel's topic.
-     *
-	 * \param origin the person, who changes the channel topic.
-	 * \param params[0] mandatory, contains the channel name.
-	 * \param params[1] optional, contains the new topic.
-	 */
-	irc_event_callback_t	event_topic;
-
-	/*!
-	 * The "kick" event is triggered upon receipt of a KICK message, which
-     * means that someone on a channel with the client (or possibly the
-     * client itself!) has been forcibly ejected.
-	 *
-	 * \param origin the person, who kicked the poor.
-	 * \param params[0] mandatory, contains the channel name.
-	 * \param params[0] optional, contains the nick of kicked person.
-	 * \param params[1] optional, contains the kick text
-	 */
-	irc_event_callback_t	event_kick;
-
-	/*!
-	 * The "channel" event is triggered upon receipt of a PRIVMSG message
-     * to an entire channel, which means that someone on a channel with
-     * the client has said something aloud. Your own messages don't trigger
-     * PRIVMSG event.
-	 *
-	 * \param origin the person, who generates the message.
-	 * \param params[0] mandatory, contains the channel name.
-	 * \param params[1] optional, contains the message text
-	 */
-	irc_event_callback_t	event_channel;
-           
-	/*!
-	 * The "privmsg" event is triggered upon receipt of a PRIVMSG message
-     * which is addressed to one or more clients, which means that someone
-     * is sending the client a private message.
-     *
-	 * \param origin the person, who generates the message.
-	 * \param params[0] mandatory, contains your nick.
-	 * \param params[1] optional, contains the message text
-	 */
-	irc_event_callback_t	event_privmsg;
-
-	/*!
-	 * The "notice" event is triggered upon receipt of a NOTICE message
-     * which means that someone has sent the client a public or private
-     * notice. According to RFC 1459, the only difference between NOTICE 
-     * and PRIVMSG is that you should NEVER automatically reply to NOTICE
-     * messages. Unfortunately, this rule is frequently violated by IRC 
-     * servers itself - for example, NICKSERV messages require reply, and 
-     * are NOTICEs.
-	 *
-	 * \param origin the person, who generates the message.
-	 * \param params[0] mandatory, contains the target nick name.
-	 * \param params[1] optional, contains the message text
-	 */
-	irc_event_callback_t	event_notice;
-
-	/*!
-	 * The "channel_notice" event is triggered upon receipt of a NOTICE
-     * message which means that someone has sent the client a public
-     * notice. According to RFC 1459, the only difference between NOTICE 
-     * and PRIVMSG is that you should NEVER automatically reply to NOTICE
-     * messages. Unfortunately, this rule is frequently violated by IRC 
-     * servers itself - for example, NICKSERV messages require reply, and 
-     * are NOTICEs.
-	 *
-	 * \param origin the person, who generates the message.
-	 * \param params[0] mandatory, contains the channel name.
-	 * \param params[1] optional, contains the message text
-	 */
-	irc_event_callback_t	event_channel_notice;
-
-	/*!
-	 * The "invite" event is triggered upon receipt of an INVITE message,
-     * which means that someone is permitting the client's entry into a +i
-     * channel.
-     *
-	 * \param origin the person, who INVITEs you.
-	 * \param params[0] mandatory, contains your nick.
-	 * \param params[1] mandatory, contains the channel name you're invited into.
-     *
-     * \sa irc_cmd_invite irc_cmd_chanmode_invite
-	 */
-	irc_event_callback_t	event_invite;
-
-	/*!
-	 * The "ctcp" event is triggered when the client receives the CTCP 
-	 * request. By default, the built-in CTCP request handler is used. The 
-	 * build-in handler automatically replies on most CTCP messages, so you
-     * will rarely need to override it.
-     *
-	 * \param origin the person, who generates the message.
-	 * \param params[0] mandatory, the complete CTCP message, including its 
-	 *                  arguments.
-	 * 
-	 * Mirc generates PING, FINGER, VERSION, TIME and ACTION messages,
-	 * check the source code of \c libirc_event_ctcp_internal function to 
-	 * see how to write your own CTCP request handler. Also you may find 
-	 * useful this question in FAQ: \ref faq4
-	 */
-	irc_event_callback_t	event_ctcp_req;
-
-	/*!
-	 * The "ctcp" event is triggered when the client receives the CTCP reply.
-     *
-	 * \param origin the person, who generates the message.
-	 * \param params[0] mandatory, the CTCP message itself with its arguments.
-	 */
-	irc_event_callback_t	event_ctcp_rep;
-
-	/*!
-	 * The "action" event is triggered when the client receives the CTCP 
-	 * ACTION message. These messages usually looks like:\n
-     * \code
-     * [23:32:55] * Tim gonna sleep.
-     * \endcode
-     *
-	 * \param origin the person, who generates the message.
-	 * \param params[0] mandatory, the ACTION message.
-	 */
-	irc_event_callback_t	event_ctcp_action;
-
-	/*!
-	 * The "unknown" event is triggered upon receipt of any number of 
-	 * unclassifiable miscellaneous messages, which aren't handled by the
-     * library.
-	 */
-	irc_event_callback_t	event_unknown;
-          
-	/*!
-	 * The "numeric" event is triggered upon receipt of any numeric response
-	 * from the server. There is a lot of such responses, see the full list
-	 * here: \ref rfcnumbers.
-     *
-     * See the params in ::irc_eventcode_callback_t specification.
-	 */
-	irc_eventcode_callback_t	event_numeric;
-
-	/*!
-	 * The "dcc chat" event is triggered when someone requests a DCC CHAT from 
-	 * you.
-     *
-     * See the params in ::irc_event_dcc_chat_t specification.
-	 */
-	irc_event_dcc_chat_t		event_dcc_chat_req;
-
-	/*!
-	 * The "dcc chat" event is triggered when someone wants to send a file 
-	 * to you via DCC SEND request.
-     *
-     * See the params in ::irc_event_dcc_send_t specification.
-	 */
-	irc_event_dcc_send_t		event_dcc_send_req;
-
-
-} irc_callbacks_t;
-
-
-#endif /* INCLUDE_IRC_EVENTS_H */
--- a/extern/libircclient/include/libirc_options.h	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-#ifndef INCLUDE_IRC_OPTIONS_H
-#define INCLUDE_IRC_OPTIONS_H
-
-#ifndef IN_INCLUDE_LIBIRC_H
-	#error This file should not be included directly, include just libircclient.h
-#endif
-
-/*! 
- * enables additional debug output 
- * \ingroup options
- */
-#define LIBIRC_OPTION_DEBUG			(1 << 1)
-
-/*! \brief allows to strip origins automatically.
- *
- * For every IRC server event, the event origin is sent in standard form:
- * nick!host\@ircserver, i.e. like tim!home\@irc.freenet.org. Such origins
- * can not be used in IRC commands, and need to be stripped (i.e. host and 
- * server part should be cut off) before using. This can be done either 
- * explicitly, by calling irc_target_get_nick(), or implicitly for all the 
- * events - by setting this option with irc_option_set().
- * \ingroup options
- */
-#define LIBIRC_OPTION_STRIPNICKS	(1 << 2)
-
-
-/*! \brief Disables the certificate verification for SSL connections
- *
- * By default the SSL connection authenticy is ensured by verifying that the certificate
- * presented by the server is signed by a known trusted certificate authority. Since those
- * typically cost money, some IRC servers use the self-signed certificates. They provide the
- * benefits of the SSL connection but since they are not signed by the Certificate Authority,
- * their authencity cannot be verified. This option, if set, disables the certificate 
- * verification - the library will accept any certificate presented by the server.
- * 
- * This option must be set before the irc_connect function is called.
- * \ingroup options
- */
-#define LIBIRC_OPTION_SSL_NO_VERIFY (1 << 3)
-
-
-#endif /* INCLUDE_IRC_OPTIONS_H */
--- a/extern/libircclient/include/libirc_rfcnumeric.h	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1255 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-/*! 
- * \file libirc_rfcnumeric.h
- * \author Georgy Yunaev
- * \version 1.0
- * \date 09.2004
- * \brief This file defines RFC numeric reply codes, which should be used in
- * ::event_numeric callback. Every code also has a comment regarding its 
- * arguments.
- */
-
-#ifndef INCLUDE_IRC_RFCNUMERIC_H
-#define INCLUDE_IRC_RFCNUMERIC_H
-
-
-/*! \brief 001 Welcome to the Internet Relay Network
-               \<nick\>!\<user\>\@\<host\>
- * 
- * The server sends replies 001 to 004 to a user upon successful registration.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_WELCOME	001
-
-
-/*! \brief 002 Your host is \<servername\>, running version \<ver\>
- * 
- * The server sends replies 001 to 004 to a user upon successful registration.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_YOURHOST	002
-
-
-/*! \brief 003 This server was created \<date\>
- * 
- * The server sends replies 001 to 004 to a user upon successful registration.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_CREATED	003
-
-
-/*! \brief 004 \<servername\> \<version\> \<available user modes\>
-               \<available channel modes\>
- * 
- * The server sends replies 001 to 004 to a user upon successful registration.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_MYINFO	004
-
-
-/*! \brief 005 Try server \<server name\>, port \<port number\>
- * 
- * Sent by the server to a user to suggest an alternative server. This is often used when the connection is refused because the server is already full.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_BOUNCE	005
-
-
-/*! \brief 302 :*1\<reply\> *( 
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_USERHOST	302
-
-
-/*! \brief 303 :*1\<nick\> *( 
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ISON	303
-
-
-/*! \brief 301 \<nick\> :\<away message\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_AWAY	301
-
-
-/*! \brief 305 :You are no longer marked as being away
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_UNAWAY	305
-
-
-/*! \brief 306 :You have been marked as being away
- * 
- * These replies are used with the AWAY command (if allowed). RPL_AWAY is sent to any client sending a PRIVMSG to a client which is away. RPL_AWAY is only sent by the server to which the client is connected. Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the client removes and sets an AWAY message.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_NOWAWAY	306
-
-
-/*! \brief 311 \<nick\> \<user\> \<host\> * :\<real name\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_WHOISUSER	311
-
-
-/*! \brief 312 \<nick\> \<server\> :\<server info\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_WHOISSERVER	312
-
-
-/*! \brief 313 \<nick\> :is an IRC operator
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_WHOISOPERATOR	313
-
-
-/*! \brief 317 \<nick\> \<integer\> :seconds idle
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_WHOISIDLE	317
-
-
-/*! \brief 318 \<nick\> :End of WHOIS list
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFWHOIS	318
-
-
-/*! \brief 319 "<nick> :*( ( "\@" / "+" ) \<channel\> " " )"
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_WHOISCHANNELS	319
-
-
-/*! \brief 314 \<nick\> \<user\> \<host\> * :\<real name\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_WHOWASUSER	314
-
-
-/*! \brief 369 \<nick\> :End of WHOWAS
- * 
- * When replying to a WHOWAS message, a server MUST use the replies RPL_WHOWASUSER, RPL_WHOISSERVER or ERR_WASNOSUCHNICK for each nickname in the presented list. At the end of all reply batches, there MUST be RPL_ENDOFWHOWAS (even if there was only one reply and it was an error).
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFWHOWAS	369
-
-
-/*! \brief 322 \<channel\> \<# visible\> :\<topic\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_LIST	322
-
-
-/*! \brief 323 :End of LIST
- * 
- * Replies RPL_LIST, RPL_LISTEND mark the actual replies with data and end of the server's response to a LIST command. If there are no channels available to return, only the end reply MUST be sent.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_LISTEND	323
-
-
-/*! \brief 325 \<channel\> \<nickname\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_UNIQOPIS	325
-
-
-/*! \brief 324 \<channel\> \<mode\> \<mode params\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_CHANNELMODEIS	324
-
-
-/*! \brief 331 \<channel\> :No topic is set
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_NOTOPIC	331
-
-
-/*! \brief 332 \<channel\> :\<topic\>
- * 
- * When sending a TOPIC message to determine the channel topic, one of two replies is sent. If the topic is set, RPL_TOPIC is sent back else RPL_NOTOPIC.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TOPIC	332
-
-
-/*! \brief 341 \<channel\> \<nick\>
- * 
- * Returned by the server to indicate that the attempted INVITE message was successful and is being passed onto the end client.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_INVITING	341
-
-
-/*! \brief 342 \<user\> :Summoning user to IRC
- * 
- * Returned by a server answering a SUMMON message to indicate that it is summoning that user.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_SUMMONING	342
-
-
-/*! \brief 346 \<channel\> \<invitemask\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_INVITELIST	346
-
-
-/*! \brief 347 \<channel\> :End of channel invite list
- * 
- * When listing the 'invitations masks' for a given channel, a server is required to send the list back using the RPL_INVITELIST and RPL_ENDOFINVITELIST messages. A separate RPL_INVITELIST is sent for each active mask. After the masks have been listed (or if none present) a RPL_ENDOFINVITELIST MUST be sent.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFINVITELIST	347
-
-
-/*! \brief 348 \<channel\> \<exceptionmask\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_EXCEPTLIST	348
-
-
-/*! \brief 349 \<channel\> :End of channel exception list
- * 
- * When listing the 'exception masks' for a given channel, a server is required to send the list back using the RPL_EXCEPTLIST and RPL_ENDOFEXCEPTLIST messages. A separate RPL_EXCEPTLIST is sent for each active mask. After the masks have been listed (or if none present) a RPL_ENDOFEXCEPTLIST MUST be sent.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFEXCEPTLIST	349
-
-
-/*! \brief 351 \<version\>.\<debuglevel\> \<server\> :\<comments\>
- * 
- * Reply by the server showing its version details. The \<version\> is the version of the software being used (including any patchlevel revisions) and the \<debuglevel\> is used to indicate if the server is running in "debug mode". The "comments" field may contain any comments about the version or further version details.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_VERSION	351
-
-
-/*! \brief 352 \<channel\> \<user\> \<host\> \<server\> \<nick\>
-              ( "H
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_WHOREPLY	352
-
-
-/*! \brief 315 \<name\> :End of WHO list
- * 
- * The RPL_WHOREPLY and RPL_ENDOFWHO pair are used to answer a WHO message. The RPL_WHOREPLY is only sent if there is an appropriate match to the WHO query. If there is a list of parameters supplied with a WHO message, a RPL_ENDOFWHO MUST be sent after processing each list item with \<name\> being the item.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFWHO	315
-
-
-/*! \brief 353 ( "=
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_NAMREPLY	353
-
-
-/*! \brief 366 \<channel\> :End of NAMES list
- * 
- * To reply to a NAMES message, a reply pair consisting of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the server back to the client. If there is no channel found as in the query, then only RPL_ENDOFNAMES is returned. The exception to this is when a NAMES message is sent with no parameters and all visible channels and contents are sent back in a series of RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark the end.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFNAMES	366
-
-
-/*! \brief 364 \<mask\> \<server\> :\<hopcount\> \<server info\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_LINKS	364
-
-
-/*! \brief 365 \<mask\> :End of LINKS list
- * 
- * In replying to the LINKS message, a server MUST send replies back using the RPL_LINKS numeric and mark the end of the list using an RPL_ENDOFLINKS reply.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFLINKS	365
-
-
-/*! \brief 367 \<channel\> \<banmask\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_BANLIST	367
-
-
-/*! \brief 368 \<channel\> :End of channel ban list
- * 
- * When listing the active 'bans' for a given channel, a server is required to send the list back using the RPL_BANLIST and RPL_ENDOFBANLIST messages. A separate RPL_BANLIST is sent for each active banmask. After the banmasks have been listed (or if none present) a RPL_ENDOFBANLIST MUST be sent.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFBANLIST	368
-
-
-/*! \brief 371 :\<string\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_INFO	371
-
-
-/*! \brief 374 :End of INFO list
- * 
- * A server responding to an INFO message is required to send all its 'info' in a series of RPL_INFO messages with a RPL_ENDOFINFO reply to indicate the end of the replies.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFINFO	374
-
-
-/*! \brief 375 :- \<server\> Message of the day - 
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_MOTDSTART	375
-
-
-/*! \brief 372 :- \<text\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_MOTD	372
-
-
-/*! \brief 376 :End of MOTD command
- * 
- * When responding to the MOTD message and the MOTD file is found, the file is displayed line by line, with each line no longer than 80 characters, using RPL_MOTD format replies. These MUST be surrounded by a RPL_MOTDSTART (before the RPL_MOTDs) and an RPL_ENDOFMOTD (after).
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFMOTD	376
-
-
-/*! \brief 381 :You are now an IRC operator
- * 
- * RPL_YOUREOPER is sent back to a client which has just successfully issued an OPER message and gained operator status.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_YOUREOPER	381
-
-
-/*! \brief 382 \<config file\> :Rehashing
- * 
- * If the REHASH option is used and an operator sends a REHASH message, an RPL_REHASHING is sent back to the operator.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_REHASHING	382
-
-
-/*! \brief 383 You are service \<servicename\>
- * 
- * Sent by the server to a service upon successful registration.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_YOURESERVICE	383
-
-
-/*! \brief 391 \<server\> :\<string showing server's local time\>
- * 
- * When replying to the TIME message, a server MUST send the reply using the RPL_TIME format above. The string showing the time need only contain the correct day and time there. There is no further requirement for the time string.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TIME	391
-
-
-/*! \brief 392 :UserID   Terminal  Host
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_USERSSTART	392
-
-
-/*! \brief 393 :\<username\> \<ttyline\> \<hostname\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_USERS	393
-
-
-/*! \brief 394 :End of users
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFUSERS	394
-
-
-/*! \brief 395 :Nobody logged in
- * 
- * If the USERS message is handled by a server, the replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and RPL_NOUSERS are used. RPL_USERSSTART MUST be sent first, following by either a sequence of RPL_USERS or a single RPL_NOUSER. Following this is RPL_ENDOFUSERS.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_NOUSERS	395
-
-
-/*! \brief 200 Link \<version \& debug level\> \<destination\>
-               \<next server\> V\<protocol version\>
-               \<link uptime in seconds\> \<backstream sendq\>
-               \<upstream sendq\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACELINK	200
-
-
-/*! \brief 201 Try. \<class\> \<server\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACECONNECTING	201
-
-
-/*! \brief 202 H.S. \<class\> \<server\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACEHANDSHAKE	202
-
-
-/*! \brief 203 ???? \<class\> [\<client IP address in dot form\>]
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACEUNKNOWN	203
-
-
-/*! \brief 204 Oper \<class\> \<nick\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACEOPERATOR	204
-
-
-/*! \brief 205 User \<class\> \<nick\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACEUSER	205
-
-
-/*! \brief 206 Serv \<class\> \<int\>S \<int\>C \<server\>
-               \<nick!user|*!*\>\@\<host|server\> V\<protocol version\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACESERVER	206
-
-
-/*! \brief 207 Service \<class\> \<name\> \<type\> \<active type\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACESERVICE	207
-
-
-/*! \brief 208 \<newtype\> 0 \<client name\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACENEWTYPE	208
-
-
-/*! \brief 209 Class \<class\> \<count\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACECLASS	209
-
-
-/*! \brief 261 File \<logfile\> \<debug level\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACELOG	261
-
-
-/*! \brief 262 \<server name\> \<version \& debug level\> :End of TRACE
- * 
- * The RPL_TRACE* are all returned by the server in response to the TRACE message. How many are returned is dependent on the TRACE message and whether it was sent by an operator or not. There is no predefined order for which occurs first. Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and RPL_TRACEHANDSHAKE are all used for connections which have not been fully established and are either unknown, still attempting to connect or in the process of completing the 'server handshake'. RPL_TRACELINK is sent by any server which handles a TRACE message and has to pass it on to another server. The list of RPL_TRACELINKs sent in response to a TRACE command traversing the IRC network should reflect the actual connectivity of the servers themselves along that path. RPL_TRACENEWTYPE is to be used for any connection which does not fit in the other categories but is being displayed anyway. RPL_TRACEEND is sent to indicate the end of the list.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRACEEND	262
-
-
-/*! \brief 211 \<linkname\> \<sendq\> \<sent messages\>
-               \<sent Kbytes\> \<received messages\>
-               \<received Kbytes\> \<time open\>
- * 
- * reports statistics on a connection. \<linkname\> identifies the particular connection, \<sendq\> is the amount of data that is queued and waiting to be sent \<sent messages\> the number of messages sent, and \<sent Kbytes\> the amount of data sent, in Kbytes. \<received messages\> and \<received Kbytes\> are the equivalent of \<sent messages\> and \<sent Kbytes\> for received data, respectively. \<time open\> indicates how long ago the connection was opened, in seconds.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_STATSLINKINFO	211
-
-
-/*! \brief 212 \<command\> \<count\> \<byte count\> \<remote count\>
- * 
- * reports statistics on commands usage.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_STATSCOMMANDS	212
-
-
-/*! \brief 219 \<stats letter\> :End of STATS report
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ENDOFSTATS	219
-
-
-/*! \brief 242 :Server Up %d days %d:%02d:%02d
- * 
- * reports the server uptime.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_STATSUPTIME	242
-
-
-/*! \brief 243 O \<hostmask\> * \<name\>
- * 
- * reports the allowed hosts from where user may become IRC operators.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_STATSOLINE	243
-
-
-/*! \brief 221 \<user mode string\>
- * 
- * To answer a query about a client's own mode, RPL_UMODEIS is sent back.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_UMODEIS	221
-
-
-/*! \brief 234 \<name\> \<server\> \<mask\> \<type\> \<hopcount\> \<info\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_SERVLIST	234
-
-
-/*! \brief 235 \<mask\> \<type\> :End of service listing
- * 
- * When listing services in reply to a SERVLIST message, a server is required to send the list back using the RPL_SERVLIST and RPL_SERVLISTEND messages. A separate RPL_SERVLIST is sent for each service. After the services have been listed (or if none present) a RPL_SERVLISTEND MUST be sent.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_SERVLISTEND	235
-
-
-/*! \brief 251 :There are \<integer\> users and \<integer\>
-               services on \<integer\> servers
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_LUSERCLIENT	251
-
-
-/*! \brief 252 \<integer\> :operator(s) online
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_LUSEROP	252
-
-
-/*! \brief 253 \<integer\> :unknown connection(s)
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_LUSERUNKNOWN	253
-
-
-/*! \brief 254 \<integer\> :channels formed
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_LUSERCHANNELS	254
-
-
-/*! \brief 255 :I have \<integer\> clients and \<integer\>
-                servers
- * 
- * In processing an LUSERS message, the server sends a set of replies from RPL_LUSERCLIENT, RPL_LUSEROP, RPL_USERUNKNOWN, RPL_LUSERCHANNELS and RPL_LUSERME. When replying, a server MUST send back RPL_LUSERCLIENT and RPL_LUSERME. The other replies are only sent back if a non-zero count is found for them.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_LUSERME	255
-
-
-/*! \brief 256 \<server\> :Administrative info
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ADMINME	256
-
-
-/*! \brief 257 :\<admin info\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ADMINLOC1	257
-
-
-/*! \brief 258 :\<admin info\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ADMINLOC2	258
-
-
-/*! \brief 259 :\<admin info\>
- * 
- * When replying to an ADMIN message, a server is expected to use replies RPL_ADMINME through to RPL_ADMINEMAIL and provide a text message with each. For RPL_ADMINLOC1 a description of what city, state and country the server is in is expected, followed by details of the institution (RPL_ADMINLOC2) and finally the administrative contact for the server (an email address here is REQUIRED) in RPL_ADMINEMAIL.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_ADMINEMAIL	259
-
-
-/*! \brief 263 \<command\> :Please wait a while and try again.
- * 
- * When a server drops a command without processing it, it MUST use the reply RPL_TRYAGAIN to inform the originating client.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_RPL_TRYAGAIN	263
-
-
-/*! \brief 401 \<nickname\> :No such nick/channel
- * 
- * Used to indicate the nickname parameter supplied to a command is currently unused.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOSUCHNICK	401
-
-
-/*! \brief 402 \<server name\> :No such server
- * 
- * Used to indicate the server name given currently does not exist.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOSUCHSERVER	402
-
-
-/*! \brief 403 \<channel name\> :No such channel
- * 
- * Used to indicate the given channel name is invalid.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOSUCHCHANNEL	403
-
-
-/*! \brief 404 \<channel name\> :Cannot send to channel
- * 
- * Sent to a user who is either (a) not on a channel which is mode +n or (b) not a chanop (or mode +v) on a channel which has mode +m set or where the user is banned and is trying to send a PRIVMSG message to that channel.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_CANNOTSENDTOCHAN	404
-
-
-/*! \brief 405 \<channel name\> :You have joined too many channels
- * 
- * Sent to a user when they have joined the maximum number of allowed channels and they try to join another channel.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_TOOMANYCHANNELS	405
-
-
-/*! \brief 406 \<nickname\> :There was no such nickname
- * 
- * Returned by WHOWAS to indicate there is no history information for that nickname.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_WASNOSUCHNICK	406
-
-
-/*! \brief 407 \<target\> :\<error code\> recipients. \<abort message\>
- * 
- * Returned to a client which is attempting to send a PRIVMSG/NOTICE using the user\@host destination format and for a user\@host which has several occurrences. - Returned to a client which trying to send a PRIVMSG/NOTICE to too many recipients. - Returned to a client which is attempting to JOIN a safe channel using the shortname when there are more than one such channel.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_TOOMANYTARGETS	407
-
-
-/*! \brief 408 \<service name\> :No such service
- * 
- * Returned to a client which is attempting to send a SQUERY to a service which does not exist.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOSUCHSERVICE	408
-
-
-/*! \brief 409 :No origin specified
- * 
- * PING or PONG message missing the originator parameter.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOORIGIN	409
-
-
-/*! \brief 411 :No recipient given (\<command\>)
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NORECIPIENT	411
-
-
-/*! \brief 412 :No text to send
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOTEXTTOSEND	412
-
-
-/*! \brief 413 \<mask\> :No toplevel domain specified
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOTOPLEVEL	413
-
-
-/*! \brief 414 \<mask\> :Wildcard in toplevel domain
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_WILDTOPLEVEL	414
-
-
-/*! \brief 415 \<mask\> :Bad Server/host mask
- * 
- * 412 - 415 are returned by PRIVMSG to indicate that the message wasn't delivered for some reason. ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that are returned when an invalid use of "PRIVMSG $\<server\>" or "PRIVMSG #\<host\>" is attempted.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_BADMASK	415
-
-
-/*! \brief 421 \<command\> :Unknown command
- * 
- * Returned to a registered client to indicate that the command sent is unknown by the server.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_UNKNOWNCOMMAND	421
-
-
-/*! \brief 422 :MOTD File is missing
- * 
- * Server's MOTD file could not be opened by the server.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOMOTD	422
-
-
-/*! \brief 423 \<server\> :No administrative info available
- * 
- * Returned by a server in response to an ADMIN message when there is an error in finding the appropriate information.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOADMININFO	423
-
-
-/*! \brief 424 :File error doing \<file op\> on \<file\>
- * 
- * Generic error message used to report a failed file operation during the processing of a message.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_FILEERROR	424
-
-
-/*! \brief 431 :No nickname given
- * 
- * Returned when a nickname parameter expected for a command and isn't found.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NONICKNAMEGIVEN	431
-
-
-/*! \brief 432 \<nick\> :Erroneous nickname
- * 
- * Returned after receiving a NICK message which contains characters which do not fall in the defined set. See section 2.3.1 for details on valid nicknames.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_ERRONEUSNICKNAME	432
-
-
-/*! \brief 433 \<nick\> :Nickname is already in use
- * 
- * Returned when a NICK message is processed that results in an attempt to change to a currently existing nickname.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NICKNAMEINUSE	433
-
-
-/*! \brief 436 \<nick\> :Nickname collision KILL from \<user\>\@\<host\>
- * 
- * Returned by a server to a client when it detects a nickname collision (registered of a NICK that already exists by another server).
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NICKCOLLISION	436
-
-
-/*! \brief 437 \<nick/channel\> :Nick/channel is temporarily unavailable
- * 
- * Returned by a server to a user trying to join a channel currently blocked by the channel delay mechanism. - Returned by a server to a user trying to change nickname when the desired nickname is blocked by the nick delay mechanism.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_UNAVAILRESOURCE	437
-
-
-/*! \brief 441 \<nick\> \<channel\> :They aren't on that channel
- * 
- * Returned by the server to indicate that the target user of the command is not on the given channel.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_USERNOTINCHANNEL	441
-
-
-/*! \brief 442 \<channel\> :You're not on that channel
- * 
- * Returned by the server whenever a client tries to perform a channel affecting command for which the client isn't a member.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOTONCHANNEL	442
-
-
-/*! \brief 443 \<user\> \<channel\> :is already on channel
- * 
- * Returned when a client tries to invite a user to a channel they are already on.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_USERONCHANNEL	443
-
-
-/*! \brief 444 \<user\> :User not logged in
- * 
- * Returned by the summon after a SUMMON command for a user was unable to be performed since they were not logged in.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOLOGIN	444
-
-
-/*! \brief 445 :SUMMON has been disabled
- * 
- * Returned as a response to the SUMMON command. MUST be returned by any server which doesn't implement it.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_SUMMONDISABLED	445
-
-
-/*! \brief 446 :USERS has been disabled
- * 
- * Returned as a response to the USERS command. MUST be returned by any server which does not implement it.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_USERSDISABLED	446
-
-
-/*! \brief 451 :You have not registered
- * 
- * Returned by the server to indicate that the client MUST be registered before the server will allow it to be parsed in detail.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOTREGISTERED	451
-
-
-/*! \brief 461 \<command\> :Not enough parameters
- * 
- * Returned by the server by numerous commands to indicate to the client that it didn't supply enough parameters.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NEEDMOREPARAMS	461
-
-
-/*! \brief 462 :Unauthorized command (already registered)
- * 
- * Returned by the server to any link which tries to change part of the registered details (such as password or user details from second USER message).
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_ALREADYREGISTRED	462
-
-
-/*! \brief 463 :Your host isn't among the privileged
- * 
- * Returned to a client which attempts to register with a server which does not been setup to allow connections from the host the attempted connection is tried.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOPERMFORHOST	463
-
-
-/*! \brief 464 :Password incorrect
- * 
- * Returned to indicate a failed attempt at registering a connection for which a password was required and was either not given or incorrect.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_PASSWDMISMATCH	464
-
-
-/*! \brief 465 :You are banned from this server
- * 
- * Returned after an attempt to connect and register yourself with a server which has been setup to explicitly deny connections to you.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_YOUREBANNEDCREEP	465
-
-
-/*! \brief 466 :You will be banned from this server
- * 
- * Sent by a server to a user to inform that access to the server will soon be denied.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_YOUWILLBEBANNED	466
-
-
-/*! \brief 467 \<channel\> :Channel key already set
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_KEYSET	467
-
-
-/*! \brief 471 \<channel\> :Cannot join channel (+l)
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_CHANNELISFULL	471
-
-
-/*! \brief 472 \<char\> :is unknown mode char to me for \<channel\>
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_UNKNOWNMODE	472
-
-
-/*! \brief 473 \<channel\> :Cannot join channel (+i)
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_INVITEONLYCHAN	473
-
-
-/*! \brief 474 \<channel\> :Cannot join channel (+b)
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_BANNEDFROMCHAN	474
-
-
-/*! \brief 475 \<channel\> :Cannot join channel (+k)
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_BADCHANNELKEY	475
-
-
-/*! \brief 476 \<channel\> :Bad Channel Mask
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_BADCHANMASK	476
-
-
-/*! \brief 477 \<channel\> :Channel doesn't support modes
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOCHANMODES	477
-
-
-/*! \brief 478 \<channel\> \<char\> :Channel list is full
- * 
- * No description available in RFC
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_BANLISTFULL	478
-
-
-/*! \brief 481 :Permission Denied- You're not an IRC operator
- * 
- * Any command requiring operator privileges to operate MUST return this error to indicate the attempt was unsuccessful.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOPRIVILEGES	481
-
-
-/*! \brief 482 \<channel\> :You're not channel operator
- * 
- * Any command requiring 'chanop' privileges (such as MODE messages) MUST return this error if the client making the attempt is not a chanop on the specified channel.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_CHANOPRIVSNEEDED	482
-
-
-/*! \brief 483 :You can't kill a server!
- * 
- * Any attempts to use the KILL command on a server are to be refused and this error returned directly to the client.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_CANTKILLSERVER	483
-
-
-/*! \brief 484 :Your connection is restricted!
- * 
- * Sent by the server to a user upon connection to indicate the restricted nature of the connection (user mode "+r").
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_RESTRICTED	484
-
-
-/*! \brief 485 :You're not the original channel operator
- * 
- * Any MODE requiring "channel creator" privileges MUST return this error if the client making the attempt is not a chanop on the specified channel.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_UNIQOPPRIVSNEEDED	485
-
-
-/*! \brief 491 :No O-lines for your host
- * 
- * If a client sends an OPER message and the server has not been configured to allow connections from the client's host as an operator, this error MUST be returned.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_NOOPERHOST	491
-
-
-/*! \brief 501 :Unknown MODE flag
- * 
- * Returned by the server to indicate that a MODE message was sent with a nickname parameter and that the a mode flag sent was not recognized.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_UMODEUNKNOWNFLAG	501
-
-
-/*! \brief 502 :Cannot change mode for other users
- * 
- * Error sent to any user trying to view or change the user mode for a user other than themselves.
- *
- * \ingroup rfcnumbers
- */
-#define LIBIRC_RFC_ERR_USERSDONTMATCH	502
-
-
-#endif /* INCLUDE_IRC_RFCNUMERIC_H */
--- a/extern/libircclient/include/libircclient.h	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1499 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-/*! 
- * \file libircclient.h
- * \author George Yunaev
- * \version 1.5
- * \date 01.2012
- * \brief This file defines all prototypes and functions to use libircclient.
- *
- * libircclient is a small but powerful library, which implements client-server IRC
- * protocol. It is designed to be small, fast, portable and compatible to RFC
- * standards, and most IRC clients. libircclient features include:
- * - Full multi-threading support.
- * - Single threads handles all the IRC processing.
- * - Support for single-threaded applications, and socket-based applications, 
- *   which use select()
- * - Synchronous and asynchronous interfaces.
- * - CTCP support with optional build-in reply code.
- * - Flexible DCC support, including both DCC chat, and DCC file transfer.
- * - Can both initiate and react to initiated DCC.
- * - Can accept or decline DCC sessions asynchronously.
- * - Plain C interface and implementation (possible to use from C++ code, 
- *   obviously)
- * - Compatible with RFC 1459 and most IRC clients.
- * - SSL support if compiled with --enable-openssl.
- * - Free, licensed under LGPL license.
- *
- * Note that to use libircclient, only libircclient.h should be included into your 
- * program. Do not include other libirc_* headers.
- */
-
-#ifndef INCLUDE_LIBIRC_H
-#define INCLUDE_LIBIRC_H
-
-#include <stdlib.h>
-
-#if !defined (_WIN32)
-	#include <sys/select.h>	/* fd_set */
-#else
-	#include <winsock2.h>
-	#include <ws2tcpip.h>
-	#if defined (ENABLE_IPV6)
-		typedef int  (WSAAPI * getaddrinfo_ptr_t)  (const char *, const char* , const struct addrinfo *, struct addrinfo **);
-		typedef void (WSAAPI * freeaddrinfo_ptr_t) (struct addrinfo*);
-	#endif
-#endif
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/*! \brief A libircclient IRC session.
- *
- * This structure describes an IRC session. Its members are internal to 
- * libircclient, and should not be used directly.
- */
-typedef struct irc_session_s	irc_session_t;
-
-/*! \brief A libircclient DCC session.
- *
- * This structure describes a DCC session used by libircclient. 
- * Its members are internal to libircclient, and should not be used directly.
- */
-typedef struct irc_dcc_session_s	irc_dcc_session_t;
-
-
-/*! \brief A DCC session identifier.
- *
- * The irc_dcc_t type is a DCC session identifier, used to identify the
- * DCC sessions in callbacks and various functions.
- */
-typedef unsigned int				irc_dcc_t;
-
-
-/*!
- * \fn typedef void (*irc_dcc_callback_t) (irc_session_t * session, irc_dcc_t id, int status, void * ctx, const char * data, unsigned int length)
- * \brief A common DCC callback, used to inform you about the current DCC state or event.
- *
- * \param session An IRC session which generates the callback
- * \param id  A DCC session id.
- * \param status An error status. 0 means no error, otherwise error code.
- * \param ctx A user-supplied context.
- * \param data Data supplied (if available)
- * \param length data length (if available)
- *
- * This callback is called for all DCC functions when state change occurs.
- *
- * For DCC CHAT, the callback is called in next circumstances:
- * - \a status is LIBIRC_ERR_CLOSED: connection is closed by remote peer. 
- *      After returning from the callback, the DCC session is automatically 
- *      destroyed.
- * - \a status is neither 0 nor LIBIRC_ERR_CLOSED: socket I/O error 
- *      (connect error, accept error, recv error, send error). After returning 
- *      from the callback, the DCC session is automatically destroyed.
- * - \a status is 0: new chat message received, \a data contains the message
- *      (null-terminated string), \a length contains the message length.
- *      
- * For DCC SEND, while file is sending, callback called in next circumstances:
- * - \a status is neither 0 nor LIBIRC_ERR_CLOSED: socket I/O error 
- *      (connect error, accept error, recv error, send error). After returning 
- *      from the callback, the DCC session is automatically destroyed.
- * - \a status is 0: new data received, \a data contains the data received,
- *      \a length contains the amount of data received.
- *      
- * For DCC RECV, while file is sending, callback called in next circumstances:
- * - \a status is neither 0 nor LIBIRC_ERR_CLOSED: socket I/O error 
- *      (connect error, accept error, recv error, send error). After returning 
- *      from the callback, the DCC session is automatically destroyed.
- * - \a status is 0, and \a data is 0: file has been received successfully.
- *      After returning from the callback, the DCC session is automatically 
- *      destroyed.
- * - \a status is 0, and \a data is not 0: new data received, \a data contains 
- *      the data received, \a length contains the amount of data received.
- *
- * \ingroup dccstuff
- */
-typedef void (*irc_dcc_callback_t) (irc_session_t * session, irc_dcc_t id, int status, void * ctx, const char * data, unsigned int length);
-
-
-#define IN_INCLUDE_LIBIRC_H
-#include "libirc_errors.h"
-#include "libirc_events.h"
-#include "libirc_options.h"
-#undef IN_INCLUDE_LIBIRC_H
-
-
-/*!
- * \fn irc_session_t * irc_create_session (irc_callbacks_t * callbacks)
- * \brief Creates and initiates a new IRC session.
- *
- * \param callbacks A structure, which defines several callbacks, which will 
- *                  be called on appropriate events. Must not be NULL.
- *
- * \return An ::irc_session_t object, or 0 if creation failed. Usually,
- *         failure is caused by out of memory error.
- *
- * Every ::irc_session_t object describes a single IRC session - a connection
- * to an IRC server, and possibly to some DCC clients. Almost every irc_* 
- * function requires this object to be passed to, and therefore this function 
- * should be called first.
- *
- * Every session created must be destroyed when it is not needed anymore
- * by calling irc_destroy_session().
- *
- * The most common function sequence is:
- * \code
- *  ... prepare irc_callbacks_t structure ...
- *  irc_create_session();
- *  irc_connect();
- *  irc_run();
- *  irc_destroy_session();
- * \endcode
- *
- * \sa irc_destroy_session
- * \ingroup initclose
- */
-irc_session_t * irc_create_session (irc_callbacks_t	* callbacks);
-
-
-/*!
- * \fn void irc_destroy_session (irc_session_t * session)
- * \brief Destroys previously created IRC session.
- *
- * \param session A session to destroy. Must not be NULL.
- *
- * This function should be used to destroy an IRC session, close the 
- * connection to the IRC server, and free all the used resources. After 
- * calling this function, you should not use this session object anymore.
- *
- * \ingroup initclose
- */
-void irc_destroy_session (irc_session_t * session);
-
-
-/*!
- * \fn int irc_connect (irc_session_t * session, const char * server, unsigned short port, const char * server_password, const char * nick, const char * username, const char * realname);
- * \brief Initiates a connection to IRC server.
- *
- * \param session A session to initiate connections on. Must not be NULL.
- * \param server  A domain name or an IP address of the IRC server to connect to. Cannot be NULL.
- *                If the library is built with SSL support and the first character is hash, tries to establish the SSL connection. 
- *                For example, the connection to "irc.example.com" is assumed to be plaintext, and connection to "#irc.example.com"
- *                is assumed to be secured by SSL. Note that SSL will only work if the library is built with the SSL support.
- * \param port    An IRC server port, usually 6667.
- * \param server_password  An IRC server password, if the server requires it.
- *                May be NULL, in this case password will not be send to the 
- *                IRC server. Vast majority of IRC servers do not require passwords.
- * \param nick    A nick, which libircclient will use to login to the IRC server.
- *                Must not be NULL.
- * \param username A username of the account, which is used to connect to the
- *                IRC server. This is for information only, will be shown in
- *                "user properties" dialogs and returned by /whois request.
- *                May be NULL, in this case 'nobody' will be sent as username.
- * \param realname A real name of the person, who connects to the IRC. Usually
- *                people put some wide-available information here (URL, small
- *                description or something else). This information also will 
- *                be shown in "user properties" dialogs and returned by /whois 
- *                request. May be NULL, in this case 'noname' will be sent as 
- *                username.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function prepares and initiates a connection to the IRC server. The
- * connection is done asynchronously (see irc_callbacks_t::event_connect), so the success 
- * return value means that connection was initiated (but not completed!)
- * successfully.
- *
- * \sa irc_run
- * \ingroup conndisc
- */
-int irc_connect (irc_session_t * session, 
-			const char * server, 
-			unsigned short port,
-			const char * server_password,
-			const char * nick,
-			const char * username,
-			const char * realname);
-
-
-/*!
- * \fn int irc_connect6 (irc_session_t * session, const char * server, unsigned short port, const char * server_password, const char * nick, const char * username, const char * realname);
- * \brief Initiates a connection to IRC server using IPv6.
- *
- * \param session A session to initiate connections on. Must not be NULL.
- * \param server  A domain name or an IP address of the IRC server to connect to. Cannot be NULL.
- *                If the library is built with SSL support and the first character is hash, tries to establish the SSL connection. 
- *                For example, the connection to "irc.example.com" is assumed to be plaintext, and connection to "#irc.example.com"
- *                is assumed to be secured by SSL. Note that SSL will only work if the library is built with the SSL support.
- * \param port    An IRC server port, usually 6667. 
- * \param server_password  An IRC server password, if the server requires it.
- *                May be NULL, in this case password will not be send to the 
- *                IRC server. Vast majority of IRC servers do not require passwords.
- * \param nick    A nick, which libircclient will use to login to the IRC server.
- *                Must not be NULL.
- * \param username A username of the account, which is used to connect to the
- *                IRC server. This is for information only, will be shown in
- *                "user properties" dialogs and returned by /whois request.
- *                May be NULL, in this case 'nobody' will be sent as username.
- * \param realname A real name of the person, who connects to the IRC. Usually
- *                people put some wide-available information here (URL, small
- *                description or something else). This information also will 
- *                be shown in "user properties" dialogs and returned by /whois 
- *                request. May be NULL, in this case 'noname' will be sent as 
- *                username.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function prepares and initiates a connection to the IRC server. The
- * connection is done asynchronously (see irc_callbacks_t::event_connect), so the success 
- * return value means that connection was initiated (but not completed!)
- * successfully.
- *
- * \sa irc_run
- * \ingroup conndisc
- */
-int irc_connect6 (irc_session_t * session, 
-			const char * server, 
-			unsigned short port,
-			const char * server_password,
-			const char * nick,
-			const char * username,
-			const char * realname);
-
-/*!
- * \fn void irc_disconnect (irc_session_t * session)
- * \brief Disconnects a connection to IRC server.
- *
- * \param session An IRC session.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno().
- *
- * This function closes the IRC connection. After that connection is closed,
- * libircclient automatically leaves irc_run loop.
- *
- * \sa irc_connect irc_run
- * \ingroup conndisc
- */
-void irc_disconnect (irc_session_t * session);
-
-
-/*!
- * \fn int irc_is_connected (irc_session_t * session)
- * \brief Checks whether the session is connecting/connected to the IRC server.
- *
- * \param session An initialized IRC session.
- *
- * \return Return code 1 means that session is connecting or connected to the
- *   IRC server, zero value means that the session has been disconnected.
- *
- * \sa irc_connect irc_run
- * \ingroup conndisc
- */
-int irc_is_connected (irc_session_t * session);
-
-
-/*!
- * \fn int irc_run (irc_session_t * session)
- * \brief Goes into forever-loop, processing IRC events and generating 
- *  callbacks.
- *
- * \param session An initiated and connected session.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno().
- *
- * This function goes into forever loop, processing the IRC events, and 
- * calling appropriate callbacks. This function will not return until the 
- * server connection is terminated - either by server, or by calling 
- * irc_cmd_quit. This function should be used, if you don't need asynchronous
- * request processing (i.e. your bot just reacts on the events, and doesn't
- * generate it asynchronously). Even in last case, you still can call irc_run,
- * and start the asynchronous thread in event_connect handler. See examples. 
- *
- * \ingroup running 
- */
-int irc_run (irc_session_t * session);
-
-
-/*!
- * \fn int irc_add_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set, int * maxfd)
- * \brief Adds IRC socket(s) for the descriptor set to use in select().
- *
- * \param session An initiated and connected session.
- * \param in_set  A FD_IN descriptor set for select()
- * \param out_set A FD_OUT descriptor set for select()
- * \param maxfd   A max descriptor found.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno().
- *
- * This function should be used when you already have a program with select()
- * based data processing. You prepare your descriptors, call this function
- * to add session's descriptor(s) into set, and then call select(). When it
- * returns, you should call irc_add_select_descriptors, which sends/recvs all
- * available data, parses received data, calls your callbacks(!), and returns.
- * Then you can process your sockets from set. See the example.
- *
- * \sa irc_process_select_descriptors
- * \ingroup running 
- */
-int irc_add_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set, int * maxfd);
-
-
-/*!
- * \fn int irc_process_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set)
- * \brief Processes the IRC socket(s), which descriptor(s) are set.
- *
- * \param session An initiated and connected session.
- * \param in_set  A FD_IN descriptor set for select()
- * \param out_set A FD_OUT descriptor set for select()
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno().
- *
- * This function should be used in pair with irc_add_select_descriptors 
- * function. See irc_add_select_descriptors description.
- *
- * \sa irc_add_select_descriptors
- * \ingroup running 
- */
-int irc_process_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set);
-
-
-/*!
- * \fn int irc_send_raw (irc_session_t * session, const char * format, ...)
- * \brief Sends raw data to the IRC server.
- *
- * \param session An initiated and connected session.
- * \param format  A printf-formatted string, followed by function args.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function sends the raw data as-is to the IRC server. Use it to 
- * generate a server command, which is not (yet) provided by libircclient 
- * directly.
- *
- * \ingroup ircmd_oth
- */
-int irc_send_raw (irc_session_t * session, const char * format, ...);
-
-
-/*!
- * \fn int irc_cmd_quit (irc_session_t * session, const char * reason)
- * \brief Sends QUIT command to the IRC server.
- *
- * \param session An initiated and connected session.
- * \param reason  A reason to quit. May be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function sends the QUIT command to the IRC server. This command 
- * forces the IRC server to close the IRC connection, and terminate the 
- * session.
- *
- * \ingroup ircmd_oth
- */
-int irc_cmd_quit (irc_session_t * session, const char * reason);
-
-
-/*!
- * \fn int irc_cmd_join (irc_session_t * session, const char * channel, const char * key)
- * \brief Joins the new IRC channel.
- *
- * \param session An initiated and connected session.
- * \param channel A channel name to join to. Must not be NULL.
- * \param key     Channel password. May be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to JOIN the IRC channel. If the channel is not exist,
- * it will be automatically created by the IRC server. Note that to JOIN the
- * password-protected channel, you must know the password, and specify it in
- * the \a key argument.
- *
- * If join is successful, the irc_callbacks_t::event_join is called (with \a origin == 
- * your nickname), then you are sent the channel's topic 
- * (using ::LIBIRC_RFC_RPL_TOPIC) and the list of users who are on the 
- * channel (using ::LIBIRC_RFC_RPL_NAMREPLY), which includes the user 
- * joining - namely you.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NEEDMOREPARAMS
- * - ::LIBIRC_RFC_ERR_BANNEDFROMCHAN
- * - ::LIBIRC_RFC_ERR_INVITEONLYCHAN
- * - ::LIBIRC_RFC_ERR_BADCHANNELKEY
- * - ::LIBIRC_RFC_ERR_CHANNELISFULL
- * - ::LIBIRC_RFC_ERR_BADCHANMASK
- * - ::LIBIRC_RFC_ERR_NOSUCHCHANNEL
- * - ::LIBIRC_RFC_ERR_TOOMANYCHANNELS
- *
- * And on success the following replies returned:
- * - ::LIBIRC_RFC_RPL_TOPIC
- * - ::LIBIRC_RFC_RPL_NAMREPLY
- * 
- * \ingroup ircmd_ch
- */
-int irc_cmd_join (irc_session_t * session, const char * channel, const char * key);
-
-
-/*!
- * \fn int irc_cmd_part (irc_session_t * session, const char * channel)
- * \brief Leaves the IRC channel.
- *
- * \param session An initiated and connected session.
- * \param channel A channel name to leave. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to leave the IRC channel you've already joined to.
- * An attempt to leave the channel you aren't in results a ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * server error.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NEEDMOREPARAMS
- * - ::LIBIRC_RFC_ERR_NOSUCHCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- *
- * \ingroup ircmd_ch
- */
-int irc_cmd_part (irc_session_t * session, const char * channel);
-
-
-/*!
- * \fn int irc_cmd_invite (irc_session_t * session, const char * nick, const char * channel)
- * \brief Invites a user to invite-only channel.
- *
- * \param session An initiated and connected session.
- * \param nick    A nick to invite. Must not be NULL.
- * \param channel A channel name to invite to. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to invite someone to invite-only channel. 
- * "Invite-only" is a channel mode, which restricts anyone, except invided,
- * to join this channel. After invitation, the user could join this channel.
- * The user, who is invited, will receive the irc_callbacks_t::event_invite event.
- * Note that you must be a channel operator to INVITE the users.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NEEDMOREPARAMS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_ERR_USERONCHANNEL
- * - ::LIBIRC_RFC_ERR_ERR_CHANOPRIVSNEEDED
- *
- * And on success one of the following replies returned:
- * - ::LIBIRC_RFC_RPL_INVITING
- * - ::LIBIRC_RFC_RPL_AWAY
- *
- * \sa irc_callbacks_t::event_invite irc_cmd_channel_mode
- * \ingroup ircmd_ch
- */
-int irc_cmd_invite (irc_session_t * session, const char * nick, const char * channel);
-
-
-/*!
- * \fn int irc_cmd_names (irc_session_t * session, const char * channel)
- * \brief Obtains a list of users who're in channel.
- *
- * \param session An initiated and connected session.
- * \param channel A channel name(s) to obtain user list. Must not be NULL. 
- *                It is possible to specify more than a single channel, but 
- *                several channel names should be separated by a comma.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to ask the IRC server for the list of the users 
- * who're in specified channel. You can list all nicknames that are visible 
- * to you on any channel that you can see. The list of users will be returned 
- * using ::RPL_NAMREPLY and ::RPL_ENDOFNAMES numeric codes.
- *
- * The channel names are returned by irc_callbacks_t::event_numeric 
- * using the following reply codes:
- * - ::LIBIRC_RFC_RPL_NAMREPLY
- * - ::LIBIRC_RFC_RPL_ENDOFNAMES
- *
- * \ingroup ircmd_ch
- */
-int irc_cmd_names (irc_session_t * session, const char * channel);
-
-
-/*!
- * \fn int irc_cmd_list (irc_session_t * session, const char * channel)
- * \brief Obtains a list of active server channels with their topics.
- *
- * \param session An initiated and connected session.
- * \param channel A channel name(s) to list. May be NULL, in which case all the
- *                channels will be listed. It is possible to specify more than 
- *                a single channel, but several channel names should be 
- *                separated by a comma.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to ask the IRC server for the active (existing) 
- * channels list. The list will be returned using ::LIBIRC_RFC_RPL_LISTSTART - 
- * ::LIBIRC_RFC_RPL_LIST - ::LIBIRC_RFC_RPL_LISTEND sequence.
- * Note that "private" channels are listed (without their topics) as channel 
- * "Prv" unless the client generating the LIST query is actually on that 
- * channel. Likewise, secret channels are 
- * not listed at all unless the client is a member of the channel in question.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NOSUCHSERVER
- *
- * And the channel list is returned using the following reply codes:
- * - ::LIBIRC_RFC_RPL_LISTSTART
- * - ::LIBIRC_RFC_RPL_LISTEND
- * - ::LIBIRC_RFC_RPL_LIST
- *
- * \ingroup ircmd_ch
- */
-int irc_cmd_list (irc_session_t * session, const char * channel);
-
-
-/*!
- * \fn int irc_cmd_topic (irc_session_t * session, const char * channel, const char * topic)
- * \brief Views or changes the channel topic.
- *
- * \param session An initiated and connected session.
- * \param channel A channel name to invite to. Must not be NULL.
- * \param topic   A new topic to change. If NULL, the old topic will be 
- *                returned, and topic won't changed.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * The irc_cmd_topic() is used to change or view the topic of a channel.
- * The topic for \a channel is returned if \a topic is NULL. If the \a topic
- * is not NULL, the topic for the \a channel will be changed. Note that, 
- * depending on \a +t channel mode, you may be required to be a channel 
- * operator to change the channel topic.
- *
- * If the command succeed, the IRC server will generate a ::RPL_NOTOPIC or 
- * ::RPL_TOPIC message, containing either old or changed topic. Also the IRC
- * server can (but not have to) generate the non-RFC ::RPL_TOPIC_EXTRA message,
- * containing the nick of person, who's changed the topic, and the time of 
- * latest topic change.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NEEDMOREPARAMS
- * - ::LIBIRC_RFC_ERR_CHANOPRIVSNEEDED
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- *
- * And the topic information is returned using one of following reply codes:
- * - ::LIBIRC_RFC_RPL_NOTOPIC
- * - ::LIBIRC_RFC_RPL_TOPIC
- *
- * \sa irc_callbacks_t::event_topic irc_cmd_channel_mode
- * \ingroup ircmd_ch
- */
-int irc_cmd_topic (irc_session_t * session, const char * channel, const char * topic);
-
-
-/*!
- * \fn int irc_cmd_channel_mode (irc_session_t * session, const char * channel, const char * mode)
- * \brief Views or changes the channel mode.
- *
- * \param session An initiated and connected session.
- * \param channel A channel name to invite to. Must not be NULL.
- * \param mode    A channel mode, described below. If NULL, the channel mode is
- *                not changed, just the old mode is returned.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * The irc_cmd_channel_mode() is used to change or view the channel modes.
- * The \a channel mode is returned if the \a mode is NULL. If the \a mode
- * is not NULL, the mode for the \a channel will be changed. Note that, 
- * only channel operators can change the channel modes.
- *
- * Channel mode is represended by the letters combination. Every letter has
- * its own meaning in channel modes. Most channel mode letters are boolean
- * (i.e. could only be set or reset), but a few channel mode letters accept a 
- * parameter. All channel options are set by adding a plus sign before the 
- * letter, and reset by adding a minus sign before the letter.
- * 
- * Here is the list of 'standard' channel modes:
- *
- * - \a o \a nickname - gives (+o nick) or takes (-o nick) the channel 
- *      operator privileges from  a \a nickname. This mode affects the 
- *      users in channel, not the channel itself. 
- *      Examples: "+o tim", "-o watson".
- *
- * - \a p - sets (+p) or resets (-p) private channel flag. 
- *      Private channels are shown in channel list as 'Prv', without the topic.
- *
- * - \a s - sets (+p) or resets (-p) secret channel flag. 
- *      Secret channels aren't shown in channel list at all.
- *
- * - \a i - sets (+i) or resets (-i) invite-only channel flag. When the flag
- *      is set, only the people who are invited by irc_cmd_invite(), can
- *      join this channel.
- *
- * - \a t - sets (+t) or resets (-t) topic settable by channel operator only
- *      flag. When the flag is set, only the channel operators can change the
- *      channel topic.
- *
- * - \a n - sets (+n) or resets (-n) the protection from the clients outside 
- *      the channel. When the \a +n mode is set, only the clients, who are in 
- *      channel, can send the messages to the channel.
- *
- * - \a m - sets (+m) or resets (-m) the moderation of the channel. When the
- *      moderation mode is set, only channel operators and the users who have
- *      the \a +v user mode can speak in the channel.
- *
- * - \a v \a nickname - gives (+v nick) or takes (-v nick) from user the 
- *      ability to speak on a moderated channel.
- *      Examples: "+v tim", "-v watson".
- *
- * - \a l \a number - sets (+l 20) or removes (-l) the restriction of maximum
- *      users in channel. When the restriction is set, and there is a number
- *      of users in the channel, no one can join the channel anymore.
- *
- * - \a k \a key - sets (+k secret) or removes (-k) the password from the 
- *      channel. When the restriction is set, any user joining the channel 
- *      required to provide a channel key.
- *
- * - \a b \a mask - sets (+b *!*@*.mil) or removes (-b *!*@*.mil) the ban mask
- *      on a user to keep him out of channel. Note that to remove the ban you 
- *      must specify the ban mask to remove, not just "-b".
- *
- * Note that the actual list of channel modes depends on the IRC server, and
- * can be bigger. If you know the popular channel modes, which aren't 
- * mentioned here - please contact me at tim@krasnogorsk.ru
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NEEDMOREPARAMS
- * - ::LIBIRC_RFC_ERR_CHANOPRIVSNEEDED
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_KEYSET
- * - ::LIBIRC_RFC_ERR_UNKNOWNMODE
- * - ::LIBIRC_RFC_ERR_NOSUCHCHANNEL
- *
- * And the mode information is given using following reply codes:
- * - ::LIBIRC_RFC_RPL_CHANNELMODEIS
- * - ::LIBIRC_RFC_RPL_BANLIST
- * - ::LIBIRC_RFC_RPL_ENDOFBANLIST
- *
- * \sa irc_cmd_topic irc_cmd_list
- * \ingroup ircmd_ch
- */
-int irc_cmd_channel_mode (irc_session_t * session, const char * channel, const char * mode);
-
-
-/*!
- * \fn int irc_cmd_user_mode (irc_session_t * session, const char * mode)
- * \brief Views or changes your own user mode.
- *
- * \param session An initiated and connected session.
- * \param mode    A user mode, described below. If NULL, the user mode is
- *                not changed, just the old mode is returned.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * The irc_cmd_user_mode() is used to change or view the user modes.
- * Note that, unlike channel modes, not all user modes can be changed. 
- * The user mode is returned if the \a mode is NULL. If the \a mode
- * is not NULL, the mode for you will be changed, and new mode will be 
- * returned.
- *
- * Like channel mode, user mode is also represended by the letters combination.
- * All the user mode letters are boolean (i.e. could only be set or reset),
- * they are set by adding a plus sign before the letter, and reset by adding 
- * a minus sign before the letter.
- * 
- * Here is the list of 'standard' user modes:
- *
- * - \a o - represents an IRC operator status. Could not be set directly (but
- *      can be reset though), to set it use the IRC \a OPER command.
- *
- * - \a i - if set, marks a user as 'invisible' - that is, not seen by lookups 
- *      if the user is not in a channel.
- *
- * - \a w - if set, marks a user as 'receiving wallops' - special messages 
- *      generated by IRC operators using WALLOPS command.
- *
- * - \a s - if set, marks a user for receipt of server notices.
- *
- * - \a r - NON-STANDARD MODE. If set, user has been authenticated with 
- *      NICKSERV IRC service.
- *
- * - \a x - NON-STANDARD MODE. If set, user's real IP is hidden by IRC 
- *      servers, to prevent scriptkiddies to do nasty things to the user's 
- *      computer.
- *
- * Note that the actual list of user modes depends on the IRC server, and
- * can be bigger. If you know the popular user modes, which aren't 
- * mentioned here - please contact me at tim@krasnogorsk.ru
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NEEDMOREPARAMS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- * - ::LIBIRC_RFC_ERR_UNKNOWNMODE
- * - ::LIBIRC_RFC_ERR_USERSDONTMATCH
- * - ::LIBIRC_RFC_ERR_UMODEUNKNOWNFLAG
- *
- * And the mode information is given using reply code ::LIBIRC_RFC_RPL_UMODEIS
- *
- * \ingroup ircmd_oth
- */
-int irc_cmd_user_mode (irc_session_t * session, const char * mode);
-
-
-/*!
- * \fn int irc_cmd_nick (irc_session_t * session, const char * newnick)
- * \brief Changes your nick.
- *
- * \param session An initiated and connected session.
- * \param newnick A new nick. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to change your current nick to another nick. Note 
- * that such a change is not always possible; for example you cannot change 
- * nick to the existing nick, or (on some servers) to the registered nick.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NONICKNAMEGIVEN
- * - ::LIBIRC_RFC_ERR_ERRONEUSNICKNAME
- * - ::LIBIRC_RFC_ERR_NICKNAMEINUSE
- * - ::LIBIRC_RFC_ERR_NICKCOLLISION
- *
- * \ingroup ircmd_oth
- */
-int irc_cmd_nick (irc_session_t * session, const char * newnick);
-
-
-/*!
- * \fn int irc_cmd_whois (irc_session_t * session, const char * nick)
- * \brief Queries the information about the nick.
- *
- * \param session An initiated and connected session.
- * \param nick    A nick to query the information abour. Must not be NULL. 
- *                A comma-separated list of several nicknames may be given.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function queries various information about the nick: username, real 
- * name, the IRC server used, the channels user is in, idle time, away mode and so on.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NOSUCHSERVER
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- * - ::LIBIRC_RFC_ERR_NONICKNAMEGIVEN
- *
- * And the information is returned using the following reply codes. The whois
- * query is completed when ::LIBIRC_RFC_RPL_ENDOFWHOIS message is received.
- * - ::LIBIRC_RFC_RPL_WHOISUSER
- * - ::LIBIRC_RFC_RPL_WHOISCHANNELS
- * - ::LIBIRC_RFC_RPL_WHOISSERVER
- * - ::LIBIRC_RFC_RPL_AWAY
- * - ::LIBIRC_RFC_RPL_WHOISOPERATOR
- * - ::LIBIRC_RFC_RPL_WHOISIDLE
- * - ::LIBIRC_RFC_RPL_ENDOFWHOIS
- *
- * \ingroup ircmd_oth
- */
-int irc_cmd_whois (irc_session_t * session, const char * nick);
-
-
-/*!
- * \fn irc_cmd_msg  (irc_session_t * session, const char * nch, const char * text)
- * \brief Sends the message to the nick or to the channel.
- *
- * \param session An initiated and connected session.
- * \param nch     A target nick or channel. Must not be NULL.
- * \param text    Message text. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to send the channel or private messages. The target
- * is determined by \a nch argument: if it describes nick, this will be a 
- * private message, if a channel name - public (channel) message. Note that
- * depending on channel modes, you may be required to join the channel to
- * send the channel messages.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NORECIPIENT
- * - ::LIBIRC_RFC_ERR_NOTEXTTOSEND
- * - ::LIBIRC_RFC_ERR_CANNOTSENDTOCHAN
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTOPLEVEL
- * - ::LIBIRC_RFC_ERR_WILDTOPLEVEL
- * - ::LIBIRC_RFC_ERR_TOOMANYTARGETS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- *
- * On success there is NOTHING generated.
- *
- * \ingroup ircmd_msg
- */
-int irc_cmd_msg  (irc_session_t * session, const char * nch, const char * text);
-
-
-/*!
- * \fn int irc_cmd_me	 (irc_session_t * session, const char * nch, const char * text)
- * \brief Sends the /me (CTCP ACTION) message to the nick or to the channel.
- *
- * \param session An initiated and connected session.
- * \param nch     A target nick or channel. Must not be NULL.
- * \param text    Action message text. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to send the /me message to channel or private.
- * As for irc_cmd_msg, the target is determined by \a nch argument.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NORECIPIENT
- * - ::LIBIRC_RFC_ERR_NOTEXTTOSEND
- * - ::LIBIRC_RFC_ERR_CANNOTSENDTOCHAN
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTOPLEVEL
- * - ::LIBIRC_RFC_ERR_WILDTOPLEVEL
- * - ::LIBIRC_RFC_ERR_TOOMANYTARGETS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- *
- * On success there is NOTHING generated. 
- * However, a ::LIBIRC_RFC_RPL_AWAY reply can be also generated.            
- *
- * \sa irc_cmd_msg
- * \ingroup ircmd_msg
- */
-int irc_cmd_me (irc_session_t * session, const char * nch, const char * text);
-
-
-/*!
- * \fn int irc_cmd_notice (irc_session_t * session, const char * nch, const char * text)
- * \brief Sends the notice to the nick or to the channel.
- *
- * \param session An initiated and connected session.
- * \param nch     A target nick or channel. Must not be NULL.
- * \param text    Notice text. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to send the channel or private notices. The target
- * is determined by \a nch argument: if it describes nick, this will be a 
- * private message, if a channel name - public (channel) message. Note that
- * depending on channel modes, you may be required to join the channel to
- * send the channel notices.
- *
- * The only difference between message and notice is that, according to RFC 
- * 1459, you must not automatically reply to NOTICE messages.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NORECIPIENT
- * - ::LIBIRC_RFC_ERR_NOTEXTTOSEND
- * - ::LIBIRC_RFC_ERR_CANNOTSENDTOCHAN
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTOPLEVEL
- * - ::LIBIRC_RFC_ERR_WILDTOPLEVEL
- * - ::LIBIRC_RFC_ERR_TOOMANYTARGETS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- *
- * On success there is NOTHING generated. On notices sent to target nick, 
- * a ::LIBIRC_RFC_RPL_AWAY reply may be generated.
- *
- * \sa irc_cmd_msg
- * \ingroup ircmd_msg
- */
-int irc_cmd_notice (irc_session_t * session, const char * nch, const char * text);
-
-
-/*!
- * \fn int irc_cmd_kick (irc_session_t * session, const char * nick, const char * channel, const char * reason)
- * \brief Kick some lazy ass out of channel.
- *
- * \param session An initiated and connected session.
- * \param nick    A nick to kick. Must not be NULL.
- * \param channel A channel to kick this nick out of. Must not be NULL.
- * \param reason  A reason to kick. May be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to kick a person out of channel. Note that you must
- * be a channel operator to kick anyone.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NEEDMOREPARAMS
- * - ::LIBIRC_RFC_ERR_BADCHANMASK
- * - ::LIBIRC_RFC_ERR_NOSUCHCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_CHANOPRIVSNEEDED
- *
- * On success the irc_callbacks_t::event_kick event will be generated.
- *
- * \sa irc_callbacks_t::event_numeric
- * \ingroup ircmd_ch
- */
-int irc_cmd_kick (irc_session_t * session, const char * nick, const char * channel, const char * reason);
-
-
-/*!
- * \fn int irc_cmd_ctcp_request (irc_session_t * session, const char * nick, const char * request)
- * \brief Generates a CTCP request.
- *
- * \param session An initiated and connected session.
- * \param nick    A target nick to send request to. Must not be NULL.
- * \param request A request string. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to send a CTCP request. There are four CTCP requests
- * supported by Mirc:
- *  VERSION - get the client software name and version
- *  FINGER  - get the client username, host and real name.
- *  PING    - get the client delay.
- *  TIME    - get the client local time.
- *
- * A reply to the CTCP request will be sent by the irc_callbacks_t::event_ctcp_rep callback;
- * be sure to define it.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NORECIPIENT
- * - ::LIBIRC_RFC_ERR_NOTEXTTOSEND
- * - ::LIBIRC_RFC_ERR_CANNOTSENDTOCHAN
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTOPLEVEL
- * - ::LIBIRC_RFC_ERR_WILDTOPLEVEL
- * - ::LIBIRC_RFC_ERR_TOOMANYTARGETS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- *
- * \sa irc_callbacks_t::event_ctcp_rep irc_callbacks_t::event_numeric
- * \ingroup ctcp
- */
-int irc_cmd_ctcp_request (irc_session_t * session, const char * nick, const char * request);
-
-
-/*!
- * \fn int irc_cmd_ctcp_reply (irc_session_t * session, const char * nick, const char * reply)
- * \brief Generates a reply to the CTCP request.
- *
- * \param session An initiated and connected session.
- * \param nick    A target nick to send request to. Must not be NULL.
- * \param reply   A reply string. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function is used to send a reply to the CTCP request, generated by 
- * irc_callbacks_t::event_ctcp_req. Note that you will not receive this event
- * unless you specify your own handler as \c event_ctcp_req callback during
- * the IRC session initialization.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NORECIPIENT
- * - ::LIBIRC_RFC_ERR_NOTEXTTOSEND
- * - ::LIBIRC_RFC_ERR_CANNOTSENDTOCHAN
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTOPLEVEL
- * - ::LIBIRC_RFC_ERR_WILDTOPLEVEL
- * - ::LIBIRC_RFC_ERR_TOOMANYTARGETS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- *
- * \ingroup ctcp
- */
-int irc_cmd_ctcp_reply (irc_session_t * session, const char * nick, const char * reply);
-
-
-/*!
- * \fn void irc_target_get_nick (const char * target, char *nick, size_t size)
- * \brief Gets the nick part from the target
- *
- * \param target  A nick in common IRC server form like tim!root\@mycomain.com
- * \param nick    A buffer to hold the nickname.
- * \param size    A buffer size. If nick is longer than buffer size, it will 
- *                be truncated.
- *
- * For most events IRC server returns 'origin' (i.e. the person, who 
- * generated this event) in i.e. "common" form, like nick!host\@domain.
- * However, all the irc_cmd_* functions require just a nick/
- * This function parses this origin, and gets the nick, storing it into 
- * user-provided buffer.
- * A buffer of size 90 should be enough for most nicks :)
- *
- * \ingroup nnparse
- */
-void irc_target_get_nick (const char * target, char *nick, size_t size);
-
-
-/*!
- * \fn void irc_target_get_host (const char * target, char *nick, size_t size)
- * \brief Gets the host part from the target
- *
- * \param target  A nick in common IRC server form like tim!root\@mydomain.com
- * \param nick    A buffer to hold the nickname.
- * \param size    A buffer size. If nick is longer than buffer size, it will 
- *                be truncated.
- *
- * For most events IRC server returns 'origin' (i.e. the person, who 
- * generated this event) in i.e. "common" form, like nick!host\@domain.
- * I don't know any command, which requires host, but it may be useful :)
- * This function parses this origin, and gets the host, storing it into 
- * user-provided buffer.
- *
- * \ingroup nnparse
- */
-void irc_target_get_host (const char * target, char *nick, size_t size);
-
-
-/*!
- * \fn int irc_dcc_chat(irc_session_t * session, void * ctx, const char * nick, irc_dcc_callback_t callback, irc_dcc_t * dccid)
- * \brief Initiates a DCC CHAT.
- *
- * \param session An initiated and connected session.
- * \param ctx     A user-supplied DCC session context, which will be passed to 
- *                the DCC callback function. May be NULL.
- * \param nick    A nick to DCC CHAT with.
- * \param callback A DCC callback function, which will be called when 
- *                anything is said by other party. Must not be NULL.
- * \param dccid   On success, DCC session ID will be stored in this var.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function requests a DCC CHAT between you and other user. For 
- * newbies, DCC chat is like private chat, but it goes directly between
- * two users, and bypasses IRC server. DCC CHAT request must be accepted 
- * by other side before you can send anything.
- *
- * When the chat is accepted, terminated, or some data is received, the 
- * callback function is called. See the details in irc_dcc_callback_t 
- * declaration.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NORECIPIENT
- * - ::LIBIRC_RFC_ERR_NOTEXTTOSEND
- * - ::LIBIRC_RFC_ERR_CANNOTSENDTOCHAN
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTOPLEVEL
- * - ::LIBIRC_RFC_ERR_WILDTOPLEVEL
- * - ::LIBIRC_RFC_ERR_TOOMANYTARGETS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- *
- * \sa irc_dcc_callback_t irc_dcc_msg
- * \ingroup dccstuff
- */
-int irc_dcc_chat (irc_session_t * session, void * ctx, const char * nick, irc_dcc_callback_t callback, irc_dcc_t * dccid);
-
-
-/*!
- * \fn int irc_dcc_msg	(irc_session_t * session, irc_dcc_t dccid, const char * text)
- * \brief Sends the message to the specific DCC CHAT
- *
- * \param session An IRC session.
- * \param dccid   A DCC session ID, which chat request must have been accepted.
- * \param text    Message text. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno().
- *
- * This function is used to send the DCC CHAT messages. DCC CHAT request
- * must be initiated and accepted first (or just accepted, if initiated by
- * other side).
- *
- * \sa irc_dcc_chat
- * \ingroup dccstuff
- */
-int irc_dcc_msg	(irc_session_t * session, irc_dcc_t dccid, const char * text);
-
-
-/*!
- * \fn int irc_dcc_accept (irc_session_t * session, irc_dcc_t dccid, void * ctx, irc_dcc_callback_t callback)
- * \brief Accepts a remote DCC CHAT or DCC RECVFILE request.
- *
- * \param session An initiated and connected session.
- * \param dccid   A DCC session ID, returned by appropriate callback.
- * \param ctx     A user-supplied DCC session context, which will be passed 
- *                to the DCC callback function. May be NULL.
- * \param callback A DCC callback function, which will be called when 
- *                anything is said by other party. Must not be NULL.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno().
- *
- * This function accepts a remote DCC request - either DCC CHAT or DCC FILE.
- * After the request is accepted, the supplied callback will be called,
- * and you can start sending messages or receiving the file.
- *
- * This function should be called only after either event_dcc_chat_req or
- * event_dcc_send_req events are generated, and should react to them. It is
- * possible not to call irc_dcc_accept or irc_dcc_decline immediately in 
- * callback function - you may just return, and call it later. However, to
- * prevent memory leaks, you must call either irc_dcc_decline or 
- * irc_dcc_accept for any incoming DCC request.
- * 
- * \sa irc_dcc_decline event_dcc_chat_req event_dcc_send_req
- * \ingroup dccstuff
- */
-int	irc_dcc_accept (irc_session_t * session, irc_dcc_t dccid, void * ctx, irc_dcc_callback_t callback);
-
-
-/*!
- * \fn int irc_dcc_decline (irc_session_t * session, irc_dcc_t dccid)
- * \brief Declines a remote DCC CHAT or DCC RECVFILE request.
- *
- * \param session An initiated and connected session.
- * \param dccid   A DCC session ID, returned by appropriate callback.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno().
- *
- * This function declines a remote DCC request - either DCC CHAT or DCC FILE.
- *
- * This function should be called only after either event_dcc_chat_req or
- * event_dcc_send_req events are generated, and should react to them. It is
- * possible not to call irc_dcc_accept or irc_dcc_decline immediately in 
- * callback function - you may just return, and call it later. However, to
- * prevent memory leaks, you must call either irc_dcc_decline or 
- * irc_dcc_accept for any incoming DCC request.
- *
- * Do not use this function to close the accepted or initiated DCC session.
- * Use irc_dcc_destroy instead.
- *
- * \sa irc_dcc_accept irc_callbacks_t::event_dcc_chat_req irc_callbacks_t::event_dcc_send_req irc_dcc_destroy
- * \ingroup dccstuff
- */
-int irc_dcc_decline (irc_session_t * session, irc_dcc_t dccid);
-
-
-/*!
- * \fn int irc_dcc_sendfile (irc_session_t * session, void * ctx, const char * nick, const char * filename, irc_dcc_callback_t callback, irc_dcc_t * dccid)
- * \brief Sends a file via DCC.
- *
- * \param session An initiated and connected session.
- * \param ctx     A user-supplied DCC session context, which will be passed to 
- *                the DCC callback function. May be NULL.
- * \param nick    A nick to send file via DCC to.
- * \param filename A file name to sent. Must be an existing file.
- * \param callback A DCC callback function, which will be called when 
- *                file sent operation is failed, progressed or completed.
- * \param dccid   On success, DCC session ID will be stored in this var.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno(). Any error, generated by the 
- *  IRC server, is available through irc_callbacks_t::event_numeric.
- *
- * This function generates a DCC SEND request to send the file. When it is
- * accepted, the file is sent to the remote party, and the DCC session is
- * closed. The send operation progress and result can be checked in 
- * callback. See the details in irc_dcc_callback_t declaration.
- *
- * Possible error responces for this command from the RFC1459:
- * - ::LIBIRC_RFC_ERR_NORECIPIENT
- * - ::LIBIRC_RFC_ERR_NOTEXTTOSEND
- * - ::LIBIRC_RFC_ERR_CANNOTSENDTOCHAN
- * - ::LIBIRC_RFC_ERR_NOTONCHANNEL
- * - ::LIBIRC_RFC_ERR_NOTOPLEVEL
- * - ::LIBIRC_RFC_ERR_WILDTOPLEVEL
- * - ::LIBIRC_RFC_ERR_TOOMANYTARGETS
- * - ::LIBIRC_RFC_ERR_NOSUCHNICK
- *
- * \sa irc_dcc_callback_t
- * \ingroup dccstuff
- */
-int irc_dcc_sendfile (irc_session_t * session, void * ctx, const char * nick, const char * filename, irc_dcc_callback_t callback, irc_dcc_t * dccid);
-
-
-/*!
- * \fn int irc_dcc_destroy (irc_session_t * session, irc_dcc_t dccid)
- * \brief Destroys a DCC session.
- *
- * \param session An initiated and connected session.
- * \param dccid   A DCC session ID.
- *
- * \return Return code 0 means success. Other value means error, the error 
- *  code may be obtained through irc_errno().
- *
- * This function closes the DCC connection (if available), and destroys
- * the DCC session, freeing the used resources. It can be called in any 
- * moment, even from callbacks or from different threads.
- *
- * Note that when DCC session is finished (either with success or failure),
- * you should not destroy it - it will be destroyed automatically.
- *
- * \ingroup dccstuff
- */
-int irc_dcc_destroy (irc_session_t * session, irc_dcc_t dccid);
-
-
-/*!
- * \fn void irc_get_version (unsigned int * high, unsigned int * low)
- * \brief Obtains a libircclient version.
- *
- * \param high A pointer to receive the high version part.
- * \param low  A pointer to receive the low version part.
- *
- * This function returns the libircclient version. You can use the version either
- * to check whether required options are available, or to output the version.
- * The preferred printf-like format string to output the version is:
- *
- * printf ("Version: %d.%02d", high, low);
- *
- * \ingroup common
- */
-void irc_get_version (unsigned int * high, unsigned int * low);
-
-
-/*!
- * \fn void irc_set_ctx (irc_session_t * session, void * ctx)
- * \brief Sets the IRC session context.
- *
- * \param session An initiated session.
- * \param ctx  A context.
- *
- * This function sets the user-defined context for this IRC session. This
- * context is not used by libircclient. Its purpose is to store session-specific
- * user data, which may be obtained later by calling irc_get_ctx().
- * Note that libircclient just 'carries out' this pointer. If you allocate some
- * memory, and store its address in ctx (most common usage), it is your 
- * responsibility to free it before calling irc_destroy_session().
- *
- * \sa irc_get_ctx
- * \ingroup contexts
- */
-void irc_set_ctx (irc_session_t * session, void * ctx);
-
-/*!
- * \fn void irc_set_ctcp_version (irc_session_t * session, const char *version)
- * \brief Sets the internal CTCP VERSION
- *
- * \param session an Initiated session.
- * \param version the version to reply
- *
- * This function sets an internal user-defined version to reply on CTCP
- * VERSION request. If none is given, a default one is provided. The parameter
- * version is copied and can be freed by the user.
- *
- * \ingroup contexts
- */
-void irc_set_ctcp_version(irc_session_t * session, const char * version);
-
-/*!
- * \fn void * irc_get_ctx (irc_session_t * session)
- * \brief Returns the IRC session context.
- *
- * \param session An initiated session.
- *
- * This function returns the IRC session context, which was set by 
- * irc_set_ctx(). If no context was set, this function returns NULL.
- *
- * \sa irc_set_ctx
- * \ingroup contexts
- */
-void * irc_get_ctx (irc_session_t * session);
-
-
-/*!
- * \fn int irc_errno (irc_session_t * session)
- * \brief Returns the last error code.
- *
- * \param session An initiated session.
- *
- * This function returns the last error code associated with last operation
- * of this IRC session. Possible error codes are defined in libirc_errors.h
- *
- * As usual, next errno rules apply:
- * - irc_errno() should be called ONLY if the called function fails;
- * - irc_errno() doesn't return 0 if function succeed; actually, the return
- *    value will be undefined.
- * - you should call irc_errno() IMMEDIATELY after function fails, before 
- *   calling any other libircclient function.
- *
- * \sa irc_strerror
- * \ingroup errors
- */
-int irc_errno (irc_session_t * session);
-
-
-/*!
- * \fn const char * irc_strerror (int ircerrno)
- * \brief Returns the text error message associated with this error code.
- *
- * \param ircerrno A numeric error code returned by irc_errno()
- *
- * This function returns the text representation of the given error code.
- *
- * \sa irc_errno()
- * \ingroup errors
- */
-const char * irc_strerror (int ircerrno);
-
-
-/*!
- * \fn void irc_option_set (irc_session_t * session, unsigned int option)
- * \brief Sets the libircclient option.
- *
- * \param session An initiated session.
- * \param option  An option from libirc_options.h
- *
- * This function sets the libircclient option, changing libircclient behavior. See the
- * option list for the meaning for every option.
- *
- * \sa irc_option_reset
- * \ingroup options
- */
-void irc_option_set (irc_session_t * session, unsigned int option);
-
-
-/*!
- * \fn void irc_option_reset (irc_session_t * session, unsigned int option)
- * \brief Resets the libircclient option.
- *
- * \param session An initiated session.
- * \param option  An option from libirc_options.h
- *
- * This function removes the previously set libircclient option, changing libircclient 
- * behavior. See the option list for the meaning for every option.
- *
- * \sa irc_option_set
- * \ingroup options
- */
-void irc_option_reset (irc_session_t * session, unsigned int option);
-
-
-/*!
- * \fn char * irc_color_strip_from_mirc (const char * message)
- * \brief Removes all the color codes and format options.
- *
- * \param message A message from IRC
- *
- * \return Returns a new plain text message with stripped mIRC color codes.
- * Note that the memory for the new message is allocated using malloc(), so
- * you should free it using free() when it is not used anymore. If memory 
- * allocation failed, returns 0.
- *
- * \sa irc_color_convert_from_mirc irc_color_convert_to_mirc
- * \ingroup colors
- */
-char * irc_color_strip_from_mirc (const char * message);
-
-
-/*!
- * \fn char * irc_color_convert_from_mirc (const char * message)
- * \brief Converts all the color codes and format options to libircclient colors.
- *
- * \param message A message from IRC
- *
- * \return Returns a new message with converted mIRC color codes and format
- * options. See the irc_color_convert_to_mirc() help to see how the colors 
- * are converted.\n
- * Note that the memory for the new message is allocated using malloc(), so
- * you should free it using free() when it is not used anymore. If memory 
- * allocation failed, returns 0.
- *
- * \sa irc_color_strip_from_mirc irc_color_convert_to_mirc
- * \ingroup colors
- */
-char * irc_color_convert_from_mirc (const char * message);
-
-
-/*!
- * \fn char * irc_color_convert_to_mirc (const char * message)
- * \brief Converts all the color codes from libircclient format to mIRC.
- *
- * \param message A message with color codes
- *
- * \return Returns a new message with converted color codes and format
- * options, or 0 if memory could not be allocated. Note that the memory for 
- * the new message is allocated using malloc(), so you should free it using 
- * free() when it is not used anymore.
- *
- * The color system of libircclient is designed to be easy to use, and 
- * portable between different IRC clients. Every color or format option is 
- * described using plain text commands written between square brackets. The 
- * possible codes are:
- * - [B] ... [/B] - bold format mode. Everything between [B] and [/B] is written in \b bold.
- * - [I] ... [/I] - italic/reverse format mode. Everything between [I] and [/I] is written in \c italic, or reversed (however, because some clients are incapable of rendering italic text, most clients display this as normal text with the background and foreground colors swapped).
- * - [U] ... [/U] - underline format mode. Everything between [U] and [/U] is written underlined.
- * - [COLOR=RED] ... [/COLOR] - write the text using specified foreground color. The color is set by using the \c COLOR keyword, and equal sign followed by text color code (see below).
- * - [COLOR=RED/BLUE] ... [/COLOR] - write the text using specified foreground and background color. The color is set by using the \c COLOR keyword, an equal sign followed by text foreground color code, a dash and a text background color code.
- * 
- * The supported text colors are:
- * - WHITE
- * - BLACK
- * - DARKBLUE
- * - DARKGREEN
- * - RED
- * - BROWN
- * - PURPLE
- * - OLIVE
- * - YELLOW
- * - GREEN
- * - TEAL
- * - CYAN
- * - BLUE
- * - MAGENTA
- * - DARKGRAY
- * - LIGHTGRAY
- * 
- * Examples of color sequences:
- * \code
- * Hello, [B]Tim[/B]. 
- * [U]Arsenal[/U] got a [COLOR=RED]red card[/COLOR]
- * The tree[U]s[/U] are [COLOR=GREEN/BLACK]green[/COLOR]
- * \endcode
- *
- * \sa irc_color_strip_from_mirc irc_color_convert_from_mirc
- * \ingroup colors
- */
-char * irc_color_convert_to_mirc (const char * message);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif /* INCLUDE_LIBIRC_H */
--- a/extern/libircclient/src/colors.c	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,388 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-#include <ctype.h>
-
-#define LIBIRC_COLORPARSER_BOLD			(1<<1)
-#define LIBIRC_COLORPARSER_UNDERLINE	(1<<2)
-#define LIBIRC_COLORPARSER_REVERSE		(1<<3)
-#define LIBIRC_COLORPARSER_COLOR		(1<<4)
-
-#define LIBIRC_COLORPARSER_MAXCOLORS	15
-
-
-static const char * color_replacement_table[] =
-{
-	"WHITE",
-	"BLACK",
-	"DARKBLUE",
-	"DARKGREEN",
-	"RED",
-	"BROWN",
-	"PURPLE",
-	"OLIVE",
-	"YELLOW",
-	"GREEN",
-	"TEAL",
-	"CYAN",
-	"BLUE",
-	"MAGENTA",
-	"DARKGRAY",
-	"LIGHTGRAY",
-	0
-};
-
-
-static inline void libirc_colorparser_addorcat (char ** destline, unsigned int * destlen, const char * str)
-{
-	unsigned int len = strlen(str);
-
-	if ( *destline )
-	{
-		strcpy (*destline, str);
-		*destline += len;
-	}
-	else
-		*destlen += len;
-}
-
-
-static void libirc_colorparser_applymask (unsigned int * mask, 
-		char ** destline, unsigned int * destlen,
-		unsigned int bitmask, const char * start, const char * end)
-{
-	if ( (*mask & bitmask) != 0 )
-	{
-		*mask &= ~bitmask;
-		libirc_colorparser_addorcat (destline, destlen, end);
-	}
-	else
-	{
-		*mask |= bitmask;
-		libirc_colorparser_addorcat (destline, destlen, start);
-	}
-}
-
-
-static void libirc_colorparser_applycolor (unsigned int * mask, 
-		char ** destline, unsigned int * destlen,
-		unsigned int colorid, unsigned int bgcolorid)
-{
-	const char * end = "[/COLOR]";
-	char startbuf[64];
-
-	if ( bgcolorid != 0 )
-		sprintf (startbuf, "[COLOR=%s/%s]", color_replacement_table[colorid], color_replacement_table[bgcolorid]);
-	else
-		sprintf (startbuf, "[COLOR=%s]", color_replacement_table[colorid]);
-
-	if ( (*mask & LIBIRC_COLORPARSER_COLOR) != 0 )
-		libirc_colorparser_addorcat (destline, destlen, end);
-
-	*mask |= LIBIRC_COLORPARSER_COLOR;
-	libirc_colorparser_addorcat (destline, destlen, startbuf);
-}
-
-
-static void libirc_colorparser_closetags (unsigned int * mask, 
-		char ** destline, unsigned int * destlen)
-{
-	if ( *mask & LIBIRC_COLORPARSER_BOLD )
-		libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_BOLD, 0, "[/B]");
-
-	if ( *mask & LIBIRC_COLORPARSER_UNDERLINE )
-		libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_UNDERLINE, 0, "[/U]");
-
-	if ( *mask & LIBIRC_COLORPARSER_REVERSE )
-		libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_REVERSE, 0, "[/I]");
-
-	if ( *mask & LIBIRC_COLORPARSER_COLOR )
-		libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_COLOR, 0, "[/COLOR]");
-}
-
-
-
-/*
- * IRC to [code] color conversion. Or strip.
- */
-static char * libirc_colorparser_irc2code (const char * source, int strip)
-{
-	unsigned int mask = 0, destlen = 0;
-	char * destline = 0, *d = 0;
-	const char *p;
-	int current_bg = 0;
-
-    /*
-     * There will be two passes. First pass calculates the total length of
-     * the destination string. The second pass allocates memory for the string,
-     * and fills it.
-     */
-	while ( destline == 0 ) // destline will be set after the 2nd pass
-	{
-		if ( destlen > 0 )
-		{
-			// This is the 2nd pass; allocate memory.
-			if ( (destline = malloc (destlen)) == 0 )
-				return 0;
-
-			d = destline;
-		}
-
-		for ( p = source; *p; p++ )
-		{
-			switch (*p)
-			{
-			case 0x02:	// bold
-				if ( strip )
-					continue;
-
-				libirc_colorparser_applymask (&mask, &d, &destlen, LIBIRC_COLORPARSER_BOLD, "[B]", "[/B]");
-				break;
-				
-			case 0x1F:	// underline
-				if ( strip )
-					continue;
-
-				libirc_colorparser_applymask (&mask, &d, &destlen, LIBIRC_COLORPARSER_UNDERLINE, "[U]", "[/U]");
-				break;
-
-			case 0x16:	// reverse
-				if ( strip )
-					continue;
-
-				libirc_colorparser_applymask (&mask, &d, &destlen, LIBIRC_COLORPARSER_REVERSE, "[I]", "[/I]");
-				break;
-
-			case 0x0F:	// reset colors
-				if ( strip )
-					continue;
-
-				libirc_colorparser_closetags (&mask, &d, &destlen);
-				break;
-
-			case 0x03:	// set color
-				if ( isdigit (p[1]) )
-				{
-					// Parse 
-					int bgcolor = -1, color = p[1] - 0x30;
-					p++;
-
-					if ( isdigit (p[1]) )
-					{
-						color = color * 10 + (p[1] - 0x30);
-						p++;
-					}
-
-					// If there is a comma, search for the following 
-					// background color
-					if ( p[1] == ',' && isdigit (p[2]) )
-					{
-						bgcolor = p[2] - 0x30;
-						p += 2;
-
-						if ( isdigit (p[1]) )
-						{
-							bgcolor = bgcolor * 10 + (p[1] - 0x30);
-							p++;
-						}
-					}
-
-					// Check for range
-					if ( color <= LIBIRC_COLORPARSER_MAXCOLORS 
-					&& bgcolor <= LIBIRC_COLORPARSER_MAXCOLORS )
-					{
-						if ( strip )
-							continue;
-
-						if ( bgcolor != -1 )
-							current_bg = bgcolor;
-
-						libirc_colorparser_applycolor (&mask, &d, &destlen, color, current_bg);
-					}
-				}
-				break;
-
-			default:
-				if ( destline )
-					*d++ = *p;
-				else
-					destlen++;
-				break;
-			}
-		}
-
-		// Close all the opened tags
-		libirc_colorparser_closetags (&mask, &d, &destlen);
-		destlen++; // for 0-terminator
-	}
-
-	*d = '\0';
-	return destline;
-}
-
-
-static int libirc_colorparser_colorlookup (const char * color)
-{
-	int i;
-	for ( i = 0; color_replacement_table[i]; i++ )
-		if ( !strcmp (color, color_replacement_table[i]) )
-			return i;
-
-	return -1;
-}
-
-
-/*
- * [code] to IRC color conversion.
- */
-char * irc_color_convert_to_mirc (const char * source)
-{
-	unsigned int destlen = 0;
-	char * destline = 0, *d = 0;
-	const char *p1, *p2, *cur;
-
-    /*
-     * There will be two passes. First pass calculates the total length of
-     * the destination string. The second pass allocates memory for the string,
-     * and fills it.
-     */
-	while ( destline == 0 ) // destline will be set after the 2nd pass
-	{
-		if ( destlen > 0 )
-		{
-			// This is the 2nd pass; allocate memory.
-			if ( (destline = malloc (destlen)) == 0 )
-				return 0;
-
-			d = destline;
-		}
-
-		cur = source;
-		while ( (p1 = strchr (cur, '[')) != 0 )
-		{
-			const char * replacedval = 0;
-			p2 = 0;
-
-			// Check if the closing bracket is available after p1
-			// and the tag length is suitable
-			if ( p1[1] != '\0' 
-			&& (p2 = strchr (p1, ']')) != 0
-			&& (p2 - p1) > 1
-			&& (p2 - p1) < 31 )
-			{
-				// Get the tag
-				char tagbuf[32];
-				int taglen = p2 - p1 - 1;
-
-				memcpy (tagbuf, p1 + 1, taglen);
-				tagbuf[taglen] = '\0';
-
-				if ( !strcmp (tagbuf, "/COLOR") )
-					replacedval = "\x0F";
-				else if ( strstr (tagbuf, "COLOR=") == tagbuf )
-				{
-					int color, bgcolor = -2;
-					char * bcol;
-
-					bcol = strchr (tagbuf + 6, '/');
-
-					if ( bcol )
-					{
-						*bcol++ = '\0';
-						bgcolor = libirc_colorparser_colorlookup (bcol);
-					}
-
-					color = libirc_colorparser_colorlookup (tagbuf + 6);
-
-					if ( color != -1 && bgcolor == -2 )
-					{
-						sprintf (tagbuf, "\x03%02d", color);
-						replacedval = tagbuf;
-					}
-					else if ( color != -1 && bgcolor >= 0 )
-					{
-						sprintf (tagbuf, "\x03%02d,%02d", color, bgcolor);
-						replacedval = tagbuf;
-					}
-				}
-				else if ( !strcmp (tagbuf, "B") || !strcmp (tagbuf, "/B") )
-					replacedval = "\x02";
-				else if ( !strcmp (tagbuf, "U") || !strcmp (tagbuf, "/U") )
-					replacedval = "\x1F";
-				else if ( !strcmp (tagbuf, "I") || !strcmp (tagbuf, "/I") )
-					replacedval = "\x16";
-			}
-
-			if ( replacedval )
-			{
-				// add a part before the tag
-				int partlen = p1 - cur;
-
-				if ( destline )
-				{
-					memcpy (d, cur, partlen);
-					d += partlen;
-				}
-				else
-					destlen += partlen;
-
-				// Add the replacement
-				libirc_colorparser_addorcat (&d, &destlen, replacedval);
-
-				// And move the pointer
-				cur = p2 + 1;
-			}
-			else
-			{
-				// add a whole part before the end tag
-				int partlen;
-
-				if ( !p2 )
-					p2 = cur + strlen(cur);
-
-				partlen = p2 - cur + 1;
-
-				if ( destline )
-				{
-					memcpy (d, cur, partlen);
-					d += partlen;
-				}
-				else
-					destlen += partlen;
-
-				// And move the pointer
-				cur = p2 + 1;
-			}
-		}
-
-		// Add the rest of string
-		libirc_colorparser_addorcat (&d, &destlen, cur);
-		destlen++; // for 0-terminator
-	}
-
-	*d = '\0';
-	return destline;
-}
-
-
-char * irc_color_strip_from_mirc (const char * message)
-{
-	return libirc_colorparser_irc2code (message, 1);
-}
-
-
-char * irc_color_convert_from_mirc (const char * message)
-{
-	return libirc_colorparser_irc2code (message, 0);
-}
--- a/extern/libircclient/src/dcc.c	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,897 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-#define LIBIRC_DCC_CHAT			1
-#define LIBIRC_DCC_SENDFILE		2
-#define LIBIRC_DCC_RECVFILE		3
-
-
-static irc_dcc_session_t * libirc_find_dcc_session (irc_session_t * session, irc_dcc_t dccid, int lock_list)
-{
-	irc_dcc_session_t * s, *found = 0;
-
-	if ( lock_list )
-		libirc_mutex_lock (&session->mutex_dcc);
-
-	for ( s = session->dcc_sessions; s; s = s->next )
-	{
-		if ( s->id == dccid )
-		{
-			found = s;
-			break;
-		}
-	}
-
-	if ( found == 0 && lock_list )
-		libirc_mutex_unlock (&session->mutex_dcc);
-
-	return found;
-}
-
-
-static void libirc_dcc_destroy_nolock (irc_session_t * session, irc_dcc_t dccid)
-{
-	irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 0);
-
-	if ( dcc )
-	{
-		if ( dcc->sock >= 0 )
-			socket_close (&dcc->sock);
-
-		dcc->state = LIBIRC_STATE_REMOVED;
-	}
-}
-
-
-static void libirc_remove_dcc_session (irc_session_t * session, irc_dcc_session_t * dcc, int lock_list)
-{
-	if ( dcc->sock >= 0 )
-		socket_close (&dcc->sock);
-
-	if ( dcc->dccsend_file_fp )
-		fclose (dcc->dccsend_file_fp);
-
-	dcc->dccsend_file_fp = 0;
-
-	libirc_mutex_destroy (&dcc->mutex_outbuf);
-
-	if ( lock_list )
-		libirc_mutex_lock (&session->mutex_dcc);
-
-	if ( session->dcc_sessions != dcc )
-	{
-		irc_dcc_session_t * s;
-		for ( s = session->dcc_sessions; s; s = s->next )
-		{
-			if ( s->next == dcc )
-			{
-				s->next = dcc->next;
-				break;
-			}
-		}
-	}
-	else
-		session->dcc_sessions = dcc->next;
-
-	if ( lock_list )
-		libirc_mutex_unlock (&session->mutex_dcc);
-
-	free (dcc);
-}
-
-
-static void libirc_dcc_add_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set, int * maxfd)
-{
-	irc_dcc_session_t * dcc, *dcc_next;
-	time_t now = time (0);
-
-	libirc_mutex_lock (&ircsession->mutex_dcc);
-
-	// Preprocessing DCC list:
-	// - ask DCC send callbacks for data;
-	// - remove unused DCC structures
-	for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc_next )
-	{
-		dcc_next = dcc->next;
-
-		// Remove timed-out sessions
-		if ( (dcc->state == LIBIRC_STATE_CONNECTING
-			|| dcc->state == LIBIRC_STATE_INIT
-			|| dcc->state == LIBIRC_STATE_LISTENING)
-		&& now - dcc->timeout > ircsession->dcc_timeout )
-		{
-			// Inform the caller about DCC timeout.
-			// Do not inform when state is LIBIRC_STATE_INIT - session
-			// was initiated from someone else, and callbacks aren't set yet.
-			if ( dcc->state != LIBIRC_STATE_INIT )
-			{
-				libirc_mutex_unlock (&ircsession->mutex_dcc);
-
-				if ( dcc->cb )
-					(*dcc->cb)(ircsession, dcc->id, LIBIRC_ERR_TIMEOUT, dcc->ctx, 0, 0);
-
-				libirc_mutex_lock (&ircsession->mutex_dcc);
-			}
-
-			libirc_remove_dcc_session (ircsession, dcc, 0);
-		}
-
-		/*
-		 * If we're sending file, and the output buffer is empty, we need
-         * to provide some data.
-         */
-		if ( dcc->state == LIBIRC_STATE_CONNECTED
-		&& dcc->dccmode == LIBIRC_DCC_SENDFILE
-		&& dcc->dccsend_file_fp
-		&& dcc->outgoing_offset == 0 )
-		{
-			int len = fread (dcc->outgoing_buf, 1, sizeof (dcc->outgoing_buf), dcc->dccsend_file_fp);
-
-			if ( len <= 0 )
-			{
-				int err = (len < 0 ? LIBIRC_ERR_READ : 0);
-			
-				libirc_mutex_unlock (&ircsession->mutex_dcc);
-
-				(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
-				libirc_mutex_lock (&ircsession->mutex_dcc);
-				libirc_dcc_destroy_nolock (ircsession, dcc->id);
-			}
-			else
-				dcc->outgoing_offset = len;
-		}
-
-		// Clean up unused sessions
-		if ( dcc->state == LIBIRC_STATE_REMOVED )
-			libirc_remove_dcc_session (ircsession, dcc, 0);
-	}
-
-	for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next )
-	{
-		switch (dcc->state)
-		{
-		case LIBIRC_STATE_LISTENING:
-			// While listening, only in_set descriptor should be set
-			libirc_add_to_set (dcc->sock, in_set, maxfd);
-			break;
-
-		case LIBIRC_STATE_CONNECTING:
-			// While connection, only out_set descriptor should be set
-			libirc_add_to_set (dcc->sock, out_set, maxfd);
-			break;
-
-		case LIBIRC_STATE_CONNECTED:
-			// Add input descriptor if there is space in input buffer
-			// and it is DCC chat (during DCC send, there is nothing to recv)
-			if ( dcc->incoming_offset < sizeof(dcc->incoming_buf) - 1 )
-				libirc_add_to_set (dcc->sock, in_set, maxfd);
-
-			// Add output descriptor if there is something in output buffer
-			libirc_mutex_lock (&dcc->mutex_outbuf);
-
-			if ( dcc->outgoing_offset > 0  )
-				libirc_add_to_set (dcc->sock, out_set, maxfd);
-
-			libirc_mutex_unlock (&dcc->mutex_outbuf);
-			break;
-
-		case LIBIRC_STATE_CONFIRM_SIZE:
-			/*
-			 * If we're receiving file, then WE should confirm the transferred
-             * part (so we have to sent data). But if we're sending the file, 
-             * then RECEIVER should confirm the packet, so we have to receive
-             * data.
-             *
-             * We don't need to LOCK_DCC_OUTBUF - during file transfer, buffers
-             * can't change asynchronously.
-             */
-             if ( dcc->dccmode == LIBIRC_DCC_RECVFILE && dcc->outgoing_offset > 0 )
-             	libirc_add_to_set (dcc->sock, out_set, maxfd);
-
-             if ( dcc->dccmode == LIBIRC_DCC_SENDFILE && dcc->incoming_offset < 4 )
-				libirc_add_to_set (dcc->sock, in_set, maxfd);
-		}
-	}
-
-	libirc_mutex_unlock (&ircsession->mutex_dcc);
-}
-
-
-static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set)
-{
-	irc_dcc_session_t * dcc;
-
-	/*
-	 * We need to use such a complex scheme here, because on every callback
-     * a number of DCC sessions could be destroyed.
-     */
-	libirc_mutex_lock (&ircsession->mutex_dcc);
-
-	for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next )
-	{
-		if ( dcc->state == LIBIRC_STATE_LISTENING
-		&& FD_ISSET (dcc->sock, in_set) )
-		{
-			socklen_t len = sizeof(dcc->remote_addr);
-
-#if defined(_WIN32)
-			SOCKET nsock, err = 0;
-#else
-			int nsock, err = 0;
-#endif
-
-			// New connection is available; accept it.
-			if ( socket_accept (&dcc->sock, &nsock, (struct sockaddr *) &dcc->remote_addr, &len) )
-				err = LIBIRC_ERR_ACCEPT;
-
-			// On success, change the active socket and change the state
-			if ( err == 0 )
-			{
-				// close the listen socket, and replace it by a newly 
-				// accepted
-				socket_close (&dcc->sock);
-				dcc->sock = nsock;
-				dcc->state = LIBIRC_STATE_CONNECTED;
-			}
-
-			// If this is DCC chat, inform the caller about accept() 
-			// success or failure.
-			// Otherwise (DCC send) there is no reason.
-			if ( dcc->dccmode == LIBIRC_DCC_CHAT )
-			{
-				libirc_mutex_unlock (&ircsession->mutex_dcc);
-				(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
-				libirc_mutex_lock (&ircsession->mutex_dcc);
-			}
-
-			if ( err )
-				libirc_dcc_destroy_nolock (ircsession, dcc->id);
-		}
-
-		if ( dcc->state == LIBIRC_STATE_CONNECTING
-		&& FD_ISSET (dcc->sock, out_set) )
-		{
-			// Now we have to determine whether the socket is connected 
-			// or the connect is failed
-			struct sockaddr_in saddr;
-			socklen_t slen = sizeof(saddr);
-			int err = 0;
-
-			if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 )
-				err = LIBIRC_ERR_CONNECT;
-
-			// On success, change the state
-			if ( err == 0 )
-				dcc->state = LIBIRC_STATE_CONNECTED;
-
-			// If this is DCC chat, inform the caller about connect()
-			// success or failure.
-			// Otherwise (DCC send) there is no reason.
-			if ( dcc->dccmode == LIBIRC_DCC_CHAT )
-			{
-				libirc_mutex_unlock (&ircsession->mutex_dcc);
-				(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
-				libirc_mutex_lock (&ircsession->mutex_dcc);
-			}
-
-			if ( err )
-				libirc_dcc_destroy_nolock (ircsession, dcc->id);
-		}
-
-		if ( dcc->state == LIBIRC_STATE_CONNECTED
-		|| dcc->state == LIBIRC_STATE_CONFIRM_SIZE )
-		{
-			if ( FD_ISSET (dcc->sock, in_set) )
-			{
-				int length, offset = 0, err = 0;
-		
-				unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset;
-
-				length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount);
-
-				if ( length < 0 )
-				{
-					err = LIBIRC_ERR_READ;
-				}	
-				else if ( length == 0 )
-				{
-					err = LIBIRC_ERR_CLOSED;
-
-					if ( dcc->dccsend_file_fp )
-					{
-						fclose (dcc->dccsend_file_fp);
-						dcc->dccsend_file_fp = 0;
-					}
-				}
-				else
-				{
-					dcc->incoming_offset += length;
-
-					if ( dcc->dccmode != LIBIRC_DCC_CHAT )
-						offset = dcc->incoming_offset;
-					else
-						offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset);
-
-					/*
-					 * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any
-                     * callbacks (except there is an error). We just receive
-                     * the data, and compare it with the amount sent.
-                     */
-					if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE )
-					{
-						if ( dcc->dccmode != LIBIRC_DCC_SENDFILE )
-							abort();
-
-						if ( dcc->incoming_offset == 4 )
-						{
-							// The order is big-endian
-							const unsigned char * bptr = (const unsigned char *) dcc->incoming_buf;
-							unsigned int received_size = (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8)  | bptr[3];
-
-							// Sent size confirmed
-							if ( dcc->file_confirm_offset == received_size )
-							{
-								dcc->state = LIBIRC_STATE_CONNECTED;
-								dcc->incoming_offset = 0;
-							}
-							else
-								err = LIBIRC_ERR_WRITE;
-						}
-					}
-					else
-					{
-						/*
-						 * If it is DCC_CHAT, we send a 0-terminated string 
-						 * (which is smaller than offset). Otherwise we send
-	                     * a full buffer. 
-	                     */
-						libirc_mutex_unlock (&ircsession->mutex_dcc);
-
-						if ( dcc->dccmode != LIBIRC_DCC_CHAT )
-						{
-							if ( dcc->dccmode != LIBIRC_DCC_RECVFILE )
-								abort();
-
-							(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset);
-
-                            /*
-                             * If the session is not terminated in callback,
-                             * put the sent amount into the sent_packet_size_net_byteorder
-                             */
-                             if ( dcc->state != LIBIRC_STATE_REMOVED )
-                             {
-                             	dcc->state = LIBIRC_STATE_CONFIRM_SIZE;
-                             	dcc->file_confirm_offset += offset;
-								
-								// Store as big endian
-								dcc->outgoing_buf[0] = (char) dcc->file_confirm_offset >> 24;
-								dcc->outgoing_buf[1] = (char) dcc->file_confirm_offset >> 16;
-								dcc->outgoing_buf[2] = (char) dcc->file_confirm_offset >> 8;
-								dcc->outgoing_buf[3] = (char) dcc->file_confirm_offset;
-                             	dcc->outgoing_offset = 4;
-							}
-						}
-						else
-							(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf));
-
-						libirc_mutex_lock (&ircsession->mutex_dcc);
-
-						if ( dcc->incoming_offset - offset > 0 )
-							memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset);
-
-						dcc->incoming_offset -= offset;
-					}
-				}
-
-                /*
-                 * If error arises somewhere above, we inform the caller 
-                 * of failure, and destroy this session.
-                 */
-				if ( err )
-				{
-					libirc_mutex_unlock (&ircsession->mutex_dcc);
-					(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
-					libirc_mutex_lock (&ircsession->mutex_dcc);
-					libirc_dcc_destroy_nolock (ircsession, dcc->id);
-				}
-			}
-
-            /*
-             * Session might be closed (with sock = -1) after the in_set 
-             * processing, so before out_set processing we should check
-             * for this case
-			 */
-			if ( dcc->state == LIBIRC_STATE_REMOVED )
-				continue;
-
-			/*
-			 * Write bit set - we can send() something, and it won't block.
-             */
-			if ( FD_ISSET (dcc->sock, out_set) )
-			{
-				int length, offset, err = 0;
-
-				/*
-				 * Because in some cases outgoing_buf could be changed 
-				 * asynchronously (by another thread), we should lock 
-				 * it.
-                 */
-				libirc_mutex_lock (&dcc->mutex_outbuf);
-
-				offset = dcc->outgoing_offset;
-		
-				if ( offset > 0 )
-				{
-					length = socket_send (&dcc->sock, dcc->outgoing_buf, offset);
-
-					if ( length < 0 )
-						err = LIBIRC_ERR_WRITE;
-					else if ( length == 0 )
-						err = LIBIRC_ERR_CLOSED;
-					else
-					{
-						/*
-						 * If this was DCC_SENDFILE, and we just sent a packet,
-						 * change the state to wait for confirmation (and store
-						 * sent packet size)
-	                     */
-						if ( dcc->state == LIBIRC_STATE_CONNECTED
-						&& dcc->dccmode == LIBIRC_DCC_SENDFILE )
-						{
-							dcc->file_confirm_offset += offset;
-							dcc->state = LIBIRC_STATE_CONFIRM_SIZE;
-
-							libirc_mutex_unlock (&ircsession->mutex_dcc);
-							libirc_mutex_unlock (&dcc->mutex_outbuf);
-							(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, offset);
-							libirc_mutex_lock (&ircsession->mutex_dcc);
-							libirc_mutex_lock (&dcc->mutex_outbuf);
-						}
-
-						if ( dcc->outgoing_offset - length > 0 )
-							memmove (dcc->outgoing_buf, dcc->outgoing_buf + length, dcc->outgoing_offset - length);
-
-						dcc->outgoing_offset -= length;
-
-						/*
-						 * If we just sent the confirmation data, change state 
-						 * back.
-                         */
-						if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE
-						&& dcc->dccmode == LIBIRC_DCC_RECVFILE
-						&& dcc->outgoing_offset == 0 )
-						{
-							/*
-							 * If the file is already received, we should inform
-                             * the caller, and close the session.
-                             */
-							if ( dcc->received_file_size == dcc->file_confirm_offset )
-                            {
-								libirc_mutex_unlock (&ircsession->mutex_dcc);
-								libirc_mutex_unlock (&dcc->mutex_outbuf);
-								(*dcc->cb)(ircsession, dcc->id, 0, dcc->ctx, 0, 0);
-								libirc_dcc_destroy_nolock (ircsession, dcc->id);
-                            }
-                            else
-                            {
-                            	/* Continue to receive the file */
-								dcc->state = LIBIRC_STATE_CONNECTED;
-							}
-						}
-					}
-				}
-
-				libirc_mutex_unlock (&dcc->mutex_outbuf);
-
-                /*
-                 * If error arises somewhere above, we inform the caller 
-                 * of failure, and destroy this session.
-                 */
-				if ( err )
-				{
-					libirc_mutex_unlock (&ircsession->mutex_dcc);
-					(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
-					libirc_mutex_lock (&ircsession->mutex_dcc);
-
-					libirc_dcc_destroy_nolock (ircsession, dcc->id);
-				}
-			}
-		}
-	}
-
-	libirc_mutex_unlock (&ircsession->mutex_dcc);
-}
-
-
-static int libirc_new_dcc_session (irc_session_t * session, unsigned long ip, unsigned short port, int dccmode, void * ctx, irc_dcc_session_t ** pdcc)
-{
-	irc_dcc_session_t * dcc = malloc (sizeof(irc_dcc_session_t));
-
-	if ( !dcc )
-		return LIBIRC_ERR_NOMEM;
-
-	// setup
-	memset (dcc, 0, sizeof(irc_dcc_session_t));
-
-	dcc->dccsend_file_fp = 0;
-
-	if ( libirc_mutex_init (&dcc->mutex_outbuf) )
-		goto cleanup_exit_error;
-
-	if ( socket_create (PF_INET, SOCK_STREAM, &dcc->sock) )
-		goto cleanup_exit_error;
-
-	if ( !ip )
-	{
-		unsigned long arg = 1;
-
-		setsockopt (dcc->sock, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, sizeof(arg));
-
-#if defined (ENABLE_IPV6)
-		if ( session->flags & SESSIONFL_USES_IPV6 )
-		{
-			struct sockaddr_in6 saddr6;
-
-			memset (&saddr6, 0, sizeof(saddr6));
-			saddr6.sin6_family = AF_INET6;
-			memcpy (&saddr6.sin6_addr, &session->local_addr6, sizeof(session->local_addr6));
-			saddr6.sin6_port = htons (0);
-
-			if ( bind (dcc->sock, (struct sockaddr *) &saddr6, sizeof(saddr6)) < 0 )
-				goto cleanup_exit_error;
-		}
-		else
-#endif
-		{
-			struct sockaddr_in saddr;
-			memset (&saddr, 0, sizeof(saddr));
-			saddr.sin_family = AF_INET;
-			memcpy (&saddr.sin_addr, &session->local_addr, sizeof(session->local_addr));
-			saddr.sin_port = htons (0);
-
-			if ( bind (dcc->sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0 )
-				goto cleanup_exit_error;
-		}
-
-		if ( listen (dcc->sock, 5) < 0 )
-			goto cleanup_exit_error;
-
-		dcc->state = LIBIRC_STATE_LISTENING;
-	}
-	else
-	{
-		// make socket non-blocking, so connect() call won't block
-		if ( socket_make_nonblocking (&dcc->sock) )
-			goto cleanup_exit_error;
-
-		memset (&dcc->remote_addr, 0, sizeof(dcc->remote_addr));
-		dcc->remote_addr.sin_family = AF_INET;
-		dcc->remote_addr.sin_addr.s_addr = htonl (ip); // what idiot came up with idea to send IP address in host-byteorder?
-        dcc->remote_addr.sin_port = htons(port);
-
-		dcc->state = LIBIRC_STATE_INIT;
-	}
-
-	dcc->dccmode = dccmode;
-	dcc->ctx = ctx;
-	time (&dcc->timeout);
-
-	// and store it
-	libirc_mutex_lock (&session->mutex_dcc);
-
-	dcc->id = session->dcc_last_id++;
-	dcc->next = session->dcc_sessions;
-	session->dcc_sessions = dcc;
-
-	libirc_mutex_unlock (&session->mutex_dcc);
-
-    *pdcc = dcc;
-    return 0;
-
-cleanup_exit_error:
-	if ( dcc->sock >= 0 )
-		socket_close (&dcc->sock);
-
-	free (dcc);
-	return LIBIRC_ERR_SOCKET;
-}
-
-
-int irc_dcc_destroy (irc_session_t * session, irc_dcc_t dccid)
-{
-	// This function doesn't actually destroy the session; it just changes
-	// its state to "removed" and closes the socket. The memory is actually
-	// freed after the processing loop.
-	irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1);
-
-	if ( !dcc )
-		return 1;
-
-	if ( dcc->sock >= 0 )
-		socket_close (&dcc->sock);
-
-	dcc->state = LIBIRC_STATE_REMOVED;
-
-	libirc_mutex_unlock (&session->mutex_dcc);
-	return 0;
-}
-
-
-int	irc_dcc_chat (irc_session_t * session, void * ctx, const char * nick, irc_dcc_callback_t callback, irc_dcc_t * dccid)
-{
-	struct sockaddr_in saddr;
-	socklen_t len = sizeof(saddr);
-	char cmdbuf[128], notbuf[128];
-	irc_dcc_session_t * dcc;
-	int err;
-
-	if ( session->state != LIBIRC_STATE_CONNECTED )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	err = libirc_new_dcc_session (session, 0, 0, LIBIRC_DCC_CHAT, ctx, &dcc);
-
-	if ( err )
-	{
-		session->lasterror = err;
-		return 1;
-	}
-
-	if ( getsockname (dcc->sock, (struct sockaddr*) &saddr, &len) < 0 )
-	{
-		session->lasterror = LIBIRC_ERR_SOCKET;
-		libirc_remove_dcc_session (session, dcc, 1);
-		return 1;
-	}
-
-	sprintf (notbuf, "DCC Chat (%s)", inet_ntoa (saddr.sin_addr));
-	sprintf (cmdbuf, "DCC CHAT chat %lu %u", (unsigned long) ntohl (saddr.sin_addr.s_addr), ntohs (saddr.sin_port));
-
-	if ( irc_cmd_notice (session, nick, notbuf)
-	|| irc_cmd_ctcp_request (session, nick, cmdbuf) )
-	{
-		libirc_remove_dcc_session (session, dcc, 1);
-		return 1;
-	}
-
-	*dccid = dcc->id;
-	dcc->cb = callback;
-	dcc->dccmode = LIBIRC_DCC_CHAT;
-
-	return 0;
-}
-
-
-int irc_dcc_msg	(irc_session_t * session, irc_dcc_t dccid, const char * text)
-{
-	irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1);
-
-	if ( !dcc )
-		return 1;
-
-	if ( dcc->dccmode != LIBIRC_DCC_CHAT )
-	{
-		session->lasterror = LIBIRC_ERR_INVAL;
-		libirc_mutex_unlock (&session->mutex_dcc);
-		return 1;
-	}
-
-	if ( (strlen(text) + 2) >= (sizeof(dcc->outgoing_buf) - dcc->outgoing_offset) )
-	{
-		session->lasterror = LIBIRC_ERR_NOMEM;
-		libirc_mutex_unlock (&session->mutex_dcc);
-		return 1;
-	}
-
-	libirc_mutex_lock (&dcc->mutex_outbuf);
-
-	strcpy (dcc->outgoing_buf + dcc->outgoing_offset, text);
-	dcc->outgoing_offset += strlen (text);
-	dcc->outgoing_buf[dcc->outgoing_offset++] = 0x0D;
-	dcc->outgoing_buf[dcc->outgoing_offset++] = 0x0A;
-
-	libirc_mutex_unlock (&dcc->mutex_outbuf);
-	libirc_mutex_unlock (&session->mutex_dcc);
-
-	return 0;
-}
-
-
-static void libirc_dcc_request (irc_session_t * session, const char * nick, const char * req)
-{
-	char filenamebuf[256];
-	unsigned long ip, size;
-	unsigned short port;
-
-	if ( sscanf (req, "DCC CHAT chat %lu %hu", &ip, &port) == 2 )
-	{
-		if ( session->callbacks.event_dcc_chat_req )
-		{
-			irc_dcc_session_t * dcc;
-
-			int err = libirc_new_dcc_session (session, ip, port, LIBIRC_DCC_CHAT, 0, &dcc);
-			if ( err )
-			{
-				session->lasterror = err;
-				return;
-			}
-
-			(*session->callbacks.event_dcc_chat_req) (session, 
-						nick, 
-						inet_ntoa (dcc->remote_addr.sin_addr),
-						dcc->id);
-		}
-
-		return;
-	}
-	else if ( sscanf (req, "DCC SEND %s %lu %hu %lu", filenamebuf, &ip, &port, &size) == 4 )
-	{
-		if ( session->callbacks.event_dcc_send_req )
-		{
-			irc_dcc_session_t * dcc;
-
-			int err = libirc_new_dcc_session (session, ip, port, LIBIRC_DCC_RECVFILE, 0, &dcc);
-			if ( err )
-			{
-				session->lasterror = err;
-				return;
-			}
-
-			(*session->callbacks.event_dcc_send_req) (session, 
-						nick, 
-						inet_ntoa (dcc->remote_addr.sin_addr),
-						filenamebuf,
-						size,
-						dcc->id);
-
-			dcc->received_file_size = size;
-		}
-
-		return;
-	}
-#if defined (ENABLE_DEBUG)
-	fprintf (stderr, "BUG: Unhandled DCC message: %s\n", req);
-	abort();
-#endif
-}
-
-
-int	irc_dcc_accept (irc_session_t * session, irc_dcc_t dccid, void * ctx, irc_dcc_callback_t callback)
-{
-	irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1);
-
-	if ( !dcc )
-		return 1;
-
-	if ( dcc->state != LIBIRC_STATE_INIT )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		libirc_mutex_unlock (&session->mutex_dcc);
-		return 1;
-	}
-
-	dcc->cb = callback;
-	dcc->ctx = ctx;
-
-	// Initiate the connect
-    if ( socket_connect (&dcc->sock, (struct sockaddr *) &dcc->remote_addr, sizeof(dcc->remote_addr)) )
-	{
-		libirc_dcc_destroy_nolock (session, dccid);
-		libirc_mutex_unlock (&session->mutex_dcc);
-		session->lasterror = LIBIRC_ERR_CONNECT;
-		return 1;
-	}
-
-	dcc->state = LIBIRC_STATE_CONNECTING;
-	libirc_mutex_unlock (&session->mutex_dcc);
-	return 0;
-}
-
-
-int	irc_dcc_decline (irc_session_t * session, irc_dcc_t dccid)
-{
-	irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1);
-
-	if ( !dcc )
-		return 1;
-
-	if ( dcc->state != LIBIRC_STATE_INIT )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		libirc_mutex_unlock (&session->mutex_dcc);
-		return 1;
-	}
-
-	libirc_dcc_destroy_nolock (session, dccid);
-	libirc_mutex_unlock (&session->mutex_dcc);
-	return 0;
-}
-
-
-int	irc_dcc_sendfile (irc_session_t * session, void * ctx, const char * nick, const char * filename, irc_dcc_callback_t callback, irc_dcc_t * dccid)
-{
-	struct sockaddr_in saddr;
-	socklen_t len = sizeof(saddr);
-	char cmdbuf[128], notbuf[128];
-	irc_dcc_session_t * dcc;
-	const char * p;
-	int err;
-	long filesize;
-
-	if ( !session || !dccid || !filename || !callback )
-	{
-		session->lasterror = LIBIRC_ERR_INVAL;
-		return 1;
-	}
-
-	if ( session->state != LIBIRC_STATE_CONNECTED )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	if ( (err = libirc_new_dcc_session (session, 0, 0, LIBIRC_DCC_SENDFILE, ctx, &dcc)) != 0 )
-	{
-		session->lasterror = err;
-		return 1;
-	}
-
-	if ( (dcc->dccsend_file_fp = fopen (filename, "rb")) == 0 )
-	{
-		libirc_remove_dcc_session (session, dcc, 1);
-		session->lasterror = LIBIRC_ERR_OPENFILE;
-		return 1;
-	}
-
-	/* Get file length */
-	if ( fseek (dcc->dccsend_file_fp, 0, SEEK_END)
-	|| (filesize = ftell (dcc->dccsend_file_fp)) == -1
-	|| fseek (dcc->dccsend_file_fp, 0, SEEK_SET) )
-	{
-		libirc_remove_dcc_session (session, dcc, 1);
-		session->lasterror = LIBIRC_ERR_NODCCSEND;
-		return 1;
-	}
-
-	if ( getsockname (dcc->sock, (struct sockaddr*) &saddr, &len) < 0 )
-	{
-		libirc_remove_dcc_session (session, dcc, 1);
-		session->lasterror = LIBIRC_ERR_SOCKET;
-		return 1;
-	}
-
-	// Remove path from the filename
-	if ( (p = strrchr (filename, '\\')) == 0
-	&& (p = strrchr (filename, '/')) == 0 )
-		p = filename;
-	else
-		p++; // skip directory slash
-
-	sprintf (notbuf, "DCC Send %s (%s)", p, inet_ntoa (saddr.sin_addr));
-	sprintf (cmdbuf, "DCC SEND %s %lu %u %ld", p, (unsigned long) ntohl (saddr.sin_addr.s_addr), ntohs (saddr.sin_port), filesize);
-
-	if ( irc_cmd_notice (session, nick, notbuf)
-	|| irc_cmd_ctcp_request (session, nick, cmdbuf) )
-	{
-		libirc_remove_dcc_session (session, dcc, 1);
-		return 1;
-	}
-
-	*dccid = dcc->id;
-	dcc->cb = callback;
-
-	return 0;
-}
--- a/extern/libircclient/src/dcc.h	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-#ifndef INCLUDE_IRC_DCC_H
-#define INCLUDE_IRC_DCC_H
-
-
-/*
- * This structure keeps the state of a single DCC connection.
- */
-struct irc_dcc_session_s
-{
-	irc_dcc_session_t	*	next;
-
-	irc_dcc_t		id;
-	void		*	ctx;
-	socket_t		sock;		/*!< DCC socket */
-	int				dccmode;	/*!< Boolean value to differ chat vs send 
-	                             requests. Changes the cb behavior - when
-	                             it is chat, data is sent by lines with 
-	                             stripped CRLFs. In file mode, the data
-	                             is sent as-is */
-	int				state;
-	time_t			timeout;
-
-	FILE		*	dccsend_file_fp;
-	unsigned int	received_file_size;
-	unsigned int	file_confirm_offset;
-
-	struct sockaddr_in	remote_addr;
-
-	char 			incoming_buf[LIBIRC_DCC_BUFFER_SIZE];
-	unsigned int	incoming_offset;
-
-	char 			outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];
-	unsigned int	outgoing_offset;
-	port_mutex_t	mutex_outbuf;
-
-	irc_dcc_callback_t		cb;
-};
-
-
-#endif /* INCLUDE_IRC_DCC_H */
--- a/extern/libircclient/src/errors.c	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-static const char * libirc_strerror[LIBIRC_ERR_MAX] = 
-{
-	"No error",
-	"Invalid argument",
-	"Host not resolved",
-	"Socket error",
-	"Could not connect",
-	"Remote connection closed",
-	"Out of memory",
-	"Could not accept new connection",
-	"Object not found",
-	"Could not DCC send this object",
-	"Read error",
-	"Write error",
-	"Illegal operation for this state",
-	"Timeout error",
-	"Could not open file",
-	"IRC session terminated",
-	"IPv6 not supported",
-	"SSL not supported",
-	"SSL initialization failed",
-	"SSL connection failed",
-	"SSL certificate verify failed",
-};
-
-
-int irc_errno (irc_session_t * session)
-{
-	return session->lasterror;
-}
-
-
-const char * irc_strerror (int ircerrno)
-{
-	if ( ircerrno >= 0 && ircerrno < LIBIRC_ERR_MAX )
-		return libirc_strerror[ircerrno];
-	else
-		return "Invalid irc_errno value";
-}
-
--- a/extern/libircclient/src/libircclient.c	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1260 +0,0 @@
-/*
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public
- * License for more details.
- */
-
-#define IS_DEBUG_ENABLED(s)	((s)->options & LIBIRC_OPTION_DEBUG)
-
-#include "portable.c"
-#include "sockets.c"
-
-#include "libircclient.h"
-#include "session.h"
-
-#include "utils.c"
-#include "errors.c"
-#include "colors.c"
-#include "dcc.c"
-#include "ssl.c"
-
-
-#ifdef _MSC_VER
-	/*
-	 * The debugger of MSVC 2005 does not like strdup.
-	 * It complains about heap corruption when free is called.
-	 * Use _strdup instead.
-	 */
-	#undef strdup
-	#define strdup _strdup
-#endif
-
-
-irc_session_t * irc_create_session (irc_callbacks_t	* callbacks)
-{
-	irc_session_t * session = malloc (sizeof(irc_session_t));
-
-	if ( !session )
-		return 0;
-
-	memset (session, 0, sizeof(irc_session_t));
-	session->sock = -1;
-
-	if ( libirc_mutex_init (&session->mutex_session)
-	|| libirc_mutex_init (&session->mutex_dcc) )
-	{
-		free (session);
-		return 0;
-	}
-
-	session->dcc_last_id = 1;
-	session->dcc_timeout = 60;
-
-	memcpy (&session->callbacks, callbacks, sizeof(irc_callbacks_t));
-
-	if ( !session->callbacks.event_ctcp_req )
-		session->callbacks.event_ctcp_req = libirc_event_ctcp_internal;
-
-	return session;
-}
-
-static void free_ircsession_strings (irc_session_t * session)
-{
-	if ( session->realname )
-		free (session->realname);
-
-	if ( session->username )
-		free (session->username);
-
-	if ( session->nick )
-		free (session->nick);
-
-	if ( session->server )
-		free (session->server);
-
-	if ( session->server_password )
-		free (session->server_password);
-
-	session->realname = 0;
-	session->username = 0;
-	session->nick = 0;
-	session->server = 0;
-	session->server_password = 0;
-}
-
-void irc_destroy_session (irc_session_t * session)
-{
-	free_ircsession_strings( session );
-
-	// The CTCP VERSION must be freed only now
-	if ( session->ctcp_version )
-		free (session->ctcp_version);
-
-	if ( session->sock >= 0 )
-		socket_close (&session->sock);
-
-#if defined (ENABLE_THREADS)
-	libirc_mutex_destroy (&session->mutex_session);
-#endif
-
-#if defined (ENABLE_SSL)
-	if ( session->ssl )
-		SSL_free( session->ssl );
-#endif
-
-	/*
-	 * delete DCC data
-	 * libirc_remove_dcc_session removes the DCC session from the list.
-	 */
-	while ( session->dcc_sessions )
-		libirc_remove_dcc_session (session, session->dcc_sessions, 0);
-
-	free (session);
-}
-
-
-int irc_connect (irc_session_t * session,
-			const char * server,
-			unsigned short port,
-			const char * server_password,
-			const char * nick,
-			const char * username,
-			const char * realname)
-{
-	struct sockaddr_in saddr;
-	char * p;
-
-	// Check and copy all the specified fields
-	if ( !server || !nick )
-	{
-		session->lasterror = LIBIRC_ERR_INVAL;
-		return 1;
-	}
-
-	if ( session->state != LIBIRC_STATE_INIT )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	// Free the strings if defined; may be the case when the session is reused after the connection fails
-	free_ircsession_strings( session );
-
-	// Handle the server # prefix (SSL)
-	if ( server[0] == SSL_PREFIX )
-	{
-#if defined (ENABLE_SSL)
-		server++;
-		session->flags |= SESSIONFL_SSL_CONNECTION;
-#else
-		session->lasterror = LIBIRC_ERR_SSL_NOT_SUPPORTED;
-		return 1;
-#endif
-	}
-
-	if ( username )
-		session->username = strdup (username);
-
-	if ( server_password )
-		session->server_password = strdup (server_password);
-
-	if ( realname )
-		session->realname = strdup (realname);
-
-	session->nick = strdup (nick);
-	session->server = strdup (server);
-
-	// If port number is zero and server contains the port, parse it
-	if ( port == 0 && (p = strchr( session->server, ':' )) != 0 )
-	{
-		// Terminate the string and parse the port number
-		*p++ = '\0';
-		port = atoi( p );
-	}
-
-	// IPv4 address resolving
-	memset( &saddr, 0, sizeof(saddr) );
-	saddr.sin_family = AF_INET;
-	saddr.sin_port = htons (port);
-	saddr.sin_addr.s_addr = inet_addr( session->server );
-
-    if ( saddr.sin_addr.s_addr == INADDR_NONE )
-    {
-		struct hostent *hp;
-#if defined HAVE_GETHOSTBYNAME_R
-		int tmp_errno;
-		struct hostent tmp_hostent;
-		char buf[2048];
-
-      	if ( gethostbyname_r (session->server, &tmp_hostent, buf, sizeof(buf), &hp, &tmp_errno) )
-      		hp = 0;
-#else
-      	hp = gethostbyname (session->server);
-#endif // HAVE_GETHOSTBYNAME_R
-		if ( !hp )
-		{
-			session->lasterror = LIBIRC_ERR_RESOLV;
-			return 1;
-		}
-
-		memcpy (&saddr.sin_addr, hp->h_addr, (size_t) hp->h_length);
-    }
-
-    // create the IRC server socket
-	if ( socket_create( PF_INET, SOCK_STREAM, &session->sock)
-	|| socket_make_nonblocking (&session->sock) )
-	{
-		session->lasterror = LIBIRC_ERR_SOCKET;
-		return 1;
-	}
-
-#if defined (ENABLE_SSL)
-	// Init the SSL stuff
-	if ( session->flags & SESSIONFL_SSL_CONNECTION )
-	{
-		int rc = ssl_init( session );
-
-		if ( rc != 0 )
-		{
-			session->lasterror = rc;
-			return 1;
-		}
-	}
-#endif
-
-    // and connect to the IRC server
-    if ( socket_connect (&session->sock, (struct sockaddr *) &saddr, sizeof(saddr)) )
-    {
-    	session->lasterror = LIBIRC_ERR_CONNECT;
-		return 1;
-    }
-
-    session->state = LIBIRC_STATE_CONNECTING;
-    session->flags = SESSIONFL_USES_IPV6; // reset in case of reconnect
-	return 0;
-}
-
-
-int irc_connect6 (irc_session_t * session,
-			const char * server,
-			unsigned short port,
-			const char * server_password,
-			const char * nick,
-			const char * username,
-			const char * realname)
-{
-#if defined (ENABLE_IPV6)
-	struct sockaddr_in6 saddr;
-	struct addrinfo ainfo, *res = NULL;
-	char portStr[32], *p;
-#if defined (_WIN32)
-	int addrlen = sizeof(saddr);
-	HMODULE hWsock;
-	getaddrinfo_ptr_t getaddrinfo_ptr;
-	freeaddrinfo_ptr_t freeaddrinfo_ptr;
-	int resolvesuccess = 0;
-#endif
-
-	// Check and copy all the specified fields
-	if ( !server || !nick )
-	{
-		session->lasterror = LIBIRC_ERR_INVAL;
-		return 1;
-	}
-
-	if ( session->state != LIBIRC_STATE_INIT )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	// Free the strings if defined; may be the case when the session is reused after the connection fails
-	free_ircsession_strings( session );
-
-	// Handle the server # prefix (SSL)
-	if ( server[0] == SSL_PREFIX )
-	{
-#if defined (ENABLE_SSL)
-		server++;
-		session->flags |= SESSIONFL_SSL_CONNECTION;
-#else
-		session->lasterror = LIBIRC_ERR_SSL_NOT_SUPPORTED;
-		return 1;
-#endif
-	}
-
-	if ( username )
-		session->username = strdup (username);
-
-	if ( server_password )
-		session->server_password = strdup (server_password);
-
-	if ( realname )
-		session->realname = strdup (realname);
-
-	session->nick = strdup (nick);
-	session->server = strdup (server);
-
-	// If port number is zero and server contains the port, parse it
-	if ( port == 0 && (p = strchr( session->server, ':' )) != 0 )
-	{
-		// Terminate the string and parse the port number
-		*p++ = '\0';
-		port = atoi( p );
-	}
-
-	memset( &saddr, 0, sizeof(saddr) );
-	saddr.sin6_family = AF_INET6;
-	saddr.sin6_port = htons (port);
-
-	sprintf( portStr, "%u", (unsigned)port );
-
-#if defined (_WIN32)
-	if ( WSAStringToAddressA( (LPSTR)session->server, AF_INET6, NULL, (struct sockaddr *)&saddr, &addrlen ) == SOCKET_ERROR )
-	{
-		hWsock = LoadLibraryA("ws2_32");
-
-		if (hWsock)
-		{
-			/* Determine functions at runtime, because windows systems < XP do not
-			 * support getaddrinfo. */
-			getaddrinfo_ptr = (getaddrinfo_ptr_t)GetProcAddress(hWsock, "getaddrinfo");
-			freeaddrinfo_ptr = (freeaddrinfo_ptr_t)GetProcAddress(hWsock, "freeaddrinfo");
-
-			if (getaddrinfo_ptr && freeaddrinfo_ptr)
-			{
-				memset(&ainfo, 0, sizeof(ainfo));
-				ainfo.ai_family = AF_INET6;
-				ainfo.ai_socktype = SOCK_STREAM;
-				ainfo.ai_protocol = 0;
-
-				if ( getaddrinfo_ptr(session->server, portStr, &ainfo, &res) == 0 && res )
-				{
-					resolvesuccess = 1;
-					memcpy( &saddr, res->ai_addr, res->ai_addrlen );
-					freeaddrinfo_ptr( res );
-				}
-			}
-			FreeLibrary(hWsock);
-		}
-		if (!resolvesuccess)
-		{
-			session->lasterror = LIBIRC_ERR_RESOLV;
-			return 1;
-		}
-	}
-#else
-	if ( inet_pton( AF_INET6, session->server, (void*) &saddr.sin6_addr ) <= 0 )
-	{
-		memset( &ainfo, 0, sizeof(ainfo) );
-		ainfo.ai_family = AF_INET6;
-		ainfo.ai_socktype = SOCK_STREAM;
-		ainfo.ai_protocol = 0;
-
-		if ( getaddrinfo( session->server, portStr, &ainfo, &res ) || !res )
-		{
-			session->lasterror = LIBIRC_ERR_RESOLV;
-			return 1;
-		}
-
-		memcpy( &saddr, res->ai_addr, res->ai_addrlen );
-		freeaddrinfo( res );
-	}
-#endif
-
-	// create the IRC server socket
-	if ( socket_create( PF_INET6, SOCK_STREAM, &session->sock)
-	|| socket_make_nonblocking (&session->sock) )
-	{
-		session->lasterror = LIBIRC_ERR_SOCKET;
-		return 1;
-	}
-
-#if defined (ENABLE_SSL)
-	// Init the SSL stuff
-	if ( session->flags & SESSIONFL_SSL_CONNECTION )
-	{
-		int rc = ssl_init( session );
-
-		if ( rc != 0 )
-			return rc;
-	}
-#endif
-
-    // and connect to the IRC server
-    if ( socket_connect (&session->sock, (struct sockaddr *) &saddr, sizeof(saddr)) )
-    {
-    	session->lasterror = LIBIRC_ERR_CONNECT;
-		return 1;
-    }
-
-    session->state = LIBIRC_STATE_CONNECTING;
-    session->flags = 0; // reset in case of reconnect
-	return 0;
-#else
-	session->lasterror = LIBIRC_ERR_NOIPV6;
-	return 1;
-#endif
-}
-
-
-int irc_is_connected (irc_session_t * session)
-{
-	return (session->state == LIBIRC_STATE_CONNECTED
-	|| session->state == LIBIRC_STATE_CONNECTING) ? 1 : 0;
-}
-
-
-int irc_run (irc_session_t * session)
-{
-	if ( session->state != LIBIRC_STATE_CONNECTING )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	while ( irc_is_connected(session) )
-	{
-		struct timeval tv;
-		fd_set in_set, out_set;
-		int maxfd = 0;
-
-		tv.tv_usec = 250000;
-		tv.tv_sec = 0;
-
-		// Init sets
-		FD_ZERO (&in_set);
-		FD_ZERO (&out_set);
-
-		irc_add_select_descriptors (session, &in_set, &out_set, &maxfd);
-
-		if ( select (maxfd + 1, &in_set, &out_set, 0, &tv) < 0 )
-		{
-			if ( socket_error() == EINTR )
-				continue;
-
-			session->lasterror = LIBIRC_ERR_TERMINATED;
-			return 1;
-		}
-
-		if ( irc_process_select_descriptors (session, &in_set, &out_set) )
-			return 1;
-	}
-
-	return 0;
-}
-
-
-int irc_add_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set, int * maxfd)
-{
-	if ( session->sock < 0
-	|| session->state == LIBIRC_STATE_INIT
-	|| session->state == LIBIRC_STATE_DISCONNECTED )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	libirc_mutex_lock (&session->mutex_session);
-
-	switch (session->state)
-	{
-	case LIBIRC_STATE_CONNECTING:
-		// While connection, only out_set descriptor should be set
-		libirc_add_to_set (session->sock, out_set, maxfd);
-		break;
-
-	case LIBIRC_STATE_CONNECTED:
-		// Add input descriptor if there is space in input buffer
-		if ( session->incoming_offset < (sizeof (session->incoming_buf) - 1)
-		|| (session->flags & SESSIONFL_SSL_WRITE_WANTS_READ) != 0 )
-			libirc_add_to_set (session->sock, in_set, maxfd);
-
-		// Add output descriptor if there is something in output buffer
-		if ( libirc_findcrlf (session->outgoing_buf, session->outgoing_offset) > 0
-		|| (session->flags & SESSIONFL_SSL_READ_WANTS_WRITE) != 0 )
-			libirc_add_to_set (session->sock, out_set, maxfd);
-
-		break;
-	}
-
-	libirc_mutex_unlock (&session->mutex_session);
-
-	libirc_dcc_add_descriptors (session, in_set, out_set, maxfd);
-	return 0;
-}
-
-
-static void libirc_process_incoming_data (irc_session_t * session, size_t process_length)
-{
-	#define MAX_PARAMS_ALLOWED 10
-	char buf[2*512], *p, *s;
-	const char * command = 0, *prefix = 0, *params[MAX_PARAMS_ALLOWED+1];
-	int code = 0, paramindex = 0;
-    char *buf_end = buf + process_length;
-
-	if ( process_length > sizeof(buf) )
-		abort(); // should be impossible
-
-	memcpy (buf, session->incoming_buf, process_length);
-	buf[process_length] = '\0';
-
-	memset ((char *)params, 0, sizeof(params));
-	p = buf;
-
-    /*
-     * From RFC 1459:
-	 *  <message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
-	 *  <prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
-	 *  <command>  ::= <letter> { <letter> } | <number> <number> <number>
-	 *  <SPACE>    ::= ' ' { ' ' }
-	 *  <params>   ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
-	 *  <middle>   ::= <Any *non-empty* sequence of octets not including SPACE
-	 *                 or NUL or CR or LF, the first of which may not be ':'>
-	 *  <trailing> ::= <Any, possibly *empty*, sequence of octets not including
-	 *                   NUL or CR or LF>
- 	 */
-
-	// Parse <prefix>
-	if ( buf[0] == ':' )
-	{
-		while ( *p && *p != ' ')
-			p++;
-
-		*p++ = '\0';
-
-		// we use buf+1 to skip the leading colon
-		prefix = buf + 1;
-
-		// If LIBIRC_OPTION_STRIPNICKS is set, we should 'clean up' nick
-		// right here
-		if ( session->options & LIBIRC_OPTION_STRIPNICKS )
-		{
-			for ( s = buf + 1; *s; s++ )
-			{
-				if ( *s == '@' || *s == '!' )
-				{
-					*s = '\0';
-					break;
-				}
-			}
-		}
-	}
-
-	// Parse <command>
-	if ( isdigit (p[0]) && isdigit (p[1]) && isdigit (p[2]) )
-	{
-		p[3] = '\0';
-		code = atoi (p);
-		p += 4;
-	}
-	else
-	{
-		s = p;
-
-		while ( *p && *p != ' ')
-			p++;
-
-		*p++ = '\0';
-
-		command = s;
-	}
-
-	// Parse middle/params
-	while ( *p &&  paramindex < MAX_PARAMS_ALLOWED )
-	{
-		// beginning from ':', this is the last param
-		if ( *p == ':' )
-		{
-			params[paramindex++] = p + 1; // skip :
-			break;
-		}
-
-		// Just a param
-		for ( s = p; *p && *p != ' '; p++ )
-			;
-
-		params[paramindex++] = s;
-
-		if ( !*p )
-			break;
-
-		*p++ = '\0';
-	}
-
-	// Handle PING/PONG
-	if ( command && !strncmp (command, "PING", buf_end - command) && params[0] )
-	{
-		if (session->callbacks.event_ping)
-			(*session->callbacks.event_ping)(session, "PING", prefix, params, paramindex);
-
-		irc_send_raw (session, "PONG %s", params[0]);
-
-		return;
-	}
-
-	// and dump
-	if ( code )
-	{
-		// We use SESSIONFL_MOTD_RECEIVED flag to check whether it is the first
-		// RPL_ENDOFMOTD or ERR_NOMOTD after the connection.
-		if ( (code == 1 || code == 376 || code == 422) && !(session->flags & SESSIONFL_MOTD_RECEIVED ) )
-		{
-			session->flags |= SESSIONFL_MOTD_RECEIVED;
-
-			if ( session->callbacks.event_connect )
-				(*session->callbacks.event_connect) (session, "CONNECT", prefix, params, paramindex);
-		}
-
-		if ( session->callbacks.event_numeric )
-			(*session->callbacks.event_numeric) (session, code, prefix, params, paramindex);
-	}
-	else
-	{
-		if ( !strncmp (command, "NICK", buf_end - command) )
-		{
-			/*
-			 * If we're changed our nick, we should save it.
-             */
-			char nickbuf[256];
-
-			irc_target_get_nick (prefix, nickbuf, sizeof(nickbuf));
-
-			if ( !strncmp (nickbuf, session->nick, strlen(session->nick)) && paramindex > 0 )
-			{
-				free (session->nick);
-				session->nick = strdup (params[0]);
-			}
-
-			if ( session->callbacks.event_nick )
-				(*session->callbacks.event_nick) (session, command, prefix, params, paramindex);
-		}
-		else if ( !strncmp (command, "QUIT", buf_end - command) )
-		{
-			if ( session->callbacks.event_quit )
-				(*session->callbacks.event_quit) (session, command, prefix, params, paramindex);
-		}
-		else if ( !strncmp (command, "JOIN", buf_end - command) )
-		{
-			if ( session->callbacks.event_join )
-				(*session->callbacks.event_join) (session, command, prefix, params, paramindex);
-		}
-		else if ( !strncmp (command, "PART", buf_end - command) )
-		{
-			if ( session->callbacks.event_part )
-				(*session->callbacks.event_part) (session, command, prefix, params, paramindex);
-		}
-		else if ( !strncmp (command, "MODE", buf_end - command) )
-		{
-			if ( paramindex > 0 && !strncmp (params[0], session->nick, strlen(session->nick)) )
-			{
-				params[0] = params[1];
-				paramindex = 1;
-
-				if ( session->callbacks.event_umode )
-					(*session->callbacks.event_umode) (session, command, prefix, params, paramindex);
-			}
-			else
-			{
-				if ( session->callbacks.event_mode )
-					(*session->callbacks.event_mode) (session, command, prefix, params, paramindex);
-			}
-		}
-		else if ( !strncmp (command, "TOPIC", buf_end - command) )
-		{
-			if ( session->callbacks.event_topic )
-				(*session->callbacks.event_topic) (session, command, prefix, params, paramindex);
-		}
-		else if ( !strncmp (command, "KICK", buf_end - command) )
-		{
-			if ( session->callbacks.event_kick )
-				(*session->callbacks.event_kick) (session, command, prefix, params, paramindex);
-		}
-		else if ( !strncmp (command, "PRIVMSG", buf_end - command) )
-		{
-			if ( paramindex > 1 )
-			{
-				size_t msglen = strlen (params[1]);
-
-				/*
-				 * Check for CTCP request (a CTCP message starts from 0x01
-				 * and ends by 0x01
-				 */
-				if ( params[1][0] == 0x01 && params[1][msglen-1] == 0x01 )
-				{
-					char ctcp_buf[128];
-
-					msglen -= 2;
-					if ( msglen > sizeof(ctcp_buf) - 1 )
-						msglen = sizeof(ctcp_buf) - 1;
-
-					memcpy (ctcp_buf, params[1] + 1, msglen);
-					ctcp_buf[msglen] = '\0';
-
-					if ( !strncasecmp(ctcp_buf, "DCC ", 4) )
-						libirc_dcc_request (session, prefix, ctcp_buf);
-					else if ( !strncasecmp( ctcp_buf, "ACTION ", 7)
-					&& session->callbacks.event_ctcp_action )
-					{
-						params[1] = ctcp_buf + 7; // the length of "ACTION "
-						paramindex = 2;
-
-						(*session->callbacks.event_ctcp_action) (session, "ACTION", prefix, params, paramindex);
-					}
-					else
-					{
-						params[0] = ctcp_buf;
-						paramindex = 1;
-
-						if ( session->callbacks.event_ctcp_req )
-							(*session->callbacks.event_ctcp_req) (session, "CTCP", prefix, params, paramindex);
-					}
-				}
-				else if ( !strncasecmp (params[0], session->nick, strlen(session->nick) ) )
-				{
-					if ( session->callbacks.event_privmsg )
-						(*session->callbacks.event_privmsg) (session, "PRIVMSG", prefix, params, paramindex);
-				}
-				else
-				{
-					if ( session->callbacks.event_channel )
-						(*session->callbacks.event_channel) (session, "CHANNEL", prefix, params, paramindex);
-				}
-			}
-		}
-		else if ( !strncmp (command, "NOTICE", buf_end - command) )
-		{
-			size_t msglen = strlen (params[1]);
-
-			/*
-			 * Check for CTCP request (a CTCP message starts from 0x01
-			 * and ends by 0x01
-             */
-			if ( paramindex > 1 && params[1][0] == 0x01 && params[1][msglen-1] == 0x01 )
-			{
-				char ctcp_buf[512];
-
-				msglen -= 2;
-				if ( msglen > sizeof(ctcp_buf) - 1 )
-					msglen = sizeof(ctcp_buf) - 1;
-
-				memcpy (ctcp_buf, params[1] + 1, msglen);
-				ctcp_buf[msglen] = '\0';
-
-				params[0] = ctcp_buf;
-				paramindex = 1;
-
-				if ( session->callbacks.event_ctcp_rep )
-					(*session->callbacks.event_ctcp_rep) (session, "CTCP", prefix, params, paramindex);
-			}
-			else if ( !strncasecmp (params[0], session->nick, strlen(session->nick) ) )
-			{
-				if ( session->callbacks.event_notice )
-					(*session->callbacks.event_notice) (session, command, prefix, params, paramindex);
-			} else {
-				if ( session->callbacks.event_channel_notice )
-					(*session->callbacks.event_channel_notice) (session, command, prefix, params, paramindex);
-			}
-		}
-		else if ( !strncmp (command, "INVITE", buf_end - command) )
-		{
-			if ( session->callbacks.event_invite )
-				(*session->callbacks.event_invite) (session, command, prefix, params, paramindex);
-		}
-		else if ( !strncmp (command, "KILL", buf_end - command) )
-		{
-			; /* ignore this event - not all servers generate this */
-		}
-	 	else
-	 	{
-			/*
-			 * The "unknown" event is triggered upon receipt of any number of
-			 * unclassifiable miscellaneous messages, which aren't handled by
-			 * the library.
-			 */
-
-			if ( session->callbacks.event_unknown )
-				(*session->callbacks.event_unknown) (session, command, prefix, params, paramindex);
-		}
-	}
-}
-
-
-int irc_process_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set)
-{
-	char buf[256], hname[256];
-
-	if ( session->sock < 0
-	|| session->state == LIBIRC_STATE_INIT
-	|| session->state == LIBIRC_STATE_DISCONNECTED )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	session->lasterror = 0;
-	libirc_dcc_process_descriptors (session, in_set, out_set);
-
-	// Handle "connection succeed" / "connection failed"
-	if ( session->state == LIBIRC_STATE_CONNECTING
-	&& FD_ISSET (session->sock, out_set) )
-	{
-		// Now we have to determine whether the socket is connected
-		// or the connect is failed
-		struct sockaddr_storage saddr, laddr;
-		socklen_t slen = sizeof(saddr);
-		socklen_t llen = sizeof(laddr);
-
-		if ( getsockname (session->sock, (struct sockaddr*)&laddr, &llen) < 0
-		|| getpeername (session->sock, (struct sockaddr*)&saddr, &slen) < 0 )
-		{
-			// connection failed
-			session->lasterror = LIBIRC_ERR_CONNECT;
-			session->state = LIBIRC_STATE_DISCONNECTED;
-			return 1;
-		}
-
-		if (saddr.ss_family == AF_INET)
-			memcpy (&session->local_addr, &((struct sockaddr_in *)&laddr)->sin_addr, sizeof(struct in_addr));
-		else
-			memcpy (&session->local_addr, &((struct sockaddr_in6 *)&laddr)->sin6_addr, sizeof(struct in6_addr));
-
-#if defined (ENABLE_DEBUG)
-		if ( IS_DEBUG_ENABLED(session) )
-			fprintf (stderr, "[DEBUG] Detected local address: %s\n", inet_ntoa(session->local_addr));
-#endif
-
-		session->state = LIBIRC_STATE_CONNECTED;
-
-		// Get the hostname
-    	if ( gethostname (hname, sizeof(hname)) < 0 )
-    		strcpy (hname, "unknown");
-
-		// Prepare the data, which should be sent to the server
-		if ( session->server_password )
-		{
-			snprintf (buf, sizeof(buf), "PASS %s", session->server_password);
-			irc_send_raw (session, buf);
-		}
-
-		snprintf (buf, sizeof(buf), "NICK %s", session->nick);
-		irc_send_raw (session, buf);
-
-		/*
-		 * RFC 1459 states that "hostname and servername are normally
-         * ignored by the IRC server when the USER command comes from
-         * a directly connected client (for security reasons)", therefore
-         * we don't need them.
-         */
-		snprintf (buf, sizeof(buf), "USER %s unknown unknown :%s",
-				session->username ? session->username : "nobody",
-				session->realname ? session->realname : "noname");
-		irc_send_raw (session, buf);
-
-		return 0;
-	}
-
-	if ( session->state != LIBIRC_STATE_CONNECTED )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	// Hey, we've got something to read!
-	if ( FD_ISSET (session->sock, in_set) )
-	{
-		int offset, length = session_socket_read( session );
-
-		if ( length < 0 )
-		{
-			if ( session->lasterror == 0 )
-				session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED);
-
-			session->state = LIBIRC_STATE_DISCONNECTED;
-			return 1;
-		}
-
-		session->incoming_offset += length;
-
-		// process the incoming data
-		while ( (offset = libirc_findcrlf (session->incoming_buf, session->incoming_offset)) > 0 )
-		{
-#if defined (ENABLE_DEBUG)
-			if ( IS_DEBUG_ENABLED(session) )
-				libirc_dump_data ("RECV", session->incoming_buf, offset);
-#endif
-			// parse the string
-			libirc_process_incoming_data (session, offset);
-
-			offset = libirc_findcrlf_offset(session->incoming_buf, offset, session->incoming_offset);
-
-			if ( session->incoming_offset - offset > 0 )
-				memmove (session->incoming_buf, session->incoming_buf + offset, session->incoming_offset - offset);
-
-			session->incoming_offset -= offset;
-		}
-	}
-
-	// We can write a stored buffer
-	if ( FD_ISSET (session->sock, out_set) )
-	{
-		int length;
-
-		// Because outgoing_buf could be changed asynchronously, we should lock any change
-		libirc_mutex_lock (&session->mutex_session);
-		length = session_socket_write( session );
-
-		if ( length < 0 )
-		{
-			if ( session->lasterror == 0 )
-				session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED);
-
-			session->state = LIBIRC_STATE_DISCONNECTED;
-
-			libirc_mutex_unlock (&session->mutex_session);
-			return 1;
-		}
-
-#if defined (ENABLE_DEBUG)
-		if ( IS_DEBUG_ENABLED(session) )
-			libirc_dump_data ("SEND", session->outgoing_buf, length);
-#endif
-
-		if ( length > 0 && session->outgoing_offset - length > 0 )
-			memmove (session->outgoing_buf, session->outgoing_buf + length, session->outgoing_offset - length);
-
-		session->outgoing_offset -= length;
-		libirc_mutex_unlock (&session->mutex_session);
-	}
-
-	return 0;
-}
-
-
-int irc_send_raw (irc_session_t * session, const char * format, ...)
-{
-	char buf[1024];
-	va_list va_alist;
-
-	if ( session->state != LIBIRC_STATE_CONNECTED )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	va_start (va_alist, format);
-	vsnprintf (buf, sizeof(buf), format, va_alist);
-	va_end (va_alist);
-
-	libirc_mutex_lock (&session->mutex_session);
-
-	if ( (strlen(buf) + 2) >= (sizeof(session->outgoing_buf) - session->outgoing_offset) )
-	{
-		libirc_mutex_unlock (&session->mutex_session);
-		session->lasterror = LIBIRC_ERR_NOMEM;
-		return 1;
-	}
-
-	strcpy (session->outgoing_buf + session->outgoing_offset, buf);
-	session->outgoing_offset += strlen (buf);
-	session->outgoing_buf[session->outgoing_offset++] = 0x0D;
-	session->outgoing_buf[session->outgoing_offset++] = 0x0A;
-
-	libirc_mutex_unlock (&session->mutex_session);
-	return 0;
-}
-
-
-int irc_cmd_quit (irc_session_t * session, const char * reason)
-{
-	return irc_send_raw (session, "QUIT :%s", reason ? reason : "quit");
-}
-
-
-int irc_cmd_join (irc_session_t * session, const char * channel, const char * key)
-{
-	if ( !channel )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	if ( key )
-		return irc_send_raw (session, "JOIN %s :%s", channel, key);
-	else
-		return irc_send_raw (session, "JOIN %s", channel);
-}
-
-
-int irc_cmd_part (irc_session_t * session, const char * channel)
-{
-	if ( !channel )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	return irc_send_raw (session, "PART %s", channel);
-}
-
-
-int irc_cmd_topic (irc_session_t * session, const char * channel, const char * topic)
-{
-	if ( !channel )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	if ( topic )
-		return irc_send_raw (session, "TOPIC %s :%s", channel, topic);
-	else
-		return irc_send_raw (session, "TOPIC %s", channel);
-}
-
-int irc_cmd_names (irc_session_t * session, const char * channel)
-{
-	if ( !channel )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	return irc_send_raw (session, "NAMES %s", channel);
-}
-
-
-int irc_cmd_list (irc_session_t * session, const char * channel)
-{
-	if ( channel )
-		return irc_send_raw (session, "LIST %s", channel);
-	else
-		return irc_send_raw (session, "LIST");
-}
-
-
-int irc_cmd_invite (irc_session_t * session, const char * nick, const char * channel)
-{
-	if ( !channel || !nick )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	return irc_send_raw (session, "INVITE %s %s", nick, channel);
-}
-
-
-int irc_cmd_kick (irc_session_t * session, const char * nick, const char * channel, const char * comment)
-{
-	if ( !channel || !nick )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	if ( comment )
-		return irc_send_raw (session, "KICK %s %s :%s", channel, nick, comment);
-	else
-		return irc_send_raw (session, "KICK %s %s", channel, nick);
-}
-
-
-int irc_cmd_msg (irc_session_t * session, const char * nch, const char * text)
-{
-	if ( !nch || !text )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	return irc_send_raw (session, "PRIVMSG %s :%s", nch, text);
-}
-
-
-int irc_cmd_notice (irc_session_t * session, const char * nch, const char * text)
-{
-	if ( !nch || !text )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	return irc_send_raw (session, "NOTICE %s :%s", nch, text);
-}
-
-void irc_target_get_nick (const char * target, char *nick, size_t size)
-{
-	char *p = strstr (target, "!");
-	unsigned int len;
-
-	if ( p )
-		len = p - target;
-	else
-		len = strlen (target);
-
-	if ( len > size-1 )
-		len = size - 1;
-
-	memcpy (nick, target, len);
-	nick[len] = '\0';
-}
-
-
-void irc_target_get_host (const char * target, char *host, size_t size)
-{
-	unsigned int len;
-	const char *p = strstr (target, "!");
-
-	if ( !p )
-		p = target;
-
-	len = strlen (p);
-
-	if ( len > size-1 )
-		len = size - 1;
-
-	memcpy (host, p, len);
-	host[len] = '\0';
-}
-
-
-int irc_cmd_ctcp_request (irc_session_t * session, const char * nick, const char * reply)
-{
-	if ( !nick || !reply )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	return irc_send_raw (session, "PRIVMSG %s :\x01%s\x01", nick, reply);
-}
-
-
-int irc_cmd_ctcp_reply (irc_session_t * session, const char * nick, const char * reply)
-{
-	if ( !nick || !reply )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	return irc_send_raw (session, "NOTICE %s :\x01%s\x01", nick, reply);
-}
-
-
-void irc_get_version (unsigned int * high, unsigned int * low)
-{
-	*high = LIBIRC_VERSION_HIGH;
-    *low = LIBIRC_VERSION_LOW;
-}
-
-
-void irc_set_ctx (irc_session_t * session, void * ctx)
-{
-	session->ctx = ctx;
-}
-
-
-void * irc_get_ctx (irc_session_t * session)
-{
-	return session->ctx;
-}
-
-
-void irc_set_ctcp_version (irc_session_t * session, const char * version)
-{
-	if ( session->ctcp_version )
-		free(session->ctcp_version);
-
-	session->ctcp_version = strdup(version);
-}
-
-
-void irc_disconnect (irc_session_t * session)
-{
-	if ( session->sock >= 0 )
-		socket_close (&session->sock);
-
-	session->sock = -1;
-	session->state = LIBIRC_STATE_INIT;
-}
-
-
-int irc_cmd_me (irc_session_t * session, const char * nch, const char * text)
-{
-	if ( !nch || !text )
-	{
-		session->lasterror = LIBIRC_ERR_STATE;
-		return 1;
-	}
-
-	return irc_send_raw (session, "PRIVMSG %s :\x01" "ACTION %s\x01", nch, text);
-}
-
-
-void irc_option_set (irc_session_t * session, unsigned int option)
-{
-	session->options |= option;
-}
-
-
-void irc_option_reset (irc_session_t * session, unsigned int option)
-{
-	session->options &= ~option;
-}
-
-
-int irc_cmd_channel_mode (irc_session_t * session, const char * channel, const char * mode)
-{
-	if ( !channel )
-	{
-		session->lasterror = LIBIRC_ERR_INVAL;
-		return 1;
-	}
-
-	if ( mode )
-		return irc_send_raw (session, "MODE %s %s", channel, mode);
-	else
-		return irc_send_raw (session, "MODE %s", channel);
-}
-
-
-int irc_cmd_user_mode (irc_session_t * session, const char * mode)
-{
-	if ( mode )
-		return irc_send_raw (session, "MODE %s %s", session->nick, mode);
-	else
-		return irc_send_raw (session, "MODE %s", session->nick);
-}
-
-
-int irc_cmd_nick (irc_session_t * session, const char * newnick)
-{
-	if ( !newnick )
-	{
-		session->lasterror = LIBIRC_ERR_INVAL;
-		return 1;
-	}
-
-	return irc_send_raw (session, "NICK %s", newnick);
-}
-
-int irc_cmd_whois (irc_session_t * session, const char * nick)
-{
-	if ( !nick )
-	{
-		session->lasterror = LIBIRC_ERR_INVAL;
-		return 1;
-	}
-
-	return irc_send_raw (session, "WHOIS %s %s", nick, nick);
-}
--- a/extern/libircclient/src/params.h	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-#ifndef INCLUDE_IRC_PARAMS_H
-#define INCLUDE_IRC_PARAMS_H
-
-
-#define LIBIRC_VERSION_HIGH			1
-#define LIBIRC_VERSION_LOW			8
-
-#define LIBIRC_BUFFER_SIZE			1024
-#define LIBIRC_DCC_BUFFER_SIZE		1024
-
-#define LIBIRC_STATE_INIT			0
-#define LIBIRC_STATE_LISTENING		1
-#define LIBIRC_STATE_CONNECTING		2
-#define LIBIRC_STATE_CONNECTED		3
-#define LIBIRC_STATE_DISCONNECTED	4
-#define LIBIRC_STATE_CONFIRM_SIZE	5	// Used only by DCC send to confirm the amount of sent data
-#define LIBIRC_STATE_REMOVED		10	// this state is used only in DCC
-
-
-#define SSL_PREFIX					'#'
-
-#endif /* INCLUDE_IRC_PARAMS_H */
--- a/extern/libircclient/src/portable.c	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public
- * License for more details.
- */
-
-#if !defined (_WIN32)
-	#include <stdio.h>
-	#include <stdarg.h>
-	#include <unistd.h>
-	#include <string.h>
-	#include <stdlib.h>
-	#include <sys/stat.h>
-	#include <sys/types.h>
-	#include <sys/socket.h>
-	#include <netdb.h>
-	#include <arpa/inet.h>
-	#include <netinet/in.h>
-	#include <fcntl.h>
-	#include <errno.h>
-	#include <ctype.h>
-	#include <time.h>
-
-	#if defined (ENABLE_THREADS)
-		#include <pthread.h>
-		typedef pthread_mutex_t		port_mutex_t;
-
-		#if !defined (PTHREAD_MUTEX_RECURSIVE) && defined (PTHREAD_MUTEX_RECURSIVE_NP)
-			#define PTHREAD_MUTEX_RECURSIVE		PTHREAD_MUTEX_RECURSIVE_NP
-		#endif
-	#endif
-#else
-	#include <winsock2.h>
-	#include <ws2tcpip.h>
-	#include <windows.h>
-	#include <time.h>
-	#include <stdio.h>
-	#include <stdarg.h>
-	#include <string.h>
-	#include <stdlib.h>
-	#include <sys/stat.h>
-
-	#if defined (ENABLE_THREADS)
-		typedef CRITICAL_SECTION	port_mutex_t;
-	#endif
-
-	#define inline
-	#define snprintf			_snprintf
-	#define vsnprintf			_vsnprintf
-	#define strncasecmp			_strnicmp
-#endif
-
-
-#if defined (ENABLE_SSL)
-	#include <openssl/ssl.h>
-	#include <openssl/err.h>
-	#include <openssl/rand.h>
-#endif
-
-
-#if defined (ENABLE_THREADS)
-static inline int libirc_mutex_init (port_mutex_t * mutex)
-{
-#if defined (_WIN32)
-	InitializeCriticalSection (mutex);
-	return 0;
-#elif defined (PTHREAD_MUTEX_RECURSIVE)
-	pthread_mutexattr_t	attr;
-
-	return (pthread_mutexattr_init (&attr)
-		|| pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE)
-		|| pthread_mutex_init (mutex, &attr));
-#else /* !defined (PTHREAD_MUTEX_RECURSIVE) */
-
-	return pthread_mutex_init (mutex, 0);
-
-#endif /* defined (_WIN32) */
-}
-
-
-static inline void libirc_mutex_destroy (port_mutex_t * mutex)
-{
-#if defined (_WIN32)
-	DeleteCriticalSection (mutex);
-#else
-	pthread_mutex_destroy (mutex);
-#endif
-}
-
-
-static inline void libirc_mutex_lock (port_mutex_t * mutex)
-{
-#if defined (_WIN32)
-	EnterCriticalSection (mutex);
-#else
-	pthread_mutex_lock (mutex);
-#endif
-}
-
-
-static inline void libirc_mutex_unlock (port_mutex_t * mutex)
-{
-#if defined (_WIN32)
-	LeaveCriticalSection (mutex);
-#else
-	pthread_mutex_unlock (mutex);
-#endif
-}
-
-#else
-
-	typedef void *	port_mutex_t;
-
-	static inline int libirc_mutex_init (port_mutex_t * mutex) { return 0; }
-	static inline void libirc_mutex_destroy (port_mutex_t * mutex) {}
-	static inline void libirc_mutex_lock (port_mutex_t * mutex) {}
-	static inline void libirc_mutex_unlock (port_mutex_t * mutex) {}
-
-#endif
-
-
-/*
- * Stub for WIN32 dll to initialize winsock API
- */
-#if defined (WIN32_DLL)
-BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
-{
-	WORD wVersionRequested = MAKEWORD (1, 1);
-    WSADATA wsaData;
-
-	switch(fdwReason)
-	{
-		case DLL_PROCESS_ATTACH:
-			if ( WSAStartup (wVersionRequested, &wsaData) != 0 )
-				return FALSE;
-
-			DisableThreadLibraryCalls (hinstDll);
-			break;
-
-		case DLL_PROCESS_DETACH:
-			WSACleanup();
-			break;
-	}
-
-	return TRUE;
-}
-#endif
--- a/extern/libircclient/src/session.h	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-
-#ifndef INCLUDE_IRC_SESSION_H
-#define INCLUDE_IRC_SESSION_H
-
-
-#include "params.h"
-#include "dcc.h"
-#include "libirc_events.h"
-
-
-// Session flags
-#define SESSIONFL_MOTD_RECEIVED			(0x00000001)
-#define SESSIONFL_SSL_CONNECTION		(0x00000002)
-#define SESSIONFL_SSL_WRITE_WANTS_READ	(0x00000004)
-#define SESSIONFL_SSL_READ_WANTS_WRITE	(0x00000008)
-#define SESSIONFL_USES_IPV6				(0x00000010)
-
-
-
-struct irc_session_s
-{
-	void		*	ctx;
-	int				dcc_timeout;
-
-	int				options;
-	int				lasterror;
-
-	char 			incoming_buf[LIBIRC_BUFFER_SIZE];
-	unsigned int	incoming_offset;
-
-	char 			outgoing_buf[LIBIRC_BUFFER_SIZE];
-	unsigned int	outgoing_offset;
-	port_mutex_t	mutex_session;
-
-	socket_t		sock;
-	int				state;
-	int				flags;
-
-	char 		  *	server;
-	char		  * server_password;
-	char 		  *	realname;
-	char		  * username;
-	char		  *	nick;
-	char		  * ctcp_version;
-
-#if defined( ENABLE_IPV6 )
-	struct in6_addr	local_addr6;
-#endif
-
-	struct in_addr	local_addr;
-	irc_dcc_t		dcc_last_id;
-	irc_dcc_session_t * dcc_sessions;
-	port_mutex_t	mutex_dcc;
-
-	irc_callbacks_t	callbacks;
-
-#if defined (ENABLE_SSL)
-	SSL 		 *	ssl;
-#endif
-
-	
-};
-
-
-#endif /* INCLUDE_IRC_SESSION_H */
--- a/extern/libircclient/src/sockets.c	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-/*
- * The sockets interface was moved out to simplify going OpenSSL integration.
- */
-#if !defined (_WIN32)
-	#include <sys/socket.h>
-	#include <netdb.h>
-	#include <arpa/inet.h>	
-	#include <netinet/in.h>
-	#include <fcntl.h>
-
-	#define IS_SOCKET_ERROR(a)	((a)<0)
-	typedef int				socket_t;
-
-#else
-	#include <winsock2.h>
-	#include <ws2tcpip.h>
-	#include <windows.h>
-
-	#define IS_SOCKET_ERROR(a)	((a)==SOCKET_ERROR)
-
-#if !defined(EWOULDBLOCK)
-	#define EWOULDBLOCK		WSAEWOULDBLOCK
-#endif
-#if !defined(EINPROGRESS)
-	#define EINPROGRESS		WSAEINPROGRESS
-#endif
-#if !defined(EINTR)
-	#define EINTR			WSAEINTR
-#endif
-#if !defined(EAGAIN)
-	#define EAGAIN			EWOULDBLOCK
-#endif
-
-	typedef SOCKET			socket_t;
-
-#endif
-
-#ifndef INADDR_NONE
-	#define INADDR_NONE 	0xFFFFFFFF
-#endif
-
-
-static int socket_error()
-{
-#if !defined (_WIN32)
-	return errno;
-#else
-	return WSAGetLastError();
-#endif
-}
-
-
-static int socket_create (int domain, int type, socket_t * sock)
-{
-	*sock = socket (domain, type, 0);
-	return IS_SOCKET_ERROR(*sock) ? 1 : 0;
-}
-
-
-static int socket_make_nonblocking (socket_t * sock)
-{
-#if !defined (_WIN32)
-	return fcntl (*sock, F_SETFL, fcntl (*sock, F_GETFL,0 ) | O_NONBLOCK) != 0;
-#else
-	unsigned long mode = 0;
-	return ioctlsocket (*sock, FIONBIO, &mode) == SOCKET_ERROR;
-#endif
-}
-
-
-static int socket_close (socket_t * sock)
-{
-#if !defined (_WIN32)
-	close (*sock);
-#else
-	closesocket (*sock);
-#endif
-
-	*sock = -1;
-	return 0;
-}
-
-
-static int socket_connect (socket_t * sock, const struct sockaddr *saddr, socklen_t len)
-{
-	while ( 1 )
-	{
-	    if ( connect (*sock, saddr, len) < 0 )
-	    {
-	    	if ( socket_error() == EINTR )
-	    		continue;
-
-			if ( socket_error() != EINPROGRESS && socket_error() != EWOULDBLOCK )
-				return 1;
-		}
-
-		return 0;
-	}
-}
-
-
-static int socket_accept (socket_t * sock, socket_t * newsock, struct sockaddr *saddr, socklen_t * len)
-{
-	while ( IS_SOCKET_ERROR(*newsock = accept (*sock, saddr, len)) )
-	{
-    	if ( socket_error() == EINTR )
-    		continue;
-
-		return 1;
-	}
-
-	return 0;
-}
-
-
-static int socket_recv (socket_t * sock, void * buf, size_t len)
-{
-	int length;
-
-	while ( (length = recv (*sock, buf, len, 0)) < 0 )
-	{
-		int err = socket_error();
-		
-		if ( err != EINTR && err != EAGAIN )
-			break;
-	}
-
-	return length;
-}
-
-
-static int socket_send (socket_t * sock, const void *buf, size_t len)
-{
-	int length;
-
-	while ( (length = send (*sock, buf, len, 0)) < 0 )
-	{
-		int err = socket_error();
-		
-		if ( err != EINTR && err != EAGAIN )
-			break;
-	}
-
-	return length;
-}
--- a/extern/libircclient/src/ssl.c	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,392 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-
-#if defined (ENABLE_SSL)
-
-// Nonzero if OpenSSL has been initialized
-static SSL_CTX * ssl_context = 0;
-
-#if defined (_WIN32)
-#include <windows.h>
-// This array will store all of the mutexes available to OpenSSL
-static CRITICAL_SECTION * mutex_buf = 0;
-
-// OpenSSL callback to utilize static locks
-static void cb_openssl_locking_function( int mode, int n, const char * file, int line )
-{
-    if ( mode & CRYPTO_LOCK)
-        EnterCriticalSection( &mutex_buf[n] );
-    else
-        LeaveCriticalSection( &mutex_buf[n] );
-}
-
-// OpenSSL callback to get the thread ID
-static unsigned long cb_openssl_id_function(void)
-{
-    return ((unsigned long) GetCurrentThreadId() );
-}
-
-static int alloc_mutexes( unsigned int total )
-{
-	unsigned int i;
-	
-	// Enable thread safety in OpenSSL
-	mutex_buf = (CRITICAL_SECTION*) malloc( total * sizeof(CRITICAL_SECTION) );
-
-	if ( !mutex_buf )
-		return -1;
-
-	for ( i = 0;  i < total;  i++)
-		InitializeCriticalSection( &(mutex_buf[i]) );
-	
-	return 0;
-}
-
-
-#else
-
-// This array will store all of the mutexes available to OpenSSL
-static pthread_mutex_t * mutex_buf = 0;
-
-// OpenSSL callback to utilize static locks
-static void cb_openssl_locking_function( int mode, int n, const char * file, int line )
-{
-    (void)file;
-    (void)line;
-
-    if ( mode & CRYPTO_LOCK)
-        pthread_mutex_lock( &mutex_buf[n] );
-    else
-        pthread_mutex_unlock( &mutex_buf[n] );
-}
-
-// OpenSSL callback to get the thread ID
-static unsigned long cb_openssl_id_function()
-{
-    return ((unsigned long) pthread_self() );
-}
-
-static int alloc_mutexes( unsigned int total )
-{
-	unsigned i;
-	
-	// Enable thread safety in OpenSSL
-	mutex_buf = (pthread_mutex_t*) malloc( total * sizeof(pthread_mutex_t) );
-
-	if ( !mutex_buf )
-		return -1;
-
-	for ( i = 0;  i < total;  i++)
-		pthread_mutex_init( &(mutex_buf[i]), 0 );
-	
-	return 0;
-}
-
-#endif
-
-static int ssl_init_context( irc_session_t * session )
-{
-	// Load the strings and init the library
-	SSL_load_error_strings();
-
-	// Enable thread safety in OpenSSL
-	if ( alloc_mutexes( CRYPTO_num_locks() ) )
-		return LIBIRC_ERR_NOMEM;
-
-	// Register our callbacks
-	CRYPTO_set_id_callback( cb_openssl_id_function );
-	CRYPTO_set_locking_callback( cb_openssl_locking_function );
-
-	// Init it
-	if ( !SSL_library_init() )
-		return LIBIRC_ERR_SSL_INIT_FAILED;
-
-	if ( RAND_status() == 0 )
-		return LIBIRC_ERR_SSL_INIT_FAILED;
-
-	// Create an SSL context; currently a single context is used for all connections
-	ssl_context = SSL_CTX_new( SSLv23_method() );
-
-	if ( !ssl_context )
-		return LIBIRC_ERR_SSL_INIT_FAILED;
-
-#if 0
-	// Disable SSLv2 as it is unsecure
-	if ( (SSL_CTX_set_options( ssl_context, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) == 0 )
-		return LIBIRC_ERR_SSL_INIT_FAILED;
-#endif
-
-	// Enable only strong ciphers
-	if ( SSL_CTX_set_cipher_list( ssl_context, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" ) != 1 )
-		return LIBIRC_ERR_SSL_INIT_FAILED;
-
-	// Set the verification
-	if ( session->options & LIBIRC_OPTION_SSL_NO_VERIFY )
-		SSL_CTX_set_verify( ssl_context, SSL_VERIFY_NONE, 0 );
-	else
-		SSL_CTX_set_verify( ssl_context, SSL_VERIFY_PEER, 0 );
-	
-	// Disable session caching
-	SSL_CTX_set_session_cache_mode( ssl_context, SSL_SESS_CACHE_OFF );
-
-	// Enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER so we can move the buffer during sending
-	SSL_CTX_set_mode( ssl_context, SSL_CTX_get_mode(ssl_context) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE );
-	
-	return 0;
-}
-
-
-#if defined (_WIN32)
-	#define SSLINIT_LOCK_MUTEX(a)		WaitForSingleObject( a, INFINITE )
-	#define SSLINIT_UNLOCK_MUTEX(a)		ReleaseMutex( a )
-#else
-	#define SSLINIT_LOCK_MUTEX(a)		pthread_mutex_lock( &a )
-	#define SSLINIT_UNLOCK_MUTEX(a)		pthread_mutex_unlock( &a )
-#endif
-
-// Initializes the SSL context. Must be called after the socket is created.
-static int ssl_init( irc_session_t * session )
-{
-	static int ssl_context_initialized = 0;
-	
-#if defined (_WIN32)
-	static HANDLE initmutex = 0;
-	
-	// First time run? Create the mutex
-	if ( initmutex == 0 )
-	{ 
-		HANDLE m = CreateMutex( 0, FALSE, 0 );
-
-		// Now we check if the mutex has already been created by another thread performing the init concurrently.
-		// If it was, we close our mutex and use the original one. This could be done synchronously by using the
-		// InterlockedCompareExchangePointer function.
-		if ( InterlockedCompareExchangePointer( &m, m, 0 ) != 0 )
-			CloseHandle( m );
-	}
-#else
-	static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER;
-#endif
-	
-	// This initialization needs to be performed only once. The problem is that it is called from
-	// irc_connect() and this function may be called simultaneously from different threads. So we have
-	// to use mutex on Linux because it allows static mutex initialization. Windows doesn't, so here 
-	// we do the sabre dance around it.
-	SSLINIT_LOCK_MUTEX( initmutex );
-
-	if ( ssl_context_initialized == 0 )
-	{
-		int res = ssl_init_context( session );
-		
-		if ( res )
-		{
-			SSLINIT_UNLOCK_MUTEX( initmutex );
-			return res;
-		}
-		
-		ssl_context_initialized = 1;
-	}
-	
-	SSLINIT_UNLOCK_MUTEX( initmutex );
-	
-	// Get the SSL context
-	session->ssl = SSL_new( ssl_context );
-
-	if ( !session->ssl )
-		return LIBIRC_ERR_SSL_INIT_FAILED;
-
-	// Let OpenSSL use our socket
-	if ( SSL_set_fd( session->ssl, session->sock) != 1 )
-		return LIBIRC_ERR_SSL_INIT_FAILED;
-	
-	// Since we're connecting on our own, tell openssl about it
-	SSL_set_connect_state( session->ssl );
-
-	return 0;
-}
-
-static void ssl_handle_error( irc_session_t * session, int ssl_error )
-{
-	if ( ERR_GET_LIB(ssl_error) == ERR_LIB_SSL )
-	{
-		if ( ERR_GET_REASON(ssl_error) == SSL_R_CERTIFICATE_VERIFY_FAILED )
-		{
-			session->lasterror = LIBIRC_ERR_SSL_CERT_VERIFY_FAILED;
-			return;
-		}
-		
-		if ( ERR_GET_REASON(ssl_error) == SSL_R_UNKNOWN_PROTOCOL )
-		{
-			session->lasterror = LIBIRC_ERR_CONNECT_SSL_FAILED;
-			return;
-		}
-	}
-
-#if defined (ENABLE_DEBUG)
-	if ( IS_DEBUG_ENABLED(session) )
-		fprintf (stderr, "[DEBUG] SSL error: %s\n\t(%d, %d)\n", 
-			 ERR_error_string( ssl_error, NULL),  ERR_GET_LIB( ssl_error), ERR_GET_REASON(ssl_error) );
-#endif
-}
-
-static int ssl_recv( irc_session_t * session )
-{
-	int count;
-	unsigned int amount = (sizeof (session->incoming_buf) - 1) - session->incoming_offset;
-	
-	ERR_clear_error();
-
-	// Read up to m_bufferLength bytes
-	count = SSL_read( session->ssl, session->incoming_buf + session->incoming_offset, amount );
-
-    if ( count > 0 )
-		return count;
-	else if ( count == 0 )
-		return -1; // remote connection closed
-	else
-	{
-		int ssl_error = SSL_get_error( session->ssl, count );
-		
-		// Handle SSL error since not all of them are actually errors
-        switch ( ssl_error )
-        {
-            case SSL_ERROR_WANT_READ:
-                // This is not really an error. We received something, but
-                // OpenSSL gave nothing to us because all it read was
-                // internal data. Repeat the same read.
-				return 0;
-
-            case SSL_ERROR_WANT_WRITE:
-                // This is not really an error. We received something, but
-                // now OpenSSL needs to send the data before returning any
-                // data to us (like negotiations). This means we'd need
-                // to wait for WRITE event, but call SSL_read() again.
-                session->flags |= SESSIONFL_SSL_READ_WANTS_WRITE;
-				return 0;
-		}
-
-		// This is an SSL error, handle it
-		ssl_handle_error( session, ERR_get_error() ); 
-	}
-	
-	return -1;
-}
-
-
-static int ssl_send( irc_session_t * session )
-{
-	int count;
-    ERR_clear_error();
-
-	count = SSL_write( session->ssl, session->outgoing_buf, session->outgoing_offset );
-
-    if ( count > 0 )
-		return count;
-    else if ( count == 0 )
-		return -1;
-    else
-    {
-		int ssl_error = SSL_get_error( session->ssl, count );
-		
-        switch ( ssl_error )
-        {
-            case SSL_ERROR_WANT_READ:
-                // This is not really an error. We sent some internal OpenSSL data,
-                // but now it needs to read more data before it can send anything.
-                // Thus we wait for READ event, but will call SSL_write() again.
-                session->flags |= SESSIONFL_SSL_WRITE_WANTS_READ;
-				return 0;
-
-           case SSL_ERROR_WANT_WRITE:
-                // This is not really an error. We sent some data, but now OpenSSL
-                // wants to send some internal data before sending ours.
-                // Repeat the same write.
-				return 0;
-        }
-        
-		// This is an SSL error, handle it
-		ssl_handle_error( session, ERR_get_error() ); 
-    }
-
-	return -1;
-}
-
-#endif
-
-
-// Handles both SSL and non-SSL reads.
-// Returns -1 in case there is an error and socket should be closed/connection terminated
-// Returns 0 in case there is a temporary error and the call should be retried (SSL_WANTS_WRITE case)
-// Returns a positive number if we actually read something
-static int session_socket_read( irc_session_t * session )
-{
-	int length;
-
-#if defined (ENABLE_SSL)
-	if ( session->ssl )
-	{
-		// Yes, I know this is tricky
-		if ( session->flags & SESSIONFL_SSL_READ_WANTS_WRITE )
-		{
-			session->flags &= ~SESSIONFL_SSL_READ_WANTS_WRITE;
-			ssl_send( session );
-			return 0;
-		}
-		
-		return ssl_recv( session );
-	}
-#endif
-	
-	length = socket_recv( &session->sock, 
-						session->incoming_buf + session->incoming_offset, 
-					    (sizeof (session->incoming_buf) - 1) - session->incoming_offset );
-	
-	// There is no "retry" errors for regular sockets
-	if ( length <= 0 )
-		return -1;
-	
-	return length;
-}
-
-// Handles both SSL and non-SSL writes.
-// Returns -1 in case there is an error and socket should be closed/connection terminated
-// Returns 0 in case there is a temporary error and the call should be retried (SSL_WANTS_WRITE case)
-// Returns a positive number if we actually sent something
-static int session_socket_write( irc_session_t * session )
-{
-	int length;
-
-#if defined (ENABLE_SSL)
-	if ( session->ssl )
-	{
-		// Yep
-		if ( session->flags & SESSIONFL_SSL_WRITE_WANTS_READ )
-		{
-			session->flags &= ~SESSIONFL_SSL_WRITE_WANTS_READ;
-			ssl_recv( session );
-			return 0;
-		}
-		
-		return ssl_send( session );
-	}
-#endif
-	
-	length = socket_send (&session->sock, session->outgoing_buf, session->outgoing_offset);
-	
-	// There is no "retry" errors for regular sockets
-	if ( length <= 0 )
-		return -1;
-	
-	return length;
-}
--- a/extern/libircclient/src/utils.c	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-/* 
- * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
- *
- * This library is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU Lesser General Public License as published by 
- * the Free Software Foundation; either version 3 of the License, or (at your 
- * option) any later version.
- *
- * This library 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 Lesser General Public 
- * License for more details.
- */
-
-static void libirc_add_to_set (int fd, fd_set *set, int * maxfd)
-{
-	FD_SET (fd, set);
-
-	if ( *maxfd < fd )
-		*maxfd = fd;
-}
-
-#if defined (ENABLE_DEBUG)
-static void libirc_dump_data (const char * prefix, const char * buf, unsigned int length)
-{
-	printf ("%s: ", prefix);
-	for ( ; length > 0; length -- )
-		printf ("%c", *buf++);
-}
-#endif
-
-
-/*
- * Finds a separator (\x0D\x0A), which separates two lines.
- */
-static int libirc_findcrlf (const char * buf, int length)
-{
-	int offset = 0;
-	for ( ; offset < length; offset++ )
-	{
-		if ( buf[offset] == 0x0D && offset < length - 1 && buf[offset+1] == 0x0A )
-			return offset;
-		if ( buf[offset] == 0x0A)
-			return offset;
-	}
-
-	return 0;
-}
-
-static int libirc_findcrlf_offset(const char *buf, int offset, const int length)
-{
-	for(; offset < length; offset++)
-	{
-		if(buf[offset] != 0x0D && buf[offset] != 0x0A)
-		{
-			break;
-		}
-	}
-	return offset;
-}
-
-static int libirc_findcrorlf (char * buf, int length)
-{
-	int offset = 0;
-	for ( ; offset < length; offset++ ) 
-	{
-		if ( buf[offset] == 0x0D || buf[offset] == 0x0A )
-		{
-			buf[offset++] = '\0';
-
-			if ( offset < (length - 1) 
-			&& (buf[offset] == 0x0D || buf[offset] == 0x0A) )
-				offset++;
-
-			return offset;
-		}
-	}
-
-	return 0;
-}
-
-
-static void libirc_event_ctcp_internal (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count)
-{
-	(void)event;
-	(void)count;
-
-	if ( origin )
-	{
-		char nickbuf[128], textbuf[256];
-		irc_target_get_nick (origin, nickbuf, sizeof(nickbuf));
-
-		if ( strstr (params[0], "PING") == params[0] )
-			irc_cmd_ctcp_reply (session, nickbuf, params[0]);
-		else if ( !strcmp (params[0], "VERSION") )
-		{
-			if ( !session->ctcp_version )
-			{
-				unsigned int high, low;
-				irc_get_version (&high, &low);
-
-				snprintf (textbuf, sizeof (textbuf), "VERSION libircclient by Georgy Yunaev ver.%d.%d", high, low);
-			}
-			else
-				snprintf (textbuf, sizeof (textbuf), "VERSION %s", session->ctcp_version);
-
-			irc_cmd_ctcp_reply (session, nickbuf, textbuf);
-		}
-		else if ( !strcmp (params[0], "FINGER") )
-		{
-			sprintf (textbuf, "FINGER %s (%s) Idle 0 seconds", 
-				session->username ? session->username : "nobody",
-				session->realname ? session->realname : "noname");
-
-			irc_cmd_ctcp_reply (session, nickbuf, textbuf);
-		}
-		else if ( !strcmp (params[0], "TIME") )
-		{
-			time_t now = time(0);
-
-#if defined (ENABLE_THREADS) && defined (HAVE_LOCALTIME_R)
-			struct tm tmtmp, *ltime = localtime_r (&now, &tmtmp);
-#else
-			struct tm * ltime = localtime (&now);
-#endif
-			strftime (textbuf, sizeof(textbuf), "%a %b %d %H:%M:%S %Z %Y", ltime);
-			irc_cmd_ctcp_reply (session, nickbuf, textbuf);
-		}
-	}
-}
--- a/irccd/main.cpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/irccd/main.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -275,7 +275,7 @@
     config.load_transports(*instance);
 
     // [server] section.
-    for (const auto& server : config.load_servers())
+    for (const auto& server : config.load_servers(*instance))
         instance->servers().add(server);
 
     // [rule] section.
--- a/libirccd-js/irccd/js/util_jsapi.cpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd-js/irccd/js/util_jsapi.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -18,8 +18,6 @@
 
 #include <climits>
 
-#include <libircclient.h>
-
 #include <irccd/string_util.hpp>
 
 #include "js_plugin.hpp"
@@ -243,7 +241,9 @@
     auto target = duk_require_string(ctx, 0);
     char nick[32] = {0};
 
+#if 0
     irc_target_get_nick(target, nick, sizeof (nick) -1);
+#endif
     duk_push_string(ctx, nick);
 
     return 1;
@@ -265,7 +265,9 @@
     auto target = duk_require_string(ctx, 0);
     char host[32] = {0};
 
+#if 0
     irc_target_get_host(target, host, sizeof (host) -1);
+#endif
     duk_push_string(ctx, host);
 
     return 1;
--- a/libirccd-test/CMakeLists.txt	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd-test/CMakeLists.txt	Thu Nov 23 22:45:12 2017 +0100
@@ -27,8 +27,6 @@
         $<$<BOOL:${HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/plugin_test.hpp>
         $<$<BOOL:${HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/js_test.hpp>
         $<$<BOOL:${HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/plugin_test.js>
-        ${libirccd-test_SOURCE_DIR}/irccd/server-tester.cpp
-        ${libirccd-test_SOURCE_DIR}/irccd/server-tester.hpp
     LIBRARIES
         $<$<BOOL:${HAVE_JS}>:libirccd-js>
         libirccd
--- a/libirccd-test/irccd/js_test.hpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd-test/irccd/js_test.hpp	Thu Nov 23 22:45:12 2017 +0100
@@ -67,7 +67,7 @@
      */
     js_test(const std::string& plugin_path = IRCCD_PLUGIN_TEST)
         : plugin_(new js_plugin("test", plugin_path))
-        , server_(new journal_server("test"))
+        , server_(new journal_server(service_, "test"))
     {
         // Irccd is mandatory at the moment.
         add<irccd_jsapi>();
--- a/libirccd-test/irccd/plugin_test.cpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd-test/irccd/plugin_test.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -39,7 +39,7 @@
 namespace irccd {
 
 plugin_test::plugin_test(std::string name, std::string path)
-    : server_(std::make_shared<journal_server>("test"))
+    : server_(std::make_shared<journal_server>(service_, "test"))
 {
     log::set_verbose(false);
     log::set_logger(std::make_unique<log::silent_logger>());
--- a/libirccd-test/irccd/server-tester.cpp	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * server-tester.cpp -- server that does nothing
- *
- * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "server-tester.hpp"
-
-namespace irccd {
-
-ServerTester::ServerTester(std::string name)
-    : server(std::move(name))
-{
-}
-
-void ServerTester::prepare(fd_set &, fd_set &, net::Handle &) noexcept
-{
-}
-
-void ServerTester::sync(fd_set &, fd_set &)
-{
-}
-
-} // !irccd
--- a/libirccd-test/irccd/server-tester.hpp	Wed Nov 22 20:10:03 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * server-tester.hpp -- server that does nothing
- *
- * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVER_TESTER_HPP
-#define IRCCD_SERVER_TESTER_HPP
-
-/**
- * \file server-tester.hpp
- * \brief Server that does nothing.
- */
-
-#include "server.hpp"
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-/**
- * \brief Useless server for testing purpose.
- */
-class IRCCD_EXPORT ServerTester : public server {
-public:
-    /**
-     * Create a server with named 'test' by default.
-     */
-    ServerTester(std::string name = "test");
-
-    /**
-     * Overload that is a no-op.
-     */
-    void prepare(fd_set &, fd_set &, net::Handle &) noexcept override;
-
-    /**
-     * Overload that is a no-op.
-     */
-    void sync(fd_set &, fd_set &) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVER_TESTER_HPP
--- a/libirccd/CMakeLists.txt	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd/CMakeLists.txt	Thu Nov 23 22:45:12 2017 +0100
@@ -26,6 +26,7 @@
     ${libirccd_SOURCE_DIR}/irccd/command.hpp
     ${libirccd_SOURCE_DIR}/irccd/config.hpp
     ${libirccd_SOURCE_DIR}/irccd/dynlib_plugin.hpp
+    ${libirccd_SOURCE_DIR}/irccd/irc.hpp
     ${libirccd_SOURCE_DIR}/irccd/irccd.hpp
     ${libirccd_SOURCE_DIR}/irccd/plugin.hpp
     ${libirccd_SOURCE_DIR}/irccd/rule.hpp
@@ -41,6 +42,7 @@
     ${libirccd_SOURCE_DIR}/irccd/command.cpp
     ${libirccd_SOURCE_DIR}/irccd/config.cpp
     ${libirccd_SOURCE_DIR}/irccd/dynlib_plugin.cpp
+    ${libirccd_SOURCE_DIR}/irccd/irc.cpp
     ${libirccd_SOURCE_DIR}/irccd/irccd.cpp
     ${libirccd_SOURCE_DIR}/irccd/plugin.cpp
     ${libirccd_SOURCE_DIR}/irccd/rule.cpp
@@ -61,7 +63,6 @@
         Boost::timer
         $<$<BOOL:${IRCCD_SYSTEM_LINUX}>:dl>
         $<$<BOOL:${IRCCD_SYSTEM_MAC}>:resolv>
-        extern-ircclient
         libcommon
     PUBLIC_INCLUDES
         $<BUILD_INTERFACE:${libirccd_SOURCE_DIR}/irccd>
--- a/libirccd/irccd/config.cpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd/irccd/config.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -306,7 +306,7 @@
     };
 }
 
-std::shared_ptr<server> load_server(const ini::section& sc, const config& config)
+std::shared_ptr<server> load_server(irccd& daemon, const ini::section& sc, const config& config)
 {
     assert(sc.key() == "server");
 
@@ -318,7 +318,7 @@
     else if (!string_util::is_identifier(it->value()))
         throw std::invalid_argument(string_util::sprintf("server: invalid identifier: %s", it->value()));
 
-    auto sv = std::make_shared<server>(it->value());
+    auto sv = std::make_shared<server>(daemon.service(), it->value());
 
     // Host
     if ((it = sc.find("host")) == sc.end())
@@ -515,7 +515,7 @@
     return rules;
 }
 
-std::vector<std::shared_ptr<server>> config::load_servers() const
+std::vector<std::shared_ptr<server>> config::load_servers(irccd& daemon) const
 {
     std::vector<std::shared_ptr<server>> servers;
 
@@ -524,7 +524,7 @@
             continue;
 
         try {
-            servers.push_back(load_server(section, *this));
+            servers.push_back(load_server(daemon, section, *this));
         } catch (const std::exception& ex) {
             log::warning(ex.what());
         }
--- a/libirccd/irccd/config.hpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd/irccd/config.hpp	Thu Nov 23 22:45:12 2017 +0100
@@ -160,9 +160,10 @@
     /**
      * Get the list of servers defined.
      *
+     * \param daemon the irccd instance
      * \return the list of servers
      */
-    std::vector<std::shared_ptr<server>> load_servers() const;
+    std::vector<std::shared_ptr<server>> load_servers(irccd& daemon) const;
 
     /**
      * Get the list of defined plugins.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd/irccd/irc.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -0,0 +1,123 @@
+/*
+ * irc.cpp -- low level IRC functions
+ *
+ * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <iterator>
+#include <sstream>
+
+#include "irc.hpp"
+
+namespace irccd {
+
+namespace irc {
+
+namespace {
+
+using boost::asio::ip::tcp;
+
+template <typename Socket>
+void do_connect(Socket& socket, tcp::resolver::iterator it, connection::connect_t handler)
+{
+    socket.close();
+    socket.async_connect(*it, [&socket, it, handler] (auto code) mutable {
+        if (code && it != tcp::resolver::iterator())
+            do_connect(socket, ++it, std::move(handler));
+        else
+            handler(code);
+    });
+}
+
+template <typename Socket>
+void do_resolve(const std::string& host,
+                const std::string& port,
+                Socket& socket,
+                connection::connect_t handler)
+{
+    auto resolver = std::make_shared<tcp::resolver>(socket.get_io_service());
+
+    resolver->async_resolve(tcp::resolver::query(host, port), [&socket, handler, resolver] (auto code, auto it) {
+        if (code)
+            handler(code);
+        else
+            do_connect(socket, it, std::move(handler));
+    });
+}
+
+} // !namespace
+
+message message::parse(const std::string& line)
+{
+    std::istringstream iss(line);
+    std::string prefix;
+
+    if (line.empty())
+        return {};
+
+    // Prefix.
+    if (line[0] == ':') {
+        iss.ignore(1);
+        iss >> prefix;
+        iss.ignore(1);
+    }
+
+    // Command.
+    std::string command;
+    iss >> command;
+    iss.ignore(1);
+
+    // Arguments.
+    std::vector<std::string> args;
+    std::istreambuf_iterator<char> it(iss), end;
+
+    while (it != end) {
+        std::string arg;
+
+        if (*it == ':')
+            arg = std::string(++it, end);
+        else {
+            while (!isspace(*it) && it != end)
+                arg.push_back(*it++);
+
+            // Skip space after param.
+            if (it != end)
+                ++it;
+        }
+
+        args.push_back(std::move(arg));
+    }
+
+    return {std::move(prefix), std::move(command), std::move(args)};
+}
+
+user user::parse(const std::string& line)
+{
+    auto pos = line.find("!");
+
+    if (pos == std::string::npos)
+        return {"", ""};
+
+    return {line.substr(0, pos), line.substr(pos + 1)};
+}
+
+void ip_connection::connect(const std::string& host, const std::string& service, connect_t handler)
+{
+    do_resolve(host, service, socket_, std::move(handler));
+}
+
+} // !irc
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd/irccd/irc.hpp	Thu Nov 23 22:45:12 2017 +0100
@@ -0,0 +1,536 @@
+/*
+ * irc.hpp -- low level IRC functions
+ *
+ * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_IRC_HPP
+#define IRCCD_IRC_HPP
+
+/**
+ * \file irc.hpp
+ * \brief Low level IRC functions.
+ */
+
+#include <deque>
+#include <functional>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <boost/asio.hpp>
+
+#if defined(HAVE_SSL)
+#   include <boost/asio/ssl.hpp>
+#endif
+
+namespace irccd {
+
+namespace irc {
+
+class message;
+
+/**
+ * \brief Describe errors.
+ */
+enum class err {
+    nosuchnick = 401,
+    nosuchserver = 402,
+    nosuchchannel = 403,
+    cannotsendtochan = 404,
+    toomanychannels = 405,
+    wasnosuchnick = 406,
+    toomanytargets = 407,
+    noorigin = 409,
+    norecipient = 411,
+    notexttosend = 412,
+    notoplevel = 413,
+    wildtoplevel = 414,
+    unknowncommand = 421,
+    nomotd = 422,
+    noadmininfo = 423,
+    fileerror = 424,
+    nonicknamegiven = 431,
+    erroneusnickname = 432,
+    nicknameinuse = 433,
+    nickcollision = 436,
+    usernotinchannel = 441,
+    notonchannel = 442,
+    useronchannel = 443,
+    nologin = 444,
+    summondisabled = 445,
+    usersdisabled = 446,
+    notregistered = 451,
+    needmoreparams = 461,
+    alreadyregistred = 462,
+    nopermforhost = 463,
+    passwdmismatch = 464,
+    yourebannedcreep = 465,
+    keyset = 467,
+    channelisfull = 471,
+    unknownmode = 472,
+    inviteonlychan = 473,
+    bannedfromchan = 474,
+    badchannelkey = 475,
+    noprivileges = 481,
+    chanoprivsneeded = 482,
+    cantkillserver = 483,
+    nooperhost = 491,
+    umodeunknownflag = 501,
+    usersdontmatch = 502
+};
+
+/**
+ * \brief Describe numeric replies.
+ */
+enum class rpl {
+    none = 300,
+    userhost = 302,
+    ison = 303,
+    away = 301,
+    unaway = 305,
+    nowaway = 306,
+    whoisuser = 311,
+    whoisserver = 312,
+    whoisoperator = 313,
+    whoisidle = 317,
+    endofwhois = 318,
+    whoischannels = 319,
+    whowasuser = 314,
+    endofwhowas = 369,
+    liststart = 321,
+    list = 322,
+    listend = 323,
+    channelmodeis = 324,
+    notopic = 331,
+    topic = 332,
+    inviting = 341,
+    summoning = 342,
+    version = 351,
+    whoreply = 352,
+    endofwho = 315,
+    namreply = 353,
+    endofnames = 366,
+    links = 364,
+    endoflinks = 365,
+    banlist = 367,
+    endofbanlist = 368,
+    info = 371,
+    endofinfo = 374,
+    motdstart = 375,
+    motd = 372,
+    endofmotd = 376,
+    youreoper = 381,
+    rehashing = 382,
+    time = 391,
+    userstart = 392,
+    users = 393,
+    endofusers = 394,
+    nousers = 395,
+    tracelink = 200,
+    traceconnecting = 201,
+    tracehandshake = 202,
+    traceunknown = 203,
+    traceoperator = 204,
+    traceuser = 205,
+    traceserver = 206,
+    tracenewtype = 208,
+    tracelog = 261,
+    statslinkinfo = 211,
+    statscommands = 212,
+    statscline = 213,
+    statsnline = 214,
+    statsiline = 215,
+    statskline = 216,
+    statsyline = 218,
+    endofstats = 219,
+    statslline = 241,
+    statsuptime = 242,
+    statsoline = 243,
+    statshline = 244,
+    umodeis = 221,
+    luserclient = 251,
+    luserop = 252,
+    luserunknown = 253,
+    luserchannels = 254,
+    luserme = 255,
+    adminme = 256,
+    adminloc1 = 257,
+    adminloc2 = 258,
+    adminemail = 259
+};
+
+/**
+ * \brief Describe a IRC message
+ */
+class message {
+private:
+    std::string prefix_;             //!< optional prefix
+    std::string command_;            //!< command (maybe string or code)
+    std::vector<std::string> args_;  //!< parameters
+
+public:
+    /**
+     * Constructor.
+     *
+     * \param prefix the optional prefix
+     * \param command the command string or number
+     * \param args the arguments
+     */
+    inline message(std::string prefix = "", std::string command = "", std::vector<std::string> args = {}) noexcept
+        : prefix_(std::move(prefix))
+        , command_(std::move(command))
+        , args_(std::move(args))
+    {
+    }
+
+    /**
+     * Get the prefix.
+     *
+     * \return the prefix
+     */
+    inline const std::string& prefix() const noexcept
+    {
+        return prefix_;
+    }
+
+    /**
+     * Get the command.
+     *
+     * \return the command
+     */
+    inline const std::string& command() const noexcept
+    {
+        return command_;
+    }
+
+    /**
+     * Get the arguments.
+     *
+     * \return the arguments
+     */
+    inline const std::vector<std::string>& args() const noexcept
+    {
+        return args_;
+    }
+
+    /**
+     * Check if the message is defined.
+     *
+     * \return true if not empty
+     */
+    inline operator bool() const noexcept
+    {
+        return !command_.empty();
+    }
+
+    /**
+     * Check if the message is empty.
+     *
+     * \return true if empty
+     */
+    inline bool operator!() const noexcept
+    {
+        return command_.empty();
+    }
+
+    /**
+     * Check if the command is of the given enum number.
+     *
+     * \return true if command is a number and equals to e
+     */
+    template <typename Enum>
+    inline bool is(Enum e) const noexcept
+    {
+        try {
+            return std::stoi(command_) == static_cast<int>(e);
+        } catch (...) {
+            return false;
+        }
+    }
+
+    /**
+     * Convenient function that returns an empty string if the nth argument is
+     * not defined.
+     *
+     * \param index the index
+     * \return a string or empty if out of bounds
+     */
+    inline const std::string& arg(unsigned short index) const noexcept
+    {
+        static const std::string dummy;
+
+        return (index >= args_.size()) ? dummy : args_[index];
+    }
+
+    /**
+     * Parse a IRC message.
+     *
+     * \param line the buffer content (without \r\n)
+     * \return the message (maybe empty if line is empty)
+     */
+    static message parse(const std::string& line);
+};
+
+/**
+ * \brief Describe a user.
+ */
+class user {
+private:
+    std::string nick_;
+    std::string host_;
+
+public:
+    /**
+     * Construct a user.
+     *
+     * \param the nickname
+     * \param the hostname
+     */
+    inline user(std::string nick, std::string host) noexcept
+        : nick_(std::move(nick))
+        , host_(std::move(host))
+    {
+    }
+
+    /**
+     * Get the nick part.
+     *
+     * \return the nickname
+     */
+    inline const std::string& nick() const noexcept
+    {
+        return nick_;
+    }
+
+    /**
+     * Get the host part.
+     *
+     * \return the host part
+     */
+    inline const std::string& host() const noexcept
+    {
+        return host_;
+    }
+
+    /**
+     * Parse a nick/host combination.
+     *
+     * \param line the line to parse
+     * \return a user
+     */
+    static user parse(const std::string& line);
+};
+
+/**
+ * \brief Abstract connection to a server.
+ */
+class connection {
+public:
+    /**
+     * Handler for connecting.
+     */
+    using connect_t = std::function<void (boost::system::error_code)>;
+
+    /**
+     * Handler for receiving.
+     */
+    using recv_t = std::function<void (boost::system::error_code, message)>;
+
+    /**
+     * Handler for sending.
+     */
+    using send_t = std::function<void (boost::system::error_code)>;
+
+    /**
+     * Default constructor.
+     */
+    connection() = default;
+
+    /**
+     * Virtual destructor defaulted.
+     */
+    virtual ~connection() = default;
+
+    /**
+     * Connect to the host.
+     *
+     * \param host the host
+     * \param service the service or port number
+     * \param handler the non-null handler
+     */
+    virtual void connect(const std::string& host, const std::string& service, connect_t handler) = 0;
+
+    /**
+     * Start receiving data.
+     *
+     * \param handler the handler to call
+     */
+    virtual void recv(recv_t handler) = 0;
+
+    /**
+     * Start sending data.
+     *
+     * \param message the raw message
+     * \param handler the handler to call
+     */
+    virtual void send(std::string message, send_t handler = nullptr) = 0;
+};
+
+/**
+ * \brief Implementation for Boost.Asio sockets.
+ *
+ * To use this class, derive from it and implement the connect function.
+ */
+template <typename Socket>
+class basic_connection : public connection {
+protected:
+    Socket socket_;
+
+private:
+    using buffer_t = boost::asio::streambuf;
+    using input_t = std::deque<recv_t>;
+    using output_t = std::deque<std::pair<std::string, send_t>>;
+
+    buffer_t buffer_;
+    input_t input_;
+    output_t output_;
+
+    void rflush();
+    void sflush();
+    void do_recv(recv_t);
+    void do_send(const std::string&, send_t);
+
+public:
+    /**
+     * Constructor.
+     *
+     * \param args the arguments to pass to the socket
+     */
+    template <typename... Args>
+    inline basic_connection(Args&&... args)
+        : socket_(std::forward<Args>(args)...)
+    {
+    }
+
+    /**
+     * \copydoc connection::recv
+     */
+    void recv(recv_t handler) override;
+
+    /**
+     * \copydoc connection::send
+     */
+    void send(std::string message, send_t handler = nullptr) override;
+};
+
+template <typename Socket>
+void basic_connection<Socket>::rflush()
+{
+    if (input_.empty())
+        return;
+
+    do_recv([this] (auto code, auto message) {
+        input_.front()(code, std::move(message));
+        input_.pop_front();
+
+        if (!code)
+            rflush();
+    });
+}
+
+template <typename Socket>
+void basic_connection<Socket>::sflush()
+{
+    if (output_.empty())
+        return;
+
+    do_send(output_.front().first, [this] (auto code) {
+        if (output_.front().second)
+            output_.front().second(code);
+
+        output_.pop_front();
+
+        if (!code)
+            sflush();
+    });
+}
+
+template <typename Socket>
+void basic_connection<Socket>::do_recv(recv_t handler)
+{
+    boost::asio::async_read_until(socket_, buffer_, "\r\n", [this, handler] (auto code, auto xfer) {
+        if (code || xfer == 0U)
+            handler(std::move(code), message());
+        else {
+            std::string str(
+                boost::asio::buffers_begin(buffer_.data()),
+                boost::asio::buffers_begin(buffer_.data()) + xfer - 2
+            );
+
+            buffer_.consume(xfer);
+            handler(std::move(code), message::parse(str));
+        }
+    });
+}
+
+template <typename Socket>
+void basic_connection<Socket>::do_send(const std::string& message, send_t handler)
+{
+    boost::asio::async_write(socket_, boost::asio::buffer(message), [handler, message] (auto code, auto xfer) {
+        handler(code);
+    });
+}
+
+template <typename Socket>
+void basic_connection<Socket>::recv(recv_t handler)
+{
+    auto in_progress = !input_.empty();
+
+    input_.push_back(std::move(handler));
+
+    if (!in_progress)
+        rflush();
+}
+
+template <typename Socket>
+void basic_connection<Socket>::send(std::string message, send_t handler)
+{
+    auto in_progress = !output_.empty();
+
+    output_.emplace_back(std::move(message + "\r\n"), std::move(handler));
+
+    if (!in_progress)
+        sflush();
+}
+
+class ip_connection : public basic_connection<boost::asio::ip::tcp::socket> {
+public:
+    /**
+     * Inherited constructors.
+     */
+    using basic_connection::basic_connection;
+
+    /**
+     * \copydoc basic_connection::connect
+     */
+    void connect(const std::string& host, const std::string& service, connect_t handler) override;
+};
+
+} // !irc
+
+} // !irccd
+
+#endif // !IRCCD_IRC_HPP
--- a/libirccd/irccd/server.cpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd/irccd/server.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -16,16 +16,13 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "sysconfig.hpp"
+
 #include <algorithm>
 #include <cerrno>
 #include <cstring>
 #include <stdexcept>
 
-#include <libircclient.h>
-#include <libirc_rfcnumeric.h>
-
-#include "sysconfig.hpp"
-
 #if !defined(IRCCD_SYSTEM_WINDOWS)
 #  include <sys/types.h>
 #  include <netinet/in.h>
@@ -41,104 +38,9 @@
 
 namespace irccd {
 
-/*
- * server::session declaration.
- * ------------------------------------------------------------------
- */
-
-class server::session {
-public:
-    std::unique_ptr<irc_session_t, void (*)(irc_session_t *)> handle_{nullptr, nullptr};
-
-    inline operator const irc_session_t*() const noexcept
-    {
-        return handle_.get();
-    }
-
-    inline operator irc_session_t*() noexcept
-    {
-        return handle_.get();
-    }
-
-    inline bool is_connected() const noexcept
-    {
-        return irc_is_connected(handle_.get());
-    }
-};
-
-/*
- * server::state declaration.
- * ------------------------------------------------------------------
- */
-
-class server::state {
-public:
-    state() = default;
-    virtual ~state() = default;
-    virtual void prepare(server&, fd_set&, fd_set&, net::Handle&) = 0;
-    virtual std::string ident() const = 0;
-};
-
-/*
- * server::disconnected_state declaration.
- * ------------------------------------------------------------------
- */
-
-class server::disconnected_state : public server::state {
-private:
-    boost::timer::cpu_timer timer_;
-
-public:
-    void prepare(server&, fd_set&, fd_set&, net::Handle&) override;
-    std::string ident() const override;
-};
-
-/*
- * server::connecting_state declaration.
- * ------------------------------------------------------------------
- */
-
-class server::connecting_state : public state {
-private:
-    enum {
-        disconnected,
-        connecting
-    } state_{disconnected};
-
-    boost::timer::cpu_timer timer_;
-
-    bool connect(server& server);
-
-public:
-    void prepare(server&, fd_set&, fd_set&, net::Handle&) override;
-    std::string ident() const override;
-};
-
-/*
- * server::connected_state declaration.
- * ------------------------------------------------------------------
- */
-
-class server::connected_state : public state {
-public:
-    void prepare(server&, fd_set&, fd_set&, net::Handle&) override;
-    std::string ident() const override;
-};
-
 namespace {
 
 /*
- * strify
- * ------------------------------------------------------------------
- *
- * Make sure to build a C++ string with a not-null C string.
- */
-inline std::string strify(const char* s)
-{
-    return (s == nullptr) ? "" : std::string(s);
-}
-
-/*
  * clean_prefix
  * ------------------------------------------------------------------
  *
@@ -158,12 +60,12 @@
 }
 
 /*
- * extract_prefixes
+ * isupport_extract_prefixes
  * ------------------------------------------------------------------
  *
  * Read modes from the IRC event numeric.
  */
-std::map<channel_mode, char> extract_prefixes(const std::string& line)
+std::map<channel_mode, char> isupport_extract_prefixes(const std::string& line)
 {
     // FIXME: what if line has different size?
     std::pair<char, char> table[16];
@@ -208,266 +110,23 @@
     jchannels_.erase(std::remove(jchannels_.begin(), jchannels_.end(), channel), jchannels_.end());
 }
 
-void server::handle_connect(const char*, const char**) noexcept
-{
-    // Reset the number of tried reconnection.
-    recocur_ = 1;
-
-    // Reset the timer.
-    timer_.start();
-
-    // Reset joined channels.
-    jchannels_.clear();
-
-    // Don't forget to change state and notify.
-    next(std::make_unique<connected_state>());
-    on_connect(connect_event{shared_from_this()});
-
-    // Auto join listed channels.
-    for (const auto& channel : rchannels_) {
-        log::info() << "server " << name_ << ": auto joining " << channel.name << std::endl;
-        join(channel.name, channel.password);
-    }
-}
+#if 0
 
 void server::handle_channel(const char* orig, const char** params) noexcept
 {
     on_message({shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
 }
 
-void server::handle_channel_mode(const char* orig, const char** params) noexcept
-{
-    on_channel_mode({
-        shared_from_this(),
-        strify(orig),
-        strify(params[0]),
-        strify(params[1]),
-        strify(params[2])
-    });
-}
-
-void server::handle_channel_notice(const char* orig, const char** params) noexcept
-{
-    on_channel_notice({
-        shared_from_this(),
-        strify(orig),
-        strify(params[0]),
-        strify(params[1])
-    });
-}
-
 void server::handle_ctcp_action(const char* orig, const char** params) noexcept
 {
     on_me({shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
 }
 
-void server::handle_invite(const char* orig, const char** params) noexcept
-{
-    // If joininvite is set, join the channel.
-    if ((flags_ & join_invite) && is_self(strify(params[0])))
-        join(strify(params[1]));
-
-    /*
-     * The libircclient says that invite contains the target nickname, it's
-     * quit uncommon to need it so it is passed as the last argument to be
-     * optional in the plugin.
-     */
-    on_invite({shared_from_this(), strify(orig), strify(params[1]), strify(params[0])});
-}
-
-void server::handle_join(const char* orig, const char** params) noexcept
-{
-    if (is_self(strify(orig)))
-        jchannels_.push_back(strify(params[0]));
-
-    on_join({shared_from_this(), strify(orig), strify(params[0])});
-}
-
-void server::handle_kick(const char* orig, const char** params) noexcept
-{
-    if (is_self(strify(params[1]))) {
-        // Remove the channel from the joined list.
-        remove_joined_channel(strify(params[0]));
-
-        // Rejoin the channel if the option has been set and I was kicked.
-        if (flags_ & auto_rejoin)
-            join(strify(params[0]));
-    }
-
-    on_kick({
-        shared_from_this(),
-        strify(orig),
-        strify(params[0]),
-        strify(params[1]),
-        strify(params[2])
-    });
-}
-
-void server::handle_mode(const char* orig, const char** params) noexcept
-{
-    on_mode({shared_from_this(), strify(orig), strify(params[1])});
-}
-
-void server::handle_nick(const char* orig, const char** params) noexcept
-{
-    // Update our nickname.
-    if (is_self(strify(orig)))
-        nickname_ = strify(params[0]);
-
-    on_nick({shared_from_this(), strify(orig), strify(params[0])});
-}
-
-void server::handle_notice(const char* orig, const char** params) noexcept
-{
-    /*
-     * Like handleInvite, the notice provides the target nickname, we discard
-     * it.
-     */
-    on_notice({shared_from_this(), strify(orig), strify(params[1])});
-}
-
-void server::handle_numeric(unsigned int event, const char** params, unsigned int c) noexcept
-{
-    if (event == LIBIRC_RFC_RPL_NAMREPLY) {
-        /*
-         * Called multiple times to list clients on a channel.
-         *
-         * params[0] == originator
-         * params[1] == '='
-         * params[2] == channel
-         * params[3] == list of users with their prefixes
-         *
-         * IDEA for the future: maybe give the appropriate mode as a second
-         * parameter in onNames.
-         */
-        if (c < 4 || params[2] == nullptr || params[3] == nullptr)
-            return;
-
-        auto users = string_util::split(params[3], " \t");
-
-        // The listing may add some prefixes, remove them if needed.
-        for (auto u : users)
-            names_map_[params[2]].insert(clean_prefix(modes_, u));
-    } else if (event == LIBIRC_RFC_RPL_ENDOFNAMES) {
-        /*
-         * Called when end of name listing has finished on a channel.
-         *
-         * params[0] == originator
-         * params[1] == channel
-         * params[2] == End of NAMES list
-         */
-        if (c < 3 || params[1] == nullptr)
-            return;
-
-        auto it = names_map_.find(params[1]);
-        if (it != names_map_.end()) {
-            on_names({
-                shared_from_this(),
-                params[1],
-                std::vector<std::string>(it->second.begin(), it->second.end())
-            });
-
-            // Don't forget to remove the list.
-            names_map_.erase(it);
-        }
-    } else if (event == LIBIRC_RFC_RPL_WHOISUSER) {
-        /*
-         * Called when whois information has been partially received.
-         *
-         * params[0] == originator
-         * params[1] == nickname
-         * params[2] == username
-         * params[3] == host
-         * params[4] == * (no idea what is that)
-         * params[5] == realname
-         */
-        if (c < 6 || !params[1] || !params[2] || !params[3] || !params[5])
-            return;
-
-        class whois info;
-
-        info.nick = strify(params[1]);
-        info.user = strify(params[2]);
-        info.host = strify(params[3]);
-        info.realname = strify(params[5]);
-
-        whois_map_.emplace(info.nick, info);
-    } else if (event == LIBIRC_RFC_RPL_WHOISCHANNELS) {
-        /*
-         * Called when we have received channels for one user.
-         *
-         * params[0] == originator
-         * params[1] == nickname
-         * params[2] == list of channels with their prefixes
-         */
-        if (c < 3 || !params[1] || !params[2])
-            return;
-
-        auto it = whois_map_.find(params[1]);
-        if (it != whois_map_.end()) {
-            auto channels = string_util::split(params[2], " \t");
-
-            // Clean their prefixes.
-            for (auto &s : channels)
-                s = clean_prefix(modes_, s);
-
-            it->second.channels = std::move(channels);
-        }
-    } else if (event == LIBIRC_RFC_RPL_ENDOFWHOIS) {
-        /*
-         * Called when whois is finished.
-         *
-         * params[0] == originator
-         * params[1] == nickname
-         * params[2] == End of WHOIS list
-         */
-        auto it = whois_map_.find(params[1]);
-        if (it != whois_map_.end()) {
-            on_whois({shared_from_this(), it->second});
-
-            // Don't forget to remove.
-            whois_map_.erase(it);
-        }
-    } else if (event == /* RPL_BOUNCE */ 5) {
-        /*
-         * The event 5 is usually RPL_BOUNCE, but we always see it as ISUPPORT.
-         */
-        for (unsigned int i = 0; i < c; ++i) {
-            if (strncmp(params[i], "PREFIX", 6) == 0) {
-                modes_ = extract_prefixes(params[i]);
-                break;
-            }
-        }
-    }
-}
-
-void server::handle_part(const char* orig, const char** params) noexcept
-{
-    // Remove the channel from the joined list if I left a channel.
-    if (is_self(strify(orig)))
-        remove_joined_channel(strify(params[0]));
-
-    on_part({shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
-}
-
-void server::handle_ping(const char*, const char**) noexcept
-{
-    // Reset the timer to detect disconnection.
-    timer_.start();
-}
-
-void server::handle_query(const char* orig, const char** params) noexcept
-{
-    on_query({shared_from_this(), strify(orig), strify(params[1])});
-}
-
-void server::handle_topic(const char* orig, const char** params) noexcept
-{
-    on_topic({shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
-}
+#endif
 
 std::shared_ptr<server> server::from_json(const nlohmann::json& object)
 {
+#if 0
     auto sv = std::make_shared<server>(json_util::require_identifier(object, "name"));
 
     sv->set_host(json_util::require_string(object, "host"));
@@ -482,16 +141,20 @@
         sv->set_port(json_util::get_uint(object, "port"));
     if (json_util::get_bool(object, "ipv6"))
         sv->set_flags(sv->flags() | server::ipv6);
+#if defined(HAVE_SSL)
     if (json_util::get_bool(object, "ssl"))
         sv->set_flags(sv->flags() | server::ssl);
     if (json_util::get_bool(object, "sslVerify"))
         sv->set_flags(sv->flags() | server::ssl_verify);
+#endif
     if (json_util::get_bool(object, "autoRejoin"))
         sv->set_flags(sv->flags() | server::auto_rejoin);
     if (json_util::get_bool(object, "joinInvite"))
         sv->set_flags(sv->flags() | server::join_invite);
 
     return sv;
+#endif
+    return nullptr;
 }
 
 channel server::split_channel(const std::string& value)
@@ -504,99 +167,354 @@
     return {value, ""};
 }
 
-server::server(std::string name)
+server::server(boost::asio::io_service& service, std::string name)
     : name_(std::move(name))
-    , session_(std::make_unique<session>())
-    , state_(std::make_unique<connecting_state>())
+    , service_(service)
 {
     // Initialize nickname and username.
     auto user = sys::username();
 
     nickname_ = user.empty() ? "irccd" : user;
     username_ = user.empty() ? "irccd" : user;
+}
 
-    irc_callbacks_t callbacks;
+void server::dispatch_channel_mode(const irc::message& msg)
+{
+    on_channel_mode({ shared_from_this(), msg.arg(0), msg.arg(1), msg.arg(2), msg.arg(3)});
+}
+
+void server::dispatch_channel_notice(const irc::message& msg)
+{
+    on_channel_notice({shared_from_this(), msg.arg(0), msg.arg(0), msg.arg(1)});
+}
+
+void server::dispatch_connect(const irc::message&)
+{
+    recocur_ = 1;
+    jchannels_.clear();
+
+#if 0
+    timer_.start();
+#endif
+
+    // Change state and auto join requested channels.
+    state_ = state_t::connected;
+    on_connect({shared_from_this()});
+
+    for (const auto& channel : rchannels_) {
+        log::info() << "server " << name_ << ": auto joining " << channel.name << std::endl;
+        join(channel.name, channel.password);
+    }
+}
 
+void server::dispatch_endofnames(const irc::message& msg)
+{
     /*
-     * GCC 4.9.2 triggers some missing-field-initializers warnings when
-     * using uniform initialization so use a std::memset as a workaround.
+     * Called when end of name listing has finished on a channel.
+     *
+     * params[0] == originator
+     * params[1] == channel
+     * params[2] == End of NAMES list
+     */
+    if (msg.args().size() < 3 || msg.arg(1) == "")
+        return;
+
+    auto it = names_map_.find(msg.arg(1));
+
+    if (it != names_map_.end()) {
+        std::vector<std::string> list(it->second.begin(), it->second.end());
+
+        on_names({shared_from_this(), msg.arg(1), std::move(list)});
+
+        // Don't forget to remove the list.
+        names_map_.erase(it);
+    }
+}
+
+void server::dispatch_endofwhois(const irc::message& msg)
+{
+    /*
+     * Called when whois is finished.
+     *
+     * params[0] == originator
+     * params[1] == nickname
+     * params[2] == End of WHOIS list
      */
-    std::memset(&callbacks, 0, sizeof (irc_callbacks_t));
+    auto it = whois_map_.find(msg.arg(1));
+    if (it != whois_map_.end()) {
+        on_whois({shared_from_this(), it->second});
+
+        // Don't forget to remove.
+        whois_map_.erase(it);
+    }
+}
+
+void server::dispatch_invite(const irc::message& msg)
+{
+    // If joininvite is set, join the channel.
+    if ((flags_ & join_invite) && is_self(msg.arg(1)))
+        join(msg.arg(2));
+
+    on_invite({shared_from_this(), msg.arg(0), msg.arg(2), msg.arg(1)});
+}
+
+void server::dispatch_isupport(const irc::message& msg)
+{
+    for (unsigned int i = 0; i < msg.args().size(); ++i) {
+        if (msg.arg(i).compare(0, 6, "PREFIX") == 0) {
+            modes_ = isupport_extract_prefixes(msg.arg(i));
+
+#if !defined(NDEBUG)
+            auto show = [this] (auto mode, auto title) {
+                auto it = modes_.find(mode);
+
+                if (it != modes_.end())
+                    log::debug(string_util::sprintf("  %-12s: %c", title, it->second));
+            };
 
+            log::debug(string_util::sprintf("server %s: isupport modes:", name_));
+            show(channel_mode::creator, "creator");
+            show(channel_mode::half_op, "half_op");
+            show(channel_mode::op, "op");
+            show(channel_mode::protection, "protection");
+            show(channel_mode::voiced, "voiced");
+#endif // !NDEBUG
+
+            break;
+        }
+    }
+
+}
+
+void server::dispatch_join(const irc::message& msg)
+{
+    if (is_self(msg.arg(0)))
+        jchannels_.push_back(msg.arg(1));
+
+    on_join({shared_from_this(), msg.arg(0), msg.arg(1)});
+}
+
+void server::dispatch_kick(const irc::message& msg)
+{
+    if (is_self(msg.arg(2))) {
+        // Remove the channel from the joined list.
+        remove_joined_channel(msg.arg(1));
+
+        // Rejoin the channel if the option has been set and I was kicked.
+        if (flags_ & auto_rejoin)
+            join(msg.arg(1));
+    }
+
+    on_kick({ shared_from_this(), msg.arg(0), msg.arg(1), msg.arg(2), msg.arg(3)});
+}
+
+void server::dispatch_mode(const irc::message& msg)
+{
+#if 0
+    on_mode({shared_from_this(), msg.arg(0), msg.arg(1)});
+#endif
+}
+
+void server::dispatch_namreply(const irc::message& msg)
+{
     /*
-     * Convert the raw pointer functions from libircclient to Server member
-     * function.
+     * Called multiple times to list clients on a channel.
+     *
+     * params[0] == originator
+     * params[1] == '='
+     * params[2] == channel
+     * params[3] == list of users with their prefixes
      *
-     * While doing this, discard useless arguments.
+     * IDEA for the future: maybe give the appropriate mode as a second
+     * parameter in onNames.
      */
+    if (msg.args().size() < 4 || msg.arg(2) == "" || msg.arg(3) == "")
+        return;
+
+    auto users = string_util::split(msg.arg(3), " \t");
+
+    // The listing may add some prefixes, remove them if needed.
+    for (auto u : users)
+        names_map_[msg.arg(2)].insert(clean_prefix(modes_, u));
+}
 
-    callbacks.event_channel = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_channel(orig, params);
-    };
-    callbacks.event_channel_notice = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_channel_notice(orig, params);
-    };
-    callbacks.event_connect = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_connect(orig, params);
-    };
-    callbacks.event_ctcp_action = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_ctcp_action(orig, params);
-    };
-    callbacks.event_invite = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_invite(orig, params);
-    };
-    callbacks.event_join = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_join(orig, params);
-    };
-    callbacks.event_kick = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_kick(orig, params);
-    };
-    callbacks.event_mode = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_channel_mode(orig, params);
-    };
-    callbacks.event_nick = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_nick(orig, params);
-    };
-    callbacks.event_notice = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_notice(orig, params);
-    };
-    callbacks.event_numeric = [] (irc_session_t* session, unsigned int event, const char*, const char** params, unsigned int count) {
-        static_cast<server*>(irc_get_ctx(session))->handle_numeric(event, params, count);
-    };
-    callbacks.event_part = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_part(orig, params);
-    };
-    callbacks.event_ping = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_ping(orig, params);
-    };
-    callbacks.event_privmsg = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_query(orig, params);
-    };
-    callbacks.event_topic = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_topic(orig, params);
-    };
-    callbacks.event_umode = [] (irc_session_t* session, const char*, const char* orig, const char** params, unsigned int) {
-        static_cast<server*>(irc_get_ctx(session))->handle_mode(orig, params);
-    };
+void server::dispatch_nick(const irc::message& msg)
+{
+    // Update our nickname.
+    if (is_self(msg.arg(0)))
+        nickname_ = msg.arg(0);
+
+    on_nick({shared_from_this(), msg.arg(0), msg.arg(1)});
+}
+
+void server::dispatch_notice(const irc::message& msg)
+{
+    on_notice({shared_from_this(), msg.arg(0), msg.arg(2)});
+}
+
+void server::dispatch_part(const irc::message& msg)
+{
+    // Remove the channel from the joined list if I left a channel.
+    if (is_self(msg.arg(0)))
+        remove_joined_channel(msg.arg(1));
+
+    on_part({shared_from_this(), msg.arg(0), msg.arg(1), msg.arg(2)});
+}
+
+void server::dispatch_ping(const irc::message& msg)
+{
+    assert(msg.command() == "PING");
+
+    conn_->send(string_util::sprintf("PONG %s", msg.arg(1)));
+}
+
+void server::dispatch_topic(const irc::message& msg)
+{
+    assert(msg.command() == "TOPIC");
+
+    on_topic({shared_from_this(), msg.arg(0), msg.arg(1), msg.arg(2)});
+}
+
+void server::dispatch_whoischannels(const irc::message& msg)
+{
+    /*
+     * Called when we have received channels for one user.
+     *
+     * params[0] == originator
+     * params[1] == nickname
+     * params[2] == list of channels with their prefixes
+     */
+    if (msg.args().size() < 3 || msg.arg(1) == "" || msg.arg(2) == "")
+        return;
+
+    auto it = whois_map_.find(msg.arg(1));
+
+    if (it != whois_map_.end()) {
+        auto channels = string_util::split(msg.arg(2), " \t");
+
+        // Clean their prefixes.
+        for (auto& s : channels)
+            s = clean_prefix(modes_, s);
+
+        it->second.channels = std::move(channels);
+    }
+}
+
+void server::dispatch_whoisuser(const irc::message& msg)
+{
+    /*
+     * Called when whois information has been partially received.
+     *
+     * params[0] == originator
+     * params[1] == nickname
+     * params[2] == username
+     * params[3] == host
+     * params[4] == * (no idea what is that)
+     * params[5] == realname
+     */
+    if (msg.args().size() < 6 || msg.arg(1) == "" || msg.arg(2) == "" || msg.arg(3) == "" || msg.arg(5) == "")
+        return;
+
+    class whois info;
 
-    session_->handle_ = {irc_create_session(&callbacks), irc_destroy_session};
+    info.nick = msg.arg(1);
+    info.user = msg.arg(2);
+    info.host = msg.arg(3);
+    info.realname = msg.arg(5);
+
+    whois_map_.emplace(info.nick, info);
+}
+
+void server::dispatch(const irc::message& message)
+{
+    if (message.is(5))
+        dispatch_isupport(message);
+    else if (message.is(irc::err::nomotd) || message.is(irc::rpl::endofmotd))
+            dispatch_connect(message);
+    else if (message.command() == "INVITE")
+        dispatch_invite(message);
+    else if (message.command() == "JOIN")
+        dispatch_join(message);
+    else if (message.command() == "KICK")
+        dispatch_kick(message);
+    else if (message.command() == "NICK")
+        dispatch_nick(message);
+    else if (message.command() == "NOTICE")
+        dispatch_notice(message);
+    else if (message.command() == "TOPIC")
+        dispatch_topic(message);
+    else if (message.command() == "PART")
+        dispatch_part(message);
+    else if (message.command() == "PING")
+        dispatch_ping(message);
+    else if (message.is(irc::rpl::namreply))
+        dispatch_namreply(message);
+    else if (message.is(irc::rpl::endofnames))
+        dispatch_endofnames(message);
+    else if (message.is(irc::rpl::endofwhois))
+        dispatch_endofwhois(message);
+    else if (message.is(irc::rpl::whoischannels))
+        dispatch_whoischannels(message);
+    else if (message.is(irc::rpl::whoisuser))
+        dispatch_whoisuser(message);
+}
 
-    // Save this to the session.
-    irc_set_ctx(*session_, this);
-    irc_set_ctcp_version(*session_, ctcpversion_.c_str());
+void server::handle_recv(boost::system::error_code code, irc::message message)
+{
+    if (code) {
+        state_ = state_t::disconnected;
+        conn_ = nullptr;
+    } else {
+        dispatch(message);
+        recv();
+    }
+}
+
+void server::recv()
+{
+    conn_->recv([this] (auto code, auto message) {
+        handle_recv(std::move(code), std::move(message));
+    });
+}
+
+void server::identify()
+{
+    assert(state_ == state_t::identifying);
+
+    log::debug(string_util::sprintf("server %s: connected, identifying", name_));
+    log::debug(string_util::sprintf("server %s: verifying server", name_));
+
+    if (!password_.empty())
+        conn_->send(string_util::sprintf("PASS %s", password_));
+
+    conn_->send(string_util::sprintf("NICK %s", nickname_));
+    conn_->send(string_util::sprintf("USER %s unknown unknown :%s", username_, realname_));
+}
+
+void server::handle_connect(boost::system::error_code code)
+{
+    if (code) {
+        // TODO: reconnect happens HERE.
+        state_ = state_t::disconnected;
+        conn_ = nullptr;
+        log::warning(string_util::sprintf("server %s: error while connecting", name_));
+        log::warning(string_util::sprintf("server %s: %s", name_, code.message()));
+    } else {
+        state_ = state_t::identifying;
+        identify();
+        recv();
+    }
 }
 
 server::~server()
 {
-    irc_disconnect(*session_);
+    disconnect();
 }
 
 void server::set_nickname(std::string nickname)
 {
-    if (session_->is_connected())
-        queue_.push([=] () {
-            return irc_cmd_nick(*session_, nickname.c_str()) == 0;
-        });
+    if (state_ == state_t::connected)
+        conn_->send(string_util::sprintf("NICK %s", nickname));
     else
         nickname_ = std::move(nickname);
 }
@@ -604,106 +522,82 @@
 void server::set_ctcp_version(std::string ctcpversion)
 {
     ctcpversion_ = std::move(ctcpversion);
-    irc_set_ctcp_version(*session_, ctcpversion_.c_str());
-}
-
-void server::next(std::unique_ptr<state> state) noexcept
-{
-    state_next_ = std::move(state);
-}
-
-std::string server::status() const noexcept
-{
-    return state_ ? state_->ident() : "null";
 }
 
-void server::update() noexcept
+void server::connect() noexcept
 {
-    if (state_next_) {
-        log::debug(string_util::sprintf("server %s: switch state %s -> %s",
-            name_, state_->ident(), state_next_->ident()));
+    assert(state_ == state_t::disconnected);
+    /*
+     * This is needed if irccd is started before DHCP or if DNS cache is
+     * outdated.
+     */
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+    (void)res_init();
+#endif
 
-        state_ = std::move(state_next_);
-        state_next_ = nullptr;
+    if (flags_ & ssl) {
+#if defined(HAVE_SSL)
+        //conn_ = std::make_unique<irc::tls_connection>(service_);
+#else
+        /*
+         * If SSL is not compiled in, the caller is responsible of not setting
+         * the flag.
+         */
+        assert(!(flags_ & ssl));
+#endif
+    } else
+        conn_ = std::make_unique<irc::ip_connection>(service_);
 
-        // Reset channels.
-        jchannels_.clear();
-    }
+    state_ = state_t::connecting;
+    conn_->connect(host_, std::to_string(port_), [this] (auto code) {
+        handle_connect(std::move(code));
+    });
 }
 
 void server::disconnect() noexcept
 {
-    using namespace std::placeholders;
-
-    irc_disconnect(*session_);
+    conn_ = nullptr;
+    state_ = state_t::disconnected;
     on_die();
 }
 
 void server::reconnect() noexcept
 {
-    irc_disconnect(*session_);
-    next(std::make_unique<connecting_state>());
-}
-
-void server::prepare(fd_set& setinput, fd_set& setoutput, net::Handle& maxfd) noexcept
-{
-    state_->prepare(*this, setinput, setoutput, maxfd);
+    disconnect();
+    connect();
 }
 
-void server::sync(fd_set &setinput, fd_set &setoutput)
+bool server::is_self(const std::string& target) const noexcept
 {
-    /*
-     * 1. Send maximum of command possible if available for write
-     *
-     * Break on the first failure to avoid changing the order of the
-     * commands if any of them fails.
-     */
-    bool done = false;
-
-    while (!queue_.empty() && !done) {
-        if (queue_.front()())
-            queue_.pop();
-        else
-            done = true;
-    }
-
-    // 2. Read data.
-    irc_process_select_descriptors(*session_, &setinput, &setoutput);
-}
-
-bool server::is_self(const std::string& nick) const noexcept
-{
-    char target[32]{0};
-
-    irc_target_get_nick(nick.c_str(), target, sizeof (target));
-
-    return nickname_ == target;
+    return nickname_ == irc::user::parse(target).nick();
 }
 
 void server::cmode(std::string channel, std::string mode)
 {
-    queue_.push([=] () {
-        return irc_cmd_channel_mode(*session_, channel.c_str(), mode.c_str()) == 0;
-    });
+    assert(channel.c_str());
+    assert(mode.c_str());
+
+    if (!mode.empty())
+        send(string_util::sprintf("MODE %s :%s", channel, mode));
+    else
+        send(string_util::sprintf("MODE %s", channel));
 }
 
 void server::cnotice(std::string channel, std::string message)
 {
-    queue_.push([=] () {
-        return irc_cmd_notice(*session_, channel.c_str(), message.c_str()) == 0;
-    });
+    notice(std::move(channel), std::move(message));
 }
 
 void server::invite(std::string target, std::string channel)
 {
-    queue_.push([=] () {
-        return irc_cmd_invite(*session_, target.c_str(), channel.c_str()) == 0;
-    });
+    assert(!target.empty());
+    assert(!channel.empty());
+
+    send(string_util::sprintf("INVITE %s %s", target, channel));
 }
 
 void server::join(std::string channel, std::string password)
 {
-    // 1. Add the channel or update it to the requested channels.
     auto it = std::find_if(rchannels_.begin(), rchannels_.end(), [&] (const auto& c) {
         return c.name == channel;
     });
@@ -713,228 +607,107 @@
     else
         *it = { channel, password };
 
-    // 2. Join if not found and connected.
-    if (session_->is_connected())
-        irc_cmd_join(*session_, channel.c_str(), password.empty() ? nullptr : password.c_str());
+    if (state_ == state_t::connected) {
+        if (password.empty())
+            send(string_util::sprintf("JOIN %s", channel));
+        else
+            send(string_util::sprintf("JOIN %s :%s", channel, password));
+    }
 }
 
 void server::kick(std::string target, std::string channel, std::string reason)
 {
-    queue_.push([=] () {
-        return irc_cmd_kick(*session_, target.c_str(), channel.c_str(), reason.c_str()) == 0;
-    });
+    assert(!target.empty());
+    assert(!channel.empty());
+
+    if (!reason.empty())
+        send(string_util::sprintf("KICK %s %s :%s", channel, target, reason));
+    else
+        send(string_util::sprintf("KICK %s %s", channel, target));
 }
 
 void server::me(std::string target, std::string message)
 {
-    queue_.push([=] () {
-        return irc_cmd_me(*session_, target.c_str(), message.c_str()) == 0;
-    });
+    assert(!target.empty());
+    assert(!message.empty());
+
+    send(string_util::sprintf("PRIVMSG %s :\x01" "ACTION %s\x01", target, message));
 }
 
 void server::message(std::string target, std::string message)
 {
-    queue_.push([=] () {
-        return irc_cmd_msg(*session_, target.c_str(), message.c_str()) == 0;
-    });
+    assert(!target.empty());
+    assert(!message.empty());
+
+    send(string_util::sprintf("PRIVMSG %s :%s", target, message));
 }
 
 void server::mode(std::string mode)
 {
-    queue_.push([=] () {
-        return irc_cmd_user_mode(*session_, mode.c_str()) == 0;
-    });
+    assert(mode.c_str());
+
+    if (!mode.empty())
+        send(string_util::sprintf("MODE %s :%s", nickname_, mode));
+    else
+        send(string_util::sprintf("MODE %s", nickname_));
 }
 
 void server::names(std::string channel)
 {
-    queue_.push([=] () {
-        return irc_cmd_names(*session_, channel.c_str()) == 0;
-    });
+    assert(channel.c_str());
+
+    send(string_util::sprintf("NAMES %s", channel));
 }
 
 void server::notice(std::string target, std::string message)
 {
-    queue_.push([=] () {
-        return irc_cmd_notice(*session_, target.c_str(), message.c_str()) == 0;
-    });
+    assert(!target.empty());
+    assert(!message.empty());
+
+    send(string_util::sprintf("NOTICE %s :%s", target, message));
 }
 
 void server::part(std::string channel, std::string reason)
 {
-    queue_.push([=] () -> bool {
-        if (reason.empty())
-            return irc_cmd_part(*session_, channel.c_str()) == 0;
+    assert(!channel.empty());
 
-        return irc_send_raw(*session_, "PART %s :%s", channel.c_str(), reason.c_str());
-    });
+    if (!reason.empty())
+        send(string_util::sprintf("PART %s :%s", channel, reason));
+    else
+        send(string_util::sprintf("PART %s", channel));
 }
 
 void server::send(std::string raw)
 {
-    queue_.push([=] () {
-        return irc_send_raw(*session_, raw.c_str()) == 0;
+    assert(!raw.empty());
+
+    // TODO: adapt to custom exception later.
+    if (state_ != state_t::connected)
+        throw std::runtime_error("server is not connected");
+
+    conn_->send(std::move(raw), [this] (auto code) {
+        if (code) {
+            state_ = state_t::disconnected;
+            conn_ = nullptr;
+        }
     });
 }
 
 void server::topic(std::string channel, std::string topic)
 {
-    queue_.push([=] () {
-        return irc_cmd_topic(*session_, channel.c_str(), topic.c_str()) == 0;
-    });
+    assert(!channel.empty());
+
+    if (!topic.empty())
+        send(string_util::sprintf("TOPIC %s :%s", channel, topic));
+    else
+        send(string_util::sprintf("TOPIC %s", channel));
 }
 
 void server::whois(std::string target)
 {
-    queue_.push([=] () {
-        return irc_cmd_whois(*session_, target.c_str()) == 0;
-    });
-}
-
-/*
- * server::disconnected_state implementation
- * ------------------------------------------------------------------
- */
-
-void server::disconnected_state::prepare(server& server, fd_set&, fd_set&, net::Handle&)
-{
-    if (server.recotries_ == 0) {
-        log::warning() << "server " << server.name_ << ": reconnection disabled, skipping" << std::endl;
-        server.on_die();
-    } else if (server.recotries_ > 0 && server.recocur_ > server.recotries_) {
-        log::warning() << "server " << server.name_ << ": giving up" << std::endl;
-        server.on_die();
-    } else {
-        if (timer_.elapsed().wall / 1000000LL > static_cast<unsigned>(server.recodelay_ * 1000)) {
-            irc_disconnect(*server.session_);
-
-            server.recocur_ ++;
-            server.next(std::make_unique<connecting_state>());
-        }
-    }
-}
-
-std::string server::disconnected_state::ident() const
-{
-    return "Disconnected";
-}
-
-/*
- * server::connecting_state implementation
- * ------------------------------------------------------------------
- */
-
-bool server::connecting_state::connect(server& server)
-{
-    auto password = server.password_.empty() ? nullptr : server.password_.c_str();
-    auto host = server.host_;
-
-    // libircclient requires # for SSL connection.
-#if defined(HAVE_SSL)
-    if (server.flags_ & server::ssl)
-        host.insert(0, 1, '#');
-    if (!(server.flags_ & server::ssl_verify))
-        irc_option_set(*server.session_, LIBIRC_OPTION_SSL_NO_VERIFY);
-#endif
-
-    int code;
-    if (server.flags() & server::ipv6) {
-        code = irc_connect6(*server.session_, host.c_str(), server.port_, password,
-                            server.nickname_.c_str(),
-                            server.username_.c_str(),
-                            server.realname_.c_str());
-    } else {
-        code = irc_connect(*server.session_, host.c_str(), server.port_, password,
-                           server.nickname_.c_str(),
-                           server.username_.c_str(),
-                           server.realname_.c_str());
-    }
-
-    return code == 0;
-}
+    assert(!target.empty());
 
-void server::connecting_state::prepare(server& server, fd_set& setinput, fd_set& setoutput, net::Handle& maxfd)
-{
-    /*
-     * The connect function will either fail if the hostname wasn't resolved or
-     * if any of the internal functions fail.
-     *
-     * It returns success if the connection was successful but it does not mean
-     * that connection is established.
-     *
-     * Because this function will be called repeatidly, the connection was
-     * started and we're still not connected in the specified timeout time, we
-     * mark the server as disconnected.
-     *
-     * Otherwise, the libircclient event_connect will change the state.
-     */
-    if (state_ == connecting) {
-        if (timer_.elapsed().wall / 1000000LL > static_cast<unsigned>(server.recodelay_ * 1000)) {
-            log::warning() << "server " << server.name() << ": timeout while connecting" << std::endl;
-            server.next(std::make_unique<disconnected_state>());
-        } else if (!irc_is_connected(*server.session_)) {
-            log::warning() << "server " << server.name_ << ": error while connecting: ";
-            log::warning() << irc_strerror(irc_errno(*server.session_)) << std::endl;
-
-            if (server.recotries_ != 0)
-                log::warning(string_util::sprintf("server %s: retrying in %hu seconds", server.name_, server.recodelay_));
-
-            server.next(std::make_unique<disconnected_state>());
-        } else
-            irc_add_select_descriptors(*server.session_, &setinput, &setoutput, reinterpret_cast<int*>(&maxfd));
-    } else {
-        /*
-         * This is needed if irccd is started before DHCP or if DNS cache is
-         * outdated.
-         */
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-        (void)res_init();
-#endif
-        log::info(string_util::sprintf("server %s: trying to connect to %s, port %hu", server.name_, server.host_, server.port_));
-
-        if (!connect(server)) {
-            log::warning() << "server " << server.name_ << ": disconnected while connecting: ";
-            log::warning() << irc_strerror(irc_errno(*server.session_)) << std::endl;
-            server.next(std::make_unique<disconnected_state>());
-        } else {
-            state_ = connecting;
-
-            if (irc_is_connected(*server.session_))
-                irc_add_select_descriptors(*server.session_, &setinput, &setoutput, reinterpret_cast<int*>(&maxfd));
-        }
-    }
-}
-
-std::string server::connecting_state::ident() const
-{
-    return "Connecting";
-}
-
-/*
- * server::connected_state implementation
- * ------------------------------------------------------------------
- */
-
-void server::connected_state::prepare(server& server, fd_set& setinput, fd_set& setoutput, net::Handle& maxfd)
-{
-    if (!irc_is_connected(*server.session_)) {
-        log::warning() << "server " << server.name_ << ": disconnected" << std::endl;
-
-        if (server.recodelay_ > 0)
-            log::warning(string_util::sprintf("server %s: retrying in %hu seconds", server.name_, server.recodelay_));
-
-        server.next(std::make_unique<disconnected_state>());
-    } else if (server.timer_.elapsed().wall / 1000000LL >= server.timeout_ * 1000) {
-        log::warning() << "server " << server.name_ << ": ping timeout after "
-                       << (server.timer_.elapsed().wall / 1000000000LL) << " seconds" << std::endl;
-        server.next(std::make_unique<disconnected_state>());
-    } else
-        irc_add_select_descriptors(*server.session_, &setinput, &setoutput, reinterpret_cast<int*>(&maxfd));
-}
-
-std::string server::connected_state::ident() const
-{
-    return "Connected";
+    send(string_util::sprintf("WHOIS %s %s", target, target));
 }
 
 } // !irccd
--- a/libirccd/irccd/server.hpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd/irccd/server.hpp	Thu Nov 23 22:45:12 2017 +0100
@@ -24,24 +24,20 @@
  * \brief IRC Server.
  */
 
+#include "sysconfig.hpp"
+
 #include <cstdint>
-#include <functional>
 #include <map>
 #include <memory>
-#include <queue>
 #include <set>
 #include <string>
-#include <unordered_map>
-#include <utility>
 #include <vector>
 
 #include <boost/signals2/signal.hpp>
-#include <boost/timer/timer.hpp>
 
 #include <json.hpp>
 
-#include "net.hpp"
-#include "sysconfig.hpp"
+#include "irc.hpp"
 
 namespace irccd {
 
@@ -265,11 +261,6 @@
 class server : public std::enable_shared_from_this<server> {
 public:
     /**
-     * Bridge for libircclient.
-     */
-    class session;
-
-    /**
      * \brief Various options for server.
      */
     enum {
@@ -281,6 +272,17 @@
     };
 
     /**
+     * \brief Describe current server state.
+     */
+    enum class state_t {
+        disconnected,       //!< not connected at all,
+        connecting,         //!< network connection in progress,
+        identifying,        //!< sending nick, user and password commands,
+        waiting,            //!< waiting for reconnection,
+        connected           //!< ready for use
+    };
+
+    /**
      * Signal: on_channel_mode
      * ----------------------------------------------------------
      *
@@ -420,10 +422,7 @@
     boost::signals2::signal<void (whois_event)> on_whois;
 
 private:
-    class state;
-    class connected_state;
-    class connecting_state;
-    class disconnected_state;
+    state_t state_{state_t::disconnected};
 
     // Requested and joined channels.
     std::vector<channel> rchannels_;
@@ -432,7 +431,7 @@
     // Identifier.
     std::string name_;
 
-    // Connection information
+    // Connection information.
     std::string host_;
     std::string password_;
     std::uint16_t port_{6667};
@@ -450,43 +449,42 @@
     std::uint16_t recodelay_{30};
     std::uint16_t timeout_{1000};
 
-    // Queue of requests to send.
-    std::queue<std::function<bool ()>> queue_;
-
-    // libircclient session (bridge).
-    std::unique_ptr<session> session_;
-
-    // States.
-    std::unique_ptr<state> state_;
-    std::unique_ptr<state> state_next_;
+    // Server information.
+    std::map<channel_mode, char> modes_;
 
     // Misc.
-    boost::timer::cpu_timer timer_;
-    std::map<channel_mode, char> modes_;
+    boost::asio::io_service& service_;
+    std::unique_ptr<irc::connection> conn_;
     std::int8_t recocur_{0};
     std::map<std::string, std::set<std::string>> names_map_;
     std::map<std::string, class whois> whois_map_;
 
-    // Private helpers.
     void remove_joined_channel(const std::string& channel);
 
-    // Handle libircclient callbacks.
-    void handle_channel(const char*, const char**) noexcept;
-    void handle_channel_mode(const char*, const char**) noexcept;
-    void handle_channel_notice(const char*, const char**) noexcept;
-    void handle_connect(const char*, const char**) noexcept;
-    void handle_ctcp_action(const char*, const char**) noexcept;
-    void handle_invite(const char*, const char**) noexcept;
-    void handle_join(const char*, const char**) noexcept;
-    void handle_kick(const char*, const char**) noexcept;
-    void handle_mode(const char*, const char**) noexcept;
-    void handle_nick(const char*, const char**) noexcept;
-    void handle_notice(const char*, const char**) noexcept;
-    void handle_numeric(unsigned int, const char**, unsigned int) noexcept;
-    void handle_part(const char*, const char**) noexcept;
-    void handle_ping(const char*, const char**) noexcept;
-    void handle_query(const char*, const char**) noexcept;
-    void handle_topic(const char*, const char**) noexcept;
+    void dispatch_channel_mode(const irc::message&);
+    void dispatch_channel_notice(const irc::message&);
+    void dispatch_connect(const irc::message&);
+    void dispatch_endofnames(const irc::message&);
+    void dispatch_endofwhois(const irc::message&);
+    void dispatch_invite(const irc::message&);
+    void dispatch_isupport(const irc::message&);
+    void dispatch_join(const irc::message&);
+    void dispatch_kick(const irc::message&);
+    void dispatch_mode(const irc::message&);
+    void dispatch_namreply(const irc::message&);
+    void dispatch_nick(const irc::message&);
+    void dispatch_notice(const irc::message&);
+    void dispatch_part(const irc::message&);
+    void dispatch_ping(const irc::message&);
+    void dispatch_topic(const irc::message&);
+    void dispatch_whoischannels(const irc::message&);
+    void dispatch_whoisuser(const irc::message&);
+    void dispatch(const irc::message&);
+
+    void handle_recv(boost::system::error_code code, irc::message message);
+    void handle_connect(boost::system::error_code);
+    void recv();
+    void identify();
 
 public:
     /**
@@ -512,9 +510,10 @@
     /**
      * Construct a server.
      *
+     * \param service the service
      * \param name the identifier
      */
-    server(std::string name);
+    server(boost::asio::io_service& service, std::string name);
 
     /**
      * Destructor. Close the connection if needed.
@@ -610,6 +609,10 @@
      */
     inline void set_flags(std::uint8_t flags) noexcept
     {
+#if !defined(HAVE_SSL)
+        assert(!(flags & ssl));
+#endif
+
         flags_ = flags;
     }
 
@@ -787,29 +790,12 @@
         return jchannels_;
     }
 
-    /**
-     * Set the next state, it is not changed immediately but on next iteration.
-     *
-     * \param state the new state
-     */
-    void next(std::unique_ptr<state> state) noexcept;
-
-    /**
-     * Get the state current id.
-     *
-     * \return the state id
-     */
-    std::string status() const noexcept;
-
-    /**
-     * Switch to next state if it has.
-     */
-    void update() noexcept;
+    virtual void connect() noexcept;
 
     /**
      * Force disconnection.
      */
-    void disconnect() noexcept;
+    virtual void disconnect() noexcept;
 
     /**
      * Asks for a reconnection.
@@ -817,22 +803,6 @@
     virtual void reconnect() noexcept;
 
     /**
-     * Prepare the IRC session.
-     *
-     * \warning Not thread-safe
-     */
-    virtual void prepare(fd_set& setinput, fd_set& setoutput, net::Handle& maxfd) noexcept;
-
-    /**
-     * Process incoming/outgoing data after selection.
-     *
-     * \param setinput
-     * \param setoutput
-     * \throw any exception that have been throw from user functions
-     */
-    virtual void sync(fd_set& setinput, fd_set& setoutput);
-
-    /**
      * Determine if the nickname is the bot itself.
      *
      * \param nick the nickname to check
--- a/libirccd/irccd/service.cpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/libirccd/irccd/service.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -821,16 +821,20 @@
 
 void server_service::prepare(fd_set& in, fd_set& out, net::Handle& max)
 {
+#if 0
     for (auto& server : servers_) {
         server->update();
         server->prepare(in, out, max);
     }
+#endif
 }
 
 void server_service::sync(fd_set& in, fd_set& out)
 {
+#if 0
     for (auto& server : servers_)
         server->sync(in, out);
+#endif
 }
 
 bool server_service::has(const std::string& name) const noexcept
@@ -873,6 +877,7 @@
         });
     });
 
+    server->connect();
     servers_.push_back(std::move(server));
 }
 
--- a/tests/CMakeLists.txt	Wed Nov 22 20:10:03 2017 +0100
+++ b/tests/CMakeLists.txt	Thu Nov 23 22:45:12 2017 +0100
@@ -51,7 +51,7 @@
 #    add_subdirectory(cmd-server-topic)
 #    add_subdirectory(dynlib_plugin)
 
-    # Misc
+    add_subdirectory(irc)
     add_subdirectory(logger)
     add_subdirectory(rules)
     add_subdirectory(util)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/irc/CMakeLists.txt	Thu Nov 23 22:45:12 2017 +0100
@@ -0,0 +1,23 @@
+#
+# CMakeLists.txt -- CMake build system for irccd
+#
+# Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+irccd_define_test(
+    NAME irc
+    SOURCES main.cpp
+    LIBRARIES libirccd
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/irc/main.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -0,0 +1,87 @@
+/*
+ * main.cpp -- test irc functions
+ *
+ * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define BOOST_TEST_MODULE "irc"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/irc.hpp>
+
+namespace irccd {
+
+BOOST_AUTO_TEST_SUITE(message_parse)
+
+BOOST_AUTO_TEST_CASE(no_prefix)
+{
+    irc::message m;
+
+    BOOST_TEST(!m);
+
+    m = irc::message::parse("PRIVMSG jean :bonjour à toi");
+    BOOST_TEST(m);
+    BOOST_TEST(m.prefix().empty());
+    BOOST_TEST(m.command() == "PRIVMSG");
+    BOOST_TEST(m.args().size() == 2U);
+    BOOST_TEST(m.args()[0] == "jean");
+    BOOST_TEST(m.args()[1] == "bonjour à toi");
+}
+
+BOOST_AUTO_TEST_CASE(prefix)
+{
+    irc::message m;
+
+    BOOST_TEST(!m);
+
+    m = irc::message::parse(":127.0.0.1 PRIVMSG jean :bonjour à toi");
+    BOOST_TEST(m);
+    BOOST_TEST(m.prefix() == "127.0.0.1");
+    BOOST_TEST(m.command() == "PRIVMSG");
+    BOOST_TEST(m.args().size() == 2U);
+    BOOST_TEST(m.args()[0] == "jean");
+    BOOST_TEST(m.args()[1] == "bonjour à toi");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(user_parse)
+
+BOOST_AUTO_TEST_CASE(basics)
+{
+    auto user = irc::user::parse("jean!~jean@127.0.0.1");
+
+    BOOST_TEST(user.nick() == "jean");
+    BOOST_TEST(user.host() == "~jean@127.0.0.1");
+}
+
+BOOST_AUTO_TEST_CASE(invalid)
+{
+    irc::user user("", "");
+
+    user = irc::user::parse("notavalidcombination");
+
+    BOOST_TEST(user.nick().empty());
+    BOOST_TEST(user.host().empty());
+
+    user = irc::user::parse("");
+
+    BOOST_TEST(user.nick().empty());
+    BOOST_TEST(user.host().empty());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // !irccd
--- a/tests/plugin-auth/main.cpp	Wed Nov 22 20:10:03 2017 +0100
+++ b/tests/plugin-auth/main.cpp	Thu Nov 23 22:45:12 2017 +0100
@@ -36,9 +36,9 @@
 public:
     auth_test()
         : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
-        , nickserv1_(std::make_shared<journal_server>("nickserv1"))
-        , nickserv2_(std::make_shared<journal_server>("nickserv2"))
-        , quakenet_(std::make_shared<journal_server>("quakenet"))
+        , nickserv1_(std::make_shared<journal_server>(service_, "nickserv1"))
+        , nickserv2_(std::make_shared<journal_server>(service_, "nickserv2"))
+        , quakenet_(std::make_shared<journal_server>(service_, "quakenet"))
     {
         plugin_->set_config({
             { "nickserv1.type", "nickserv" },