Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+388
View File
@@ -0,0 +1,388 @@
/*
* 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 = (char*)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 = (char*)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);
}
+892
View File
@@ -0,0 +1,892 @@
/*
* 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 = (irc_dcc_session_t*)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;
}
+54
View File
@@ -0,0 +1,54 @@
/*
* 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 */
+54
View File
@@ -0,0 +1,54 @@
/*
* 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";
}
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
/*
* 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 7
#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 */
+157
View File
@@ -0,0 +1,157 @@
/*
* Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#if !defined (_WIN32)
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdlib.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
#define EAGAIN EWOULDBLOCK
#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
+78
View File
@@ -0,0 +1,78 @@
/*
* 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;
#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 */
+150
View File
@@ -0,0 +1,150 @@
/*
* 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)
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS
#define EINTR WSAEINTR
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;
}
+387
View File
@@ -0,0 +1,387 @@
/*
* 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()
{
return ((unsigned long) GetCurrentThreadId() );
}
static int alloc_mutexes( unsigned int total )
{
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 )
{
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 )
{
int 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;
}
+121
View File
@@ -0,0 +1,121 @@
/*
* 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)
{
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") )
{
unsigned int high, low;
irc_get_version (&high, &low);
sprintf (textbuf, "VERSION libirc by Georgy Yunaev ver.%d.%d", high, low);
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);
}
}
}