| *.hmap | *.hmap | ||||
| *.ipa | *.ipa | ||||
| libirc* | |||||
| .DS_Store | |||||
| *~ | *~ |
| Subproject commit 04f57f59ef78d2605d4835eda60a0a54c88ab088 |
| // | |||||
| // NSData+SA_NSDataExtensions.h | |||||
| // | |||||
| // Copyright (c) 2015 Said Achmiz. | |||||
| // | |||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| // of this software and associated documentation files (the "Software"), to deal | |||||
| // in the Software without restriction, including without limitation the rights | |||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| // copies of the Software, and to permit persons to whom the Software is | |||||
| // furnished to do so, subject to the following conditions: | |||||
| // | |||||
| // The above copyright notice and this permission notice shall be included in all | |||||
| // copies or substantial portions of the Software. | |||||
| // | |||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| // SOFTWARE. | |||||
| #import <Foundation/Foundation.h> | |||||
| /** \category NSData+SA_NSDataExtensions | |||||
| * @brief Adds several utility methods to NSData. | |||||
| */ | |||||
| @interface NSData (SA_NSDataExtensions) | |||||
| // NOTE on stripping nulls from the ends of byte arrays. | |||||
| // | |||||
| // If you strip a null from the end of an array which is something other than | |||||
| // a null-terminated C string (such as, for example, the bytes representing a | |||||
| // UTF-16 string), and thereby cause yourself difficulties, you have only | |||||
| // yourself to blame. Be sure that you know what your NSData objects are | |||||
| // supposed to contain! | |||||
| /** Returns YES if the last byte of the stored data is null, NO otherwise. | |||||
| */ | |||||
| @property (readonly, getter=isNullTerminated) BOOL nullTerminated; | |||||
| /** Returns the stored bytes as a null-terminated C string (byte array). | |||||
| If the stored data is already null-terminated, the returned pointer will | |||||
| be a pointer to the bytes managed by the receiver. If it is not already | |||||
| null-terminated, the returned pointer will point to bytes managed by a | |||||
| copy of the receiver (and the bytes of the copy will be null-terminated). | |||||
| */ | |||||
| @property (readonly) const char *SA_terminatedCString; | |||||
| /** Returns data containing the stored bytes as a null-terminated C string | |||||
| (byte array). | |||||
| If the stored data is already null-terminated, this method simply returns | |||||
| the receiver. If it is not already null-terminated, this method returns a | |||||
| reference to a fresh copy of the receiver (a copy that contains a | |||||
| null-terminated byte array, of course). | |||||
| */ | |||||
| @property (readonly) NSData *SA_dataWithTerminatedCString; | |||||
| /** Returns the stored bytes as an non-null-terminated byte array. | |||||
| If the stored data was not null-terminated to begin with, the returned | |||||
| pointer will be a pointer to the bytes managed by the receiver. If the | |||||
| stored data was null-terminated, the returned pointer will point to bytes | |||||
| managed by a copy of the receiver (and the bytes of the copy will not be | |||||
| null-terminated; but see NOTE). | |||||
| NOTE: If the receiver's *last* byte is null, the bytes pointed to by the | |||||
| returned pointer will have that null stripped; but if there are any more | |||||
| null bytes prior to that last null, they will remain untouched! | |||||
| */ | |||||
| @property (readonly) const char *SA_unterminatedByteString; | |||||
| /** Returns data containing the stored bytes as a non-null-terminated byte | |||||
| array. | |||||
| If the stored data was not null-terminated to begin with, this method simply | |||||
| returns the receiver. If the stored data was null-terminated, this method | |||||
| returns a reference to a fresh copy of the receiver (a copy that contains a | |||||
| non-null-terminated byte array, of course; but see NOTE). | |||||
| NOTE: If the receiver's *last* byte is null, the bytes managed by the | |||||
| returned object will have that null stripped; but if there are any more | |||||
| null bytes prior to that last null, they will remain untouched! | |||||
| */ | |||||
| @property (readonly) NSData *SA_dataWithUnterminatedByteString; | |||||
| /** Returns an NSData object containing a blank C string (i.e. a byte sequence | |||||
| of length 1, containing the null character '\0'). | |||||
| */ | |||||
| +(NSData *)dataWithBlankCString; | |||||
| @end |
| // | |||||
| // NSData+SA_NSDataExtensions.m | |||||
| // | |||||
| // Copyright (c) 2015 Said Achmiz. | |||||
| // | |||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| // of this software and associated documentation files (the "Software"), to deal | |||||
| // in the Software without restriction, including without limitation the rights | |||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| // copies of the Software, and to permit persons to whom the Software is | |||||
| // furnished to do so, subject to the following conditions: | |||||
| // | |||||
| // The above copyright notice and this permission notice shall be included in all | |||||
| // copies or substantial portions of the Software. | |||||
| // | |||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| // SOFTWARE. | |||||
| #import "NSData+SA_NSDataExtensions.h" | |||||
| @implementation NSData (SA_NSDataExtensions) | |||||
| -(BOOL)isNullTerminated | |||||
| { | |||||
| return (((char*) self.bytes)[self.length - 1] == '\0'); | |||||
| } | |||||
| -(const char *)SA_terminatedCString | |||||
| { | |||||
| return self.SA_dataWithTerminatedCString.bytes; | |||||
| } | |||||
| -(NSData *)SA_dataWithTerminatedCString | |||||
| { | |||||
| if(self.length == 0) | |||||
| { | |||||
| return [NSData dataWithBytes:"\0" length:1]; | |||||
| } | |||||
| else if(self.isNullTerminated) | |||||
| { | |||||
| return self; | |||||
| } | |||||
| else | |||||
| { | |||||
| char* terminated_string_buffer = malloc(self.length + 1); | |||||
| [self getBytes:terminated_string_buffer length:self.length]; | |||||
| terminated_string_buffer[self.length] = '\0'; | |||||
| return [NSData dataWithBytesNoCopy:terminated_string_buffer length:(self.length + 1) freeWhenDone:YES]; | |||||
| } | |||||
| } | |||||
| -(const char *)SA_unterminatedByteString | |||||
| { | |||||
| return self.SA_dataWithUnterminatedByteString.bytes; | |||||
| } | |||||
| -(NSData *)SA_dataWithUnterminatedByteString | |||||
| { | |||||
| if(self.length == 0 || self.isNullTerminated == NO) | |||||
| { | |||||
| return self; | |||||
| } | |||||
| else | |||||
| { | |||||
| char* unterminated_string_buffer = malloc(self.length - 1); | |||||
| [self getBytes:unterminated_string_buffer length:self.length - 1]; | |||||
| return [NSData dataWithBytesNoCopy:unterminated_string_buffer length:(self.length - 1) freeWhenDone:YES]; | |||||
| } | |||||
| } | |||||
| +(NSData *)dataWithBlankCString | |||||
| { | |||||
| return [NSData dataWithBytes:"\0" length:1]; | |||||
| } | |||||
| @end |
| NSData+SA_NSDataExtensions | |||||
| Adds utility functions to NSData, that help deal with null termination of C strings. | |||||
| This category on NSData adds properties that allow you to get the null-terminated or non-null-terminated versions of byte arrays stored as NSData objects, and to easily check whether an NSData's byte array is, or is not, null-terminated (that is, whether its last byte is a null). | |||||
| Copyright (c) 2015 Said Achmiz. |
| /* src/config.h. Generated from config.h.in by configure. */ | |||||
| /* include/config.h.in. Generated from configure.in by autoheader. */ | |||||
| /* Define to 1 if you have the `getaddrinfo' function. */ | |||||
| /* #undef HAVE_GETADDRINFO */ | |||||
| /* Define to 1 if you have the `gethostbyname_r' function. */ | |||||
| /* #undef HAVE_GETHOSTBYNAME_R */ | |||||
| /* Define to 1 if you have the `inet_ntoa' function. */ | |||||
| /* #undef HAVE_INET_NTOA */ | |||||
| /* Define to 1 if you have the `inet_pton' function. */ | |||||
| /* #undef HAVE_INET_PTON */ | |||||
| /* Define to 1 if you have the <inttypes.h> header file. */ | |||||
| /* #undef HAVE_INTTYPES_H */ | |||||
| /* Define to 1 if you have the `localtime_r' function. */ | |||||
| /* #undef HAVE_LOCALTIME_R */ | |||||
| /* Define to 1 if your system has a GNU libc compatible `malloc' function, and | |||||
| to 0 otherwise. */ | |||||
| #define HAVE_MALLOC 0 | |||||
| /* Define to 1 if you have the <memory.h> header file. */ | |||||
| /* #undef HAVE_MEMORY_H */ | |||||
| /* Define to 1 if you have the `socket' function. */ | |||||
| /* #undef HAVE_SOCKET */ | |||||
| /* Define to 1 if `stat' has the bug that it succeeds when given the | |||||
| zero-length file name argument. */ | |||||
| #define HAVE_STAT_EMPTY_STRING_BUG 1 | |||||
| /* Define to 1 if stdbool.h conforms to C99. */ | |||||
| #define HAVE_STDBOOL_H 1 | |||||
| /* Define to 1 if you have the <stdint.h> header file. */ | |||||
| /* #undef HAVE_STDINT_H */ | |||||
| /* Define to 1 if you have the <stdlib.h> header file. */ | |||||
| /* #undef HAVE_STDLIB_H */ | |||||
| /* Define to 1 if you have the <strings.h> header file. */ | |||||
| /* #undef HAVE_STRINGS_H */ | |||||
| /* Define to 1 if you have the <string.h> header file. */ | |||||
| /* #undef HAVE_STRING_H */ | |||||
| /* Define to 1 if you have the <sys/select.h> header file. */ | |||||
| /* #undef HAVE_SYS_SELECT_H */ | |||||
| /* Define to 1 if you have the <sys/socket.h> header file. */ | |||||
| /* #undef HAVE_SYS_SOCKET_H */ | |||||
| /* Define to 1 if you have the <sys/stat.h> header file. */ | |||||
| /* #undef HAVE_SYS_STAT_H */ | |||||
| /* Define to 1 if you have the <sys/types.h> header file. */ | |||||
| /* #undef HAVE_SYS_TYPES_H */ | |||||
| /* Define to 1 if you have the <unistd.h> header file. */ | |||||
| /* #undef HAVE_UNISTD_H */ | |||||
| /* Define to 1 if the system has the type `_Bool'. */ | |||||
| /* #undef HAVE__BOOL */ | |||||
| /* Define to 1 if `lstat' dereferences a symlink specified with a trailing | |||||
| slash. */ | |||||
| /* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ | |||||
| /* Define to the address where bug reports for this package should be sent. */ | |||||
| #define PACKAGE_BUGREPORT "gyunaev@ulduzsoft.com" | |||||
| /* Define to the full name of this package. */ | |||||
| #define PACKAGE_NAME "libircclient" | |||||
| /* Define to the full name and version of this package. */ | |||||
| #define PACKAGE_STRING "libircclient 1.8" | |||||
| /* Define to the one symbol short name of this package. */ | |||||
| #define PACKAGE_TARNAME "libircclient" | |||||
| /* Define to the version of this package. */ | |||||
| #define PACKAGE_VERSION "1.8" | |||||
| /* Define to the type of arg 1 for `select'. */ | |||||
| #define SELECT_TYPE_ARG1 int | |||||
| /* Define to the type of args 2, 3 and 4 for `select'. */ | |||||
| #define SELECT_TYPE_ARG234 (int *) | |||||
| /* Define to the type of arg 5 for `select'. */ | |||||
| #define SELECT_TYPE_ARG5 (struct timeval *) | |||||
| /* Define to 1 if you have the ANSI C header files. */ | |||||
| /* #undef STDC_HEADERS */ | |||||
| /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ | |||||
| /* #undef TIME_WITH_SYS_TIME */ | |||||
| /* Define to empty if `const' does not conform to ANSI C. */ | |||||
| /* #undef const */ | |||||
| /* Define to rpl_malloc if the replacement function should be used. */ | |||||
| /* #undef malloc */ | |||||
| /* Define to `unsigned int' if <sys/types.h> does not define. */ | |||||
| /* #undef size_t */ |
| /* | |||||
| * 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 */ |
| /* | |||||
| * 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 */ |
| /* | |||||
| * 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 "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 "privmsg" event is triggered upon receipt of a PRIVMSG message | |||||
| * which is addressed to no one in particular, but it sent to the client | |||||
| * anyway. | |||||
| * | |||||
| * \param origin the person, who generates the message. | |||||
| * \param params optional, contains who knows what. | |||||
| */ | |||||
| irc_event_callback_t event_server_msg; | |||||
| /*! | |||||
| * 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 "server_notice" event is triggered upon receipt of a NOTICE | |||||
| * message which means that the server has sent the client a notice. | |||||
| * This notice is not necessarily addressed to the client's nick | |||||
| * (for example, AUTH notices, sent before the client's nick is known). | |||||
| * 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 optional, contains who knows what. | |||||
| */ | |||||
| irc_event_callback_t event_server_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 */ |
| /* | |||||
| * 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 */ |
| /* | |||||
| * 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 */ |
| /* | |||||
| * 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 */ |
| /* | |||||
| * 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); | |||||
| } |
| /* | |||||
| * 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); | |||||
| int nsock, err = 0; | |||||
| // 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; | |||||
| } |
| /* | |||||
| * 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"; | |||||
| } | |||||
| /* | |||||
| * 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 "config.h" | |||||
| #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 |
| /* | |||||
| * 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; | |||||
| } |
| /* | |||||
| * 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; | |||||
| // 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; | |||||
| // 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; | |||||
| } |
| /* | |||||
| * 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); | |||||
| } | |||||
| } | |||||
| } |