| /* #undef HAVE_GETHOSTBYNAME_R */ | /* #undef HAVE_GETHOSTBYNAME_R */ | ||||
| /* Define to 1 if you have the `inet_ntoa' function. */ | /* Define to 1 if you have the `inet_ntoa' function. */ | ||||
| /* #undef HAVE_INET_NTOA */ | |||||
| #define HAVE_INET_NTOA 1 | |||||
| /* Define to 1 if you have the `inet_pton' function. */ | /* Define to 1 if you have the `inet_pton' function. */ | ||||
| /* #undef HAVE_INET_PTON */ | /* #undef HAVE_INET_PTON */ | ||||
| /* Define to 1 if you have the <inttypes.h> header file. */ | /* Define to 1 if you have the <inttypes.h> header file. */ | ||||
| /* #undef HAVE_INTTYPES_H */ | |||||
| #define HAVE_INTTYPES_H 1 | |||||
| /* Define to 1 if you have the `localtime_r' function. */ | /* Define to 1 if you have the `localtime_r' function. */ | ||||
| /* #undef HAVE_LOCALTIME_R */ | |||||
| #define HAVE_LOCALTIME_R 1 | |||||
| /* Define to 1 if your system has a GNU libc compatible `malloc' function, and | /* Define to 1 if your system has a GNU libc compatible `malloc' function, and | ||||
| to 0 otherwise. */ | to 0 otherwise. */ | ||||
| #define HAVE_MALLOC 0 | |||||
| #define HAVE_MALLOC 1 | |||||
| /* Define to 1 if you have the <memory.h> header file. */ | /* Define to 1 if you have the <memory.h> header file. */ | ||||
| /* #undef HAVE_MEMORY_H */ | |||||
| #define HAVE_MEMORY_H 1 | |||||
| /* Define to 1 if you have the `socket' function. */ | /* Define to 1 if you have the `socket' function. */ | ||||
| /* #undef HAVE_SOCKET */ | |||||
| #define HAVE_SOCKET 1 | |||||
| /* Define to 1 if `stat' has the bug that it succeeds when given the | /* Define to 1 if `stat' has the bug that it succeeds when given the | ||||
| zero-length file name argument. */ | zero-length file name argument. */ | ||||
| #define HAVE_STAT_EMPTY_STRING_BUG 1 | |||||
| /* #undef HAVE_STAT_EMPTY_STRING_BUG */ | |||||
| /* Define to 1 if stdbool.h conforms to C99. */ | /* Define to 1 if stdbool.h conforms to C99. */ | ||||
| #define HAVE_STDBOOL_H 1 | #define HAVE_STDBOOL_H 1 | ||||
| /* Define to 1 if you have the <stdint.h> header file. */ | /* Define to 1 if you have the <stdint.h> header file. */ | ||||
| /* #undef HAVE_STDINT_H */ | |||||
| #define HAVE_STDINT_H 1 | |||||
| /* Define to 1 if you have the <stdlib.h> header file. */ | /* Define to 1 if you have the <stdlib.h> header file. */ | ||||
| /* #undef HAVE_STDLIB_H */ | |||||
| #define HAVE_STDLIB_H 1 | |||||
| /* Define to 1 if you have the <strings.h> header file. */ | /* Define to 1 if you have the <strings.h> header file. */ | ||||
| /* #undef HAVE_STRINGS_H */ | |||||
| #define HAVE_STRINGS_H 1 | |||||
| /* Define to 1 if you have the <string.h> header file. */ | /* Define to 1 if you have the <string.h> header file. */ | ||||
| /* #undef HAVE_STRING_H */ | |||||
| #define HAVE_STRING_H 1 | |||||
| /* Define to 1 if you have the <sys/select.h> header file. */ | /* Define to 1 if you have the <sys/select.h> header file. */ | ||||
| /* #undef HAVE_SYS_SELECT_H */ | |||||
| #define HAVE_SYS_SELECT_H 1 | |||||
| /* Define to 1 if you have the <sys/socket.h> header file. */ | /* Define to 1 if you have the <sys/socket.h> header file. */ | ||||
| /* #undef HAVE_SYS_SOCKET_H */ | |||||
| #define HAVE_SYS_SOCKET_H 1 | |||||
| /* Define to 1 if you have the <sys/stat.h> header file. */ | /* Define to 1 if you have the <sys/stat.h> header file. */ | ||||
| /* #undef HAVE_SYS_STAT_H */ | |||||
| #define HAVE_SYS_STAT_H 1 | |||||
| /* Define to 1 if you have the <sys/types.h> header file. */ | /* Define to 1 if you have the <sys/types.h> header file. */ | ||||
| /* #undef HAVE_SYS_TYPES_H */ | |||||
| #define HAVE_SYS_TYPES_H 1 | |||||
| /* Define to 1 if you have the <unistd.h> header file. */ | /* Define to 1 if you have the <unistd.h> header file. */ | ||||
| /* #undef HAVE_UNISTD_H */ | |||||
| #define HAVE_UNISTD_H 1 | |||||
| /* Define to 1 if the system has the type `_Bool'. */ | /* Define to 1 if the system has the type `_Bool'. */ | ||||
| /* #undef HAVE__BOOL */ | |||||
| #define HAVE__BOOL 1 | |||||
| /* Define to 1 if `lstat' dereferences a symlink specified with a trailing | /* Define to 1 if `lstat' dereferences a symlink specified with a trailing | ||||
| slash. */ | slash. */ | ||||
| #define PACKAGE_NAME "libircclient" | #define PACKAGE_NAME "libircclient" | ||||
| /* Define to the full name and version of this package. */ | /* Define to the full name and version of this package. */ | ||||
| #define PACKAGE_STRING "libircclient 1.8" | |||||
| #define PACKAGE_STRING "libircclient 1.3" | |||||
| /* Define to the one symbol short name of this package. */ | /* Define to the one symbol short name of this package. */ | ||||
| #define PACKAGE_TARNAME "libircclient" | #define PACKAGE_TARNAME "libircclient" | ||||
| /* Define to the version of this package. */ | /* Define to the version of this package. */ | ||||
| #define PACKAGE_VERSION "1.8" | |||||
| #define PACKAGE_VERSION "1.3" | |||||
| /* Define to the type of arg 1 for `select'. */ | /* Define to the type of arg 1 for `select'. */ | ||||
| #define SELECT_TYPE_ARG1 int | #define SELECT_TYPE_ARG1 int | ||||
| /* Define to the type of args 2, 3 and 4 for `select'. */ | /* Define to the type of args 2, 3 and 4 for `select'. */ | ||||
| #define SELECT_TYPE_ARG234 (int *) | |||||
| #define SELECT_TYPE_ARG234 (fd_set *) | |||||
| /* Define to the type of arg 5 for `select'. */ | /* Define to the type of arg 5 for `select'. */ | ||||
| #define SELECT_TYPE_ARG5 (struct timeval *) | #define SELECT_TYPE_ARG5 (struct timeval *) | ||||
| /* Define to 1 if you have the ANSI C header files. */ | /* Define to 1 if you have the ANSI C header files. */ | ||||
| /* #undef STDC_HEADERS */ | |||||
| #define STDC_HEADERS 1 | |||||
| /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ | /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ | ||||
| /* #undef TIME_WITH_SYS_TIME */ | |||||
| #define TIME_WITH_SYS_TIME 1 | |||||
| /* Define to empty if `const' does not conform to ANSI C. */ | /* Define to empty if `const' does not conform to ANSI C. */ | ||||
| /* #undef const */ | /* #undef const */ |
| /* | /* | ||||
| * This structure keeps the state of a single DCC connection. | * This structure keeps the state of a single DCC connection. | ||||
| */ | */ | ||||
| struct irc_dcc_session_s | |||||
| { | |||||
| struct irc_dcc_session_s { | |||||
| irc_dcc_session_t * next; | irc_dcc_session_t * next; | ||||
| irc_dcc_t id; | irc_dcc_t id; | ||||
| time_t timeout; | time_t timeout; | ||||
| FILE * dccsend_file_fp; | FILE * dccsend_file_fp; | ||||
| unsigned int received_file_size; | |||||
| unsigned int file_confirm_offset; | |||||
| size_t received_file_size; | |||||
| size_t file_confirm_offset; | |||||
| struct sockaddr_in remote_addr; | struct sockaddr_in remote_addr; | ||||
| char incoming_buf[LIBIRC_DCC_BUFFER_SIZE]; | char incoming_buf[LIBIRC_DCC_BUFFER_SIZE]; | ||||
| unsigned int incoming_offset; | |||||
| size_t incoming_offset; | |||||
| char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE]; | char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE]; | ||||
| unsigned int outgoing_offset; | |||||
| size_t outgoing_offset; | |||||
| port_mutex_t mutex_outbuf; | port_mutex_t mutex_outbuf; | ||||
| irc_dcc_callback_t cb; | irc_dcc_callback_t cb; |
| * | * | ||||
| * \ingroup events | * \ingroup events | ||||
| */ | */ | ||||
| typedef void (*irc_event_callback_t) (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count); | |||||
| typedef void (*irc_event_callback_t) (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup events | * \ingroup events | ||||
| */ | */ | ||||
| typedef void (*irc_eventcode_callback_t) (irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count); | |||||
| typedef void (*irc_eventcode_callback_t) (irc_session_t *session, unsigned int event, const char *origin, const char **params, unsigned int count); | |||||
| /*! | /*! | ||||
| * \sa irc_dcc_accept or irc_dcc_decline | * \sa irc_dcc_accept or irc_dcc_decline | ||||
| * \ingroup events | * \ingroup events | ||||
| */ | */ | ||||
| typedef void (*irc_event_dcc_chat_t) (irc_session_t * session, const char * nick, const char * addr, irc_dcc_t dccid); | |||||
| 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) | * \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 | |||||
| * \brief A remote DCC SEND request callback | |||||
| * | * | ||||
| * \param session the session, which generates an event | * \param session the session, which generates an event | ||||
| * \param nick the person who requested DCC CHAT with you. | |||||
| * \param nick the person who requested DCC SEND to you. | |||||
| * \param addr the person's IP address in decimal-dot notation. | * \param addr the person's IP address in decimal-dot notation. | ||||
| * \param filename the sent filename. | * \param filename the sent filename. | ||||
| * \param size the filename size. | * \param size the filename size. | ||||
| * \sa irc_dcc_accept or irc_dcc_decline | * \sa irc_dcc_accept or irc_dcc_decline | ||||
| * \ingroup events | * \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); | |||||
| typedef void (*irc_event_dcc_send_t) (irc_session_t *session, const char *nick, const char *addr, const char *filename, size_t size, irc_dcc_t dccid); | |||||
| /*! \brief Event callbacks structure. | /*! \brief Event callbacks structure. | ||||
| */ | */ | ||||
| irc_event_callback_t event_connect; | irc_event_callback_t event_connect; | ||||
| /*! | |||||
| * The "ping" event is triggered when the client receives a PING message. | |||||
| * It is only generated if the LIBIRC_OPTION_PING_PASSTHROUGH option is set; | |||||
| * otherwise, the library responds to PING messages automatically. | |||||
| * | |||||
| * \param origin the person, who generated the ping. | |||||
| * \param params[0] mandatory, contains who knows what. | |||||
| */ | |||||
| irc_event_callback_t event_ping; | |||||
| /*! | /*! | ||||
| * The "nick" event is triggered when the client receives a NICK message, | * The "nick" event is triggered when the client receives a NICK message, | ||||
| * meaning that someone (including you) on a channel with the client has | * meaning that someone (including you) on a channel with the client has | ||||
| */ | */ | ||||
| irc_event_callback_t event_kick; | irc_event_callback_t event_kick; | ||||
| /*! | |||||
| * The "error" event is triggered upon receipt of an ERROR message, which | |||||
| * (when sent to clients) usually means the client has been disconnected. | |||||
| * | |||||
| * \param origin the person, who generates the message. | |||||
| * \param params optional, contains who knows what. | |||||
| */ | |||||
| irc_event_callback_t event_error; | |||||
| /*! | /*! | ||||
| * The "channel" event is triggered upon receipt of a PRIVMSG message | * The "channel" event is triggered upon receipt of a PRIVMSG message | ||||
| * to an entire channel, which means that someone on a channel with | * to an entire channel, which means that someone on a channel with | ||||
| irc_event_dcc_chat_t event_dcc_chat_req; | irc_event_dcc_chat_t event_dcc_chat_req; | ||||
| /*! | /*! | ||||
| * The "dcc chat" event is triggered when someone wants to send a file | |||||
| * The "dcc send" event is triggered when someone wants to send a file | |||||
| * to you via DCC SEND request. | * to you via DCC SEND request. | ||||
| * | * | ||||
| * See the params in ::irc_event_dcc_send_t specification. | * See the params in ::irc_event_dcc_send_t specification. |
| */ | */ | ||||
| #define LIBIRC_OPTION_SSL_NO_VERIFY (1 << 3) | #define LIBIRC_OPTION_SSL_NO_VERIFY (1 << 3) | ||||
| /*! \brief Disables automatic response to PING messages. | |||||
| * | |||||
| * The library will still generate events for PING messages, if an event handler | |||||
| * is provided. | |||||
| * \ingroup options | |||||
| */ | |||||
| #define LIBIRC_OPTION_IGNORE_PING (1 << 4) | |||||
| #endif /* INCLUDE_IRC_OPTIONS_H */ | #endif /* INCLUDE_IRC_OPTIONS_H */ |
| /*! | /*! | ||||
| * \file libircclient.h | * \file libircclient.h | ||||
| * \author George Yunaev | * \author George Yunaev | ||||
| * \version 1.5 | |||||
| * \version 1.9 | |||||
| * \date 01.2012 | * \date 01.2012 | ||||
| * \brief This file defines all prototypes and functions to use libircclient. | * \brief This file defines all prototypes and functions to use libircclient. | ||||
| * | * | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #if !defined (WIN32) | |||||
| #if !defined (_WIN32) | |||||
| #include <sys/select.h> /* fd_set */ | #include <sys/select.h> /* fd_set */ | ||||
| #else | #else | ||||
| #include <winsock2.h> | #include <winsock2.h> | ||||
| * | * | ||||
| * \ingroup dccstuff | * \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); | |||||
| typedef void (*irc_dcc_callback_t) (irc_session_t *session, irc_dcc_t id, int status, void *ctx, const char *data, size_t length); | |||||
| #define IN_INCLUDE_LIBIRC_H | #define IN_INCLUDE_LIBIRC_H | ||||
| * \sa irc_destroy_session | * \sa irc_destroy_session | ||||
| * \ingroup initclose | * \ingroup initclose | ||||
| */ | */ | ||||
| irc_session_t * irc_create_session (irc_callbacks_t * callbacks); | |||||
| irc_session_t* irc_create_session (irc_callbacks_t *callbacks); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup initclose | * \ingroup initclose | ||||
| */ | */ | ||||
| void irc_destroy_session (irc_session_t * session); | |||||
| void irc_destroy_session (irc_session_t *session); | |||||
| /*! | /*! | ||||
| * \sa irc_run | * \sa irc_run | ||||
| * \ingroup conndisc | * \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); | |||||
| 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); | |||||
| /*! | /*! | ||||
| * \sa irc_run | * \sa irc_run | ||||
| * \ingroup conndisc | * \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); | |||||
| 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) | * \fn void irc_disconnect (irc_session_t * session) | ||||
| * \sa irc_connect irc_run | * \sa irc_connect irc_run | ||||
| * \ingroup conndisc | * \ingroup conndisc | ||||
| */ | */ | ||||
| void irc_disconnect (irc_session_t * session); | |||||
| void irc_disconnect (irc_session_t *session); | |||||
| /*! | /*! | ||||
| * \sa irc_connect irc_run | * \sa irc_connect irc_run | ||||
| * \ingroup conndisc | * \ingroup conndisc | ||||
| */ | */ | ||||
| int irc_is_connected (irc_session_t * session); | |||||
| int irc_is_connected (irc_session_t *session); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup running | * \ingroup running | ||||
| */ | */ | ||||
| int irc_run (irc_session_t * session); | |||||
| int irc_run (irc_session_t *session); | |||||
| /*! | /*! | ||||
| * \sa irc_process_select_descriptors | * \sa irc_process_select_descriptors | ||||
| * \ingroup running | * \ingroup running | ||||
| */ | */ | ||||
| int irc_add_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set, int * maxfd); | |||||
| int irc_add_select_descriptors (irc_session_t *session, | |||||
| fd_set *in_set, | |||||
| fd_set *out_set, | |||||
| int *maxfd); | |||||
| /*! | /*! | ||||
| * \sa irc_add_select_descriptors | * \sa irc_add_select_descriptors | ||||
| * \ingroup running | * \ingroup running | ||||
| */ | */ | ||||
| int irc_process_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set); | |||||
| int irc_process_select_descriptors (irc_session_t *session, | |||||
| fd_set *in_set, | |||||
| fd_set *out_set); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_oth | * \ingroup ircmd_oth | ||||
| */ | */ | ||||
| int irc_send_raw (irc_session_t * session, const char * format, ...); | |||||
| int irc_send_raw (irc_session_t *session, | |||||
| const char *format, | |||||
| ...); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_oth | * \ingroup ircmd_oth | ||||
| */ | */ | ||||
| int irc_cmd_quit (irc_session_t * session, const char * reason); | |||||
| int irc_cmd_quit (irc_session_t *session, | |||||
| const char *reason); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_ch | * \ingroup ircmd_ch | ||||
| */ | */ | ||||
| int irc_cmd_join (irc_session_t * session, const char * channel, const char * key); | |||||
| int irc_cmd_join (irc_session_t *session, | |||||
| const char *channel, | |||||
| const char *key); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_ch | * \ingroup ircmd_ch | ||||
| */ | */ | ||||
| int irc_cmd_part (irc_session_t * session, const char * channel); | |||||
| int irc_cmd_part (irc_session_t *session, | |||||
| const char *channel); | |||||
| /*! | /*! | ||||
| * \sa irc_callbacks_t::event_invite irc_cmd_channel_mode | * \sa irc_callbacks_t::event_invite irc_cmd_channel_mode | ||||
| * \ingroup ircmd_ch | * \ingroup ircmd_ch | ||||
| */ | */ | ||||
| int irc_cmd_invite (irc_session_t * session, const char * nick, const char * channel); | |||||
| int irc_cmd_invite (irc_session_t *session, | |||||
| const char *nick, | |||||
| const char *channel); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_ch | * \ingroup ircmd_ch | ||||
| */ | */ | ||||
| int irc_cmd_names (irc_session_t * session, const char * channel); | |||||
| int irc_cmd_names (irc_session_t *session, | |||||
| const char *channel); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_ch | * \ingroup ircmd_ch | ||||
| */ | */ | ||||
| int irc_cmd_list (irc_session_t * session, const char * channel); | |||||
| int irc_cmd_list (irc_session_t *session, | |||||
| const char *channel); | |||||
| /*! | /*! | ||||
| * \sa irc_callbacks_t::event_topic irc_cmd_channel_mode | * \sa irc_callbacks_t::event_topic irc_cmd_channel_mode | ||||
| * \ingroup ircmd_ch | * \ingroup ircmd_ch | ||||
| */ | */ | ||||
| int irc_cmd_topic (irc_session_t * session, const char * channel, const char * topic); | |||||
| int irc_cmd_topic (irc_session_t *session, | |||||
| const char *channel, | |||||
| const char *topic); | |||||
| /*! | /*! | ||||
| * \sa irc_cmd_topic irc_cmd_list | * \sa irc_cmd_topic irc_cmd_list | ||||
| * \ingroup ircmd_ch | * \ingroup ircmd_ch | ||||
| */ | */ | ||||
| int irc_cmd_channel_mode (irc_session_t * session, const char * channel, const char * mode); | |||||
| int irc_cmd_channel_mode (irc_session_t *session, | |||||
| const char *channel, | |||||
| const char *mode); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_oth | * \ingroup ircmd_oth | ||||
| */ | */ | ||||
| int irc_cmd_user_mode (irc_session_t * session, const char * mode); | |||||
| int irc_cmd_user_mode (irc_session_t *session, | |||||
| const char *mode); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_oth | * \ingroup ircmd_oth | ||||
| */ | */ | ||||
| int irc_cmd_nick (irc_session_t * session, const char * newnick); | |||||
| int irc_cmd_nick (irc_session_t *session, | |||||
| const char *newnick); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_oth | * \ingroup ircmd_oth | ||||
| */ | */ | ||||
| int irc_cmd_whois (irc_session_t * session, const char * nick); | |||||
| int irc_cmd_whois (irc_session_t *session, | |||||
| const char *nick); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ircmd_msg | * \ingroup ircmd_msg | ||||
| */ | */ | ||||
| int irc_cmd_msg (irc_session_t * session, const char * nch, const char * text); | |||||
| int irc_cmd_msg (irc_session_t *session, | |||||
| const char *nch, | |||||
| const char *text); | |||||
| /*! | /*! | ||||
| * \sa irc_cmd_msg | * \sa irc_cmd_msg | ||||
| * \ingroup ircmd_msg | * \ingroup ircmd_msg | ||||
| */ | */ | ||||
| int irc_cmd_me (irc_session_t * session, const char * nch, const char * text); | |||||
| int irc_cmd_me (irc_session_t *session, | |||||
| const char *nch, | |||||
| const char *text); | |||||
| /*! | /*! | ||||
| * \sa irc_cmd_msg | * \sa irc_cmd_msg | ||||
| * \ingroup ircmd_msg | * \ingroup ircmd_msg | ||||
| */ | */ | ||||
| int irc_cmd_notice (irc_session_t * session, const char * nch, const char * text); | |||||
| int irc_cmd_notice (irc_session_t *session, | |||||
| const char *nch, | |||||
| const char *text); | |||||
| /*! | /*! | ||||
| * \sa irc_callbacks_t::event_numeric | * \sa irc_callbacks_t::event_numeric | ||||
| * \ingroup ircmd_ch | * \ingroup ircmd_ch | ||||
| */ | */ | ||||
| int irc_cmd_kick (irc_session_t * session, const char * nick, const char * channel, const char * reason); | |||||
| int irc_cmd_kick (irc_session_t *session, | |||||
| const char *nick, | |||||
| const char *channel, | |||||
| const char *reason); | |||||
| /*! | /*! | ||||
| * \sa irc_callbacks_t::event_ctcp_rep irc_callbacks_t::event_numeric | * \sa irc_callbacks_t::event_ctcp_rep irc_callbacks_t::event_numeric | ||||
| * \ingroup ctcp | * \ingroup ctcp | ||||
| */ | */ | ||||
| int irc_cmd_ctcp_request (irc_session_t * session, const char * nick, const char * request); | |||||
| int irc_cmd_ctcp_request (irc_session_t *session, | |||||
| const char *nick, | |||||
| const char *request); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup ctcp | * \ingroup ctcp | ||||
| */ | */ | ||||
| int irc_cmd_ctcp_reply (irc_session_t * session, const char * nick, const char * reply); | |||||
| 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) | * \fn void irc_target_get_nick (const char * target, char *nick, size_t size) | ||||
| * \brief Gets the nick part from the target | * \brief Gets the nick part from the target | ||||
| * | * | ||||
| * \param target A nick in common IRC server form like tim!root\@mycomain.com | |||||
| * \param target A nick in common IRC server form like tim!root\@mycomain.com; cannot be NULL | |||||
| * \param nick A buffer to hold the nickname. | * \param nick A buffer to hold the nickname. | ||||
| * \param size A buffer size. If nick is longer than buffer size, it will | * \param size A buffer size. If nick is longer than buffer size, it will | ||||
| * be truncated. | * be truncated. | ||||
| * | * | ||||
| * For most events IRC server returns 'origin' (i.e. the person, who | * For most events IRC server returns 'origin' (i.e. the person, who | ||||
| * generated this event) in i.e. "common" form, like nick!host\@domain. | * generated this event) in i.e. "common" form, like nick!host\@domain. | ||||
| * However, all the irc_cmd_* functions require just a nick/ | |||||
| * However, all the irc_cmd_* functions require just a nick. | |||||
| * This function parses this origin, and gets the nick, storing it into | * This function parses this origin, and gets the nick, storing it into | ||||
| * user-provided buffer. | * user-provided buffer. | ||||
| * A buffer of size 90 should be enough for most nicks :) | * A buffer of size 90 should be enough for most nicks :) | ||||
| * | * | ||||
| * \ingroup nnparse | * \ingroup nnparse | ||||
| */ | */ | ||||
| void irc_target_get_nick (const char * target, char *nick, size_t size); | |||||
| void irc_target_get_nick (const char *target, | |||||
| char *nick, | |||||
| size_t size); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup nnparse | * \ingroup nnparse | ||||
| */ | */ | ||||
| void irc_target_get_host (const char * target, char *nick, size_t size); | |||||
| 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) | * \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. | * \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. | |||||
| * \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 | * \return Return code 0 means success. Other value means error, the error | ||||
| * code may be obtained through irc_errno(). Any error, generated by the | * code may be obtained through irc_errno(). Any error, generated by the | ||||
| * \sa irc_dcc_callback_t irc_dcc_msg | * \sa irc_dcc_callback_t irc_dcc_msg | ||||
| * \ingroup dccstuff | * \ingroup dccstuff | ||||
| */ | */ | ||||
| int irc_dcc_chat (irc_session_t * session, void * ctx, const char * nick, irc_dcc_callback_t callback, irc_dcc_t * dccid); | |||||
| int irc_dcc_chat (irc_session_t *session, | |||||
| void *ctx, | |||||
| const char *nick, | |||||
| irc_dcc_callback_t | |||||
| callback, | |||||
| irc_dcc_t *dccid); | |||||
| /*! | /*! | ||||
| * \sa irc_dcc_chat | * \sa irc_dcc_chat | ||||
| * \ingroup dccstuff | * \ingroup dccstuff | ||||
| */ | */ | ||||
| int irc_dcc_msg (irc_session_t * session, irc_dcc_t dccid, const char * text); | |||||
| int irc_dcc_msg (irc_session_t *session, | |||||
| irc_dcc_t dccid, | |||||
| const char *text); | |||||
| /*! | /*! | ||||
| * \sa irc_dcc_decline event_dcc_chat_req event_dcc_send_req | * \sa irc_dcc_decline event_dcc_chat_req event_dcc_send_req | ||||
| * \ingroup dccstuff | * \ingroup dccstuff | ||||
| */ | */ | ||||
| int irc_dcc_accept (irc_session_t * session, irc_dcc_t dccid, void * ctx, irc_dcc_callback_t callback); | |||||
| int irc_dcc_accept (irc_session_t *session, | |||||
| irc_dcc_t dccid, | |||||
| void *ctx, | |||||
| irc_dcc_callback_t callback); | |||||
| /*! | /*! | ||||
| * \sa irc_dcc_accept irc_callbacks_t::event_dcc_chat_req irc_callbacks_t::event_dcc_send_req irc_dcc_destroy | * \sa irc_dcc_accept irc_callbacks_t::event_dcc_chat_req irc_callbacks_t::event_dcc_send_req irc_dcc_destroy | ||||
| * \ingroup dccstuff | * \ingroup dccstuff | ||||
| */ | */ | ||||
| int irc_dcc_decline (irc_session_t * session, irc_dcc_t dccid); | |||||
| int irc_dcc_decline (irc_session_t *session, | |||||
| irc_dcc_t dccid); | |||||
| /*! | /*! | ||||
| * \sa irc_dcc_callback_t | * \sa irc_dcc_callback_t | ||||
| * \ingroup dccstuff | * \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); | |||||
| 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); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup dccstuff | * \ingroup dccstuff | ||||
| */ | */ | ||||
| int irc_dcc_destroy (irc_session_t * session, irc_dcc_t dccid); | |||||
| int irc_dcc_destroy (irc_session_t *session, | |||||
| irc_dcc_t dccid); | |||||
| /*! | /*! | ||||
| * | * | ||||
| * \ingroup common | * \ingroup common | ||||
| */ | */ | ||||
| void irc_get_version (unsigned int * high, unsigned int * low); | |||||
| void irc_get_version (unsigned int *high, | |||||
| unsigned int *low); | |||||
| /*! | /*! | ||||
| * \sa irc_get_ctx | * \sa irc_get_ctx | ||||
| * \ingroup contexts | * \ingroup contexts | ||||
| */ | */ | ||||
| void irc_set_ctx (irc_session_t * session, void * ctx); | |||||
| void irc_set_ctx (irc_session_t *session, | |||||
| void *ctx); | |||||
| /*! | /*! | ||||
| * \fn void irc_set_ctcp_version (irc_session_t * session, const char *version) | * \fn void irc_set_ctcp_version (irc_session_t * session, const char *version) | ||||
| * | * | ||||
| * \ingroup contexts | * \ingroup contexts | ||||
| */ | */ | ||||
| void irc_set_ctcp_version(irc_session_t * session, const char * version); | |||||
| void irc_set_ctcp_version(irc_session_t *session, | |||||
| const char *version); | |||||
| /*! | /*! | ||||
| * \fn void * irc_get_ctx (irc_session_t * session) | * \fn void * irc_get_ctx (irc_session_t * session) | ||||
| * \sa irc_set_ctx | * \sa irc_set_ctx | ||||
| * \ingroup contexts | * \ingroup contexts | ||||
| */ | */ | ||||
| void * irc_get_ctx (irc_session_t * session); | |||||
| void* irc_get_ctx (irc_session_t *session); | |||||
| /*! | /*! | ||||
| * \sa irc_strerror | * \sa irc_strerror | ||||
| * \ingroup errors | * \ingroup errors | ||||
| */ | */ | ||||
| int irc_errno (irc_session_t * session); | |||||
| int irc_errno (irc_session_t *session); | |||||
| /*! | /*! | ||||
| * \sa irc_errno() | * \sa irc_errno() | ||||
| * \ingroup errors | * \ingroup errors | ||||
| */ | */ | ||||
| const char * irc_strerror (int ircerrno); | |||||
| const char* irc_strerror (int ircerrno); | |||||
| /*! | /*! | ||||
| * \sa irc_option_reset | * \sa irc_option_reset | ||||
| * \ingroup options | * \ingroup options | ||||
| */ | */ | ||||
| void irc_option_set (irc_session_t * session, unsigned int option); | |||||
| void irc_option_set (irc_session_t *session, | |||||
| unsigned int option); | |||||
| /*! | /*! | ||||
| * \sa irc_option_set | * \sa irc_option_set | ||||
| * \ingroup options | * \ingroup options | ||||
| */ | */ | ||||
| void irc_option_reset (irc_session_t * session, unsigned int option); | |||||
| void irc_option_reset (irc_session_t *session, | |||||
| unsigned int option); | |||||
| /*! | /*! | ||||
| * \sa irc_color_convert_from_mirc irc_color_convert_to_mirc | * \sa irc_color_convert_from_mirc irc_color_convert_to_mirc | ||||
| * \ingroup colors | * \ingroup colors | ||||
| */ | */ | ||||
| char * irc_color_strip_from_mirc (const char * message); | |||||
| char* irc_color_strip_from_mirc (const char *message); | |||||
| /*! | /*! | ||||
| * \sa irc_color_strip_from_mirc irc_color_convert_to_mirc | * \sa irc_color_strip_from_mirc irc_color_convert_to_mirc | ||||
| * \ingroup colors | * \ingroup colors | ||||
| */ | */ | ||||
| char * irc_color_convert_from_mirc (const char * message); | |||||
| char* irc_color_convert_from_mirc (const char *message); | |||||
| /*! | /*! | ||||
| * \sa irc_color_strip_from_mirc irc_color_convert_from_mirc | * \sa irc_color_strip_from_mirc irc_color_convert_from_mirc | ||||
| * \ingroup colors | * \ingroup colors | ||||
| */ | */ | ||||
| char * irc_color_convert_to_mirc (const char * message); | |||||
| char* irc_color_convert_to_mirc (const char *message); | |||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| } | } |
| #define LIBIRC_VERSION_HIGH 1 | #define LIBIRC_VERSION_HIGH 1 | ||||
| #define LIBIRC_VERSION_LOW 8 | |||||
| #define LIBIRC_VERSION_LOW 10 | |||||
| #define LIBIRC_BUFFER_SIZE 1024 | #define LIBIRC_BUFFER_SIZE 1024 | ||||
| #define LIBIRC_DCC_BUFFER_SIZE 1024 | #define LIBIRC_DCC_BUFFER_SIZE 1024 |
| struct irc_session_s | |||||
| { | |||||
| struct irc_session_s { | |||||
| void * ctx; | void * ctx; | ||||
| int dcc_timeout; | int dcc_timeout; | ||||
| int lasterror; | int lasterror; | ||||
| char incoming_buf[LIBIRC_BUFFER_SIZE]; | char incoming_buf[LIBIRC_BUFFER_SIZE]; | ||||
| unsigned int incoming_offset; | |||||
| size_t incoming_offset; | |||||
| char outgoing_buf[LIBIRC_BUFFER_SIZE]; | char outgoing_buf[LIBIRC_BUFFER_SIZE]; | ||||
| unsigned int outgoing_offset; | |||||
| size_t outgoing_offset; | |||||
| port_mutex_t mutex_session; | port_mutex_t mutex_session; | ||||
| socket_t sock; | socket_t sock; | ||||
| SSL * ssl; | SSL * ssl; | ||||
| #endif | #endif | ||||
| }; | }; | ||||
| #define LIBIRC_COLORPARSER_MAXCOLORS 15 | #define LIBIRC_COLORPARSER_MAXCOLORS 15 | ||||
| static const char * color_replacement_table[] = | |||||
| { | |||||
| #define max(a,b) \ | |||||
| ({ __typeof__ (a) _a = (a); \ | |||||
| __typeof__ (b) _b = (b); \ | |||||
| _a > _b ? _a : _b; }) | |||||
| static const char *color_replacement_table[] = { | |||||
| "WHITE", | "WHITE", | ||||
| "BLACK", | "BLACK", | ||||
| "DARKBLUE", | "DARKBLUE", | ||||
| }; | }; | ||||
| static inline void libirc_colorparser_addorcat (char ** destline, unsigned int * destlen, const char * str) | |||||
| { | |||||
| unsigned int len = strlen(str); | |||||
| static inline void libirc_colorparser_addorcat (char **destline, | |||||
| unsigned int *destlen, | |||||
| const char *str) { | |||||
| size_t len = strlen(str); | |||||
| if ( *destline ) | |||||
| { | |||||
| if (*destline) { | |||||
| strcpy (*destline, str); | strcpy (*destline, str); | ||||
| *destline += len; | *destline += len; | ||||
| } | |||||
| else | |||||
| } else { | |||||
| *destlen += len; | *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 ) | |||||
| { | |||||
| 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; | *mask &= ~bitmask; | ||||
| libirc_colorparser_addorcat (destline, destlen, end); | libirc_colorparser_addorcat (destline, destlen, end); | ||||
| } | |||||
| else | |||||
| { | |||||
| } else { | |||||
| *mask |= bitmask; | *mask |= bitmask; | ||||
| libirc_colorparser_addorcat (destline, destlen, start); | 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]"; | |||||
| 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]; | char startbuf[64]; | ||||
| if ( bgcolorid != 0 ) | |||||
| if (bgcolorid != 0) | |||||
| sprintf (startbuf, "[COLOR=%s/%s]", color_replacement_table[colorid], color_replacement_table[bgcolorid]); | sprintf (startbuf, "[COLOR=%s/%s]", color_replacement_table[colorid], color_replacement_table[bgcolorid]); | ||||
| else | else | ||||
| sprintf (startbuf, "[COLOR=%s]", color_replacement_table[colorid]); | sprintf (startbuf, "[COLOR=%s]", color_replacement_table[colorid]); | ||||
| if ( (*mask & LIBIRC_COLORPARSER_COLOR) != 0 ) | |||||
| if ((*mask & LIBIRC_COLORPARSER_COLOR) != 0) | |||||
| libirc_colorparser_addorcat (destline, destlen, end); | libirc_colorparser_addorcat (destline, destlen, end); | ||||
| *mask |= LIBIRC_COLORPARSER_COLOR; | *mask |= LIBIRC_COLORPARSER_COLOR; | ||||
| libirc_colorparser_addorcat (destline, destlen, startbuf); | |||||
| libirc_colorparser_addorcat(destline, destlen, startbuf); | |||||
| } | } | ||||
| static void libirc_colorparser_closetags (unsigned int * mask, | |||||
| char ** destline, unsigned int * destlen) | |||||
| { | |||||
| if ( *mask & LIBIRC_COLORPARSER_BOLD ) | |||||
| 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]"); | libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_BOLD, 0, "[/B]"); | ||||
| if ( *mask & LIBIRC_COLORPARSER_UNDERLINE ) | |||||
| if (*mask & LIBIRC_COLORPARSER_UNDERLINE) | |||||
| libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_UNDERLINE, 0, "[/U]"); | libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_UNDERLINE, 0, "[/U]"); | ||||
| if ( *mask & LIBIRC_COLORPARSER_REVERSE ) | |||||
| if (*mask & LIBIRC_COLORPARSER_REVERSE) | |||||
| libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_REVERSE, 0, "[/I]"); | libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_REVERSE, 0, "[/I]"); | ||||
| if ( *mask & LIBIRC_COLORPARSER_COLOR ) | |||||
| if (*mask & LIBIRC_COLORPARSER_COLOR) | |||||
| libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_COLOR, 0, "[/COLOR]"); | libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_COLOR, 0, "[/COLOR]"); | ||||
| } | } | ||||
| /* | /* | ||||
| * IRC to [code] color conversion. Or strip. | * IRC to [code] color conversion. Or strip. | ||||
| */ | */ | ||||
| static char * libirc_colorparser_irc2code (const char * source, int strip) | |||||
| { | |||||
| static char* libirc_colorparser_irc2code (const char *source, | |||||
| int strip) { | |||||
| unsigned int mask = 0, destlen = 0; | unsigned int mask = 0, destlen = 0; | ||||
| char * destline = 0, *d = 0; | |||||
| char *destline = 0, *d = 0; | |||||
| const char *p; | const char *p; | ||||
| int current_bg = 0; | |||||
| unsigned int current_bg = 0; | |||||
| /* | /* | ||||
| * There will be two passes. First pass calculates the total length of | * There will be two passes. First pass calculates the total length of | ||||
| * the destination string. The second pass allocates memory for the string, | * the destination string. The second pass allocates memory for the string, | ||||
| * and fills it. | * and fills it. | ||||
| */ | */ | ||||
| while ( destline == 0 ) // destline will be set after the 2nd pass | |||||
| { | |||||
| if ( destlen > 0 ) | |||||
| { | |||||
| while (destline == 0) { // destline will be set after the 2nd pass | |||||
| if (destlen > 0) { | |||||
| // This is the 2nd pass; allocate memory. | // This is the 2nd pass; allocate memory. | ||||
| if ( (destline = malloc (destlen)) == 0 ) | |||||
| if ((destline = malloc (destlen)) == 0) | |||||
| return 0; | return 0; | ||||
| d = destline; | 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++; | |||||
| } | |||||
| 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; | |||||
| // If there is a comma, search for the following | |||||
| // background color | |||||
| if ( p[1] == ',' && isdigit (p[2]) ) | |||||
| { | |||||
| bgcolor = p[2] - 0x30; | |||||
| p += 2; | |||||
| libirc_colorparser_applymask(&mask, &d, &destlen, LIBIRC_COLORPARSER_REVERSE, "[I]", "[/I]"); | |||||
| break; | |||||
| } | |||||
| case 0x0F: { // reset colors | |||||
| if (strip) | |||||
| continue; | |||||
| if ( isdigit (p[1]) ) | |||||
| { | |||||
| bgcolor = bgcolor * 10 + (p[1] - 0x30); | |||||
| 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++; | p++; | ||||
| } | } | ||||
| } | |||||
| // Check for range | |||||
| if ( color <= LIBIRC_COLORPARSER_MAXCOLORS | |||||
| && bgcolor <= LIBIRC_COLORPARSER_MAXCOLORS ) | |||||
| { | |||||
| if ( strip ) | |||||
| continue; | |||||
| // 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; | |||||
| if (bgcolor != -1) | |||||
| current_bg = (unsigned int) bgcolor; | |||||
| libirc_colorparser_applycolor (&mask, &d, &destlen, color, current_bg); | |||||
| libirc_colorparser_applycolor(&mask, &d, &destlen, (unsigned int) max(color, 0), current_bg); | |||||
| } | |||||
| } | } | ||||
| break; | |||||
| } | |||||
| default: { | |||||
| if (destline) | |||||
| *d++ = *p; | |||||
| else | |||||
| destlen++; | |||||
| break; | |||||
| } | } | ||||
| break; | |||||
| default: | |||||
| if ( destline ) | |||||
| *d++ = *p; | |||||
| else | |||||
| destlen++; | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static int libirc_colorparser_colorlookup (const char * color) | |||||
| { | |||||
| static int libirc_colorparser_colorlookup (const char *color) { | |||||
| int i; | int i; | ||||
| for ( i = 0; color_replacement_table[i]; i++ ) | |||||
| if ( !strcmp (color, color_replacement_table[i]) ) | |||||
| for (i = 0; color_replacement_table[i]; i++) | |||||
| if (!strcmp(color, color_replacement_table[i])) | |||||
| return i; | return i; | ||||
| return -1; | return -1; | ||||
| /* | /* | ||||
| * [code] to IRC color conversion. | * [code] to IRC color conversion. | ||||
| */ | */ | ||||
| char * irc_color_convert_to_mirc (const char * source) | |||||
| { | |||||
| char* irc_color_convert_to_mirc (const char *source) { | |||||
| unsigned int destlen = 0; | unsigned int destlen = 0; | ||||
| char * destline = 0, *d = 0; | |||||
| char *destline = 0, *d = 0; | |||||
| const char *p1, *p2, *cur; | const char *p1, *p2, *cur; | ||||
| /* | /* | ||||
| * the destination string. The second pass allocates memory for the string, | * the destination string. The second pass allocates memory for the string, | ||||
| * and fills it. | * and fills it. | ||||
| */ | */ | ||||
| while ( destline == 0 ) // destline will be set after the 2nd pass | |||||
| { | |||||
| if ( destlen > 0 ) | |||||
| { | |||||
| while (destline == 0) { // destline will be set after the 2nd pass | |||||
| if (destlen > 0) { | |||||
| // This is the 2nd pass; allocate memory. | // This is the 2nd pass; allocate memory. | ||||
| if ( (destline = malloc (destlen)) == 0 ) | |||||
| if ((destline = malloc (destlen)) == 0) | |||||
| return 0; | return 0; | ||||
| d = destline; | d = destline; | ||||
| } | } | ||||
| cur = source; | cur = source; | ||||
| while ( (p1 = strchr (cur, '[')) != 0 ) | |||||
| { | |||||
| const char * replacedval = 0; | |||||
| while ((p1 = strchr(cur, '[')) != 0) { | |||||
| const char *replacedval = 0; | |||||
| p2 = 0; | p2 = 0; | ||||
| // Check if the closing bracket is available after p1 | // Check if the closing bracket is available after p1 | ||||
| // and the tag length is suitable | // and the tag length is suitable | ||||
| if ( p1[1] != '\0' | |||||
| && (p2 = strchr (p1, ']')) != 0 | |||||
| && (p2 - p1) > 1 | |||||
| && (p2 - p1) < 31 ) | |||||
| { | |||||
| if ( p1[1] != '\0' | |||||
| && (p2 = strchr (p1, ']')) != 0 | |||||
| && (p2 - p1) > 1 | |||||
| && (p2 - p1) < 31) { | |||||
| // Get the tag | // Get the tag | ||||
| char tagbuf[32]; | char tagbuf[32]; | ||||
| int taglen = p2 - p1 - 1; | |||||
| ssize_t taglen = p2 - p1 - 1; | |||||
| memcpy (tagbuf, p1 + 1, taglen); | memcpy (tagbuf, p1 + 1, taglen); | ||||
| tagbuf[taglen] = '\0'; | tagbuf[taglen] = '\0'; | ||||
| if ( !strcmp (tagbuf, "/COLOR") ) | |||||
| if (!strcmp (tagbuf, "/COLOR")) { | |||||
| replacedval = "\x0F"; | replacedval = "\x0F"; | ||||
| else if ( strstr (tagbuf, "COLOR=") == tagbuf ) | |||||
| { | |||||
| } else if (strstr(tagbuf, "COLOR=") == tagbuf) { | |||||
| int color, bgcolor = -2; | int color, bgcolor = -2; | ||||
| char * bcol; | |||||
| char *bcol; | |||||
| bcol = strchr (tagbuf + 6, '/'); | |||||
| bcol = strchr(tagbuf + 6, '/'); | |||||
| if ( bcol ) | |||||
| { | |||||
| if (bcol) { | |||||
| *bcol++ = '\0'; | *bcol++ = '\0'; | ||||
| bgcolor = libirc_colorparser_colorlookup (bcol); | |||||
| bgcolor = libirc_colorparser_colorlookup(bcol); | |||||
| } | } | ||||
| color = libirc_colorparser_colorlookup (tagbuf + 6); | |||||
| color = libirc_colorparser_colorlookup(tagbuf + 6); | |||||
| if ( color != -1 && bgcolor == -2 ) | |||||
| { | |||||
| if (color != -1 && bgcolor == -2) { | |||||
| sprintf (tagbuf, "\x03%02d", color); | sprintf (tagbuf, "\x03%02d", color); | ||||
| replacedval = tagbuf; | replacedval = tagbuf; | ||||
| } | |||||
| else if ( color != -1 && bgcolor >= 0 ) | |||||
| { | |||||
| } else if (color != -1 && bgcolor >= 0) { | |||||
| sprintf (tagbuf, "\x03%02d,%02d", color, bgcolor); | sprintf (tagbuf, "\x03%02d,%02d", color, bgcolor); | ||||
| replacedval = tagbuf; | replacedval = tagbuf; | ||||
| } | } | ||||
| } | |||||
| else if ( !strcmp (tagbuf, "B") || !strcmp (tagbuf, "/B") ) | |||||
| } else if (!strcmp (tagbuf, "B") || !strcmp (tagbuf, "/B")) { | |||||
| replacedval = "\x02"; | replacedval = "\x02"; | ||||
| else if ( !strcmp (tagbuf, "U") || !strcmp (tagbuf, "/U") ) | |||||
| } else if (!strcmp (tagbuf, "U") || !strcmp (tagbuf, "/U")) { | |||||
| replacedval = "\x1F"; | replacedval = "\x1F"; | ||||
| else if ( !strcmp (tagbuf, "I") || !strcmp (tagbuf, "/I") ) | |||||
| } else if (!strcmp (tagbuf, "I") || !strcmp (tagbuf, "/I")) { | |||||
| replacedval = "\x16"; | replacedval = "\x16"; | ||||
| } | |||||
| } | } | ||||
| if ( replacedval ) | |||||
| { | |||||
| if (replacedval) { | |||||
| // add a part before the tag | // add a part before the tag | ||||
| int partlen = p1 - cur; | |||||
| ssize_t partlen = p1 - cur; | |||||
| if ( destline ) | |||||
| { | |||||
| if (destline) { | |||||
| memcpy (d, cur, partlen); | memcpy (d, cur, partlen); | ||||
| d += partlen; | d += partlen; | ||||
| } | |||||
| else | |||||
| } else { | |||||
| destlen += partlen; | destlen += partlen; | ||||
| } | |||||
| // Add the replacement | // Add the replacement | ||||
| libirc_colorparser_addorcat (&d, &destlen, replacedval); | |||||
| libirc_colorparser_addorcat(&d, &destlen, replacedval); | |||||
| // And move the pointer | // And move the pointer | ||||
| cur = p2 + 1; | cur = p2 + 1; | ||||
| } | |||||
| else | |||||
| { | |||||
| } else { | |||||
| // add a whole part before the end tag | // add a whole part before the end tag | ||||
| int partlen; | |||||
| ssize_t partlen; | |||||
| if ( !p2 ) | |||||
| if (!p2) | |||||
| p2 = cur + strlen(cur); | p2 = cur + strlen(cur); | ||||
| partlen = p2 - cur + 1; | partlen = p2 - cur + 1; | ||||
| if ( destline ) | |||||
| { | |||||
| if (destline) { | |||||
| memcpy (d, cur, partlen); | memcpy (d, cur, partlen); | ||||
| d += partlen; | d += partlen; | ||||
| } | |||||
| else | |||||
| } else { | |||||
| destlen += partlen; | destlen += partlen; | ||||
| } | |||||
| // And move the pointer | // And move the pointer | ||||
| cur = p2 + 1; | cur = p2 + 1; | ||||
| } | } | ||||
| // Add the rest of string | // Add the rest of string | ||||
| libirc_colorparser_addorcat (&d, &destlen, cur); | |||||
| libirc_colorparser_addorcat(&d, &destlen, cur); | |||||
| destlen++; // for 0-terminator | destlen++; // for 0-terminator | ||||
| } | } | ||||
| } | } | ||||
| char * irc_color_strip_from_mirc (const char * message) | |||||
| { | |||||
| char* irc_color_strip_from_mirc (const char *message) { | |||||
| return libirc_colorparser_irc2code (message, 1); | return libirc_colorparser_irc2code (message, 1); | ||||
| } | } | ||||
| char * irc_color_convert_from_mirc (const char * message) | |||||
| { | |||||
| char* irc_color_convert_from_mirc (const char *message) { | |||||
| return libirc_colorparser_irc2code (message, 0); | return libirc_colorparser_irc2code (message, 0); | ||||
| } | } |
| #if defined (WIN32_DLL) | #if defined (WIN32_DLL) | ||||
| BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) | BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) | ||||
| { | { | ||||
| WORD wVersionRequested = MAKEWORD (1, 1); | |||||
| WSADATA wsaData; | |||||
| switch(fdwReason) | switch(fdwReason) | ||||
| { | { | ||||
| case DLL_PROCESS_ATTACH: | case DLL_PROCESS_ATTACH: | ||||
| if ( WSAStartup (wVersionRequested, &wsaData) != 0 ) | |||||
| return FALSE; | |||||
| DisableThreadLibraryCalls (hinstDll); | DisableThreadLibraryCalls (hinstDll); | ||||
| break; | break; | ||||
| case DLL_PROCESS_DETACH: | case DLL_PROCESS_DETACH: | ||||
| WSACleanup(); | |||||
| break; | break; | ||||
| } | } | ||||
| #endif | #endif | ||||
| static int socket_error() | |||||
| { | |||||
| static int socket_error() { | |||||
| #if !defined (_WIN32) | #if !defined (_WIN32) | ||||
| return errno; | return errno; | ||||
| #else | #else | ||||
| } | } | ||||
| static int socket_create (int domain, int type, socket_t * sock) | |||||
| { | |||||
| static int socket_create (int domain, | |||||
| int type, | |||||
| socket_t *sock) { | |||||
| *sock = socket (domain, type, 0); | *sock = socket (domain, type, 0); | ||||
| return IS_SOCKET_ERROR(*sock) ? 1 : 0; | return IS_SOCKET_ERROR(*sock) ? 1 : 0; | ||||
| } | } | ||||
| static int socket_make_nonblocking (socket_t * sock) | |||||
| { | |||||
| static int socket_make_nonblocking (socket_t *sock) { | |||||
| #if !defined (_WIN32) | #if !defined (_WIN32) | ||||
| return fcntl (*sock, F_SETFL, fcntl (*sock, F_GETFL,0 ) | O_NONBLOCK) != 0; | return fcntl (*sock, F_SETFL, fcntl (*sock, F_GETFL,0 ) | O_NONBLOCK) != 0; | ||||
| #else | #else | ||||
| unsigned long mode = 0; | |||||
| unsigned long mode = 1; | |||||
| return ioctlsocket (*sock, FIONBIO, &mode) == SOCKET_ERROR; | return ioctlsocket (*sock, FIONBIO, &mode) == SOCKET_ERROR; | ||||
| #endif | #endif | ||||
| } | } | ||||
| static int socket_close (socket_t * sock) | |||||
| { | |||||
| static int socket_close (socket_t *sock) { | |||||
| #if !defined (_WIN32) | #if !defined (_WIN32) | ||||
| close (*sock); | close (*sock); | ||||
| #else | #else | ||||
| } | } | ||||
| 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 ) | |||||
| static ssize_t 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; | continue; | ||||
| if ( socket_error() != EINPROGRESS && socket_error() != EWOULDBLOCK ) | |||||
| if (socket_error() != EINPROGRESS && socket_error() != EWOULDBLOCK) | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| } | } | ||||
| 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 ) | |||||
| static ssize_t 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; | continue; | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| static int socket_recv (socket_t * sock, void * buf, size_t len) | |||||
| { | |||||
| int length; | |||||
| static ssize_t socket_recv (socket_t *sock, | |||||
| void *buf, | |||||
| size_t len) { | |||||
| ssize_t length; | |||||
| while ( (length = recv (*sock, buf, len, 0)) < 0 ) | |||||
| { | |||||
| while ((length = recv(*sock, buf, len, 0)) < 0) { | |||||
| int err = socket_error(); | int err = socket_error(); | ||||
| if ( err != EINTR && err != EAGAIN ) | |||||
| if (err != EINTR && err != EAGAIN) | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| static int socket_send (socket_t * sock, const void *buf, size_t len) | |||||
| { | |||||
| int length; | |||||
| static ssize_t socket_send (socket_t *sock, | |||||
| const void *buf, | |||||
| size_t len) { | |||||
| ssize_t length; | |||||
| while ( (length = send (*sock, buf, len, 0)) < 0 ) | |||||
| { | |||||
| while ((length = send(*sock, buf, len, 0)) < 0) { | |||||
| int err = socket_error(); | int err = socket_error(); | ||||
| if ( err != EINTR && err != EAGAIN ) | if ( err != EINTR && err != EAGAIN ) |
| static CRITICAL_SECTION * mutex_buf = 0; | static CRITICAL_SECTION * mutex_buf = 0; | ||||
| // OpenSSL callback to utilize static locks | // 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] ); | |||||
| static void cb_openssl_locking_function (int mode, | |||||
| int n, | |||||
| const char *file, | |||||
| int line) { | |||||
| if (mode & CRYPTO_LOCK) | |||||
| EnterCriticalSection(&mutex_buf[n]); | |||||
| else | else | ||||
| LeaveCriticalSection( &mutex_buf[n] ); | |||||
| LeaveCriticalSection(&mutex_buf[n]); | |||||
| } | } | ||||
| // OpenSSL callback to get the thread ID | // OpenSSL callback to get the thread ID | ||||
| static unsigned long cb_openssl_id_function(void) | |||||
| { | |||||
| return ((unsigned long) GetCurrentThreadId() ); | |||||
| static unsigned long cb_openssl_id_function (void) { | |||||
| return ((unsigned long) GetCurrentThreadId()); | |||||
| } | } | ||||
| static int alloc_mutexes( unsigned int total ) | |||||
| { | |||||
| static int alloc_mutexes (unsigned int total) { | |||||
| unsigned int i; | unsigned int i; | ||||
| // Enable thread safety in OpenSSL | // Enable thread safety in OpenSSL | ||||
| mutex_buf = (CRITICAL_SECTION*) malloc( total * sizeof(CRITICAL_SECTION) ); | |||||
| mutex_buf = (CRITICAL_SECTION *) malloc(total *sizeof(CRITICAL_SECTION)); | |||||
| if ( !mutex_buf ) | |||||
| if (!mutex_buf) | |||||
| return -1; | return -1; | ||||
| for ( i = 0; i < total; i++) | |||||
| InitializeCriticalSection( &(mutex_buf[i]) ); | |||||
| for (i = 0; i < total; i++) | |||||
| InitializeCriticalSection(&(mutex_buf[i])); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| static pthread_mutex_t * mutex_buf = 0; | static pthread_mutex_t * mutex_buf = 0; | ||||
| // OpenSSL callback to utilize static locks | // OpenSSL callback to utilize static locks | ||||
| static void cb_openssl_locking_function( int mode, int n, const char * file, int line ) | |||||
| { | |||||
| static void cb_openssl_locking_function (int mode, | |||||
| int n, | |||||
| const char *file, | |||||
| int line) { | |||||
| (void)file; | (void)file; | ||||
| (void)line; | (void)line; | ||||
| if ( mode & CRYPTO_LOCK) | |||||
| pthread_mutex_lock( &mutex_buf[n] ); | |||||
| if (mode & CRYPTO_LOCK) | |||||
| pthread_mutex_lock(&mutex_buf[n]); | |||||
| else | else | ||||
| pthread_mutex_unlock( &mutex_buf[n] ); | |||||
| pthread_mutex_unlock(&mutex_buf[n]); | |||||
| } | } | ||||
| // OpenSSL callback to get the thread ID | // OpenSSL callback to get the thread ID | ||||
| static unsigned long cb_openssl_id_function() | |||||
| { | |||||
| return ((unsigned long) pthread_self() ); | |||||
| static void cb_openssl_id_function(CRYPTO_THREADID * id) { | |||||
| CRYPTO_THREADID_set_pointer(id, pthread_self()); | |||||
| } | } | ||||
| static int alloc_mutexes( unsigned int total ) | |||||
| { | |||||
| static int alloc_mutexes(unsigned int total) { | |||||
| unsigned i; | unsigned i; | ||||
| // Enable thread safety in OpenSSL | // Enable thread safety in OpenSSL | ||||
| mutex_buf = (pthread_mutex_t*) malloc( total * sizeof(pthread_mutex_t) ); | |||||
| mutex_buf = (pthread_mutex_t *) malloc(total *sizeof(pthread_mutex_t)); | |||||
| if ( !mutex_buf ) | |||||
| if (!mutex_buf) | |||||
| return -1; | return -1; | ||||
| for ( i = 0; i < total; i++) | |||||
| pthread_mutex_init( &(mutex_buf[i]), 0 ); | |||||
| for (i = 0; i < total; i++) | |||||
| pthread_mutex_init(&(mutex_buf[i]), 0); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| #endif | #endif | ||||
| static int ssl_init_context( irc_session_t * session ) | |||||
| { | |||||
| static int ssl_init_context(irc_session_t *session) { | |||||
| // Load the strings and init the library | // Load the strings and init the library | ||||
| SSL_load_error_strings(); | SSL_load_error_strings(); | ||||
| // Enable thread safety in OpenSSL | // Enable thread safety in OpenSSL | ||||
| if ( alloc_mutexes( CRYPTO_num_locks() ) ) | |||||
| if (alloc_mutexes( CRYPTO_num_locks())) | |||||
| return LIBIRC_ERR_NOMEM; | return LIBIRC_ERR_NOMEM; | ||||
| // Register our callbacks | // Register our callbacks | ||||
| CRYPTO_set_id_callback( cb_openssl_id_function ); | |||||
| CRYPTO_set_locking_callback( cb_openssl_locking_function ); | |||||
| CRYPTO_THREADID_set_callback(cb_openssl_id_function); | |||||
| CRYPTO_set_locking_callback(cb_openssl_locking_function); | |||||
| // Init it | // Init it | ||||
| if ( !SSL_library_init() ) | |||||
| return LIBIRC_ERR_SSL_INIT_FAILED; | |||||
| #if OPENSSL_VERSION_NUMBER < 0x10100000L | |||||
| SSL_library_init(); | |||||
| #else | |||||
| OPENSSL_init_ssl(0, NULL); | |||||
| #endif | |||||
| if ( RAND_status() == 0 ) | |||||
| if (RAND_status() == 0) | |||||
| return LIBIRC_ERR_SSL_INIT_FAILED; | return LIBIRC_ERR_SSL_INIT_FAILED; | ||||
| // Create an SSL context; currently a single context is used for all connections | // Create an SSL context; currently a single context is used for all connections | ||||
| ssl_context = SSL_CTX_new( SSLv23_method() ); | |||||
| ssl_context = SSL_CTX_new(SSLv23_method()); | |||||
| if ( !ssl_context ) | |||||
| if (!ssl_context) | |||||
| return LIBIRC_ERR_SSL_INIT_FAILED; | return LIBIRC_ERR_SSL_INIT_FAILED; | ||||
| // Disable SSLv2 as it is unsecure | // Disable SSLv2 as it is unsecure | ||||
| if ( (SSL_CTX_set_options( ssl_context, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) == 0 ) | |||||
| if ((SSL_CTX_set_options(ssl_context, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) == 0) | |||||
| return LIBIRC_ERR_SSL_INIT_FAILED; | return LIBIRC_ERR_SSL_INIT_FAILED; | ||||
| // Enable only strong ciphers | // Enable only strong ciphers | ||||
| if ( SSL_CTX_set_cipher_list( ssl_context, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" ) != 1 ) | |||||
| if (SSL_CTX_set_cipher_list(ssl_context, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH") != 1) | |||||
| return LIBIRC_ERR_SSL_INIT_FAILED; | return LIBIRC_ERR_SSL_INIT_FAILED; | ||||
| // Set the verification | // Set the verification | ||||
| if ( session->options & LIBIRC_OPTION_SSL_NO_VERIFY ) | |||||
| SSL_CTX_set_verify( ssl_context, SSL_VERIFY_NONE, 0 ); | |||||
| if (session->options & LIBIRC_OPTION_SSL_NO_VERIFY) | |||||
| SSL_CTX_set_verify(ssl_context, SSL_VERIFY_NONE, 0); | |||||
| else | else | ||||
| SSL_CTX_set_verify( ssl_context, SSL_VERIFY_PEER, 0 ); | |||||
| SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, 0); | |||||
| // Disable session caching | // Disable session caching | ||||
| SSL_CTX_set_session_cache_mode( ssl_context, SSL_SESS_CACHE_OFF ); | |||||
| 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 | // 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 ); | |||||
| 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; | return 0; | ||||
| } | } | ||||
| #endif | #endif | ||||
| // Initializes the SSL context. Must be called after the socket is created. | // Initializes the SSL context. Must be called after the socket is created. | ||||
| static int ssl_init( irc_session_t * session ) | |||||
| { | |||||
| static int ssl_init (irc_session_t *session { | |||||
| static int ssl_context_initialized = 0; | static int ssl_context_initialized = 0; | ||||
| #if defined (_WIN32) | #if defined (_WIN32) | ||||
| static HANDLE initmutex = 0; | static HANDLE initmutex = 0; | ||||
| // First time run? Create the mutex | // First time run? Create the mutex | ||||
| if ( initmutex == 0 ) | |||||
| { | |||||
| HANDLE m = CreateMutex( 0, FALSE, 0 ); | |||||
| 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. | // 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 | // If it was, we close our mutex and use the original one. This could be done synchronously by using the | ||||
| // InterlockedCompareExchangePointer function. | // InterlockedCompareExchangePointer function. | ||||
| if ( InterlockedCompareExchangePointer( &m, m, 0 ) != 0 ) | |||||
| CloseHandle( m ); | |||||
| if (InterlockedCompareExchangePointer(&m, m, 0) != 0) | |||||
| CloseHandle(m); | |||||
| } | } | ||||
| #else | #else | ||||
| static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER; | static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER; | ||||
| // irc_connect() and this function may be called simultaneously from different threads. So we have | // 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 | // to use mutex on Linux because it allows static mutex initialization. Windows doesn't, so here | ||||
| // we do the sabre dance around it. | // we do the sabre dance around it. | ||||
| SSLINIT_LOCK_MUTEX( initmutex ); | |||||
| SSLINIT_LOCK_MUTEX(initmutex); | |||||
| if ( ssl_context_initialized == 0 ) | |||||
| { | |||||
| int res = ssl_init_context( session ); | |||||
| if (ssl_context_initialized == 0) { | |||||
| int res = ssl_init_context(session); | |||||
| if ( res ) | |||||
| { | |||||
| SSLINIT_UNLOCK_MUTEX( initmutex ); | |||||
| if (res) { | |||||
| SSLINIT_UNLOCK_MUTEX(initmutex); | |||||
| return res; | return res; | ||||
| } | } | ||||
| ssl_context_initialized = 1; | ssl_context_initialized = 1; | ||||
| } | } | ||||
| SSLINIT_UNLOCK_MUTEX( initmutex ); | |||||
| SSLINIT_UNLOCK_MUTEX(initmutex); | |||||
| // Get the SSL context | // Get the SSL context | ||||
| session->ssl = SSL_new( ssl_context ); | |||||
| session->ssl = SSL_new(ssl_context); | |||||
| if ( !session->ssl ) | |||||
| if (!session->ssl) | |||||
| return LIBIRC_ERR_SSL_INIT_FAILED; | return LIBIRC_ERR_SSL_INIT_FAILED; | ||||
| // Let OpenSSL use our socket | // Let OpenSSL use our socket | ||||
| if ( SSL_set_fd( session->ssl, session->sock) != 1 ) | |||||
| if (SSL_set_fd( session->ssl, session->sock) != 1) | |||||
| return LIBIRC_ERR_SSL_INIT_FAILED; | return LIBIRC_ERR_SSL_INIT_FAILED; | ||||
| // Since we're connecting on our own, tell openssl about it | // Since we're connecting on our own, tell openssl about it | ||||
| SSL_set_connect_state( session->ssl ); | |||||
| SSL_set_connect_state(session->ssl); | |||||
| return 0; | 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 ) | |||||
| { | |||||
| 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; | session->lasterror = LIBIRC_ERR_SSL_CERT_VERIFY_FAILED; | ||||
| return; | return; | ||||
| } | } | ||||
| if ( ERR_GET_REASON(ssl_error) == SSL_R_UNKNOWN_PROTOCOL ) | |||||
| { | |||||
| if (ERR_GET_REASON(ssl_error) == SSL_R_UNKNOWN_PROTOCOL) { | |||||
| session->lasterror = LIBIRC_ERR_CONNECT_SSL_FAILED; | session->lasterror = LIBIRC_ERR_CONNECT_SSL_FAILED; | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| #if defined (ENABLE_DEBUG) | #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) ); | |||||
| 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 | #endif | ||||
| } | } | ||||
| static int ssl_recv( irc_session_t * session ) | |||||
| { | |||||
| static int ssl_recv (irc_session_t *session) { | |||||
| int count; | int count; | ||||
| unsigned int amount = (sizeof (session->incoming_buf) - 1) - session->incoming_offset; | unsigned int amount = (sizeof (session->incoming_buf) - 1) - session->incoming_offset; | ||||
| ERR_clear_error(); | ERR_clear_error(); | ||||
| // Read up to m_bufferLength bytes | // Read up to m_bufferLength bytes | ||||
| count = SSL_read( session->ssl, session->incoming_buf + session->incoming_offset, amount ); | |||||
| count = SSL_read(session->ssl, | |||||
| session->incoming_buf + session->incoming_offset, | |||||
| amount); | |||||
| if ( count > 0 ) | |||||
| if (count > 0) { | |||||
| return count; | return count; | ||||
| else if ( count == 0 ) | |||||
| } else if (count == 0) { | |||||
| return -1; // remote connection closed | return -1; // remote connection closed | ||||
| else | |||||
| { | |||||
| int ssl_error = SSL_get_error( session->ssl, count ); | |||||
| } else { | |||||
| int ssl_error = SSL_get_error(session->ssl, count); | |||||
| // Handle SSL error since not all of them are actually errors | // Handle SSL error since not all of them are actually errors | ||||
| switch ( ssl_error ) | |||||
| { | |||||
| switch (ssl_error) { | |||||
| case SSL_ERROR_WANT_READ: | case SSL_ERROR_WANT_READ: | ||||
| // This is not really an error. We received something, but | // This is not really an error. We received something, but | ||||
| // OpenSSL gave nothing to us because all it read was | // OpenSSL gave nothing to us because all it read was | ||||
| } | } | ||||
| // This is an SSL error, handle it | // This is an SSL error, handle it | ||||
| ssl_handle_error( session, ERR_get_error() ); | |||||
| ssl_handle_error(session, ERR_get_error()); | |||||
| } | } | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| static int ssl_send( irc_session_t * session ) | |||||
| { | |||||
| static int ssl_send (irc_session_t *session) { | |||||
| int count; | int count; | ||||
| ERR_clear_error(); | ERR_clear_error(); | ||||
| count = SSL_write( session->ssl, session->outgoing_buf, session->outgoing_offset ); | |||||
| count = SSL_write(session->ssl, | |||||
| session->outgoing_buf, | |||||
| session->outgoing_offset); | |||||
| if ( count > 0 ) | |||||
| if (count > 0) { | |||||
| return count; | return count; | ||||
| else if ( count == 0 ) | |||||
| } else if (count == 0) { | |||||
| return -1; | return -1; | ||||
| else | |||||
| { | |||||
| int ssl_error = SSL_get_error( session->ssl, count ); | |||||
| } else { | |||||
| int ssl_error = SSL_get_error(session->ssl, count); | |||||
| switch ( ssl_error ) | |||||
| { | |||||
| switch (ssl_error) { | |||||
| case SSL_ERROR_WANT_READ: | case SSL_ERROR_WANT_READ: | ||||
| // This is not really an error. We sent some internal OpenSSL data, | // 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. | // but now it needs to read more data before it can send anything. | ||||
| } | } | ||||
| // This is an SSL error, handle it | // This is an SSL error, handle it | ||||
| ssl_handle_error( session, ERR_get_error() ); | |||||
| ssl_handle_error(session, ERR_get_error()); | |||||
| } | } | ||||
| return -1; | return -1; | ||||
| // Returns -1 in case there is an error and socket should be closed/connection terminated | // 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 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 | // Returns a positive number if we actually read something | ||||
| static int session_socket_read( irc_session_t * session ) | |||||
| { | |||||
| int length; | |||||
| static ssize_t session_socket_read (irc_session_t *session) { | |||||
| ssize_t length; | |||||
| #if defined (ENABLE_SSL) | #if defined (ENABLE_SSL) | ||||
| if ( session->ssl ) | |||||
| { | |||||
| if (session->ssl) { | |||||
| // Yes, I know this is tricky | // Yes, I know this is tricky | ||||
| if ( session->flags & SESSIONFL_SSL_READ_WANTS_WRITE ) | |||||
| { | |||||
| if (session->flags & SESSIONFL_SSL_READ_WANTS_WRITE) { | |||||
| session->flags &= ~SESSIONFL_SSL_READ_WANTS_WRITE; | session->flags &= ~SESSIONFL_SSL_READ_WANTS_WRITE; | ||||
| ssl_send( session ); | |||||
| ssl_send(session); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| return ssl_recv( session ); | |||||
| return ssl_recv(session); | |||||
| } | } | ||||
| #endif | #endif | ||||
| length = socket_recv( &session->sock, | |||||
| session->incoming_buf + session->incoming_offset, | |||||
| (sizeof (session->incoming_buf) - 1) - session->incoming_offset ); | |||||
| 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 | // There is no "retry" errors for regular sockets | ||||
| if ( length <= 0 ) | |||||
| if (length <= 0) | |||||
| return -1; | return -1; | ||||
| return length; | return length; | ||||
| // Returns -1 in case there is an error and socket should be closed/connection terminated | // 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 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 | // Returns a positive number if we actually sent something | ||||
| static int session_socket_write( irc_session_t * session ) | |||||
| { | |||||
| int length; | |||||
| static ssize_t session_socket_write (irc_session_t *session) { | |||||
| ssize_t length; | |||||
| #if defined (ENABLE_SSL) | #if defined (ENABLE_SSL) | ||||
| if ( session->ssl ) | |||||
| { | |||||
| if (session->ssl) { | |||||
| // Yep | // Yep | ||||
| if ( session->flags & SESSIONFL_SSL_WRITE_WANTS_READ ) | |||||
| { | |||||
| if (session->flags & SESSIONFL_SSL_WRITE_WANTS_READ) { | |||||
| session->flags &= ~SESSIONFL_SSL_WRITE_WANTS_READ; | session->flags &= ~SESSIONFL_SSL_WRITE_WANTS_READ; | ||||
| ssl_recv( session ); | |||||
| ssl_recv(session); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| return ssl_send( session ); | |||||
| return ssl_send(session); | |||||
| } | } | ||||
| #endif | #endif | ||||
| length = socket_send (&session->sock, session->outgoing_buf, session->outgoing_offset); | |||||
| length = socket_send (&session->sock, | |||||
| session->outgoing_buf, | |||||
| session->outgoing_offset); | |||||
| // There is no "retry" errors for regular sockets | // There is no "retry" errors for regular sockets | ||||
| if ( length <= 0 ) | |||||
| if (length <= 0) | |||||
| return -1; | return -1; | ||||
| return length; | return length; |
| * License for more details. | * License for more details. | ||||
| */ | */ | ||||
| static void libirc_add_to_set (int fd, fd_set *set, int * maxfd) | |||||
| { | |||||
| static void libirc_add_to_set (int fd, | |||||
| fd_set *set, | |||||
| int *maxfd) { | |||||
| FD_SET (fd, set); | FD_SET (fd, set); | ||||
| if ( *maxfd < fd ) | |||||
| if (*maxfd < fd) | |||||
| *maxfd = fd; | *maxfd = fd; | ||||
| } | } | ||||
| #if defined (ENABLE_DEBUG) | #if defined (ENABLE_DEBUG) | ||||
| static void libirc_dump_data (const char * prefix, const char * buf, unsigned int length) | |||||
| { | |||||
| static void libirc_dump_data (const char *prefix, | |||||
| const char *buf, | |||||
| unsigned int length) { | |||||
| printf ("%s: ", prefix); | printf ("%s: ", prefix); | ||||
| for ( ; length > 0; length -- ) | |||||
| for (; length > 0; length--) | |||||
| printf ("%c", *buf++); | printf ("%c", *buf++); | ||||
| } | } | ||||
| #endif | #endif | ||||
| /* | /* | ||||
| * Finds a separator (\x0D\x0A), which separates two lines. | * 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 ) | |||||
| static size_t libirc_findcrlf (const char *buf, | |||||
| size_t length) { | |||||
| size_t offset = 0; | |||||
| for (; offset < length; offset++) { | |||||
| if ( buf[offset] == 0x0D | |||||
| && offset < length - 1 | |||||
| && buf[offset+1] == 0x0A) | |||||
| return offset; | return offset; | ||||
| if ( buf[offset] == 0x0A) | |||||
| if (buf[offset] == 0x0A) | |||||
| return offset; | return offset; | ||||
| } | } | ||||
| return 0; | 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) | |||||
| { | |||||
| static size_t libirc_findcrlf_offset (const char *buf, | |||||
| size_t offset, | |||||
| const size_t length) { | |||||
| for(; offset < length; offset++) { | |||||
| if ( buf[offset] != 0x0D | |||||
| && buf[offset] != 0x0A) { | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return offset; | return offset; | ||||
| } | } | ||||
| static int libirc_findcrorlf (char * buf, int length) | |||||
| { | |||||
| int offset = 0; | |||||
| for ( ; offset < length; offset++ ) | |||||
| { | |||||
| if ( buf[offset] == 0x0D || buf[offset] == 0x0A ) | |||||
| { | |||||
| static size_t libirc_findcrorlf (char *buf, | |||||
| size_t length) { | |||||
| size_t offset = 0; | |||||
| for (; offset < length; offset++) { | |||||
| if ( buf[offset] == 0x0D | |||||
| || buf[offset] == 0x0A) { | |||||
| buf[offset++] = '\0'; | buf[offset++] = '\0'; | ||||
| if ( offset < (length - 1) | |||||
| && (buf[offset] == 0x0D || buf[offset] == 0x0A) ) | |||||
| if ( offset < (length - 1) | |||||
| && ( buf[offset] == 0x0D | |||||
| || buf[offset] == 0x0A) | |||||
| ) | |||||
| offset++; | offset++; | ||||
| return offset; | return offset; | ||||
| } | } | ||||
| 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; | |||||
| 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 ) | |||||
| { | |||||
| if (origin) { | |||||
| char nickbuf[128], textbuf[256]; | char nickbuf[128], textbuf[256]; | ||||
| irc_target_get_nick (origin, nickbuf, sizeof(nickbuf)); | irc_target_get_nick (origin, nickbuf, sizeof(nickbuf)); | ||||
| if ( strstr (params[0], "PING") == params[0] ) | |||||
| if (strstr (params[0], "PING") == params[0]) { | |||||
| irc_cmd_ctcp_reply (session, nickbuf, params[0]); | irc_cmd_ctcp_reply (session, nickbuf, params[0]); | ||||
| else if ( !strcmp (params[0], "VERSION") ) | |||||
| { | |||||
| if ( !session->ctcp_version ) | |||||
| { | |||||
| } else if (!strcmp (params[0], "VERSION")) { | |||||
| if (!session->ctcp_version) { | |||||
| unsigned int high, low; | unsigned int high, low; | ||||
| irc_get_version (&high, &low); | irc_get_version (&high, &low); | ||||
| snprintf (textbuf, sizeof (textbuf), "VERSION libircclient by Georgy Yunaev ver.%d.%d", high, low); | snprintf (textbuf, sizeof (textbuf), "VERSION libircclient by Georgy Yunaev ver.%d.%d", high, low); | ||||
| } | |||||
| else | |||||
| } else { | |||||
| snprintf (textbuf, sizeof (textbuf), "VERSION %s", session->ctcp_version); | snprintf (textbuf, sizeof (textbuf), "VERSION %s", session->ctcp_version); | ||||
| } | |||||
| irc_cmd_ctcp_reply (session, nickbuf, textbuf); | irc_cmd_ctcp_reply (session, nickbuf, textbuf); | ||||
| } | |||||
| else if ( !strcmp (params[0], "FINGER") ) | |||||
| { | |||||
| } else if (!strcmp (params[0], "FINGER")) { | |||||
| sprintf (textbuf, "FINGER %s (%s) Idle 0 seconds", | sprintf (textbuf, "FINGER %s (%s) Idle 0 seconds", | ||||
| session->username ? session->username : "nobody", | session->username ? session->username : "nobody", | ||||
| session->realname ? session->realname : "noname"); | session->realname ? session->realname : "noname"); | ||||
| irc_cmd_ctcp_reply (session, nickbuf, textbuf); | irc_cmd_ctcp_reply (session, nickbuf, textbuf); | ||||
| } | |||||
| else if ( !strcmp (params[0], "TIME") ) | |||||
| { | |||||
| } else if (!strcmp (params[0], "TIME")) { | |||||
| time_t now = time(0); | time_t now = time(0); | ||||
| #if defined (ENABLE_THREADS) && defined (HAVE_LOCALTIME_R) | #if defined (ENABLE_THREADS) && defined (HAVE_LOCALTIME_R) | ||||
| struct tm tmtmp, *ltime = localtime_r (&now, &tmtmp); | struct tm tmtmp, *ltime = localtime_r (&now, &tmtmp); | ||||
| #else | #else | ||||
| struct tm * ltime = localtime (&now); | |||||
| struct tm *ltime = localtime (&now); | |||||
| #endif | #endif | ||||
| strftime (textbuf, sizeof(textbuf), "%a %b %d %H:%M:%S %Z %Y", ltime); | strftime (textbuf, sizeof(textbuf), "%a %b %d %H:%M:%S %Z %Y", ltime); | ||||
| irc_cmd_ctcp_reply (session, nickbuf, textbuf); | irc_cmd_ctcp_reply (session, nickbuf, textbuf); |