/*
* Cookie Caching stuff. Adam wrote this, apparently just some
* derivatives of n's SNAC work. I cleaned it up, added comments.
*
*/
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ /********************************************************************\
* BitlBee -- An IRC to other IM-networks gateway *
* *
* Copyright 2002-2004 Wilmer van der Gaast and others *
\********************************************************************/
/* Main file */
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License with
the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
if not, write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#define BITLBEE_CORE
#include "bitlbee.h"
#include "commands.h"
#include "protocols/nogaim.h"
#include "help.h"
#include "ipc.h"
#include <signal.h>
#include <stdio.h>
#include <errno.h>
static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition condition );
int bitlbee_daemon_init()
{
struct addrinfo *res, hints, *addrinfo_bind;
int i;
FILE *fp;
log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG );
log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG );
memset( &hints, 0, sizeof( hints ) );
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE
#ifdef AI_ADDRCONFIG
| AI_ADDRCONFIG
#endif
;
i = getaddrinfo( global.conf->iface_in, global.conf->port, &hints, &addrinfo_bind );
if( i )
{
log_message( LOGLVL_ERROR, "Couldn't parse address `%s': %s",
global.conf->iface_in, gai_strerror(i) );
return -1;
}
global.listen_socket = -1;
for( res = addrinfo_bind; res; res = res->ai_next )
{
global.listen_socket = socket( res->ai_family, res->ai_socktype, res->ai_protocol );
if( global.listen_socket < 0 )
continue;
/* TIME_WAIT (?) sucks.. */
i = 1;
setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
i = bind( global.listen_socket, res->ai_addr, res->ai_addrlen );
if( i == -1 )
{
log_error( "bind" );
return( -1 );
}
break;
}
freeaddrinfo( addrinfo_bind );
i = listen( global.listen_socket, 10 );
if( i == -1 )
{
log_error( "listen" );
return( -1 );
}
global.listen_watch_source_id = b_input_add( global.listen_socket, B_EV_IO_READ, bitlbee_io_new_client, NULL );
#ifndef _WIN32
if( !global.conf->nofork )
{
i = fork();
if( i == -1 )
{
log_error( "fork" );
return( -1 );
}
else if( i != 0 )
exit( 0 );
chdir( "/" );
/* Sometimes std* are already closed (for example when we're in a RESTARTed
BitlBee process. So let's only close TTY-fds. */
if( isatty( 0 ) ) close( 0 );
if( isatty( 1 ) ) close( 1 );
if( isatty( 2 ) ) close( 2 );
}
#endif
if( global.conf->runmode == RUNMODE_FORKDAEMON )
ipc_master_load_state( getenv( "_BITLBEE_RESTART_STATE" ) );
if( global.conf->runmode == RUNMODE_DAEMON || global.conf->runmode == RUNMODE_FORKDAEMON )
ipc_master_listen_socket();
#ifndef _WIN32
if( ( fp = fopen( global.conf->pidfile, "w" ) ) )
{
fprintf( fp, "%d\n", (int) getpid() );
fclose( fp );
}
else
{
log_message( LOGLVL_WARNING, "Warning: Couldn't write PID to `%s'", global.conf->pidfile );
}
#endif
return( 0 );
}
int bitlbee_inetd_init()
{
if( !irc_new( 0 ) )
return( 1 );
return( 0 );
}
gboolean bitlbee_io_current_client_read( gpointer data, gint fd, b_input_condition cond )
{
irc_t *irc = data;
char line[513];
int st;
st = read( irc->fd, line, sizeof( line ) - 1 );
if( st == 0 )
{
irc_abort( irc, 1, "Connection reset by peer" );
return FALSE;
}
else if( st < 0 )
{
if( sockerr_again() )
{
return TRUE;
}
else
{
irc_abort( irc, 1, "Read error: %s", strerror( errno ) );
return FALSE;
}
}
line[st] = '\0';
if( irc->readbuffer == NULL )
{
irc->readbuffer = g_strdup( line );
}
else
{
irc->readbuffer = g_renew( char, irc->readbuffer, strlen( irc->readbuffer ) + strlen ( line ) + 1 );
strcpy( ( irc->readbuffer + strlen( irc->readbuffer ) ), line );
}
irc_process( irc );
/* Normally, irc_process() shouldn't call irc_free() but irc_abort(). Just in case: */
if( !g_slist_find( irc_connection_list, irc ) )
{
log_message( LOGLVL_WARNING, "Abnormal termination of connection with fd %d.", fd );
return FALSE;
}
/* Very naughty, go read the RFCs! >:) */
if( irc->readbuffer && ( strlen( irc->readbuffer ) > 1024 ) )
{
irc_abort( irc, 0, "Maximum line length exceeded" );
return FALSE;
}
return TRUE;
}
gboolean bitlbee_io_current_client_write( gpointer data, gint fd, b_input_condition cond )
{
irc_t *irc = data;
int st, size;
char *temp;
if( irc->sendbuffer == NULL )
return FALSE;
size = strlen( irc->sendbuffer );
st = write( irc->fd, irc->sendbuffer, size );
if( st == 0 || ( st < 0 && !sockerr_again() ) )
{
irc_abort( irc, 1, "Write error: %s", strerror( errno ) );
return FALSE;
}
else if( st < 0 ) /* && sockerr_again() */
{
return TRUE;
}
if( st == size )
{
if( irc->status & USTATUS_SHUTDOWN )
{
irc_free( irc );
}
else
{
g_free( irc->sendbuffer );
irc->sendbuffer = NULL;
irc->w_watch_source_id = 0;
}
return FALSE;
}
else
{
temp = g_strdup( irc->sendbuffer + st );
g_free( irc->sendbuffer );
irc->sendbuffer = temp;
return TRUE;
}
}
static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition condition )
{
socklen_t size = sizeof( struct sockaddr_in );
struct sockaddr_in conn_info;
int new_socket = accept( global.listen_socket, (struct sockaddr *) &conn_info, &size );
if( new_socket == -1 )
{
log_message( LOGLVL_WARNING, "Could not accept new connection: %s", strerror( errno ) );
return TRUE;
}
#ifndef _WIN32
if( global.conf->runmode == RUNMODE_FORKDAEMON )
{
pid_t client_pid = 0;
int fds[2];
if( socketpair( AF_UNIX, SOCK_STREAM, 0, fds ) == -1 )
{
log_message( LOGLVL_WARNING, "Could not create IPC socket for client: %s", strerror( errno ) );
fds[0] = fds[1] = -1;
}
sock_make_nonblocking( fds[0] );
sock_make_nonblocking( fds[1] );
client_pid = fork();
if( client_pid > 0 && fds[0] != -1 )
{
struct bitlbee_child *child;
child = g_new0( struct bitlbee_child, 1 );
child->pid = client_pid;
child->ipc_fd = fds[0];
child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
child_list = g_slist_append( child_list, child );
log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid );
/* Close some things we don't need in the parent process. */
close( new_socket );
close( fds[1] );
}
else if( client_pid == 0 )
{
irc_t *irc;
/* Since we're fork()ing here, let's make sure we won't
get the same random numbers as the parent/siblings. */
srand( time( NULL ) ^ getpid() );
b_main_init();
/* Close the listening socket, we're a client. */
close( global.listen_socket );
b_event_remove( global.listen_watch_source_id );
/* Make the connection. */
irc = irc_new( new_socket );
/* We can store the IPC fd there now. */
global.listen_socket = fds[1];
global.listen_watch_source_id = b_input_add( fds[1], B_EV_IO_READ, ipc_child_read, irc );
close( fds[0] );
ipc_master_free_all();
}
}
else
#endif
{
log_message( LOGLVL_INFO, "Creating new connection with fd %d.", new_socket );
irc_new( new_socket );
}
return TRUE;
}
gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond )
{
/* Try to save data for all active connections (if desired). */
while( irc_connection_list != NULL )
irc_free( irc_connection_list->data );
/* We'll only reach this point when not running in inetd mode: */
b_main_quit();
return FALSE;
}