From 2c2df7dd91930345a9b22a8bb61327d1dcc7e3d5 Mon Sep 17 00:00:00 2001 From: ulim Date: Wed, 28 Nov 2007 22:07:30 +0100 Subject: Initial import of jabber file receive and DCC send support. This introduces only a few changes to bitlbees code, mainly the addition of the "transfers" command. This is known to work with Kopete, Psi, and Pidgin (formerly known as gaim). At least with Pidgin also over a proxy. DCC has only been tested with irssi. IPV6 is untested but should work. Currently, only receiving via SOCKS5BYTESREAMS is implemented. I'm not sure if the alternative(in-band bytestreams IBB) is worth implementing since I didn't see a client yet that can do it. Additionally, it is probably very slow and needs support by the server as well. --- Makefile | 4 +- conf.c | 1 + conf.h | 1 + dcc.c | 534 +++++++++++++++++++++++++++++++++++++ dcc.h | 125 +++++++++ irc.h | 1 + protocols/ft.h | 153 +++++++++++ protocols/jabber/Makefile | 2 +- protocols/jabber/iq.c | 28 +- protocols/jabber/jabber.h | 58 +++- protocols/jabber/jabber_util.c | 6 +- protocols/jabber/si.c | 246 +++++++++++++++++ protocols/jabber/stream.c | 593 +++++++++++++++++++++++++++++++++++++++++ protocols/nogaim.h | 1 + root_commands.c | 66 +++++ 15 files changed, 1795 insertions(+), 24 deletions(-) create mode 100644 dcc.c create mode 100644 dcc.h create mode 100644 protocols/ft.h create mode 100644 protocols/jabber/si.c create mode 100644 protocols/jabber/stream.c diff --git a/Makefile b/Makefile index a9dd2e17..1f250420 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,8 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o user.o -headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h +objects = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o user.o dcc.o +headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h dcc.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h protocols/ft.h subdirs = lib protocols # Expansion of variables diff --git a/conf.c b/conf.c index 82487ddf..1768866b 100644 --- a/conf.c +++ b/conf.c @@ -62,6 +62,7 @@ conf_t *conf_load( int argc, char *argv[] ) conf->motdfile = g_strdup( ETCDIR "/motd.txt" ); conf->ping_interval = 180; conf->ping_timeout = 300; + conf->max_filetransfer_size = G_MAXUINT; proxytype = 0; i = conf_loadini( conf, CONF_FILE ); diff --git a/conf.h b/conf.h index 5138ce11..c88c8303 100644 --- a/conf.h +++ b/conf.h @@ -48,6 +48,7 @@ typedef struct conf char **migrate_storage; int ping_interval; int ping_timeout; + size_t max_filetransfer_size; } conf_t; G_GNUC_MALLOC conf_t *conf_load( int argc, char *argv[] ); diff --git a/dcc.c b/dcc.c new file mode 100644 index 00000000..73fe0180 --- /dev/null +++ b/dcc.c @@ -0,0 +1,534 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway * +* * +* Copyright 2007 Uli Meis * +\********************************************************************/ + +/* + 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 "ft.h" +#include "dcc.h" +#include +#include + +/* + * Since that might be confusing a note on naming: + * + * Generic dcc functions start with + * + * dcc_ + * + * ,methods specific to DCC SEND start with + * + * dccs_ + * + * . Since we can be on both ends of a DCC SEND, + * functions specific to one end are called + * + * dccs_send and dccs_recv + * + * ,respectively. + */ + + +/* + * used to generate a unique local transfer id the user + * can use to reject/cancel transfers + */ +unsigned int local_transfer_id=1; + +/* + * just for debugging the nr. of chunks we received from im-protocols and the total data + */ +unsigned int receivedchunks=0, receiveddata=0; + +/* + * If using DCC SEND AHEAD this value will be set before the first transfer starts. + * Not that in this case it degenerates to the maximum message size to send() and + * has nothing to do with packets. + */ +#ifdef DCC_SEND_AHEAD +int max_packet_size = DCC_PACKET_SIZE; +#else +int max_packet_size = 0; +#endif + +static void dcc_finish( file_transfer_t *file ); +static void dcc_close( file_transfer_t *file ); +gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); +gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ); +int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); + +/* As defined in ft.h */ +file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) +{ + user_t *u = user_findhandle( ic, handle ); + /* one could handle this more intelligent like imcb_buddy_msg. + * can't call it directly though cause it does some wrapping. + * Maybe give imcb_buddy_msg a parameter NO_WRAPPING? */ + if (!u) return NULL; + + return dccs_send_start( ic, u->nick, file_name, file_size ); +}; + +/* As defined in ft.h */ +void imcb_file_canceled( file_transfer_t *file, char *reason ) +{ + if( file->canceled ) + file->canceled( file, reason ); + + dcc_close( file ); +} + +/* As defined in ft.h */ +gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size ) +{ + return dccs_send_write( file, data, data_size ); +} + +/* This is where the sending magic starts... */ +file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ) +{ + file_transfer_t *file; + dcc_file_transfer_t *df; + struct sockaddr_storage **saddr; + + if( file_size > global.conf->max_filetransfer_size ) + return NULL; + + /* alloc stuff */ + file = g_new0( file_transfer_t, 1 ); + file->priv = df = g_new0( dcc_file_transfer_t, 1); + file->file_size = file_size; + file->file_name = g_strdup( file_name ); + file->local_id = local_transfer_id++; + df->ic = ic; + df->ft = file; + + /* listen and request */ + if( !dcc_listen( df, saddr ) || + !dccs_send_request( df, user_nick, *saddr ) ) + return NULL; + + g_free( *saddr ); + + /* watch */ + df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); + + df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file ); + + return file; +} + +/* Used pretty much everywhere in the code to abort a transfer */ +gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ) +{ + file_transfer_t *file = df->ft; + va_list params; + va_start( params, reason ); + char *msg = g_strdup_vprintf( reason, params ); + va_end( params ); + + file->status |= FT_STATUS_CANCELED; + + if( file->canceled ) + file->canceled( file, msg ); + else + imcb_log( df->ic, "DCC transfer aborted: %s", msg ); + + g_free( msg ); + + dcc_close( df->ft ); + + return FALSE; +} + +/* used extensively for socket operations */ +#define ASSERTSOCKOP(op, msg) \ + if( (op) == -1 ) \ + return dcc_abort( df , msg ": %s", strerror( errno ) ); + +/* Creates the "DCC SEND" line and sends it to the server */ +int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ) +{ + char ipaddr[INET6_ADDRSTRLEN]; + const void *netaddr; + int port; + char *cmd; + + if( saddr->ss_family == AF_INET ) + { + struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr; + + /* + * this is so ridiculous. We're supposed to convert the address to + * host byte order!!! Let's exclude anyone running big endian just + * for the fun of it... + */ + sprintf( ipaddr, "%d", + htonl( saddr_ipv4->sin_addr.s_addr ) ); + port = saddr_ipv4->sin_port; + } else + { + struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr; + + netaddr = &saddr_ipv6->sin6_addr.s6_addr; + port = saddr_ipv6->sin6_port; + + /* + * Didn't find docs about this, but it seems that's the way irssi does it + */ + if( !inet_ntop( saddr->ss_family, netaddr, ipaddr, sizeof( ipaddr ) ) ) + return dcc_abort( df, "inet_ntop failed: %s", strerror( errno ) ); + } + + port = ntohs( port ); + + cmd = g_strdup_printf( "\001DCC SEND %s %s %u %zu\001", + df->ft->file_name, ipaddr, port, df->ft->file_size ); + + if ( !irc_msgfrom( df->ic->irc, user_nick, cmd ) ) + return dcc_abort( df, "couldn't send 'DCC SEND' message to %s", user_nick ); + + g_free( cmd ); + + /* message is sortof redundant cause the users client probably informs him about that. remove? */ + imcb_log( df->ic, "Transferring file %s: Chose local address %s for DCC connection", df->ft->file_name, ipaddr ); + + return TRUE; +} + +/* + * Creates a listening socket and returns it in saddr_ptr. + */ +gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ) +{ + file_transfer_t *file = df->ft; + struct sockaddr_storage *saddr; + int fd; + char hostname[ HOST_NAME_MAX + 1 ]; + struct addrinfo hints, *rp; + socklen_t ssize = sizeof( struct sockaddr_storage ); + + /* won't be long till someone asks for this to be configurable :) */ + + ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) + return dcc_abort( df, "getaddrinfo()" ); + + saddr = g_new( struct sockaddr_storage, 1 ); + + *saddr_ptr = saddr; + + memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); + + ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); + + ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); + + freeaddrinfo( rp ); + + ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + + ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + + file->status = FT_STATUS_LISTENING; + + return TRUE; +} + +/* + * After setup, the transfer itself is handled entirely by this function. + * There are basically four things to handle: connect, receive, send, and error. + */ +gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) +{ + dcc_file_transfer_t *df = data; + file_transfer_t *file = df->ft; + struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT }; + short revents; + + if ( poll( &pfd, 1, 0 ) == -1 ) + { + imcb_log( df->ic, "poll() failed, weird!" ); + revents = 0; + }; + + revents = pfd.revents; + + if( revents & POLLERR ) + { + int sockerror; + socklen_t errlen = sizeof( sockerror ); + + if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) + return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" ); + + return dcc_abort( df, "Socket error: %s", strerror( sockerror ) ); + } + + if( revents & POLLHUP ) + return dcc_abort( df, "Remote end closed connection" ); + + if( ( revents & POLLIN ) && + ( file->status & FT_STATUS_LISTENING ) ) + { + struct sockaddr *clt_addr; + socklen_t ssize = sizeof( clt_addr ); + + /* Connect */ + + ASSERTSOCKOP( df->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + + closesocket( fd ); + fd = df->fd; + file->status = FT_STATUS_TRANSFERING; + sock_make_nonblocking( fd ); + +#ifdef DCC_SEND_AHEAD + /* + * use twice the maximum segment size as a maximum for calls to send(). + */ + if( max_packet_size == 0 ) + { + unsigned int mpslen = sizeof( max_packet_size ); + if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) ) + return dcc_abort( df, "getsockopt() failed" ); + max_packet_size *= 2; + } +#endif + /* IM protocol callback */ + + if( file->accept ) + file->accept( file ); + /* reschedule for reading on new fd */ + df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df ); + + return FALSE; + } + + if( revents & POLLIN ) + { + int bytes_received; + int ret; + + ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), MSG_PEEK ), "Receiving" ); + + if( ret == 0 ) + return dcc_abort( df, "Remote end closed connection" ); + + if( ret < 4 ) + { + imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only 2 bytes instead of 4, shouldn't happen too often!" ); + return TRUE; + } + + ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), 0 ), "Receiving" ); + if( ret != 4 ) + return dcc_abort( df, "MSG_PEEK'ed 4, but can only dequeue %d bytes", ret ); + + bytes_received = ntohl( bytes_received ); + + /* If any of this is actually happening, the receiver should buy a new IRC client */ + + if ( bytes_received > df->bytes_sent ) + return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", bytes_received, df->bytes_sent ); + + if ( bytes_received < file->bytes_transferred ) + return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", bytes_received, file->bytes_transferred ); + + file->bytes_transferred = bytes_received; + + if( file->bytes_transferred >= file->file_size ) { + dcc_finish( file ); + return FALSE; + } + +#ifndef DCC_SEND_AHEAD + /* reschedule writer if neccessary */ + if( file->bytes_transferred >= df->bytes_sent && + df->watch_out == 0 && + df->queued_bytes > 0 ) { + df->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, dcc_send_proto, df ); + } +#endif + return TRUE; + } + + if( revents & POLLOUT ) + { + struct dcc_buffer *dccb; + int ret; + char *msg; + + if( df->queued_bytes == 0 ) + { + /* shouldn't happen */ + imcb_log( df->ic, "WARNING: DCC SEND: write called with empty queue" ); + + df->watch_out = 0; + return FALSE; + } + + /* start where we left off */ + if( !( df->queued_buffers ) || + !( dccb = df->queued_buffers->data ) ) + return dcc_abort( df, "BUG in DCC SEND: queued data but no buffers" ); + + msg = dccb->b + df->buffer_pos; + + int msgsize = MIN( +#ifndef DCC_SEND_AHEAD + file->bytes_transferred + MAX_PACKET_SIZE - df->bytes_sent, +#else + max_packet_size, +#endif + dccb->len - df->buffer_pos ); + + if ( msgsize == 0 ) + { + df->watch_out = 0; + return FALSE; + } + + ASSERTSOCKOP( ret = send( fd, msg, msgsize, 0 ), "Sending data" ); + + if( ret == 0 ) + return dcc_abort( df, "Remote end closed connection" ); + + df->bytes_sent += ret; + df->queued_bytes -= ret; + df->buffer_pos += ret; + + if( df->buffer_pos == dccb->len ) + { + df->buffer_pos = 0; + df->queued_buffers = g_slist_remove( df->queued_buffers, dccb ); + g_free( dccb->b ); + g_free( dccb ); + } + + if( ( df->queued_bytes < DCC_QUEUE_THRESHOLD_LOW ) && file->out_of_data ) + file->out_of_data( file ); + + if( df->queued_bytes > 0 ) + { + /* Who knows how long the event loop cycle will take, + * let's just try to send some more now. */ +#ifndef DCC_SEND_AHEAD + if( df->bytes_sent < ( file->bytes_transferred + max_packet_size ) ) +#endif + return dccs_send_proto( df, fd, cond ); + } + + df->watch_out = 0; + return FALSE; + } + + /* Send buffer full, come back later */ + + return TRUE; +} + +/* + * Incoming data. Note that the buffer MUST NOT be freed by the caller! + * We don't copy the buffer but put it in our queue. + * + * */ +gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size ) +{ + dcc_file_transfer_t *df = file->priv; + struct dcc_buffer *dccb = g_new0( struct dcc_buffer, 1 ); + + receivedchunks++; receiveddata += data_size; + + dccb->b = data; + dccb->len = data_size; + + df->queued_buffers = g_slist_append( df->queued_buffers, dccb ); + + df->queued_bytes += data_size; + + if( ( file->status & FT_STATUS_TRANSFERING ) && +#ifndef DCC_SEND_AHEAD + ( file->bytes_transferred >= df->bytes_sent ) && +#endif + ( df->watch_out == 0 ) && + ( df->queued_bytes > 0 ) ) + { + df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_proto, df ); + } + + return df->queued_bytes > DCC_QUEUE_THRESHOLD_HIGH; +} + +/* + * Cleans up after a transfer. + */ +static void dcc_close( file_transfer_t *file ) +{ + dcc_file_transfer_t *df = file->priv; + + if( file->free ) + file->free( file ); + + closesocket( df->fd ); + + if( df->watch_in ) + b_event_remove( df->watch_in ); + + if( df->watch_out ) + b_event_remove( df->watch_out ); + + if( df->queued_buffers ) + { + struct dcc_buffer *dccb; + GSList *gslist = df->queued_buffers; + + for( ; gslist ; gslist = g_slist_next( gslist ) ) + { + dccb = gslist->data; + g_free( dccb->b ); + g_free( dccb ); + } + g_slist_free( df->queued_buffers ); + } + + df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file ); + + g_free( df ); + g_free( file->file_name ); + g_free( file ); +} + +void dcc_finish( file_transfer_t *file ) +{ + file->status |= FT_STATUS_FINISHED; + + if( file->finished ) + file->finished( file ); + + dcc_close( file ); +} diff --git a/dcc.h b/dcc.h new file mode 100644 index 00000000..205107c5 --- /dev/null +++ b/dcc.h @@ -0,0 +1,125 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway * +* * +* Copyright 2006 Marijn Kruisselbrink and others * +* Copyright 2007 Uli Meis * +\********************************************************************/ + +/* + 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 +*/ + +/* + * DCC SEND + * + * Historically, DCC means send 1024 Bytes and wait for a 4 byte reply + * acknowledging all transferred data. This is ridiculous for two reasons. The + * first being that TCP is a stream oriented protocol that doesn't care much + * about your idea of a packet. The second reason being that TCP is a reliable + * transfer protocol with its own sophisticated ACK mechanism, making DCCs ACK + * mechanism look like a joke. For these reasons, DCCs requirements have + * (hopefully) been relaxed in most implementations and this implementation + * depends upon at least the following: The 1024 bytes need not be transferred + * at once, i.e. packets can be smaller. A second relaxation has apparently + * gotten the name "DCC SEND ahead" which basically means to not give a damn + * about those DCC ACKs and just send data as you please. This behaviour is + * enabled by default. Note that this also means that packets may be as large + * as the maximum segment size. + */ + +#ifndef _DCC_H +#define _DCC_H + +/* don't wait for acknowledgments */ +#define DCC_SEND_AHEAD + +/* This multiplier specifies how many bytes we + * can go ahead within one event loop cycle. Notice that all in all, + * we can easily be more ahead if the event loop shoots often enough. + * (or the receiver processes slow enough) + * + * Setting this value too high will cause send buffer overflows. + */ +#define DCC_SEND_AHEAD_MUL 10 + +/* + * queue thresholds for the out of data and overflow conditions + */ +#define DCC_QUEUE_THRESHOLD_LOW 2048 +#define DCC_QUEUE_THRESHOLD_HIGH 65536 + +/* only used in non-ahead mode */ +#define DCC_PACKET_SIZE 1024 + +/* stores buffers handed over by IM protocols */ +struct dcc_buffer { + char *b; + int len; +}; + +typedef struct dcc_file_transfer { + + struct im_connection *ic; + + /* + * Depending in the status of the file transfer, this is either the socket that is + * being listened on for connections, or the socket over which the file transfer is + * taking place. + */ + int fd; + + /* + * IDs returned by b_input_add for watch_ing over the above socket. + */ + gint watch_in; /* readable */ + gint watch_out; /* writable */ + + /* + * The total number of queued bytes. The following equality should always hold: + * + * queued_bytes = sum(queued_buffers.len) - buffer_pos + */ + unsigned int queued_bytes; + + /* + * A list of dcc_buffer structures. + * These are provided by the protocols directly so that no copying is neccessary. + */ + GSList *queued_buffers; + + /* + * current position within the first buffer. + * Is non-null if the whole buffer couldn't be sent at once. + */ + int buffer_pos; + + /* + * The total amount of bytes that have been sent to the irc client. + */ + size_t bytes_sent; + + /* imcb's handle */ + file_transfer_t *ft; + +} dcc_file_transfer_t; + +file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ); + +void dcc_canceled( file_transfer_t *file, char *reason ); + +gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size ); + +#endif diff --git a/irc.h b/irc.h index 8be3579e..d581e813 100644 --- a/irc.h +++ b/irc.h @@ -83,6 +83,7 @@ typedef struct irc struct query *queries; struct account *accounts; + GSList *file_transfers; struct __USER *users; GHashTable *userhash; diff --git a/protocols/ft.h b/protocols/ft.h new file mode 100644 index 00000000..0ff44873 --- /dev/null +++ b/protocols/ft.h @@ -0,0 +1,153 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway * +* * +* Copyright 2006 Marijn Kruisselbrink and others * +\********************************************************************/ + +/* Generic file transfer header */ + +/* + 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 +*/ + +#ifndef _FT_H +#define _FT_H + +typedef enum { + FT_STATUS_LISTENING = 1, + FT_STATUS_TRANSFERING = 2, + FT_STATUS_FINISHED = 4, + FT_STATUS_CANCELED = 8 +} file_status_t; + +/* + * This structure holds all irc specific information regarding an incoming (from the point of view of + * the irc client) file transfer. New instances of this struct should only be created by calling the + * imcb_file_send_start() method, which will initialize most of the fields. The data field and the various + * methods are zero-initialized. Instances will automatically be deleted once the transfer is completed, + * canceled, or the connection to the irc client has been lost (note that also if only the irc connection + * and not the dcc connection is lost, the file transfer will still be canceled and freed). + * + * The following (poor ascii-art) diagram illustrates what methods are called for which status-changes: + * + * /-----------\ /----------\ + * -------> | LISTENING | -----------------> | CANCELED | + * \-----------/ [canceled,]free \----------/ + * | + * | accept + * V + * /------ /-------------\ /------------------------\ + * out_of_data | | TRANSFERING | -----------------> | TRANSFERING | CANCELED | + * \-----> \-------------/ [canceled,]free \------------------------/ + * | + * | finished,free + * V + * /------------------------\ + * | TRANSFERING | FINISHED | + * \------------------------/ + */ +typedef struct file_transfer { + /* + * The current status of this file transfer. + */ + file_status_t status; + + /* + * file size + */ + size_t file_size; + + /* + * Number of bytes that have been successfully transferred. + */ + size_t bytes_transferred; + + /* + * Time started. Used to calculate kb/s. + */ + time_t started; + + /* + * file name + */ + char *file_name; + + /* + * A unique local ID for this file transfer. + */ + unsigned int local_id; + + /* + * IM-protocol specific data associated with this file transfer. + */ + gpointer data; + + /* + * Private data. + */ + gpointer priv; + + /* + * If set, called after succesful connection setup. + */ + void (*accept) ( struct file_transfer *file ); + + /* + * If set, called when the transfer is canceled or finished. + * Subsequently, this structure will be freed. + * + */ + void (*free) ( struct file_transfer *file ); + + /* + * If set, called when the transfer is finished and successful. + */ + void (*finished) ( struct file_transfer *file ); + + /* + * If set, called when the transfer is canceled. + * ( canceled either by the transfer implementation or by + * a call to imcb_file_canceled ) + */ + void (*canceled) ( struct file_transfer *file, char *reason ); + + /* + * If set, called when the transfer queue is running empty and + * more data can be added. + */ + void (*out_of_data) ( struct file_transfer *file ); + +} file_transfer_t; + +/* + * This starts a file transfer from bitlbee to the user (currently via DCC). + */ +file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ); + +/* + * This should be called by a protocol when the transfer is canceled. Note that + * the canceled() and free() callbacks given in file will be called by this function. + */ +void imcb_file_canceled( file_transfer_t *file, char *reason ); + +/* + * The given buffer is queued for transfer and MUST NOT be freed by the caller. + * When the method returns false the caller should not invoke this method again + * until out_of_data has been called. + */ +gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size ); + +#endif diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index e042f812..47c832ae 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o si.o stream.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 595718fb..df0102b8 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -103,6 +103,9 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) XMLNS_TIME, XMLNS_CHATSTATES, XMLNS_MUC, + XMLNS_SI, + XMLNS_BYTESTREAMS, + XMLNS_FILETRANSFER, NULL }; const char **f; @@ -122,24 +125,26 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) else { xt_free_node( reply ); - reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" ); + reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL ); pack = 0; } } else if( strcmp( type, "set" ) == 0 ) { - if( !( c = xt_find_node( node->children, "query" ) ) || + if( ( c = xt_find_node( node->children, "si" ) ) && + ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) ) + { + return jabber_si_handle_request( ic, node, c ); + } else if( !( c = xt_find_node( node->children, "query" ) ) || !( s = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-%s packet", type ); return XT_HANDLED; - } - + } else if( strcmp( s, XMLNS_ROSTER ) == 0 ) + { /* This is a roster push. XMPP servers send this when someone was added to (or removed from) the buddy list. AFAIK they're sent even if we added this buddy in our own session. */ - if( strcmp( s, XMLNS_ROSTER ) == 0 ) - { int bare_len = strlen( ic->acc->user ); if( ( s = xt_find_attr( node, "from" ) ) == NULL || @@ -156,14 +161,17 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) imcb_log( ic, "WARNING: %s tried to fake a roster push!", s ? s : "(unknown)" ); xt_free_node( reply ); - reply = jabber_make_error_packet( node, "not-allowed", "cancel" ); + reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL ); pack = 0; } - } - else + } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) + { + /* Bytestream Request (stage 2 of file transfer) */ + return jabber_bs_request( ic, node, c ); + } else { xt_free_node( reply ); - reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" ); + reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL ); pack = 0; } } diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index fc9d2fc4..0cb2b733 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -80,6 +80,8 @@ struct jabber_data char *cached_id_prefix; GHashTable *node_cache; GHashTable *buddies; + + GSList *filetransfers; }; struct jabber_away_state @@ -123,6 +125,30 @@ struct jabber_chat struct jabber_buddy *me; }; +struct jabber_transfer +{ + /* bitlbee's handle for this transfer */ + file_transfer_t *ft; + + /* the stream's private handle */ + gpointer streamhandle; + + struct im_connection *ic; + + int watch_in; + int watch_out; + + char *ini_jid; + char *tgt_jid; + char *iq_id; + char *sid; + int accepted; + + size_t bytesread, byteswritten; + int receiver_overflow; + int fd; +}; + #define JABBER_XMLCONSOLE_HANDLE "xmlconsole" #define JABBER_PORT_DEFAULT "5222" @@ -148,15 +174,21 @@ struct jabber_chat #define XMLNS_ROSTER "jabber:iq:roster" /* Some supported extensions/legacy stuff */ -#define XMLNS_AUTH "jabber:iq:auth" /* XEP-0078 */ -#define XMLNS_VERSION "jabber:iq:version" /* XEP-0092 */ -#define XMLNS_TIME "jabber:iq:time" /* XEP-0090 */ -#define XMLNS_VCARD "vcard-temp" /* XEP-0054 */ -#define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */ -#define XMLNS_CHATSTATES "http://jabber.org/protocol/chatstates" /* 0085 */ -#define XMLNS_DISCOVER "http://jabber.org/protocol/disco#info" /* 0030 */ -#define XMLNS_MUC "http://jabber.org/protocol/muc" /* XEP-0045 */ -#define XMLNS_MUC_USER "http://jabber.org/protocol/muc#user"/* XEP-0045 */ +#define XMLNS_AUTH "jabber:iq:auth" /* XEP-0078 */ +#define XMLNS_VERSION "jabber:iq:version" /* XEP-0092 */ +#define XMLNS_TIME "jabber:iq:time" /* XEP-0090 */ +#define XMLNS_VCARD "vcard-temp" /* XEP-0054 */ +#define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */ +#define XMLNS_XDATA "jabber:x:data" /* XEP-0004 */ +#define XMLNS_CHATSTATES "http://jabber.org/protocol/chatstates" /* XEP-0085 */ +#define XMLNS_DISCOVER "http://jabber.org/protocol/disco#info" /* XEP-0030 */ +#define XMLNS_MUC "http://jabber.org/protocol/muc" /* XEP-0045 */ +#define XMLNS_MUC_USER "http://jabber.org/protocol/muc#user" /* XEP-0045 */ +#define XMLNS_FEATURE "http://jabber.org/protocol/feature-neg" /* XEP-0020 */ +#define XMLNS_SI "http://jabber.org/protocol/si" /* XEP-0095 */ +#define XMLNS_FILETRANSFER "http://jabber.org/protocol/si/profile/file-transfer" /* XEP-0096 */ +#define XMLNS_BYTESTREAMS "http://jabber.org/protocol/bytestreams" /* XEP-0065 */ +#define XMLNS_IBB "http://jabber.org/protocol/ibb" /* XEP-0047 */ /* iq.c */ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); @@ -167,6 +199,12 @@ int jabber_get_vcard( struct im_connection *ic, char *bare_jid ); int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); int jabber_remove_from_roster( struct im_connection *ic, char *handle ); +/* si.c */ +int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode); + +/* stream.c */ +int jabber_bs_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); + /* message.c */ xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); @@ -179,7 +217,7 @@ int presence_send_request( struct im_connection *ic, char *handle, char *request char *set_eval_priority( set_t *set, char *value ); char *set_eval_tls( set_t *set, char *value ); struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children ); -struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type ); +struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type, char *err_code ); void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func ); struct xt_node *jabber_cache_get( struct im_connection *ic, char *id ); void jabber_cache_entry_free( gpointer entry ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 43b91fe3..0c5b813e 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -96,7 +96,7 @@ struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_ return node; } -struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type ) +struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type, char *err_code ) { struct xt_node *node, *c; char *to; @@ -109,6 +109,10 @@ struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, c = xt_new_node( "error", NULL, c ); xt_add_attr( c, "type", err_type ); + /* Add the error code, if present */ + if (err_code) + xt_add_attr( c, "code", err_code ); + /* To make the actual error packet, we copy the original packet and add our /type="error" tag. Including the original packet is recommended, so let's just do it. */ diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c new file mode 100644 index 00000000..d16f723a --- /dev/null +++ b/protocols/jabber/si.c @@ -0,0 +1,246 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - SI packets * +* * +* Copyright 2007 Uli Meis * +* * +* 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 along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "jabber.h" +#include "sha1.h" + +void jabber_si_answer_request( file_transfer_t *ft ); + +/* imcb callback */ +void jabber_si_free_transfer( file_transfer_t *ft) +{ + struct jabber_transfer *tf = ft->data; + struct jabber_data *jd = tf->ic->proto_data; + + if ( tf->watch_in ) + b_event_remove( tf->watch_in ); + + jd->filetransfers = g_slist_remove( jd->filetransfers, tf ); + + if( tf->fd ) + { + close( tf->fd ); + tf->fd = 0; + } + + g_free( tf->ini_jid ); + g_free( tf->tgt_jid ); + g_free( tf->iq_id ); + g_free( tf->sid ); +} + +/* imcb callback */ +void jabber_si_finished( file_transfer_t *ft ) +{ + struct jabber_transfer *tf = ft->data; + + imcb_log( tf->ic, "File %s transferred successfully!" , ft->file_name ); +} + +/* imcb callback */ +void jabber_si_canceled( file_transfer_t *ft, char *reason ) +{ + struct jabber_transfer *tf = ft->data; + struct xt_node *reply, *iqnode; + + if( tf->accepted ) + return; + + iqnode = jabber_make_packet( "iq", "error", tf->ini_jid, NULL ); + xt_add_attr( iqnode, "id", tf->iq_id ); + reply = jabber_make_error_packet( iqnode, "forbidden", "cancel", "403" ); + xt_free_node( iqnode ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_log( tf->ic, "WARNING: Error generating reply to file transfer request" ); + xt_free_node( reply ); + +} + +/* + * First function that gets called when a file transfer request comes in. + * A lot to parse. + * + * We choose a stream type from the options given by the initiator. + * Then we wait for imcb to call the accept or cancel callbacks. + */ +int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode) +{ + struct xt_node *c, *d, *reply; + char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid; + struct jabber_buddy *bud; + int requestok = FALSE; + char *name; + size_t size; + struct jabber_transfer *tf; + struct jabber_data *jd = ic->proto_data; + file_transfer_t *ft; + + /* All this means we expect something like this: ( I think ) + * + * + * + * + * + * + * + */ + if( !( ini_jid = xt_find_attr( node, "from" ) ) || + !( tgt_jid = xt_find_attr( node, "to" ) ) || + !( iq_id = xt_find_attr( node, "id" ) ) || + !( sid = xt_find_attr( sinode, "id" ) ) || + !( strcmp( xt_find_attr( sinode, "profile" ), XMLNS_FILETRANSFER ) == 0 ) || + !( d = xt_find_node( sinode->children, "file" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || + !( name = xt_find_attr( d, "name" ) ) || + !( size = (size_t) atoll( xt_find_attr( d, "size" ) ) ) || + !( d = xt_find_node( sinode->children, "feature" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || + !( d = xt_find_node( d->children, "x" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_XDATA ) == 0 ) || + !( strcmp( xt_find_attr( d, "type" ), "form" ) == 0 ) || + !( d = xt_find_node( d->children, "field" ) ) || + !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) ) + { + imcb_log( ic, "WARNING: Received incomplete Stream Initiation request" ); + } else + { + /* Check if we support one of the options */ + + c = d->children; + while( ( c = xt_find_node( c, "option" ) ) ) + if( ( d = xt_find_node( c->children, "value" ) ) && + ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) + { + requestok = TRUE; + break; + } + } + + if ( requestok ) + { + /* Figure out who the transfer should come frome... */ + + if( ( s = strchr( ini_jid, '/' ) ) ) + { + if( ( bud = jabber_buddy_by_jid( ic, ini_jid, GET_BUDDY_EXACT ) ) ) + { + bud->last_act = time( NULL ); + ext_jid = bud->ext_jid ? : bud->bare_jid; + } + else + *s = 0; /* We need to generate a bare JID now. */ + } + + if( !( ft = imcb_file_send_start( ic, ext_jid, name, size ) ) ) + { + imcb_log( ic, "WARNING: Error handling transfer request from %s", ini_jid); + requestok = FALSE; + } + + *s = '/'; + } else + imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); + + if ( !requestok ) + { + reply = jabber_make_error_packet( node, "item-not-found", "cancel", NULL ); + if (!jabber_write_packet( ic, reply )) + imcb_log( ic, "WARNING: Error generating reply to file transfer request" ); + xt_free_node( reply ); + return XT_HANDLED; + } + + /* Request is fine. */ + + imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", xt_find_attr( node, "from" ), name, size/1024 ); + + imcb_log( ic, "Accept the DCC transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); + + tf = g_new0( struct jabber_transfer, 1 ); + + tf->ini_jid = g_strdup( ini_jid ); + tf->tgt_jid = g_strdup( tgt_jid ); + tf->iq_id = g_strdup( iq_id ); + tf->sid = g_strdup( sid ); + tf->ic = ic; + tf->ft = ft; + tf->ft->data = tf; + tf->ft->accept = jabber_si_answer_request; + tf->ft->free = jabber_si_free_transfer; + tf->ft->finished = jabber_si_finished; + tf->ft->canceled = jabber_si_canceled; + + jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); + + return XT_HANDLED; +} + +/* + * imcb called the accept callback which probably means that the user accepted this file transfer. + * We send our response to the initiator. + * In the next step, the initiator will send us a request for the given stream type. + * (currently that can only be a SOCKS5 bytestream) + */ +void jabber_si_answer_request( file_transfer_t *ft ) { + struct jabber_transfer *tf = ft->data; + struct xt_node *node, *sinode, *reply; + + /* generate response, start with the SI tag */ + sinode = xt_new_node( "si", NULL, NULL ); + xt_add_attr( sinode, "xmlns", XMLNS_SI ); + xt_add_attr( sinode, "profile", XMLNS_FILETRANSFER ); + xt_add_attr( sinode, "id", tf->sid ); + + /* now the file tag */ + node = xt_new_node( "file", NULL, NULL ); + xt_add_attr( node, "xmlns", XMLNS_FILETRANSFER ); + + xt_add_child( sinode, node ); + + /* and finally the feature tag */ + node = xt_new_node( "field", NULL, NULL ); + xt_add_attr( node, "var", "stream-method" ); + xt_add_attr( node, "type", "list-single" ); + + /* Currently all we can do. One could also implement in-band (IBB) */ + xt_add_child( node, xt_new_node( "value", XMLNS_BYTESTREAMS, NULL ) ); + + node = xt_new_node( "x", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_XDATA ); + xt_add_attr( node, "type", "submit" ); + + node = xt_new_node( "feature", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_FEATURE ); + + xt_add_child( sinode, node ); + + reply = jabber_make_packet( "iq", "result", tf->ini_jid, sinode ); + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_log( tf->ic, "WARNING: Error generating reply to file transfer request" ); + else + tf->accepted = TRUE; + xt_free_node( reply ); +} diff --git a/protocols/jabber/stream.c b/protocols/jabber/stream.c new file mode 100644 index 00000000..c88a72fd --- /dev/null +++ b/protocols/jabber/stream.c @@ -0,0 +1,593 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - stream handling * +* * +* Copyright 2007 Uli Meis * +* * +* 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 along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "jabber.h" +#include "sha1.h" +#include + +/* Some structs for the SOCKS5 handshake */ + +struct bs_handshake_data { + + struct jabber_transfer *tf; + + /* element and elements */ + struct xt_node *qnode, *shnode; + + enum + { + BS_PHASE_CONNECT, + BS_PHASE_CONNECTED, + BS_PHASE_REQUEST, + BS_PHASE_REPLY, + BS_PHASE_REPLY_HAVE_LEN + } phase; + + /* SHA1( SID + Initiator JID + Target JID) */ + char *pseudoadr; + + void (*parentfree) ( file_transfer_t *ft ); + + gint connect_timeout; +}; + +struct socks5_hdr +{ + unsigned char ver; + union + { + unsigned char cmd; + unsigned char rep; + } cmdrep; + unsigned char rsv; + unsigned char atyp; +}; + +struct socks5_message +{ + struct socks5_hdr hdr; + unsigned char addrlen; + unsigned char address[64]; +}; + +/* connect() timeout in seconds. */ +#define JABBER_BS_CONTIMEOUT 15 + +/* shouldn't matter if it's mostly too much, kernel's smart about that + * and will only reserve some address space */ +#define JABBER_BS_BUFSIZE 65536 + +gboolean jabber_bs_handshake( gpointer data, gint fd, b_input_condition cond ); + +gboolean jabber_bs_handshake_abort( struct bs_handshake_data *bhd, char *format, ... ); + +void jabber_bs_answer_request( struct bs_handshake_data *bhd ); + +gboolean jabber_bs_read( gpointer data, gint fd, b_input_condition cond ); + +void jabber_bs_out_of_data( file_transfer_t *ft ); + +void jabber_bs_canceled( file_transfer_t *ft , char *reason ); + + +void jabber_bs_free_transfer( file_transfer_t *ft) { + struct jabber_transfer *tf = ft->data; + struct bs_handshake_data *bhd = tf->streamhandle; + void (*parentfree) ( file_transfer_t *ft ); + + parentfree = bhd->parentfree; + + if ( tf->watch_in ) + b_event_remove( tf->watch_in ); + + if( tf->watch_out ) + b_event_remove( tf->watch_out ); + + g_free( bhd->pseudoadr ); + xt_free_node( bhd->qnode ); + g_free( bhd ); + + parentfree( ft ); +} + +/* + * Parses an incoming bytestream request and calls jabber_bs_handshake on success. + */ +int jabber_bs_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) +{ + char *sid, *ini_jid, *tgt_jid, *mode, *iq_id; + struct jabber_data *jd = ic->proto_data; + struct jabber_transfer *tf = NULL; + GSList *tflist; + struct bs_handshake_data *bhd; + + sha1_state_t sha; + char hash_hex[41]; + unsigned char hash[20]; + int i; + + if( !(iq_id = xt_find_attr( node, "id" ) ) || + !(ini_jid = xt_find_attr( node, "from" ) ) || + !(tgt_jid = xt_find_attr( node, "to" ) ) || + !(sid = xt_find_attr( qnode, "sid" ) ) ) + { + imcb_log( ic, "WARNING: Received incomplete SI bytestream request"); + return XT_HANDLED; + } + + if( ( mode = xt_find_attr( qnode, "mode" ) ) && + ( strcmp( mode, "tcp" ) != 0 ) ) + { + imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) ); + return XT_HANDLED; + } + + /* Let's see if we can find out what this bytestream should be for... */ + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) && + ( strcmp( tft->ini_jid, ini_jid ) == 0 ) && + ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if (!tf) + { + imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); + return XT_HANDLED; + } + + /* iq_id and canceled can be reused since SI is done */ + g_free( tf->iq_id ); + tf->iq_id = g_strdup( iq_id ); + + tf->ft->canceled = jabber_bs_canceled; + + /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ + sha1_init( &sha ); + sha1_append( &sha, (unsigned char*) sid, strlen( sid ) ); + sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) ); + sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) ); + sha1_finish( &sha, hash ); + + for( i = 0; i < 20; i ++ ) + sprintf( hash_hex + i * 2, "%02x", hash[i] ); + + bhd = g_new0( struct bs_handshake_data, 1 ); + bhd->tf = tf; + bhd->qnode = xt_dup( qnode ); + bhd->shnode = bhd->qnode->children; + bhd->phase = BS_PHASE_CONNECT; + bhd->pseudoadr = g_strdup( hash_hex ); + tf->streamhandle = bhd; + bhd->parentfree = tf->ft->free; + tf->ft->free = jabber_bs_free_transfer; + + jabber_bs_handshake( bhd, 0, 0 ); + + return XT_HANDLED; +} + +/* + * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect(). + */ +gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ) +{ + struct bs_handshake_data *bhd = data; + + bhd->connect_timeout = 0; + + jabber_bs_handshake_abort( bhd, "no connection after %d seconds", JABBER_BS_CONTIMEOUT ); + + return FALSE; +} + +/* + * This is what a protocol handshake can look like in cooperative multitasking :) + * Might be confusing at first because it's called from different places and is recursing. + * (places being the event thread, bs_request, bs_handshake_abort, and itself) + * + * All in all, it turned out quite nice :) + */ +gboolean jabber_bs_handshake( gpointer data, gint fd, b_input_condition cond ) +{ + +/* very useful */ +#define ASSERTSOCKOP(op, msg) \ + if( (op) == -1 ) \ + return jabber_bs_handshake_abort( bhd , msg ": %s", strerror( errno ) ); + + struct bs_handshake_data *bhd = data; + struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; + short revents; + + if ( bhd->connect_timeout ) + { + b_event_remove( bhd->connect_timeout ); + bhd->connect_timeout = 0; + } + + + /* we need the real io condition */ + if ( poll( &pfd, 1, 0 ) == -1 ) + { + imcb_log( bhd->tf->ic, "poll() failed, weird!" ); + revents = 0; + }; + + revents = pfd.revents; + + if( revents & POLLERR ) + { + int sockerror; + socklen_t errlen = sizeof( sockerror ); + + if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) + return jabber_bs_handshake_abort( bhd, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" ); + + if ( bhd->phase == BS_PHASE_CONNECTED ) + return jabber_bs_handshake_abort( bhd, "connect() failed: %s", strerror( sockerror ) ); + + return jabber_bs_handshake_abort( bhd, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) ); + } + + if( revents & POLLHUP ) + return jabber_bs_handshake_abort( bhd, "Remote end closed connection" ); + + + switch( bhd->phase ) + { + case BS_PHASE_CONNECT: + { + struct xt_node *c; + char *host, *port; + struct addrinfo hints, *rp; + + if( ( c = bhd->shnode = xt_find_node( bhd->shnode, "streamhost" ) ) && + ( port = xt_find_attr( c, "port" ) ) && + ( host = xt_find_attr( c, "host" ) ) && + xt_find_attr( c, "jid" ) ) + { + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + + if ( getaddrinfo( host, port, &hints, &rp ) != 0 ) + return jabber_bs_handshake_abort( bhd, "getaddrinfo() failed: %s", strerror( errno ) ); + + ASSERTSOCKOP( bhd->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); + + sock_make_nonblocking( fd ); + + imcb_log( bhd->tf->ic, "Transferring file %s: Connecting to streamhost %s:%s", bhd->tf->ft->file_name, host, port ); + + if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && + ( errno != EINPROGRESS ) ) + return jabber_bs_handshake_abort( bhd , "connect() failed: %s", strerror( errno ) ); + + freeaddrinfo( rp ); + + bhd->phase = BS_PHASE_CONNECTED; + + bhd->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_handshake, bhd ); + + /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ + bhd->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bhd ); + + bhd->tf->watch_in = 0; + return FALSE; + } else + return jabber_bs_handshake_abort( bhd, c ? "incomplete streamhost entry: host=%s port=%s jid=%s" : NULL, + host, port, xt_find_attr( c, "jid" ) ); + } + case BS_PHASE_CONNECTED: + { + struct { + unsigned char ver; + unsigned char nmethods; + unsigned char method; + } socks5_hello = { + .ver = 5, + .nmethods = 1, + .method = 0x00 /* no auth */ + /* one could also implement username/password. If you know + * a jabber client or proxy that actually does it, tell me. + */ + }; + + ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" ); + + bhd->phase = BS_PHASE_REQUEST; + + bhd->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_handshake, bhd ); + + bhd->tf->watch_out = 0; + return FALSE; + } + case BS_PHASE_REQUEST: + { + struct socks5_message socks5_connect = + { + .hdr = + { + .ver = 5, + .cmdrep.cmd = 0x01, + .rsv = 0, + .atyp = 0x03 + }, + .addrlen = strlen( bhd->pseudoadr ) + }; + int ret; + char buf[2]; + + /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ + ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" ); + + if( !( ret == 2 ) || + !( buf[0] == 5 ) || + !( buf[1] == 0 ) ) + return jabber_bs_handshake_abort( bhd, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", + ret, buf[0], buf[1] ); + + /* copy hash into connect message */ + memcpy( socks5_connect.address, bhd->pseudoadr, socks5_connect.addrlen ); + + /* after the address comes the port, which is always 0 */ + memset( socks5_connect.address + socks5_connect.addrlen, 0, sizeof( in_port_t ) ); + + ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_hdr ) + 1 + socks5_connect.addrlen + sizeof( in_port_t ), 0 ) , "Sending SOCKS5 Connect" ); + + bhd->phase = BS_PHASE_REPLY; + + return TRUE; + } + case BS_PHASE_REPLY: + case BS_PHASE_REPLY_HAVE_LEN: + { + /* we have to wait till we have the address length, then we know how much data is left + * (not that we'd actually care about that data, but we need to eat it all up anyway) + */ + struct socks5_message socks5_reply; + int ret; + int expectedbytes = + sizeof( struct socks5_hdr ) + 1 + + ( bhd->phase == BS_PHASE_REPLY_HAVE_LEN ? socks5_reply.addrlen + sizeof( in_port_t ) : 0 ); + + /* notice the peek, we're doing this till enough is there */ + ASSERTSOCKOP( ret = recv( fd, &socks5_reply, expectedbytes, MSG_PEEK ) , "Peeking for SOCKS5 CONNECT reply" ); + + if ( ret == 0 ) + return jabber_bs_handshake_abort( bhd , "peer has shutdown connection" ); + + /* come again */ + if ( ret < expectedbytes ) + return TRUE; + + if ( bhd->phase == BS_PHASE_REPLY ) + { + if( !( socks5_reply.hdr.ver == 5 ) || + !( socks5_reply.hdr.cmdrep.rep == 0 ) || + !( socks5_reply.hdr.atyp == 3 ) || + !( socks5_reply.addrlen <= 62 ) ) /* should also be 40, but who cares as long as all fits in the buffer... */ + return jabber_bs_handshake_abort( bhd, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", + socks5_reply.hdr.ver, + socks5_reply.hdr.cmdrep.rep, + socks5_reply.hdr.atyp, + socks5_reply.addrlen); + + /* and again for the rest */ + bhd->phase = BS_PHASE_REPLY_HAVE_LEN; + + /* since it's very likely that the rest is there as well, + * let's not wait for the event loop to call us again */ + return jabber_bs_handshake( bhd , fd, 0 ); + } + + /* got it all, remove it from the queue */ + ASSERTSOCKOP( ret = recv( fd, &socks5_reply, expectedbytes, 0 ) , "Dequeueing MSG_PEEK'ed data after SOCKS5 CONNECT" ); + + /* this shouldn't happen */ + if ( ret < expectedbytes ) + return jabber_bs_handshake_abort( bhd, "internal error, couldn't dequeue MSG_PEEK'ed data after SOCKS5 CONNECT" ); + + /* we're actually done now... */ + + jabber_bs_answer_request( bhd ); + + bhd->tf->watch_in = 0; + return FALSE; + } + default: + /* BUG */ + imcb_log( bhd->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + + bhd->tf->watch_in = 0; + return FALSE; + } +#undef ASSERTSOCKOP +#undef JABBER_BS_ERR_CONDS +} + +/* + * If the handshake failed we can try the next streamhost, if there is one. + * An intelligent sender would probably specify himself as the first streamhost and + * a proxy as the second (Kopete is an example here). That way, a (potentially) + * slow proxy is only used if neccessary. + */ +gboolean jabber_bs_handshake_abort( struct bs_handshake_data *bhd, char *format, ... ) +{ + struct jabber_transfer *tf = bhd->tf; + struct xt_node *reply, *iqnode; + + if( bhd->shnode ) + { + if( format ) { + va_list params; + va_start( params, format ); + char error[128]; + + if( vsnprintf( error, 128, format, params ) < 0 ) + sprintf( error, "internal error parsing error string (BUG)" ); + va_end( params ); + + imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", + tf->ft->file_name, + xt_find_attr( bhd->shnode, "host" ), + xt_find_attr( bhd->shnode, "port" ), + error ); + } + + /* Alright, this streamhost failed, let's try the next... */ + bhd->phase = BS_PHASE_CONNECT; + bhd->shnode = bhd->shnode->next; + + /* the if is not neccessary but saves us one recursion */ + if( bhd->shnode ) + return jabber_bs_handshake( bhd, 0, 0 ); + } + + /* out of stream hosts */ + + iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); + reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" ); + xt_free_node( iqnode ); + + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); + xt_free_node( reply ); + + imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); + + bhd->tf->watch_in = 0; + return FALSE; +} + +/* + * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose. + * If he is the streamhost himself, he might already know that. However, if it's a proxy, + * the initiator will have to make a connection himself. + */ +void jabber_bs_answer_request( struct bs_handshake_data *bhd ) +{ + struct jabber_transfer *tf = bhd->tf; + struct xt_node *reply; + + imcb_log( tf->ic, "Transferring file %s: established SOCKS5 connection to %s:%s", + tf->ft->file_name, + xt_find_attr( bhd->shnode, "host" ), + xt_find_attr( bhd->shnode, "port" ) ); + + tf->ft->data = tf; + tf->ft->started = time( NULL ); + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_read, tf ); + tf->ft->out_of_data = jabber_bs_out_of_data; + + reply = xt_new_node( "streamhost-used", NULL, NULL ); + xt_add_attr( reply, "jid", xt_find_attr( bhd->shnode, "jid" ) ); + + reply = xt_new_node( "query", NULL, reply ); + xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); + + reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply ); + + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_file_canceled( tf->ft, "Error transmitting bytestream response" ); + xt_free_node( reply ); +} + +/* Reads till it is unscheduled or the receiver signifies an overflow. */ +gboolean jabber_bs_read( gpointer data, gint fd, b_input_condition cond ) +{ + int ret; + struct jabber_transfer *tf = data; + char *buffer = g_malloc( JABBER_BS_BUFSIZE ); + + if (tf->receiver_overflow) + { + if( tf->watch_in ) + { + /* should never happen, BUG */ + imcb_file_canceled( tf->ft, "Bug in jabber file transfer code: read while overflow is true. Please report" ); + return FALSE; + } + } + + ret = recv( fd, buffer, JABBER_BS_BUFSIZE, 0 ); + + if( ret == -1 ) + { + /* shouldn't actually happen */ + if( errno == EAGAIN ) + return TRUE; + + imcb_file_canceled( tf->ft, "Error reading tcp socket" ); /* , strerror( errnum ) */ + + return FALSE; + } + + /* that should be all */ + if( ret == 0 ) + return FALSE; + + tf->bytesread += ret; + + buffer = g_realloc( buffer, ret ); + + if ( ( tf->receiver_overflow = imcb_file_write( tf->ft, buffer, ret ) ) ) + { + /* wait for imcb to run out of data */ + tf->watch_in = 0; + return FALSE; + } + + + return TRUE; +} + +/* imcb callback that is invoked when it runs out of data. + * We reschedule jabber_bs_read here if neccessary. */ +void jabber_bs_out_of_data( file_transfer_t *ft ) +{ + struct jabber_transfer *tf = ft->data; + + tf->receiver_overflow = FALSE; + + if ( !tf->watch_in ) + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_read, tf ); +} + +/* Bad luck */ +void jabber_bs_canceled( file_transfer_t *ft , char *reason ) +{ + struct jabber_transfer *tf = ft->data; + + imcb_log( tf->ic, "File transfer aborted: %s", reason ); +} diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 0e890464..8651754a 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -42,6 +42,7 @@ #include "account.h" #include "proxy.h" #include "md5.h" +#include "ft.h" #define BUF_LEN MSG_LEN #define BUF_LONG ( BUF_LEN * 2 ) diff --git a/root_commands.c b/root_commands.c index 642f5374..2da77519 100644 --- a/root_commands.c +++ b/root_commands.c @@ -974,6 +974,71 @@ static void cmd_join_chat( irc_t *irc, char **cmd ) } } +static void cmd_transfers( irc_t *irc, char **cmd ) +{ + GSList *files = irc->file_transfers; + enum { LIST, REJECT, CANCEL }; + int subcmd = LIST; + int fid; + + if( !files ) + { + irc_usermsg( irc, "No pending transfers" ); + return; + } + + if( cmd[1] && + ( strcmp( cmd[1], "reject" ) == 0 ) ) + { + subcmd = REJECT; + } + else if( cmd[1] && + ( strcmp( cmd[1], "cancel" ) == 0 ) && + cmd[2] && + ( fid = atoi( cmd[2] ) ) ) + { + subcmd = CANCEL; + } + + for( ; files; files = g_slist_next( files ) ) + { + file_transfer_t *file = files->data; + + switch( subcmd ) { + case LIST: + if ( file->status == FT_STATUS_LISTENING ) + irc_usermsg( irc, + "Pending file(id %d): %s (Listening...)", file->local_id, file->file_name); + else + { + int kb_per_s = 0; + time_t diff = time( NULL ) - file->started; + if ( ( file->started > 0 ) && ( file->bytes_transferred > 0 ) ) + kb_per_s = file->bytes_transferred / 1024 / diff; + + irc_usermsg( irc, + "Pending file(id %d): %s (%10zd/%zd kb, %d kb/s)", file->local_id, file->file_name, + file->bytes_transferred/1024, file->file_size/1024, kb_per_s); + } + break; + case REJECT: + if( file->status == FT_STATUS_LISTENING ) + { + irc_usermsg( irc, "Rejecting file transfer for %s", file->file_name ); + imcb_file_canceled( file, "Denied by user" ); + } + break; + case CANCEL: + if( file->local_id == fid ) + { + irc_usermsg( irc, "Canceling file transfer for %s", file->file_name ); + imcb_file_canceled( file, "Canceled by user" ); + } + break; + } + } +} + const command_t commands[] = { { "help", 0, cmd_help, 0 }, { "identify", 1, cmd_identify, 0 }, @@ -994,5 +1059,6 @@ const command_t commands[] = { { "nick", 1, cmd_nick, 0 }, { "qlist", 0, cmd_qlist, 0 }, { "join_chat", 2, cmd_join_chat, 0 }, + { "transfers", 0, cmd_transfers, 0 }, { NULL } }; -- cgit v1.2.3 From 2ff20765990c756533957e8da9c7c29dd3102e79 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 3 Dec 2007 15:28:45 +0100 Subject: Intermediate commit. Sending seems to work. TODOs: * move from out_of_data to is_writable, eliminate buffers * implement "transfers reject [id]" * documentation in commands.xml * implement throughput and cummulative throughput boundaries * feature discovery before sending * implement sending over a proxy (proxy discovery, socks5 client handshake for sending, activate message) * integrate toxik-mek-ft --- dcc.c | 312 ++++++++++++-- dcc.h | 6 +- doc/user-guide/commands.xml | 43 ++ irc.c | 16 +- protocols/ft.h | 15 +- protocols/jabber/Makefile | 2 +- protocols/jabber/iq.c | 2 +- protocols/jabber/jabber.c | 1 + protocols/jabber/jabber.h | 13 +- protocols/jabber/jabber_util.c | 7 + protocols/jabber/s5bytestream.c | 906 ++++++++++++++++++++++++++++++++++++++++ protocols/jabber/si.c | 191 ++++++++- protocols/jabber/stream.c | 593 -------------------------- protocols/nogaim.h | 3 + 14 files changed, 1460 insertions(+), 650 deletions(-) create mode 100644 protocols/jabber/s5bytestream.c delete mode 100644 protocols/jabber/stream.c diff --git a/dcc.c b/dcc.c index 73fe0180..a5c77ce8 100644 --- a/dcc.c +++ b/dcc.c @@ -27,6 +27,7 @@ #include "dcc.h" #include #include +#include /* * Since that might be confusing a note on naming: @@ -75,6 +76,9 @@ static void dcc_close( file_transfer_t *file ); gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ); int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); +gboolean dccs_recv_start( file_transfer_t *ft ); +gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); +void dccs_recv_out_of_data( file_transfer_t *ft ); /* As defined in ft.h */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) @@ -103,31 +107,44 @@ gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size return dccs_send_write( file, data, data_size ); } +/* As defined in ft.h */ +gboolean imcb_file_recv_start( file_transfer_t *ft ) +{ + return dccs_recv_start( ft ); +} + +dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic ) +{ + file_transfer_t *file = g_new0( file_transfer_t, 1 ); + dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1); + file->file_size = file_size; + file->file_name = g_strdup( file_name ); + file->local_id = local_transfer_id++; + df->ic = ic; + df->ft = file; + + return df; +} + /* This is where the sending magic starts... */ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ) { file_transfer_t *file; dcc_file_transfer_t *df; - struct sockaddr_storage **saddr; + struct sockaddr_storage *saddr; if( file_size > global.conf->max_filetransfer_size ) return NULL; - /* alloc stuff */ - file = g_new0( file_transfer_t, 1 ); - file->priv = df = g_new0( dcc_file_transfer_t, 1); - file->file_size = file_size; - file->file_name = g_strdup( file_name ); - file->local_id = local_transfer_id++; - df->ic = ic; - df->ft = file; - + df = dcc_alloc_transfer( file_name, file_size, ic ); + file = df->ft; + /* listen and request */ - if( !dcc_listen( df, saddr ) || - !dccs_send_request( df, user_nick, *saddr ) ) + if( !dcc_listen( df, &saddr ) || + !dccs_send_request( df, user_nick, saddr ) ) return NULL; - g_free( *saddr ); + g_free( saddr ); /* watch */ df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); @@ -260,25 +277,15 @@ gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_pt } /* - * After setup, the transfer itself is handled entirely by this function. - * There are basically four things to handle: connect, receive, send, and error. + * Checks poll(), same for receiving and sending */ -gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) +gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents ) { - dcc_file_transfer_t *df = data; - file_transfer_t *file = df->ft; struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT }; - short revents; - - if ( poll( &pfd, 1, 0 ) == -1 ) - { - imcb_log( df->ic, "poll() failed, weird!" ); - revents = 0; - }; - revents = pfd.revents; + ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" ) - if( revents & POLLERR ) + if( pfd.revents & POLLERR ) { int sockerror; socklen_t errlen = sizeof( sockerror ); @@ -289,9 +296,44 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return dcc_abort( df, "Socket error: %s", strerror( sockerror ) ); } - if( revents & POLLHUP ) + if( pfd.revents & POLLHUP ) return dcc_abort( df, "Remote end closed connection" ); + *revents = pfd.revents; + + return TRUE; +} + +gboolean dcc_check_maxseg( dcc_file_transfer_t *df, int fd ) +{ +#ifdef DCC_SEND_AHEAD + /* + * use twice the maximum segment size as a maximum for calls to send(). + */ + if( max_packet_size == 0 ) + { + unsigned int mpslen = sizeof( max_packet_size ); + if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) ) + return dcc_abort( df, "getsockopt() failed" ); + max_packet_size *= 2; + } +#endif + return TRUE; +} + +/* + * After setup, the transfer itself is handled entirely by this function. + * There are basically four things to handle: connect, receive, send, and error. + */ +gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) +{ + dcc_file_transfer_t *df = data; + file_transfer_t *file = df->ft; + short revents; + + if( !dcc_poll( df, fd, &revents) ) + return FALSE; + if( ( revents & POLLIN ) && ( file->status & FT_STATUS_LISTENING ) ) { @@ -304,21 +346,12 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) closesocket( fd ); fd = df->fd; - file->status = FT_STATUS_TRANSFERING; + file->status = FT_STATUS_TRANSFERRING; sock_make_nonblocking( fd ); -#ifdef DCC_SEND_AHEAD - /* - * use twice the maximum segment size as a maximum for calls to send(). - */ - if( max_packet_size == 0 ) - { - unsigned int mpslen = sizeof( max_packet_size ); - if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) ) - return dcc_abort( df, "getsockopt() failed" ); - max_packet_size *= 2; - } -#endif + if ( !dcc_check_maxseg( df, fd ) ) + return FALSE; + /* IM protocol callback */ if( file->accept ) @@ -452,6 +485,113 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return TRUE; } +gboolean dccs_recv_start( file_transfer_t *ft ) +{ + dcc_file_transfer_t *df = ft->priv; + struct sockaddr_storage *saddr = &df->saddr; + int fd; + socklen_t sa_len = saddr->ss_family == AF_INET ? + sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 ); + + if( !ft->write ) + return dcc_abort( df, "Protocol didn't register write()" ); + + ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" ); + + sock_make_nonblocking( fd ); + + if( ( connect( fd, (struct sockaddr *)saddr, sa_len ) == -1 ) && + ( errno != EINPROGRESS ) ) + return dcc_abort( df, "Connecting" ); + + ft->status = FT_STATUS_CONNECTING; + + /* watch */ + df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df ); + ft->out_of_data = dccs_recv_out_of_data; + + return TRUE; +} + +gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) +{ + dcc_file_transfer_t *df = data; + file_transfer_t *ft = df->ft; + short revents; + + if( !dcc_poll( df, fd, &revents ) ) + return FALSE; + + if( ( revents & POLLOUT ) && + ( ft->status & FT_STATUS_CONNECTING ) ) + { + ft->status = FT_STATUS_TRANSFERRING; + if ( !dcc_check_maxseg( df, fd ) ) + return FALSE; + + //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + + df->watch_out = 0; + return FALSE; + } + + if( revents & POLLIN ) + { + char *buffer = g_malloc( 65536 ); + int ret, done; + + ASSERTSOCKOP( ret = recv( fd, buffer, 65536, 0 ), "Receiving" ); + + if( ret == 0 ) + return dcc_abort( df, "Remote end closed connection" ); + + buffer = g_realloc( buffer, ret ); + + df->bytes_sent += ret; + + done = df->bytes_sent >= ft->file_size; + + if( ( ( df->bytes_sent - ft->bytes_transferred ) > DCC_PACKET_SIZE ) || + done ) + { + int ack, ackret; + ack = htonl( ft->bytes_transferred = df->bytes_sent ); + + ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" ); + + if ( ackret != 4 ) + return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ret ); + } + + if( !ft->write( df->ft, buffer, ret ) && !done ) + { + df->watch_in = 0; + return FALSE; + } + + if( done ) + { + closesocket( fd ); + dcc_finish( ft ); + + df->watch_in = 0; + return FALSE; + } + + return TRUE; + } + + return TRUE; +} + +void dccs_recv_out_of_data( file_transfer_t *ft ) +{ + dcc_file_transfer_t *df = ft->priv; + + if( !df->watch_in ) + df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); +} + /* * Incoming data. Note that the buffer MUST NOT be freed by the caller! * We don't copy the buffer but put it in our queue. @@ -471,7 +611,7 @@ gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int dat df->queued_bytes += data_size; - if( ( file->status & FT_STATUS_TRANSFERING ) && + if( ( file->status & FT_STATUS_TRANSFERRING ) && #ifndef DCC_SEND_AHEAD ( file->bytes_transferred >= df->bytes_sent ) && #endif @@ -532,3 +672,91 @@ void dcc_finish( file_transfer_t *file ) dcc_close( file ); } + +/* + * DCC SEND + * + * filename can be in "" or not. If it is, " can probably be escaped... + * IP can be an unsigned int (IPV4) or something else (IPV6) + * + */ +file_transfer_t *dcc_request( struct im_connection *ic, char *line ) +{ + char *pattern = "SEND" + " (([^\"][^ ]*)|\"([^\"]|\\\")*\")" + " (([0-9]*)|([^ ]*))" + " ([0-9]*)" + " ([0-9]*)\001"; + regmatch_t pmatch[9]; + regex_t re; + file_transfer_t *ft; + dcc_file_transfer_t *df; + + if( regcomp( &re, pattern, REG_EXTENDED ) ) + return NULL; + if( regexec( &re, line, 9, pmatch, 0 ) ) + return NULL; + + if( ( pmatch[1].rm_so > 0 ) && + ( pmatch[4].rm_so > 0 ) && + ( pmatch[7].rm_so > 0 ) && + ( pmatch[8].rm_so > 0 ) ) + { + char *input = g_strdup( line ); + char *filename, *host, *port; + size_t filesize; + struct addrinfo hints, *rp; + + /* "filename" or filename */ + if ( pmatch[2].rm_so > 0 ) + { + input[pmatch[2].rm_eo] = '\0'; + filename = input + pmatch[2].rm_so; + } else + { + input[pmatch[3].rm_eo] = '\0'; + filename = input + pmatch[3].rm_so; + } + + input[pmatch[4].rm_eo] = '\0'; + + /* number means ipv4, something else means ipv6 */ + if ( pmatch[5].rm_so > 0 ) + { + struct in_addr ipaddr = { htonl( atoi( input + pmatch[5].rm_so ) ) }; + host = inet_ntoa( ipaddr ); + } else + { + /* Contains non-numbers, hopefully an IPV6 address */ + host = input + pmatch[6].rm_so; + } + + input[pmatch[7].rm_eo] = '\0'; + input[pmatch[8].rm_eo] = '\0'; + + port = input + pmatch[7].rm_so; + filesize = atoll( input + pmatch[8].rm_so ); + + memset( &hints, 0, sizeof ( struct addrinfo ) ); + if ( getaddrinfo( host, port, &hints, &rp ) ) + { + g_free( input ); + return NULL; + } + + df = dcc_alloc_transfer( filename, filesize, ic ); + ft = df->ft; + ft->sending = TRUE; + memcpy( &df->saddr, rp->ai_addr, rp->ai_addrlen ); + + freeaddrinfo( rp ); + g_free( input ); + + df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, ft ); + + return ft; + } + + return NULL; +} + diff --git a/dcc.h b/dcc.h index 205107c5..d5805a1f 100644 --- a/dcc.h +++ b/dcc.h @@ -111,9 +111,12 @@ typedef struct dcc_file_transfer { */ size_t bytes_sent; - /* imcb's handle */ + /* imc's handle */ file_transfer_t *ft; + /* if we're receiving, this is the sender's socket address */ + struct sockaddr_storage saddr; + } dcc_file_transfer_t; file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ); @@ -122,4 +125,5 @@ void dcc_canceled( file_transfer_t *file, char *reason ); gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size ); +file_transfer_t *dcc_request( struct im_connection *ic, char *line ); #endif diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index ac9bdf11..8c874014 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -860,4 +860,47 @@ + + + Monitor, cancel, or reject file transfers + transfers [<cancel> id | <reject>] + + + + Without parameters the currently pending file transfers and their status will be listed. Available actions are cancel and reject. See help transfers <action> for more information. + + + + transfers + + + + + Cancels the file transfer with the given id + transfers <cancel> id + + + Cancels the file transfer with the given id + + + + transfers cancel 1 + Canceling file transfer for test + + + + + Rejects all incoming transfers + transfers <reject> + + + Rejects all incoming (not already transferring) file transfers. Since you probably have only one incoming transfer at a time, no id is neccessary. Or is it? + + + + transfers reject + + + + diff --git a/irc.c b/irc.c index 5531addb..96f75073 100644 --- a/irc.c +++ b/irc.c @@ -27,6 +27,10 @@ #include "bitlbee.h" #include "crypting.h" #include "ipc.h" +#include "dcc.h" + +#include +#include static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); @@ -980,9 +984,19 @@ int irc_send( irc_t *irc, char *nick, char *s, int flags ) } return( 1 ); } + else if( g_strncasecmp( s + 1, "DCC", 3 ) == 0 ) + { + if( u && u->ic && u->ic->acc->prpl->transfer_request ) + { + file_transfer_t *ft = dcc_request( u->ic, s + 5 ); + if ( ft ) + u->ic->acc->prpl->transfer_request( u->ic, ft, u->handle ); + } + return( 1 ); + } else { - irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" ); + irc_usermsg( irc, "Supported CTCPs are ACTION, VERSION, PING, TYPING, DCC" ); return( 0 ); } } diff --git a/protocols/ft.h b/protocols/ft.h index 0ff44873..d41eb6c1 100644 --- a/protocols/ft.h +++ b/protocols/ft.h @@ -28,9 +28,10 @@ typedef enum { FT_STATUS_LISTENING = 1, - FT_STATUS_TRANSFERING = 2, + FT_STATUS_TRANSFERRING = 2, FT_STATUS_FINISHED = 4, - FT_STATUS_CANCELED = 8 + FT_STATUS_CANCELED = 8, + FT_STATUS_CONNECTING = 16 } file_status_t; /* @@ -60,6 +61,10 @@ typedef enum { * \------------------------/ */ typedef struct file_transfer { + + /* Are we sending something? */ + int sending; + /* * The current status of this file transfer. */ @@ -130,6 +135,11 @@ typedef struct file_transfer { */ void (*out_of_data) ( struct file_transfer *file ); + /* + * When sending files, protocols register this function to receive data. + */ + gboolean (*write) (struct file_transfer *file, char *buffer, int len ); + } file_transfer_t; /* @@ -150,4 +160,5 @@ void imcb_file_canceled( file_transfer_t *file, char *reason ); */ gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size ); +gboolean imcb_file_recv_start( file_transfer_t *ft ); #endif diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index 47c832ae..21d7ef07 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o si.o stream.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o si.o s5bytestream.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index df0102b8..77def222 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -167,7 +167,7 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) { /* Bytestream Request (stage 2 of file transfer) */ - return jabber_bs_request( ic, node, c ); + return jabber_bs_recv_request( ic, node, c ); } else { xt_free_node( reply ); diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index b0651a59..1f4f42ea 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -501,6 +501,7 @@ void jabber_initmodule() ret->keepalive = jabber_keepalive; ret->send_typing = jabber_send_typing; ret->handle_cmp = g_strcasecmp; + ret->transfer_request = jabber_si_transfer_request; register_protocol( ret ); } diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 0cb2b733..cb52d396 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -147,6 +147,7 @@ struct jabber_transfer size_t bytesread, byteswritten; int receiver_overflow; int fd; + struct sockaddr_storage saddr; }; #define JABBER_XMLCONSOLE_HANDLE "xmlconsole" @@ -200,10 +201,14 @@ int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); int jabber_remove_from_roster( struct im_connection *ic, char *handle ); /* si.c */ -int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode); - -/* stream.c */ -int jabber_bs_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); +int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode ); +void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ); +void jabber_si_free_transfer( file_transfer_t *ft); + +/* s5bytestream.c */ +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); +gboolean jabber_bs_send_start( struct jabber_transfer *tf ); +gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, int len ); /* message.c */ xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 0c5b813e..6bb65878 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -264,7 +264,14 @@ char *jabber_normalize( const char *orig ) len = strlen( orig ); new = g_new( char, len + 1 ); for( i = 0; i < len; i ++ ) + { + /* don't normalize the resource */ + if( orig[i] == '/' ) + break; new[i] = tolower( orig[i] ); + } + for( ; i < len; i ++ ) + new[i] = orig[i]; new[i] = 0; return new; diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c new file mode 100644 index 00000000..e2f32bd0 --- /dev/null +++ b/protocols/jabber/s5bytestream.c @@ -0,0 +1,906 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - SOCKS5 Bytestreams ( XEP-0065 ) * +* * +* Copyright 2007 Uli Meis * +* * +* 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 along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "jabber.h" +#include "sha1.h" +#include + +struct bs_transfer { + + struct jabber_transfer *tf; + + /* element and elements */ + struct xt_node *qnode, *shnode; + + enum + { + BS_PHASE_CONNECT, + BS_PHASE_CONNECTED, + BS_PHASE_REQUEST, + BS_PHASE_REPLY + } phase; + + /* SHA1( SID + Initiator JID + Target JID) */ + char *pseudoadr; + + gint connect_timeout; +}; + +struct socks5_message +{ + unsigned char ver; + union + { + unsigned char cmd; + unsigned char rep; + } cmdrep; + unsigned char rsv; + unsigned char atyp; + unsigned char addrlen; + unsigned char address[40]; + in_port_t port; +} __attribute__ ((packed)); + +/* connect() timeout in seconds. */ +#define JABBER_BS_CONTIMEOUT 15 +/* listen timeout */ +#define JABBER_BS_LISTEN_TIMEOUT 90 + +/* very useful */ +#define ASSERTSOCKOP(op, msg) \ + if( (op) == -1 ) \ + return jabber_bs_abort( bt , msg ": %s", strerror( errno ) ); + +#define JABBER_BS_BUFSIZE 65536 + +gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ); +void jabber_bs_canceled( file_transfer_t *ft , char *reason ); +void jabber_bs_free_transfer( file_transfer_t *ft); +gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ); +gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ); +gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ); + +void jabber_bs_recv_answer_request( struct bs_transfer *bt ); +gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ); +void jabber_bs_recv_out_of_data( file_transfer_t *ft ); +gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ); +gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ); +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); + +gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error ); +gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *port ); +gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ); +gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ); + +/* + * Frees a bs_transfer struct and calls the SI free function + */ +void jabber_bs_free_transfer( file_transfer_t *ft) { + struct jabber_transfer *tf = ft->data; + struct bs_transfer *bt = tf->streamhandle; + + if ( tf->watch_in ) + b_event_remove( tf->watch_in ); + + if( tf->watch_out ) + b_event_remove( tf->watch_out ); + + g_free( bt->pseudoadr ); + xt_free_node( bt->qnode ); + g_free( bt ); +//iq_id + jabber_si_free_transfer( ft ); +} + +gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ) +{ + int ret; + int fd = bt->tf->fd; + + ASSERTSOCKOP( ret = recv( fd, buffer, buflen, MSG_PEEK ), "MSG_PEEK'ing" ); + + if( ret == 0 ) + return jabber_bs_abort( bt, "Remote end closed connection" ); + + if( ret < buflen ) + return ret; + + ASSERTSOCKOP( ret = recv( fd, buffer, buflen, 0 ), "Dequeuing after MSG_PEEK" ); + + if( ret != buflen ) + return jabber_bs_abort( bt, "recv returned less than previous recv with MSG_PEEK" ); + + return ret; +} + + +/* + * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect(). + */ +gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ) +{ + struct bs_transfer *bt = data; + + bt->connect_timeout = 0; + + jabber_bs_abort( bt, "no connection after %d seconds", bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT ); + + return FALSE; +} + +gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ) +{ + struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; + + if ( bt->connect_timeout ) + { + b_event_remove( bt->connect_timeout ); + bt->connect_timeout = 0; + } + + ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" ) + + if( pfd.revents & POLLERR ) + { + int sockerror; + socklen_t errlen = sizeof( sockerror ); + + if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) + return jabber_bs_abort( bt, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" ); + + if ( bt->phase == BS_PHASE_CONNECTED ) + return jabber_bs_abort( bt, "connect failed: %s", strerror( sockerror ) ); + + return jabber_bs_abort( bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) ); + } + + if( pfd.revents & POLLHUP ) + return jabber_bs_abort( bt, "Remote end closed connection" ); + + *revents = pfd.revents; + + return TRUE; +} + +gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ) +{ + va_list params; + va_start( params, format ); + char error[128]; + + if( vsnprintf( error, 128, format, params ) < 0 ) + sprintf( error, "internal error parsing error string (BUG)" ); + va_end( params ); + if( bt->tf->ft->sending ) + return jabber_bs_recv_handshake_abort( bt, error ); + else + return jabber_bs_send_handshake_abort( bt, error ); +} + +/* Bad luck */ +void jabber_bs_canceled( file_transfer_t *ft , char *reason ) +{ + struct jabber_transfer *tf = ft->data; + + imcb_log( tf->ic, "File transfer aborted: %s", reason ); +} + +/* + * Parses an incoming bytestream request and calls jabber_bs_handshake on success. + */ +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) +{ + char *sid, *ini_jid, *tgt_jid, *mode, *iq_id; + struct jabber_data *jd = ic->proto_data; + struct jabber_transfer *tf = NULL; + GSList *tflist; + struct bs_transfer *bt; + + sha1_state_t sha; + char hash_hex[41]; + unsigned char hash[20]; + int i; + + if( !(iq_id = xt_find_attr( node, "id" ) ) || + !(ini_jid = xt_find_attr( node, "from" ) ) || + !(tgt_jid = xt_find_attr( node, "to" ) ) || + !(sid = xt_find_attr( qnode, "sid" ) ) ) + { + imcb_log( ic, "WARNING: Received incomplete SI bytestream request"); + return XT_HANDLED; + } + + if( ( mode = xt_find_attr( qnode, "mode" ) ) && + ( strcmp( mode, "tcp" ) != 0 ) ) + { + imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) ); + return XT_HANDLED; + } + + /* Let's see if we can find out what this bytestream should be for... */ + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) && + ( strcmp( tft->ini_jid, ini_jid ) == 0 ) && + ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if (!tf) + { + imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); + return XT_HANDLED; + } + + /* iq_id and canceled can be reused since SI is done */ + g_free( tf->iq_id ); + tf->iq_id = g_strdup( iq_id ); + + tf->ft->canceled = jabber_bs_canceled; + + /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ + sha1_init( &sha ); + sha1_append( &sha, (unsigned char*) sid, strlen( sid ) ); + sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) ); + sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) ); + sha1_finish( &sha, hash ); + + for( i = 0; i < 20; i ++ ) + sprintf( hash_hex + i * 2, "%02x", hash[i] ); + + bt = g_new0( struct bs_transfer, 1 ); + bt->tf = tf; + bt->qnode = xt_dup( qnode ); + bt->shnode = bt->qnode->children; + bt->phase = BS_PHASE_CONNECT; + bt->pseudoadr = g_strdup( hash_hex ); + tf->streamhandle = bt; + tf->ft->free = jabber_bs_free_transfer; + + jabber_bs_recv_handshake( bt, 0, 0 ); + + return XT_HANDLED; +} +/* + * This is what a protocol handshake can look like in cooperative multitasking :) + * Might be confusing at first because it's called from different places and is recursing. + * (places being the event thread, bs_request, bs_handshake_abort, and itself) + * + * All in all, it turned out quite nice :) + */ +gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ) +{ + + struct bs_transfer *bt = data; + short revents; + + if ( !jabber_bs_poll( bt, fd, &revents ) ) + return FALSE; + + switch( bt->phase ) + { + case BS_PHASE_CONNECT: + { + struct xt_node *c; + char *host, *port; + struct addrinfo hints, *rp; + + if( ( c = bt->shnode = xt_find_node( bt->shnode, "streamhost" ) ) && + ( port = xt_find_attr( c, "port" ) ) && + ( host = xt_find_attr( c, "host" ) ) && + xt_find_attr( c, "jid" ) ) + { + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + + if ( getaddrinfo( host, port, &hints, &rp ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) ); + + ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); + + sock_make_nonblocking( fd ); + + imcb_log( bt->tf->ic, "Transferring file %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, host, port ); + + if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && + ( errno != EINPROGRESS ) ) + return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) ); + + freeaddrinfo( rp ); + + bt->phase = BS_PHASE_CONNECTED; + + bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); + + /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ + bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + + bt->tf->watch_in = 0; + return FALSE; + } else + return jabber_bs_abort( bt, c ? "incomplete streamhost entry: host=%s port=%s jid=%s" : NULL, + host, port, xt_find_attr( c, "jid" ) ); + } + case BS_PHASE_CONNECTED: + { + struct { + unsigned char ver; + unsigned char nmethods; + unsigned char method; + } socks5_hello = { + .ver = 5, + .nmethods = 1, + .method = 0x00 /* no auth */ + /* one could also implement username/password. If you know + * a jabber client or proxy that actually does it, tell me. + */ + }; + + ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" ); + + bt->phase = BS_PHASE_REQUEST; + + bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_recv_handshake, bt ); + + bt->tf->watch_out = 0; + return FALSE; + } + case BS_PHASE_REQUEST: + { + struct socks5_message socks5_connect = + { + .ver = 5, + .cmdrep.cmd = 0x01, + .rsv = 0, + .atyp = 0x03, + .addrlen = strlen( bt->pseudoadr ), + .port = 0 + }; + int ret; + char buf[2]; + + /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ + ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" ); + + if( !( ret == 2 ) || + !( buf[0] == 5 ) || + !( buf[1] == 0 ) ) + return jabber_bs_abort( bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", + ret, buf[0], buf[1] ); + + /* copy hash into connect message */ + memcpy( socks5_connect.address, bt->pseudoadr, socks5_connect.addrlen ); + + ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_message ), 0 ) , "Sending SOCKS5 Connect" ); + + bt->phase = BS_PHASE_REPLY; + + return TRUE; + } + case BS_PHASE_REPLY: + { + struct socks5_message socks5_reply; + int ret; + + if ( !( ret = jabber_bs_peek( bt, &socks5_reply, sizeof( struct socks5_message ) ) ) ) + return FALSE; + + if ( ret < sizeof( socks5_reply ) ) + return TRUE; + + if( !( socks5_reply.ver == 5 ) || + !( socks5_reply.cmdrep.rep == 0 ) || + !( socks5_reply.atyp == 3 ) || + !( socks5_reply.addrlen == 40 ) ) + return jabber_bs_abort( bt, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", + socks5_reply.ver, + socks5_reply.cmdrep.rep, + socks5_reply.atyp, + socks5_reply.addrlen); + + jabber_bs_recv_answer_request( bt ); + + // reset in answer_request bt->tf->watch_in = 0; + return FALSE; + } + default: + /* BUG */ + imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + + bt->tf->watch_in = 0; + return FALSE; + } +} + +/* + * If the handshake failed we can try the next streamhost, if there is one. + * An intelligent sender would probably specify himself as the first streamhost and + * a proxy as the second (Kopete is an example here). That way, a (potentially) + * slow proxy is only used if neccessary. + */ +gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) +{ + struct jabber_transfer *tf = bt->tf; + struct xt_node *reply, *iqnode; + + if( bt->shnode ) + { + imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", + tf->ft->file_name, + xt_find_attr( bt->shnode, "host" ), + xt_find_attr( bt->shnode, "port" ), + error ); + + /* Alright, this streamhost failed, let's try the next... */ + bt->phase = BS_PHASE_CONNECT; + bt->shnode = bt->shnode->next; + + /* the if is not neccessary but saves us one recursion */ + if( bt->shnode ) + return jabber_bs_recv_handshake( bt, 0, 0 ); + } + + /* out of stream hosts */ + + iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); + reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" ); + xt_free_node( iqnode ); + + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); + xt_free_node( reply ); + + imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); + + bt->tf->watch_in = 0; + return FALSE; +} + +/* + * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose. + * If he is the streamhost himself, he might already know that. However, if it's a proxy, + * the initiator will have to make a connection himself. + */ +void jabber_bs_recv_answer_request( struct bs_transfer *bt ) +{ + struct jabber_transfer *tf = bt->tf; + struct xt_node *reply; + + imcb_log( tf->ic, "Transferring file %s: established SOCKS5 connection to %s:%s", + tf->ft->file_name, + xt_find_attr( bt->shnode, "host" ), + xt_find_attr( bt->shnode, "port" ) ); + + tf->ft->data = tf; + tf->ft->started = time( NULL ); + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, tf ); + tf->ft->out_of_data = jabber_bs_recv_out_of_data; + + reply = xt_new_node( "streamhost-used", NULL, NULL ); + xt_add_attr( reply, "jid", xt_find_attr( bt->shnode, "jid" ) ); + + reply = xt_new_node( "query", NULL, reply ); + xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); + + reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply ); + + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_file_canceled( tf->ft, "Error transmitting bytestream response" ); + xt_free_node( reply ); +} + +/* Reads till it is unscheduled or the receiver signifies an overflow. */ +gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) +{ + int ret; + struct jabber_transfer *tf = data; + struct bs_transfer *bt = tf->streamhandle; + char *buffer = g_malloc( JABBER_BS_BUFSIZE ); + + if (tf->receiver_overflow) + { + if( tf->watch_in ) + { + /* should never happen, BUG */ + imcb_file_canceled( tf->ft, "Bug in jabber file transfer code: read while overflow is true. Please report" ); + return FALSE; + } + } + + ASSERTSOCKOP( ret = recv( fd, buffer, JABBER_BS_BUFSIZE, 0 ) , "Receiving" ); + + /* that should be all */ + if( ret == 0 ) + return FALSE; + + tf->bytesread += ret; + + buffer = g_realloc( buffer, ret ); + + if ( ( tf->receiver_overflow = imcb_file_write( tf->ft, buffer, ret ) ) ) + { + /* wait for imcb to run out of data */ + tf->watch_in = 0; + return FALSE; + } + + return TRUE; +} + +/* imcb callback that is invoked when it runs out of data. + * We reschedule jabber_bs_read here if neccessary. */ +void jabber_bs_recv_out_of_data( file_transfer_t *ft ) +{ + struct jabber_transfer *tf = ft->data; + + tf->receiver_overflow = FALSE; + + if ( !tf->watch_in ) + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, tf ); +} + +/* signal ood and be done */ +gboolean jabber_bs_send_can_write( gpointer data, gint fd, b_input_condition cond ) +{ + struct bs_transfer *bt = data; + + bt->tf->ft->out_of_data( bt->tf->ft ); + + bt->tf->watch_out = 0; + return FALSE; +} + +/* try to send the stuff. If you can't return false and wait for writable */ +gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, int len ) +{ + struct jabber_transfer *tf = ft->data; + struct bs_transfer *bt = tf->streamhandle; + int ret; + + if ( ( ( ret = send( tf->fd, buffer, len, 0 ) ) == -1 ) && + ( errno != EAGAIN ) ) + return jabber_bs_abort( bt, "send failed on socket with: %s", strerror( errno ) ); + + if( ret == 0 ) + return jabber_bs_abort( bt, "Remote end closed connection" ); + + if( ret == -1 ) + { + bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); + return FALSE; + } + + return TRUE; +} + +static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { + struct jabber_transfer *tf = NULL; + struct jabber_data *jd = ic->proto_data; + struct bs_transfer *bt; + GSList *tflist; + struct xt_node *c; + char *sid, *jid; + + if( !( c = xt_find_node( node->children, "query" ) ) || + !( c = xt_find_node( c->children, "streamhost-used" ) ) || + !( jid = xt_find_attr( c, "jid" ) ) ) + + { + imcb_log( ic, "WARNING: Received incomplete bytestream reply" ); + return XT_HANDLED; + } + + if( !( c = xt_find_node( orig->children, "query" ) ) || + !( sid = xt_find_attr( c, "sid" ) ) ) + { + imcb_log( ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply" ); + return XT_HANDLED; + } + + /* Let's see if we can find out what this bytestream should be for... */ + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if( !tf ) + { + imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply to unknown request" ); + return XT_HANDLED; + } + + bt = tf->streamhandle; + + tf->accepted = TRUE; + + if( bt->phase == BS_PHASE_REPLY ) + { + tf->ft->started = time( NULL ); + tf->ft->out_of_data( tf->ft ); + } + + //bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_write, tf ); + + return XT_HANDLED; +} + +gboolean jabber_bs_send_start( struct jabber_transfer *tf ) +{ + char host[INET6_ADDRSTRLEN], port[6]; + struct bs_transfer *bt; + sha1_state_t sha; + char hash_hex[41]; + unsigned char hash[20]; + int i; + + /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ + sha1_init( &sha ); + sha1_append( &sha, (unsigned char*) tf->sid, strlen( tf->sid ) ); + sha1_append( &sha, (unsigned char*) tf->ini_jid, strlen( tf->ini_jid ) ); + sha1_append( &sha, (unsigned char*) tf->tgt_jid, strlen( tf->tgt_jid ) ); + sha1_finish( &sha, hash ); + + for( i = 0; i < 20; i ++ ) + sprintf( hash_hex + i * 2, "%02x", hash[i] ); + + bt = g_new0( struct bs_transfer, 1 ); + bt->tf = tf; + //bt->qnode = xt_dup( qnode ); + //bt->shnode = bt->qnode->children; + bt->phase = BS_PHASE_CONNECT; + bt->pseudoadr = g_strdup( hash_hex ); + tf->streamhandle = bt; + tf->ft->free = jabber_bs_free_transfer; + tf->ft->canceled = jabber_bs_canceled; + + if ( !jabber_bs_send_listen( bt, &tf->saddr, host, port ) ) + return FALSE; + + bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + return jabber_bs_send_request( tf, host, port ); +} + +gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *port ) +{ + struct xt_node *sh, *query, *iq; + + sh = xt_new_node( "streamhost", NULL, NULL ); + xt_add_attr( sh, "jid", tf->ini_jid ); + xt_add_attr( sh, "host", host ); + xt_add_attr( sh, "port", port ); + + query = xt_new_node( "query", NULL, NULL ); + xt_add_attr( query, "xmlns", XMLNS_BYTESTREAMS ); + xt_add_attr( query, "sid", tf->sid ); + xt_add_attr( query, "mode", "tcp" ); + xt_add_child( query, sh ); + + iq = jabber_make_packet( "iq", "set", tf->tgt_jid, query ); + xt_add_attr( iq, "from", tf->ini_jid ); + + //xt_free_node( query ); + + jabber_cache_add( tf->ic, iq, jabber_bs_send_handle_reply ); + + if( !jabber_write_packet( tf->ic, iq ) ) + imcb_file_canceled( tf->ft, "Error transmitting bytestream request" ); + return TRUE; +} + +gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) +{ + struct jabber_transfer *tf = bt->tf; + + imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", + tf->ft->file_name, + error ); + + imcb_file_canceled( tf->ft, error ); + + return FALSE; +} + +/* + * Creates a listening socket and returns it in saddr_ptr. + */ +gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ) +{ + struct jabber_transfer *tf = bt->tf; + int fd; + char hostname[ HOST_NAME_MAX + 1 ]; + struct addrinfo hints, *rp; + socklen_t ssize = sizeof( struct sockaddr_storage ); + + /* won't be long till someone asks for this to be configurable :) */ + + ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo()" ); + + memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); + + ASSERTSOCKOP( fd = tf->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); + + ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); + + freeaddrinfo( rp ); + + ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + + if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? + ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr + , host, INET6_ADDRSTRLEN ) ) + return jabber_bs_abort( bt, "inet_ntop failed on listening socket" ); + + ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + + if( saddr->ss_family == AF_INET ) + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); + else + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); + + return TRUE; +} + +/* + * SOCKS5BYTESTREAM protocol for the sender + */ +gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ) +{ + struct bs_transfer *bt = data; + struct jabber_transfer *tf = bt->tf; + short revents; + + if ( !jabber_bs_poll( bt, fd, &revents ) ) + return FALSE; + + switch( bt->phase ) + { + case BS_PHASE_CONNECT: + { + struct sockaddr_storage clt_addr; + socklen_t ssize = sizeof( clt_addr ); + + /* Connect */ + + ASSERTSOCKOP( tf->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + + closesocket( fd ); + fd = tf->fd; + sock_make_nonblocking( fd ); + + bt->phase = BS_PHASE_CONNECTED; + + bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + return FALSE; + } + case BS_PHASE_CONNECTED: + { + int ret, have_noauth=FALSE; + struct { + unsigned char ver; + unsigned char method; + } socks5_auth_reply = { .ver = 5, .method = 0 }; + struct { + unsigned char ver; + unsigned char nmethods; + unsigned char method; + } socks5_hello; + + if( !( ret = jabber_bs_peek( bt, &socks5_hello, sizeof( socks5_hello ) ) ) ) + return FALSE; + + if( ret < sizeof( socks5_hello ) ) + return TRUE; + + if( !( socks5_hello.ver == 5 ) || + !( socks5_hello.nmethods >= 1 ) || + !( socks5_hello.nmethods < 32 ) ) + return jabber_bs_abort( bt, "Invalid auth request ver=%d nmethods=%d method=%d", socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method ); + + have_noauth = socks5_hello.method == 0; + + if( socks5_hello.nmethods > 1 ) + { + char mbuf[32]; + int i; + ASSERTSOCKOP( ret = recv( fd, mbuf, socks5_hello.nmethods - 1, 0 ) , "Receiving auth methods" ); + if( ret < ( socks5_hello.nmethods - 1 ) ) + return jabber_bs_abort( bt, "Partial auth request"); + for( i = 0 ; !have_noauth && ( i < socks5_hello.nmethods - 1 ) ; i ++ ) + if( mbuf[i] == 0 ) + have_noauth = TRUE; + } + + if( !have_noauth ) + return jabber_bs_abort( bt, "Auth request didn't include no authentication" ); + + ASSERTSOCKOP( send( fd, &socks5_auth_reply, sizeof( socks5_auth_reply ) , 0 ), "Sending auth reply" ); + + bt->phase = BS_PHASE_REQUEST; + + return TRUE; + } + case BS_PHASE_REQUEST: + { + struct socks5_message socks5_connect; + int msgsize = sizeof( struct socks5_message ); + + if( !jabber_bs_peek( bt, &socks5_connect, msgsize ) ) + return FALSE; + + if( !( socks5_connect.ver == 5) || + !( socks5_connect.cmdrep.cmd == 1 ) || + !( socks5_connect.atyp == 3 ) || + !(socks5_connect.addrlen == 40 ) ) + return jabber_bs_abort( bt, "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)", socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd, socks5_connect.atyp ); + if( !( memcmp( socks5_connect.address, bt->pseudoadr, 40 ) == 0 ) ) + return jabber_bs_abort( bt, "SOCKS5 Connect message contained wrong digest"); + + socks5_connect.cmdrep.rep = 0; + + ASSERTSOCKOP( send( fd, &socks5_connect, msgsize, 0 ), "Sending connect reply" ); + + bt->phase = BS_PHASE_REPLY; + + /* don't start sending till the streamhost-used message comes in */ + if( tf->accepted ) + { + tf->ft->started = time( NULL ); + tf->ft->out_of_data( tf->ft ); + } + + tf->watch_in = 0; + return FALSE; + + } + default: + /* BUG */ + imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + + bt->tf->watch_in = 0; + return FALSE; + } +} +#undef ASSERTSOCKOP diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index d16f723a..598cbd03 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -25,8 +25,9 @@ #include "sha1.h" void jabber_si_answer_request( file_transfer_t *ft ); +int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf ); -/* imcb callback */ +/* file_transfer free() callback */ void jabber_si_free_transfer( file_transfer_t *ft) { struct jabber_transfer *tf = ft->data; @@ -49,7 +50,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) g_free( tf->sid ); } -/* imcb callback */ +/* file_transfer finished() callback */ void jabber_si_finished( file_transfer_t *ft ) { struct jabber_transfer *tf = ft->data; @@ -57,7 +58,7 @@ void jabber_si_finished( file_transfer_t *ft ) imcb_log( tf->ic, "File %s transferred successfully!" , ft->file_name ); } -/* imcb callback */ +/* file_transfer canceled() callback */ void jabber_si_canceled( file_transfer_t *ft, char *reason ) { struct jabber_transfer *tf = ft->data; @@ -77,6 +78,29 @@ void jabber_si_canceled( file_transfer_t *ft, char *reason ) } +void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ) +{ + struct jabber_transfer *tf; + struct jabber_data *jd = ic->proto_data; + + imcb_log( ic, "Incoming file from %s : %s %zd bytes", ic->irc->nick, ft->file_name, ft->file_size ); + + tf = g_new0( struct jabber_transfer, 1 ); + + tf->ic = ic; + tf->ft = ft; + tf->ft->data = tf; + tf->ft->free = jabber_si_free_transfer; + tf->ft->finished = jabber_si_finished; + ft->write = jabber_bs_send_write; + + jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); + + jabber_si_send_request( ic, who, tf ); + + imcb_file_recv_start( ft ); +} + /* * First function that gets called when a file transfer request comes in. * A lot to parse. @@ -135,6 +159,9 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st requestok = TRUE; break; } + + if ( !requestok ) + imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); } if ( requestok ) @@ -159,8 +186,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st } *s = '/'; - } else - imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); + } if ( !requestok ) { @@ -244,3 +270,158 @@ void jabber_si_answer_request( file_transfer_t *ft ) { tf->accepted = TRUE; xt_free_node( reply ); } + +static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ + struct xt_node *c, *d; + char *ini_jid, *tgt_jid; + GSList *tflist; + struct jabber_transfer *tf=NULL; + struct jabber_data *jd = ic->proto_data; + char *sid; + + if( !( tgt_jid = xt_find_attr( node, "from" ) ) || + !( ini_jid = xt_find_attr( node, "to" ) ) ) + { + imcb_log( ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid ); + return XT_HANDLED; + } + + imcb_log( ic, "GOT RESPONSE TO FILE" ); + /* All this means we expect something like this: ( I think ) + * + * + * + * + * + * + * + */ + if( !( tgt_jid = xt_find_attr( node, "from" ) ) || + !( ini_jid = xt_find_attr( node, "to" ) ) || + !( c = xt_find_node( node->children, "si" ) ) || + !( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) || + !( sid = xt_find_attr( c, "id" ) )|| + !( d = xt_find_node( c->children, "file" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || + !( d = xt_find_node( c->children, "feature" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || + !( d = xt_find_node( d->children, "x" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_XDATA ) == 0 ) || + !( strcmp( xt_find_attr( d, "type" ), "submit" ) == 0 ) || + !( d = xt_find_node( d->children, "field" ) ) || + !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) || + !( d = xt_find_node( d->children, "value" ) ) ) + { + imcb_log( ic, "WARNING: Received incomplete Stream Initiation response" ); + return XT_HANDLED; + } + + if( !( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) { + /* since we should only have advertised what we can do and the peer should + * only have chosen what we offered, this should never happen */ + imcb_log( ic, "WARNING: Received invalid Stream Initiation response, method %s", d->text ); + + return XT_HANDLED; + } + + /* Let's see if we can find out what this bytestream should be for... */ + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if (!tf) + { + imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); + return XT_HANDLED; + } + + tf->ini_jid = g_strdup( ini_jid ); + tf->tgt_jid = g_strdup( tgt_jid ); + + jabber_bs_send_start( tf ); + + return XT_HANDLED; +} + +int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf ) +{ + struct xt_node *node, *sinode; + struct jabber_buddy *bud; + + /* who knows how many bits the future holds :) */ + char filesizestr[ 1 + ( int ) ( 0.301029995663981198f * sizeof( size_t ) * 8 ) ]; + + const char *methods[] = + { + XMLNS_BYTESTREAMS, + //XMLNS_IBB, + NULL + }; + const char **m; + char *s; + + /* Maybe we should hash this? */ + tf->sid = g_strdup_printf( "BitlBeeJabberSID%d", tf->ft->local_id ); + + if( ( s = strchr( who, '=' ) ) && jabber_chat_by_name( ic, s + 1 ) ) + bud = jabber_buddy_by_ext_jid( ic, who, 0 ); + else + bud = jabber_buddy_by_jid( ic, who, 0 ); + + /* start with the SI tag */ + sinode = xt_new_node( "si", NULL, NULL ); + xt_add_attr( sinode, "xmlns", XMLNS_SI ); + xt_add_attr( sinode, "profile", XMLNS_FILETRANSFER ); + xt_add_attr( sinode, "id", tf->sid ); + +/* if( mimetype ) + xt_add_attr( node, "mime-type", mimetype ); */ + + /* now the file tag */ +/* if( desc ) + node = xt_new_node( "desc", descr, NULL ); */ + node = xt_new_node( "range", NULL, NULL ); + + sprintf( filesizestr, "%zd", tf->ft->file_size ); + node = xt_new_node( "file", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_FILETRANSFER ); + xt_add_attr( node, "name", tf->ft->file_name ); + xt_add_attr( node, "size", filesizestr ); +/* if (hash) + xt_add_attr( node, "hash", hash ); + if (date) + xt_add_attr( node, "date", date ); */ + + xt_add_child( sinode, node ); + + /* and finally the feature tag */ + node = xt_new_node( "field", NULL, NULL ); + xt_add_attr( node, "var", "stream-method" ); + xt_add_attr( node, "type", "list-single" ); + + for ( m = methods ; *m ; m ++ ) + xt_add_child( node, xt_new_node( "option", NULL, xt_new_node( "value", (char *)*m, NULL ) ) ); + + node = xt_new_node( "x", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_XDATA ); + xt_add_attr( node, "type", "form" ); + + node = xt_new_node( "feature", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_FEATURE ); + + xt_add_child( sinode, node ); + + /* and we are there... */ + node = jabber_make_packet( "iq", "set", bud ? bud->full_jid : who, sinode ); + jabber_cache_add( ic, node, jabber_si_handle_response ); + + return jabber_write_packet( ic, node ); +} diff --git a/protocols/jabber/stream.c b/protocols/jabber/stream.c deleted file mode 100644 index c88a72fd..00000000 --- a/protocols/jabber/stream.c +++ /dev/null @@ -1,593 +0,0 @@ -/***************************************************************************\ -* * -* BitlBee - An IRC to IM gateway * -* Jabber module - stream handling * -* * -* Copyright 2007 Uli Meis * -* * -* 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 along * -* with this program; if not, write to the Free Software Foundation, Inc., * -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -* * -\***************************************************************************/ - -#include "jabber.h" -#include "sha1.h" -#include - -/* Some structs for the SOCKS5 handshake */ - -struct bs_handshake_data { - - struct jabber_transfer *tf; - - /* element and elements */ - struct xt_node *qnode, *shnode; - - enum - { - BS_PHASE_CONNECT, - BS_PHASE_CONNECTED, - BS_PHASE_REQUEST, - BS_PHASE_REPLY, - BS_PHASE_REPLY_HAVE_LEN - } phase; - - /* SHA1( SID + Initiator JID + Target JID) */ - char *pseudoadr; - - void (*parentfree) ( file_transfer_t *ft ); - - gint connect_timeout; -}; - -struct socks5_hdr -{ - unsigned char ver; - union - { - unsigned char cmd; - unsigned char rep; - } cmdrep; - unsigned char rsv; - unsigned char atyp; -}; - -struct socks5_message -{ - struct socks5_hdr hdr; - unsigned char addrlen; - unsigned char address[64]; -}; - -/* connect() timeout in seconds. */ -#define JABBER_BS_CONTIMEOUT 15 - -/* shouldn't matter if it's mostly too much, kernel's smart about that - * and will only reserve some address space */ -#define JABBER_BS_BUFSIZE 65536 - -gboolean jabber_bs_handshake( gpointer data, gint fd, b_input_condition cond ); - -gboolean jabber_bs_handshake_abort( struct bs_handshake_data *bhd, char *format, ... ); - -void jabber_bs_answer_request( struct bs_handshake_data *bhd ); - -gboolean jabber_bs_read( gpointer data, gint fd, b_input_condition cond ); - -void jabber_bs_out_of_data( file_transfer_t *ft ); - -void jabber_bs_canceled( file_transfer_t *ft , char *reason ); - - -void jabber_bs_free_transfer( file_transfer_t *ft) { - struct jabber_transfer *tf = ft->data; - struct bs_handshake_data *bhd = tf->streamhandle; - void (*parentfree) ( file_transfer_t *ft ); - - parentfree = bhd->parentfree; - - if ( tf->watch_in ) - b_event_remove( tf->watch_in ); - - if( tf->watch_out ) - b_event_remove( tf->watch_out ); - - g_free( bhd->pseudoadr ); - xt_free_node( bhd->qnode ); - g_free( bhd ); - - parentfree( ft ); -} - -/* - * Parses an incoming bytestream request and calls jabber_bs_handshake on success. - */ -int jabber_bs_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) -{ - char *sid, *ini_jid, *tgt_jid, *mode, *iq_id; - struct jabber_data *jd = ic->proto_data; - struct jabber_transfer *tf = NULL; - GSList *tflist; - struct bs_handshake_data *bhd; - - sha1_state_t sha; - char hash_hex[41]; - unsigned char hash[20]; - int i; - - if( !(iq_id = xt_find_attr( node, "id" ) ) || - !(ini_jid = xt_find_attr( node, "from" ) ) || - !(tgt_jid = xt_find_attr( node, "to" ) ) || - !(sid = xt_find_attr( qnode, "sid" ) ) ) - { - imcb_log( ic, "WARNING: Received incomplete SI bytestream request"); - return XT_HANDLED; - } - - if( ( mode = xt_find_attr( qnode, "mode" ) ) && - ( strcmp( mode, "tcp" ) != 0 ) ) - { - imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) ); - return XT_HANDLED; - } - - /* Let's see if we can find out what this bytestream should be for... */ - - for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) - { - struct jabber_transfer *tft = tflist->data; - if( ( strcmp( tft->sid, sid ) == 0 ) && - ( strcmp( tft->ini_jid, ini_jid ) == 0 ) && - ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) ) - { - tf = tft; - break; - } - } - - if (!tf) - { - imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); - return XT_HANDLED; - } - - /* iq_id and canceled can be reused since SI is done */ - g_free( tf->iq_id ); - tf->iq_id = g_strdup( iq_id ); - - tf->ft->canceled = jabber_bs_canceled; - - /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ - sha1_init( &sha ); - sha1_append( &sha, (unsigned char*) sid, strlen( sid ) ); - sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) ); - sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) ); - sha1_finish( &sha, hash ); - - for( i = 0; i < 20; i ++ ) - sprintf( hash_hex + i * 2, "%02x", hash[i] ); - - bhd = g_new0( struct bs_handshake_data, 1 ); - bhd->tf = tf; - bhd->qnode = xt_dup( qnode ); - bhd->shnode = bhd->qnode->children; - bhd->phase = BS_PHASE_CONNECT; - bhd->pseudoadr = g_strdup( hash_hex ); - tf->streamhandle = bhd; - bhd->parentfree = tf->ft->free; - tf->ft->free = jabber_bs_free_transfer; - - jabber_bs_handshake( bhd, 0, 0 ); - - return XT_HANDLED; -} - -/* - * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect(). - */ -gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ) -{ - struct bs_handshake_data *bhd = data; - - bhd->connect_timeout = 0; - - jabber_bs_handshake_abort( bhd, "no connection after %d seconds", JABBER_BS_CONTIMEOUT ); - - return FALSE; -} - -/* - * This is what a protocol handshake can look like in cooperative multitasking :) - * Might be confusing at first because it's called from different places and is recursing. - * (places being the event thread, bs_request, bs_handshake_abort, and itself) - * - * All in all, it turned out quite nice :) - */ -gboolean jabber_bs_handshake( gpointer data, gint fd, b_input_condition cond ) -{ - -/* very useful */ -#define ASSERTSOCKOP(op, msg) \ - if( (op) == -1 ) \ - return jabber_bs_handshake_abort( bhd , msg ": %s", strerror( errno ) ); - - struct bs_handshake_data *bhd = data; - struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; - short revents; - - if ( bhd->connect_timeout ) - { - b_event_remove( bhd->connect_timeout ); - bhd->connect_timeout = 0; - } - - - /* we need the real io condition */ - if ( poll( &pfd, 1, 0 ) == -1 ) - { - imcb_log( bhd->tf->ic, "poll() failed, weird!" ); - revents = 0; - }; - - revents = pfd.revents; - - if( revents & POLLERR ) - { - int sockerror; - socklen_t errlen = sizeof( sockerror ); - - if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) - return jabber_bs_handshake_abort( bhd, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" ); - - if ( bhd->phase == BS_PHASE_CONNECTED ) - return jabber_bs_handshake_abort( bhd, "connect() failed: %s", strerror( sockerror ) ); - - return jabber_bs_handshake_abort( bhd, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) ); - } - - if( revents & POLLHUP ) - return jabber_bs_handshake_abort( bhd, "Remote end closed connection" ); - - - switch( bhd->phase ) - { - case BS_PHASE_CONNECT: - { - struct xt_node *c; - char *host, *port; - struct addrinfo hints, *rp; - - if( ( c = bhd->shnode = xt_find_node( bhd->shnode, "streamhost" ) ) && - ( port = xt_find_attr( c, "port" ) ) && - ( host = xt_find_attr( c, "host" ) ) && - xt_find_attr( c, "jid" ) ) - { - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; - - if ( getaddrinfo( host, port, &hints, &rp ) != 0 ) - return jabber_bs_handshake_abort( bhd, "getaddrinfo() failed: %s", strerror( errno ) ); - - ASSERTSOCKOP( bhd->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); - - sock_make_nonblocking( fd ); - - imcb_log( bhd->tf->ic, "Transferring file %s: Connecting to streamhost %s:%s", bhd->tf->ft->file_name, host, port ); - - if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && - ( errno != EINPROGRESS ) ) - return jabber_bs_handshake_abort( bhd , "connect() failed: %s", strerror( errno ) ); - - freeaddrinfo( rp ); - - bhd->phase = BS_PHASE_CONNECTED; - - bhd->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_handshake, bhd ); - - /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ - bhd->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bhd ); - - bhd->tf->watch_in = 0; - return FALSE; - } else - return jabber_bs_handshake_abort( bhd, c ? "incomplete streamhost entry: host=%s port=%s jid=%s" : NULL, - host, port, xt_find_attr( c, "jid" ) ); - } - case BS_PHASE_CONNECTED: - { - struct { - unsigned char ver; - unsigned char nmethods; - unsigned char method; - } socks5_hello = { - .ver = 5, - .nmethods = 1, - .method = 0x00 /* no auth */ - /* one could also implement username/password. If you know - * a jabber client or proxy that actually does it, tell me. - */ - }; - - ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" ); - - bhd->phase = BS_PHASE_REQUEST; - - bhd->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_handshake, bhd ); - - bhd->tf->watch_out = 0; - return FALSE; - } - case BS_PHASE_REQUEST: - { - struct socks5_message socks5_connect = - { - .hdr = - { - .ver = 5, - .cmdrep.cmd = 0x01, - .rsv = 0, - .atyp = 0x03 - }, - .addrlen = strlen( bhd->pseudoadr ) - }; - int ret; - char buf[2]; - - /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ - ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" ); - - if( !( ret == 2 ) || - !( buf[0] == 5 ) || - !( buf[1] == 0 ) ) - return jabber_bs_handshake_abort( bhd, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", - ret, buf[0], buf[1] ); - - /* copy hash into connect message */ - memcpy( socks5_connect.address, bhd->pseudoadr, socks5_connect.addrlen ); - - /* after the address comes the port, which is always 0 */ - memset( socks5_connect.address + socks5_connect.addrlen, 0, sizeof( in_port_t ) ); - - ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_hdr ) + 1 + socks5_connect.addrlen + sizeof( in_port_t ), 0 ) , "Sending SOCKS5 Connect" ); - - bhd->phase = BS_PHASE_REPLY; - - return TRUE; - } - case BS_PHASE_REPLY: - case BS_PHASE_REPLY_HAVE_LEN: - { - /* we have to wait till we have the address length, then we know how much data is left - * (not that we'd actually care about that data, but we need to eat it all up anyway) - */ - struct socks5_message socks5_reply; - int ret; - int expectedbytes = - sizeof( struct socks5_hdr ) + 1 + - ( bhd->phase == BS_PHASE_REPLY_HAVE_LEN ? socks5_reply.addrlen + sizeof( in_port_t ) : 0 ); - - /* notice the peek, we're doing this till enough is there */ - ASSERTSOCKOP( ret = recv( fd, &socks5_reply, expectedbytes, MSG_PEEK ) , "Peeking for SOCKS5 CONNECT reply" ); - - if ( ret == 0 ) - return jabber_bs_handshake_abort( bhd , "peer has shutdown connection" ); - - /* come again */ - if ( ret < expectedbytes ) - return TRUE; - - if ( bhd->phase == BS_PHASE_REPLY ) - { - if( !( socks5_reply.hdr.ver == 5 ) || - !( socks5_reply.hdr.cmdrep.rep == 0 ) || - !( socks5_reply.hdr.atyp == 3 ) || - !( socks5_reply.addrlen <= 62 ) ) /* should also be 40, but who cares as long as all fits in the buffer... */ - return jabber_bs_handshake_abort( bhd, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", - socks5_reply.hdr.ver, - socks5_reply.hdr.cmdrep.rep, - socks5_reply.hdr.atyp, - socks5_reply.addrlen); - - /* and again for the rest */ - bhd->phase = BS_PHASE_REPLY_HAVE_LEN; - - /* since it's very likely that the rest is there as well, - * let's not wait for the event loop to call us again */ - return jabber_bs_handshake( bhd , fd, 0 ); - } - - /* got it all, remove it from the queue */ - ASSERTSOCKOP( ret = recv( fd, &socks5_reply, expectedbytes, 0 ) , "Dequeueing MSG_PEEK'ed data after SOCKS5 CONNECT" ); - - /* this shouldn't happen */ - if ( ret < expectedbytes ) - return jabber_bs_handshake_abort( bhd, "internal error, couldn't dequeue MSG_PEEK'ed data after SOCKS5 CONNECT" ); - - /* we're actually done now... */ - - jabber_bs_answer_request( bhd ); - - bhd->tf->watch_in = 0; - return FALSE; - } - default: - /* BUG */ - imcb_log( bhd->tf->ic, "BUG in file transfer code: undefined handshake phase" ); - - bhd->tf->watch_in = 0; - return FALSE; - } -#undef ASSERTSOCKOP -#undef JABBER_BS_ERR_CONDS -} - -/* - * If the handshake failed we can try the next streamhost, if there is one. - * An intelligent sender would probably specify himself as the first streamhost and - * a proxy as the second (Kopete is an example here). That way, a (potentially) - * slow proxy is only used if neccessary. - */ -gboolean jabber_bs_handshake_abort( struct bs_handshake_data *bhd, char *format, ... ) -{ - struct jabber_transfer *tf = bhd->tf; - struct xt_node *reply, *iqnode; - - if( bhd->shnode ) - { - if( format ) { - va_list params; - va_start( params, format ); - char error[128]; - - if( vsnprintf( error, 128, format, params ) < 0 ) - sprintf( error, "internal error parsing error string (BUG)" ); - va_end( params ); - - imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", - tf->ft->file_name, - xt_find_attr( bhd->shnode, "host" ), - xt_find_attr( bhd->shnode, "port" ), - error ); - } - - /* Alright, this streamhost failed, let's try the next... */ - bhd->phase = BS_PHASE_CONNECT; - bhd->shnode = bhd->shnode->next; - - /* the if is not neccessary but saves us one recursion */ - if( bhd->shnode ) - return jabber_bs_handshake( bhd, 0, 0 ); - } - - /* out of stream hosts */ - - iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); - reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" ); - xt_free_node( iqnode ); - - xt_add_attr( reply, "id", tf->iq_id ); - - if( !jabber_write_packet( tf->ic, reply ) ) - imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); - xt_free_node( reply ); - - imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); - - bhd->tf->watch_in = 0; - return FALSE; -} - -/* - * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose. - * If he is the streamhost himself, he might already know that. However, if it's a proxy, - * the initiator will have to make a connection himself. - */ -void jabber_bs_answer_request( struct bs_handshake_data *bhd ) -{ - struct jabber_transfer *tf = bhd->tf; - struct xt_node *reply; - - imcb_log( tf->ic, "Transferring file %s: established SOCKS5 connection to %s:%s", - tf->ft->file_name, - xt_find_attr( bhd->shnode, "host" ), - xt_find_attr( bhd->shnode, "port" ) ); - - tf->ft->data = tf; - tf->ft->started = time( NULL ); - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_read, tf ); - tf->ft->out_of_data = jabber_bs_out_of_data; - - reply = xt_new_node( "streamhost-used", NULL, NULL ); - xt_add_attr( reply, "jid", xt_find_attr( bhd->shnode, "jid" ) ); - - reply = xt_new_node( "query", NULL, reply ); - xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); - - reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply ); - - xt_add_attr( reply, "id", tf->iq_id ); - - if( !jabber_write_packet( tf->ic, reply ) ) - imcb_file_canceled( tf->ft, "Error transmitting bytestream response" ); - xt_free_node( reply ); -} - -/* Reads till it is unscheduled or the receiver signifies an overflow. */ -gboolean jabber_bs_read( gpointer data, gint fd, b_input_condition cond ) -{ - int ret; - struct jabber_transfer *tf = data; - char *buffer = g_malloc( JABBER_BS_BUFSIZE ); - - if (tf->receiver_overflow) - { - if( tf->watch_in ) - { - /* should never happen, BUG */ - imcb_file_canceled( tf->ft, "Bug in jabber file transfer code: read while overflow is true. Please report" ); - return FALSE; - } - } - - ret = recv( fd, buffer, JABBER_BS_BUFSIZE, 0 ); - - if( ret == -1 ) - { - /* shouldn't actually happen */ - if( errno == EAGAIN ) - return TRUE; - - imcb_file_canceled( tf->ft, "Error reading tcp socket" ); /* , strerror( errnum ) */ - - return FALSE; - } - - /* that should be all */ - if( ret == 0 ) - return FALSE; - - tf->bytesread += ret; - - buffer = g_realloc( buffer, ret ); - - if ( ( tf->receiver_overflow = imcb_file_write( tf->ft, buffer, ret ) ) ) - { - /* wait for imcb to run out of data */ - tf->watch_in = 0; - return FALSE; - } - - - return TRUE; -} - -/* imcb callback that is invoked when it runs out of data. - * We reschedule jabber_bs_read here if neccessary. */ -void jabber_bs_out_of_data( file_transfer_t *ft ) -{ - struct jabber_transfer *tf = ft->data; - - tf->receiver_overflow = FALSE; - - if ( !tf->watch_in ) - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_read, tf ); -} - -/* Bad luck */ -void jabber_bs_canceled( file_transfer_t *ft , char *reason ) -{ - struct jabber_transfer *tf = ft->data; - - imcb_log( tf->ic, "File transfer aborted: %s", reason ); -} diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 8651754a..f17c5a1e 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -228,6 +228,9 @@ struct prpl { /* Mainly for AOL, since they think "Bung hole" == "Bu ngho le". *sigh* * - Most protocols will just want to set this to g_strcasecmp().*/ int (* handle_cmp) (const char *who1, const char *who2); + + /* Incoming transfer request */ + void (* transfer_request) (struct im_connection *, file_transfer_t *ft, char *handle ); }; /* im_api core stuff. */ -- cgit v1.2.3 From dce390357114e30a424106c99e49cef1e682e1af Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 4 Dec 2007 01:48:57 +0100 Subject: Send and receive seems to work now! Also adopted the new buffering strategy, only one buffer of 2k per transfer now. --- dcc.c | 207 ++++++++++------------------------------ dcc.h | 47 +-------- protocols/ft.h | 31 +++--- protocols/jabber/jabber.h | 3 +- protocols/jabber/s5bytestream.c | 137 ++++++++++++++------------ protocols/jabber/si.c | 23 ++--- 6 files changed, 160 insertions(+), 288 deletions(-) diff --git a/dcc.c b/dcc.c index a5c77ce8..24673085 100644 --- a/dcc.c +++ b/dcc.c @@ -60,16 +60,7 @@ unsigned int local_transfer_id=1; */ unsigned int receivedchunks=0, receiveddata=0; -/* - * If using DCC SEND AHEAD this value will be set before the first transfer starts. - * Not that in this case it degenerates to the maximum message size to send() and - * has nothing to do with packets. - */ -#ifdef DCC_SEND_AHEAD -int max_packet_size = DCC_PACKET_SIZE; -#else int max_packet_size = 0; -#endif static void dcc_finish( file_transfer_t *file ); static void dcc_close( file_transfer_t *file ); @@ -78,7 +69,7 @@ gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_pt int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); gboolean dccs_recv_start( file_transfer_t *ft ); gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); -void dccs_recv_out_of_data( file_transfer_t *ft ); +gboolean dccs_recv_write_request( file_transfer_t *ft ); /* As defined in ft.h */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) @@ -101,12 +92,6 @@ void imcb_file_canceled( file_transfer_t *file, char *reason ) dcc_close( file ); } -/* As defined in ft.h */ -gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size ) -{ - return dccs_send_write( file, data, data_size ); -} - /* As defined in ft.h */ gboolean imcb_file_recv_start( file_transfer_t *ft ) { @@ -138,6 +123,8 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df = dcc_alloc_transfer( file_name, file_size, ic ); file = df->ft; + file->write = dccs_send_write; + file->sending = TRUE; /* listen and request */ if( !dcc_listen( df, &saddr ) || @@ -194,13 +181,8 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc { struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr; - /* - * this is so ridiculous. We're supposed to convert the address to - * host byte order!!! Let's exclude anyone running big endian just - * for the fun of it... - */ sprintf( ipaddr, "%d", - htonl( saddr_ipv4->sin_addr.s_addr ) ); + ntohl( saddr_ipv4->sin_addr.s_addr ) ); port = saddr_ipv4->sin_port; } else { @@ -226,9 +208,6 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc g_free( cmd ); - /* message is sortof redundant cause the users client probably informs him about that. remove? */ - imcb_log( df->ic, "Transferring file %s: Chose local address %s for DCC connection", df->ft->file_name, ipaddr ); - return TRUE; } @@ -304,9 +283,11 @@ gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents ) return TRUE; } +/* + * fills max_packet_size with twice the TCP maximum segment size + */ gboolean dcc_check_maxseg( dcc_file_transfer_t *df, int fd ) { -#ifdef DCC_SEND_AHEAD /* * use twice the maximum segment size as a maximum for calls to send(). */ @@ -317,7 +298,6 @@ gboolean dcc_check_maxseg( dcc_file_transfer_t *df, int fd ) return dcc_abort( df, "getsockopt() failed" ); max_packet_size *= 2; } -#endif return TRUE; } @@ -353,9 +333,9 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return FALSE; /* IM protocol callback */ - if( file->accept ) file->accept( file ); + /* reschedule for reading on new fd */ df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df ); @@ -399,89 +379,9 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return FALSE; } -#ifndef DCC_SEND_AHEAD - /* reschedule writer if neccessary */ - if( file->bytes_transferred >= df->bytes_sent && - df->watch_out == 0 && - df->queued_bytes > 0 ) { - df->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, dcc_send_proto, df ); - } -#endif return TRUE; } - if( revents & POLLOUT ) - { - struct dcc_buffer *dccb; - int ret; - char *msg; - - if( df->queued_bytes == 0 ) - { - /* shouldn't happen */ - imcb_log( df->ic, "WARNING: DCC SEND: write called with empty queue" ); - - df->watch_out = 0; - return FALSE; - } - - /* start where we left off */ - if( !( df->queued_buffers ) || - !( dccb = df->queued_buffers->data ) ) - return dcc_abort( df, "BUG in DCC SEND: queued data but no buffers" ); - - msg = dccb->b + df->buffer_pos; - - int msgsize = MIN( -#ifndef DCC_SEND_AHEAD - file->bytes_transferred + MAX_PACKET_SIZE - df->bytes_sent, -#else - max_packet_size, -#endif - dccb->len - df->buffer_pos ); - - if ( msgsize == 0 ) - { - df->watch_out = 0; - return FALSE; - } - - ASSERTSOCKOP( ret = send( fd, msg, msgsize, 0 ), "Sending data" ); - - if( ret == 0 ) - return dcc_abort( df, "Remote end closed connection" ); - - df->bytes_sent += ret; - df->queued_bytes -= ret; - df->buffer_pos += ret; - - if( df->buffer_pos == dccb->len ) - { - df->buffer_pos = 0; - df->queued_buffers = g_slist_remove( df->queued_buffers, dccb ); - g_free( dccb->b ); - g_free( dccb ); - } - - if( ( df->queued_bytes < DCC_QUEUE_THRESHOLD_LOW ) && file->out_of_data ) - file->out_of_data( file ); - - if( df->queued_bytes > 0 ) - { - /* Who knows how long the event loop cycle will take, - * let's just try to send some more now. */ -#ifndef DCC_SEND_AHEAD - if( df->bytes_sent < ( file->bytes_transferred + max_packet_size ) ) -#endif - return dccs_send_proto( df, fd, cond ); - } - - df->watch_out = 0; - return FALSE; - } - - /* Send buffer full, come back later */ - return TRUE; } @@ -508,7 +408,7 @@ gboolean dccs_recv_start( file_transfer_t *ft ) /* watch */ df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df ); - ft->out_of_data = dccs_recv_out_of_data; + ft->write_request = dccs_recv_write_request; return TRUE; } @@ -537,16 +437,13 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) if( revents & POLLIN ) { - char *buffer = g_malloc( 65536 ); int ret, done; - ASSERTSOCKOP( ret = recv( fd, buffer, 65536, 0 ), "Receiving" ); + ASSERTSOCKOP( ret = recv( fd, ft->buffer, sizeof( ft->buffer ), 0 ), "Receiving" ); if( ret == 0 ) return dcc_abort( df, "Remote end closed connection" ); - buffer = g_realloc( buffer, ret ); - df->bytes_sent += ret; done = df->bytes_sent >= ft->file_size; @@ -560,14 +457,11 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" ); if ( ackret != 4 ) - return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ret ); + return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret ); } - if( !ft->write( df->ft, buffer, ret ) && !done ) - { - df->watch_in = 0; + if( !ft->write( df->ft, ft->buffer, ret ) ) return FALSE; - } if( done ) { @@ -578,50 +472,63 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) return FALSE; } - return TRUE; + df->watch_in = 0; + return FALSE; } return TRUE; } -void dccs_recv_out_of_data( file_transfer_t *ft ) +gboolean dccs_recv_write_request( file_transfer_t *ft ) { dcc_file_transfer_t *df = ft->priv; - if( !df->watch_in ) - df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + if( df->watch_in ) + return dcc_abort( df, "BUG: write_request() called while watching" ); + + df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + + return TRUE; +} + +gboolean dccs_send_can_write( gpointer data, gint fd, b_input_condition cond ) +{ + struct dcc_file_transfer *df = data; + df->watch_out = 0; + + df->ft->write_request( df->ft ); + return FALSE; } /* - * Incoming data. Note that the buffer MUST NOT be freed by the caller! - * We don't copy the buffer but put it in our queue. + * Incoming data. * - * */ -gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size ) + */ +gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_len ) { dcc_file_transfer_t *df = file->priv; - struct dcc_buffer *dccb = g_new0( struct dcc_buffer, 1 ); + int ret; + + receivedchunks++; receiveddata += data_len; - receivedchunks++; receiveddata += data_size; + if( df->watch_out ) + return dcc_abort( df, "BUG: write() called while watching" ); - dccb->b = data; - dccb->len = data_size; + ASSERTSOCKOP( ret = send( df->fd, data, data_len, 0 ), "Sending data" ); - df->queued_buffers = g_slist_append( df->queued_buffers, dccb ); + if( ret == 0 ) + return dcc_abort( df, "Remote end closed connection" ); - df->queued_bytes += data_size; + /* TODO: this should really not be fatal */ + if( ret < data_len ) + return dcc_abort( df, "send() sent %d instead of %d", ret, data_len ); - if( ( file->status & FT_STATUS_TRANSFERRING ) && -#ifndef DCC_SEND_AHEAD - ( file->bytes_transferred >= df->bytes_sent ) && -#endif - ( df->watch_out == 0 ) && - ( df->queued_bytes > 0 ) ) - { - df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_proto, df ); - } - - return df->queued_bytes > DCC_QUEUE_THRESHOLD_HIGH; + df->bytes_sent += ret; + + if( df->bytes_sent < df->ft->file_size ) + df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_can_write, df ); + + return TRUE; } /* @@ -642,20 +549,6 @@ static void dcc_close( file_transfer_t *file ) if( df->watch_out ) b_event_remove( df->watch_out ); - if( df->queued_buffers ) - { - struct dcc_buffer *dccb; - GSList *gslist = df->queued_buffers; - - for( ; gslist ; gslist = g_slist_next( gslist ) ) - { - dccb = gslist->data; - g_free( dccb->b ); - g_free( dccb ); - } - g_slist_free( df->queued_buffers ); - } - df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file ); g_free( df ); diff --git a/dcc.h b/dcc.h index d5805a1f..e53823e7 100644 --- a/dcc.h +++ b/dcc.h @@ -43,33 +43,9 @@ #ifndef _DCC_H #define _DCC_H -/* don't wait for acknowledgments */ -#define DCC_SEND_AHEAD - -/* This multiplier specifies how many bytes we - * can go ahead within one event loop cycle. Notice that all in all, - * we can easily be more ahead if the event loop shoots often enough. - * (or the receiver processes slow enough) - * - * Setting this value too high will cause send buffer overflows. - */ -#define DCC_SEND_AHEAD_MUL 10 - -/* - * queue thresholds for the out of data and overflow conditions - */ -#define DCC_QUEUE_THRESHOLD_LOW 2048 -#define DCC_QUEUE_THRESHOLD_HIGH 65536 - -/* only used in non-ahead mode */ +/* Send an ACK after receiving this amount of data */ #define DCC_PACKET_SIZE 1024 -/* stores buffers handed over by IM protocols */ -struct dcc_buffer { - char *b; - int len; -}; - typedef struct dcc_file_transfer { struct im_connection *ic; @@ -87,25 +63,6 @@ typedef struct dcc_file_transfer { gint watch_in; /* readable */ gint watch_out; /* writable */ - /* - * The total number of queued bytes. The following equality should always hold: - * - * queued_bytes = sum(queued_buffers.len) - buffer_pos - */ - unsigned int queued_bytes; - - /* - * A list of dcc_buffer structures. - * These are provided by the protocols directly so that no copying is neccessary. - */ - GSList *queued_buffers; - - /* - * current position within the first buffer. - * Is non-null if the whole buffer couldn't be sent at once. - */ - int buffer_pos; - /* * The total amount of bytes that have been sent to the irc client. */ @@ -123,7 +80,7 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha void dcc_canceled( file_transfer_t *file, char *reason ); -gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size ); +gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_size ); file_transfer_t *dcc_request( struct im_connection *ic, char *line ); #endif diff --git a/protocols/ft.h b/protocols/ft.h index d41eb6c1..d35580d0 100644 --- a/protocols/ft.h +++ b/protocols/ft.h @@ -26,6 +26,13 @@ #ifndef _FT_H #define _FT_H +/* + * One buffer is needed for each transfer. The receiver stores a message + * in it and gives it to the sender. The sender will stall the receiver + * till the buffer has been sent out. + */ +#define FT_BUFFER_SIZE 2048 + typedef enum { FT_STATUS_LISTENING = 1, FT_STATUS_TRANSFERRING = 2, @@ -130,15 +137,24 @@ typedef struct file_transfer { void (*canceled) ( struct file_transfer *file, char *reason ); /* - * If set, called when the transfer queue is running empty and - * more data can be added. + * called by the sending side to indicate that it is writable. + * The callee should check if data is available and call the + * function(as seen below) if that is the case. */ - void (*out_of_data) ( struct file_transfer *file ); + gboolean (*write_request) ( struct file_transfer *file ); /* * When sending files, protocols register this function to receive data. + * This should only be called once after write_request is called. The caller + * should not read more data until write_request is called again. This technique + * avoids buffering. + */ + gboolean (*write) (struct file_transfer *file, char *buffer, unsigned int len ); + + /* The send buffer associated with this transfer. + * Since receivers always wait for a write_request call one is enough. */ - gboolean (*write) (struct file_transfer *file, char *buffer, int len ); + char buffer[FT_BUFFER_SIZE]; } file_transfer_t; @@ -153,12 +169,5 @@ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *user_nick */ void imcb_file_canceled( file_transfer_t *file, char *reason ); -/* - * The given buffer is queued for transfer and MUST NOT be freed by the caller. - * When the method returns false the caller should not invoke this method again - * until out_of_data has been called. - */ -gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size ); - gboolean imcb_file_recv_start( file_transfer_t *ft ); #endif diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index cb52d396..45082fee 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -145,7 +145,6 @@ struct jabber_transfer int accepted; size_t bytesread, byteswritten; - int receiver_overflow; int fd; struct sockaddr_storage saddr; }; @@ -208,7 +207,7 @@ void jabber_si_free_transfer( file_transfer_t *ft); /* s5bytestream.c */ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); gboolean jabber_bs_send_start( struct jabber_transfer *tf ); -gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, int len ); +gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int len ); /* message.c */ xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index e2f32bd0..de173d19 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -71,8 +71,6 @@ struct socks5_message if( (op) == -1 ) \ return jabber_bs_abort( bt , msg ": %s", strerror( errno ) ); -#define JABBER_BS_BUFSIZE 65536 - gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ); void jabber_bs_canceled( file_transfer_t *ft , char *reason ); void jabber_bs_free_transfer( file_transfer_t *ft); @@ -82,7 +80,7 @@ gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ); void jabber_bs_recv_answer_request( struct bs_transfer *bt ); gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ); -void jabber_bs_recv_out_of_data( file_transfer_t *ft ); +gboolean jabber_bs_recv_write_request( file_transfer_t *ft ); gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ); gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ); int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); @@ -108,7 +106,7 @@ void jabber_bs_free_transfer( file_transfer_t *ft) { g_free( bt->pseudoadr ); xt_free_node( bt->qnode ); g_free( bt ); -//iq_id + jabber_si_free_transfer( ft ); } @@ -325,7 +323,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con sock_make_nonblocking( fd ); - imcb_log( bt->tf->ic, "Transferring file %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, host, port ); + imcb_log( bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, host, port ); if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && ( errno != EINPROGRESS ) ) @@ -425,7 +423,6 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con jabber_bs_recv_answer_request( bt ); - // reset in answer_request bt->tf->watch_in = 0; return FALSE; } default: @@ -440,8 +437,10 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con /* * If the handshake failed we can try the next streamhost, if there is one. * An intelligent sender would probably specify himself as the first streamhost and - * a proxy as the second (Kopete is an example here). That way, a (potentially) - * slow proxy is only used if neccessary. + * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially) + * slow proxy is only used if neccessary. This of course also means, that the timeout + * per streamhost should be kept short. If one or two firewalled adresses are specified, + * they have to timeout first before a proxy is tried. */ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) { @@ -493,15 +492,15 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) struct jabber_transfer *tf = bt->tf; struct xt_node *reply; - imcb_log( tf->ic, "Transferring file %s: established SOCKS5 connection to %s:%s", + imcb_log( tf->ic, "File %s: established SOCKS5 connection to %s:%s", tf->ft->file_name, xt_find_attr( bt->shnode, "host" ), xt_find_attr( bt->shnode, "port" ) ); tf->ft->data = tf; tf->ft->started = time( NULL ); - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, tf ); - tf->ft->out_of_data = jabber_bs_recv_out_of_data; + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); + tf->ft->write_request = jabber_bs_recv_write_request; reply = xt_new_node( "streamhost-used", NULL, NULL ); xt_add_attr( reply, "jid", xt_find_attr( bt->shnode, "jid" ) ); @@ -518,90 +517,107 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) xt_free_node( reply ); } -/* Reads till it is unscheduled or the receiver signifies an overflow. */ +/* + * This function is called from write_request directly. If no data is available, it will install itself + * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again. + */ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) { int ret; - struct jabber_transfer *tf = data; - struct bs_transfer *bt = tf->streamhandle; - char *buffer = g_malloc( JABBER_BS_BUFSIZE ); + struct bs_transfer *bt = data; + struct jabber_transfer *tf = bt->tf; - if (tf->receiver_overflow) + if( fd != 0 ) /* called via event thread */ + { + tf->watch_in = 0; + ASSERTSOCKOP( ret = recv( fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) , "Receiving" ); + } + else { - if( tf->watch_in ) + /* called directly. There might not be any data available. */ + if( ( ( ret = recv( tf->fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) ) == -1 ) && + ( errno != EAGAIN ) ) + return jabber_bs_abort( bt, "Receiving: %s", strerror( errno ) ); + + if( ( ret == -1 ) && ( errno == EAGAIN ) ) { - /* should never happen, BUG */ - imcb_file_canceled( tf->ft, "Bug in jabber file transfer code: read while overflow is true. Please report" ); + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); return FALSE; } } - ASSERTSOCKOP( ret = recv( fd, buffer, JABBER_BS_BUFSIZE, 0 ) , "Receiving" ); - - /* that should be all */ + /* shouldn't happen since we know the file size */ if( ret == 0 ) - return FALSE; + return jabber_bs_abort( bt, "Remote end closed connection" ); tf->bytesread += ret; - buffer = g_realloc( buffer, ret ); + tf->ft->write( tf->ft, tf->ft->buffer, ret ); - if ( ( tf->receiver_overflow = imcb_file_write( tf->ft, buffer, ret ) ) ) - { - /* wait for imcb to run out of data */ - tf->watch_in = 0; - return FALSE; - } - - return TRUE; + return FALSE; } -/* imcb callback that is invoked when it runs out of data. - * We reschedule jabber_bs_read here if neccessary. */ -void jabber_bs_recv_out_of_data( file_transfer_t *ft ) +/* + * imc callback that is invoked when it is ready to receive some data. + */ +gboolean jabber_bs_recv_write_request( file_transfer_t *ft ) { struct jabber_transfer *tf = ft->data; - tf->receiver_overflow = FALSE; + if( tf->watch_in ) + { + imcb_file_canceled( ft, "BUG in jabber file transfer: write_request called when already watching for input" ); + return FALSE; + } + + jabber_bs_recv_read( tf->streamhandle, 0 , 0 ); - if ( !tf->watch_in ) - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, tf ); + return TRUE; } -/* signal ood and be done */ +/* + * Issues a write_request to imc. + * */ gboolean jabber_bs_send_can_write( gpointer data, gint fd, b_input_condition cond ) { struct bs_transfer *bt = data; - bt->tf->ft->out_of_data( bt->tf->ft ); - bt->tf->watch_out = 0; + + bt->tf->ft->write_request( bt->tf->ft ); + return FALSE; } -/* try to send the stuff. If you can't return false and wait for writable */ -gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, int len ) +/* + * This should only be called if we can write, so just do it. + * Add a write watch so we can write more during the next cycle (if possible). + */ +gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int len ) { struct jabber_transfer *tf = ft->data; struct bs_transfer *bt = tf->streamhandle; int ret; - if ( ( ( ret = send( tf->fd, buffer, len, 0 ) ) == -1 ) && - ( errno != EAGAIN ) ) - return jabber_bs_abort( bt, "send failed on socket with: %s", strerror( errno ) ); + if( tf->watch_out ) + return jabber_bs_abort( bt, "BUG: write() called while watching " ); - if( ret == 0 ) - return jabber_bs_abort( bt, "Remote end closed connection" ); + ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" ); + + tf->byteswritten += ret; - if( ret == -1 ) - { - bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); - return FALSE; - } + /* TODO: this should really not be fatal */ + if( ret < len ) + return jabber_bs_abort( bt, "send() sent %d instead of %d (send buffer too big!)", ret, len ); + + bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); return TRUE; } +/* + * Handles the reply by the receiver containing the used streamhost. + */ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct jabber_transfer *tf = NULL; struct jabber_data *jd = ic->proto_data; @@ -650,12 +666,11 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt if( bt->phase == BS_PHASE_REPLY ) { + /* handshake went through, let's start transferring */ tf->ft->started = time( NULL ); - tf->ft->out_of_data( tf->ft ); + tf->ft->write_request( tf->ft ); } - //bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_write, tf ); - return XT_HANDLED; } @@ -680,8 +695,6 @@ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) bt = g_new0( struct bs_transfer, 1 ); bt->tf = tf; - //bt->qnode = xt_dup( qnode ); - //bt->shnode = bt->qnode->children; bt->phase = BS_PHASE_CONNECT; bt->pseudoadr = g_strdup( hash_hex ); tf->streamhandle = bt; @@ -714,8 +727,6 @@ gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *p iq = jabber_make_packet( "iq", "set", tf->tgt_jid, query ); xt_add_attr( iq, "from", tf->ini_jid ); - //xt_free_node( query ); - jabber_cache_add( tf->ic, iq, jabber_bs_send_handle_reply ); if( !jabber_write_packet( tf->ic, iq ) ) @@ -884,11 +895,13 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_REPLY; - /* don't start sending till the streamhost-used message comes in */ + imcb_log( tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name ); + if( tf->accepted ) { + /* streamhost-used message came already in(possible?), let's start sending */ tf->ft->started = time( NULL ); - tf->ft->out_of_data( tf->ft ); + tf->ft->write_request( tf->ft ); } tf->watch_in = 0; diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 598cbd03..ffde6418 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -83,7 +83,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, struct jabber_transfer *tf; struct jabber_data *jd = ic->proto_data; - imcb_log( ic, "Incoming file from %s : %s %zd bytes", ic->irc->nick, ft->file_name, ft->file_size ); + imcb_log( ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who ); tf = g_new0( struct jabber_transfer, 1 ); @@ -223,7 +223,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st } /* - * imcb called the accept callback which probably means that the user accepted this file transfer. + * imc called the accept callback which probably means that the user accepted this file transfer. * We send our response to the initiator. * In the next step, the initiator will send us a request for the given stream type. * (currently that can only be a SOCKS5 bytestream) @@ -274,11 +274,10 @@ void jabber_si_answer_request( file_transfer_t *ft ) { static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct xt_node *c, *d; - char *ini_jid, *tgt_jid; + char *ini_jid, *tgt_jid, *iq_id; GSList *tflist; struct jabber_transfer *tf=NULL; struct jabber_data *jd = ic->proto_data; - char *sid; if( !( tgt_jid = xt_find_attr( node, "from" ) ) || !( ini_jid = xt_find_attr( node, "to" ) ) ) @@ -287,11 +286,10 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n return XT_HANDLED; } - imcb_log( ic, "GOT RESPONSE TO FILE" ); /* All this means we expect something like this: ( I think ) - * + * * - * + * [ ] <-- not neccessary * * * @@ -299,11 +297,11 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n */ if( !( tgt_jid = xt_find_attr( node, "from" ) ) || !( ini_jid = xt_find_attr( node, "to" ) ) || + !( iq_id = xt_find_attr( node, "id" ) ) || !( c = xt_find_node( node->children, "si" ) ) || !( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) || - !( sid = xt_find_attr( c, "id" ) )|| - !( d = xt_find_node( c->children, "file" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || +/* !( d = xt_find_node( c->children, "file" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || */ !( d = xt_find_node( c->children, "feature" ) ) || !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || !( d = xt_find_node( d->children, "x" ) ) || @@ -330,7 +328,7 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) { struct jabber_transfer *tft = tflist->data; - if( ( strcmp( tft->sid, sid ) == 0 ) ) + if( ( strcmp( tft->iq_id, iq_id ) == 0 ) ) { tf = tft; break; @@ -346,6 +344,8 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n tf->ini_jid = g_strdup( ini_jid ); tf->tgt_jid = g_strdup( tgt_jid ); + imcb_log( ic, "File %s: %s accepted the transfer!", tf->ft->file_name, tgt_jid ); + jabber_bs_send_start( tf ); return XT_HANDLED; @@ -422,6 +422,7 @@ int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_tr /* and we are there... */ node = jabber_make_packet( "iq", "set", bud ? bud->full_jid : who, sinode ); jabber_cache_add( ic, node, jabber_si_handle_response ); + tf->iq_id = g_strdup( xt_find_attr( node, "id" ) ); return jabber_write_packet( ic, node ); } -- cgit v1.2.3 From 793cc254ad2479d95d00266d6cb7ab2bcd158834 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 4 Dec 2007 02:21:45 +0100 Subject: Follow rename of jabber_chat_by_jid from upstrem --- protocols/jabber/si.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index ffde6418..0b94f81b 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -371,7 +371,7 @@ int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_tr /* Maybe we should hash this? */ tf->sid = g_strdup_printf( "BitlBeeJabberSID%d", tf->ft->local_id ); - if( ( s = strchr( who, '=' ) ) && jabber_chat_by_name( ic, s + 1 ) ) + if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) ) bud = jabber_buddy_by_ext_jid( ic, who, 0 ); else bud = jabber_buddy_by_jid( ic, who, 0 ); -- cgit v1.2.3 From dc0ba9c85539533349353713162f94077fb27be3 Mon Sep 17 00:00:00 2001 From: kenobi Date: Tue, 18 Dec 2007 03:07:59 +0100 Subject: sending via proxy --- dcc.c | 1 - protocols/jabber/iq.c | 162 +++++++++++++++++++++- protocols/jabber/jabber.h | 16 ++- protocols/jabber/s5bytestream.c | 298 ++++++++++++++++++++++++++++++---------- protocols/jabber/si.c | 10 ++ 5 files changed, 410 insertions(+), 77 deletions(-) diff --git a/dcc.c b/dcc.c index 24673085..2fa3f745 100644 --- a/dcc.c +++ b/dcc.c @@ -124,7 +124,6 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df = dcc_alloc_transfer( file_name, file_size, ic ); file = df->ft; file->write = dccs_send_write; - file->sending = TRUE; /* listen and request */ if( !dcc_listen( df, &saddr ) || diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 8cf6c7f1..8bd1111a 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -80,7 +80,7 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) ); xt_add_child( reply, xt_new_node( "tz", buf, NULL ) ); } - else if( strcmp( s, XMLNS_DISCOVER ) == 0 ) + else if( strcmp( s, XMLNS_DISCO_INFO ) == 0 ) { const char *features[] = { XMLNS_VERSION, XMLNS_TIME, @@ -564,3 +564,163 @@ int jabber_remove_from_roster( struct im_connection *ic, char *handle ) xt_free_node( node ); return st; } + +xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); + +xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) +{ + struct xt_node *node, *query; + struct jabber_buddy *bud; + + if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL ) + { + /* Who cares about the unknown... */ + imcb_log( ic, "Couldnt find the man: %s", bare_jid); + return 0; + } + + if( bud->features ) /* been here already */ + return XT_HANDLED; + + node = xt_new_node( "query", NULL, NULL ); + xt_add_attr( node, "xmlns", XMLNS_DISCO_INFO ); + + if( !( query = jabber_make_packet( "iq", "get", bare_jid, node ) ) ) + { + imcb_log( ic, "WARNING: Couldn't generate feature query" ); + xt_free_node( node ); + } + + jabber_cache_add( ic, query, jabber_iq_parse_features ); + + return jabber_write_packet( ic, query ); +} + +xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ + struct xt_node *c; + struct jabber_buddy *bud; + char *feature; + + if( !( c = xt_find_node( node->children, "query" ) ) || + !( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) ) + { + imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); + return XT_HANDLED; + } + if( ( bud = jabber_buddy_by_jid( ic, xt_find_attr( node, "from") , 0 ) ) == NULL ) + { + /* Who cares about the unknown... */ + imcb_log( ic, "Couldnt find the man: %s", xt_find_attr( node, "from")); + return 0; + } + + c = c->children; + while( ( c = xt_find_node( c, "feature" ) ) ) { + feature = xt_find_attr( c, "var" ); + bud->features = g_slist_append(bud->features, g_strdup(feature) ); + c = c->next; + } + + return XT_HANDLED; +} + +xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); + +xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns ) +{ + struct xt_node *node, *query; + struct jabber_data *jd = ic->proto_data; + + node = xt_new_node( "query", NULL, NULL ); + xt_add_attr( node, "xmlns", xmlns ); + + if( !( query = jabber_make_packet( "iq", "get", jid, node ) ) ) + { + imcb_log( ic, "WARNING: Couldn't generate server query" ); + xt_free_node( node ); + } + + jd->have_streamhosts--; + jabber_cache_add( ic, query, jabber_iq_parse_server_features ); + + return jabber_write_packet( ic, query ); +} + +/* + * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info + */ +xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ + struct xt_node *c; + struct jabber_data *jd = ic->proto_data; + + if( !( c = xt_find_node( node->children, "query" ) ) || + !xt_find_attr( node, "from" ) ) + { + imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); + return XT_HANDLED; + } + + jd->have_streamhosts++; + + if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_ITEMS ) == 0 ) + { + char *item, *itemjid; + + /* answer from server */ + + c = c->children; + while( ( c = xt_find_node( c, "item" ) ) ) + { + item = xt_find_attr( c, "name" ); + itemjid = xt_find_attr( c, "jid" ); + + jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO ); + + c = c->next; + } + } else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) + { + char *category, *type; + + /* answer from potential proxy */ + + c = c->children; + while( ( c = xt_find_node( c, "identity" ) ) ) + { + category = xt_find_attr( c, "category" ); + type = xt_find_attr( c, "type" ); + + if( type && ( strcmp( type, "bytestreams" ) == 0 ) && + category && ( strcmp( category, "proxy" ) == 0 ) ) + jabber_iq_query_server( ic, xt_find_attr( node, "from" ), XMLNS_BYTESTREAMS ); + + c = c->next; + } + } else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_BYTESTREAMS ) == 0 ) + { + char *host, *jid; + int port; + + /* answer from proxy */ + + if( ( c = xt_find_node( c->children, "streamhost" ) ) && + ( host = xt_find_attr( c, "host" ) ) && + ( port = atoi( xt_find_attr( c, "port" ) ) ) && + ( jid = xt_find_attr( c, "jid" ) ) ) + { + jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( jid ); + sh->host = g_strdup( host ); + sprintf( sh->port, "%u", port ); + + imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port ); + jd->streamhosts = g_slist_append( jd->streamhosts, sh ); + } + } + + if( jd->have_streamhosts == 0 ) + jd->have_streamhosts++; + return XT_HANDLED; +} diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index c518f541..9edb844e 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -56,6 +56,14 @@ typedef enum have a real JID. */ } jabber_buddy_flags_t; +/* Stores a streamhost's(a.k.a. proxy) data */ +typedef struct +{ + char *jid; + char *host; + char port[6]; +} jabber_streamhost_t; + struct jabber_data { struct im_connection *ic; @@ -82,6 +90,8 @@ struct jabber_data GHashTable *buddies; GSList *filetransfers; + GSList *streamhosts; + int have_streamhosts; }; struct jabber_away_state @@ -110,6 +120,7 @@ struct jabber_buddy int priority; struct jabber_away_state *away_state; char *away_message; + GSList *features; time_t last_act; jabber_buddy_flags_t flags; @@ -181,7 +192,8 @@ struct jabber_transfer #define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */ #define XMLNS_XDATA "jabber:x:data" /* XEP-0004 */ #define XMLNS_CHATSTATES "http://jabber.org/protocol/chatstates" /* XEP-0085 */ -#define XMLNS_DISCOVER "http://jabber.org/protocol/disco#info" /* XEP-0030 */ +#define XMLNS_DISCO_INFO "http://jabber.org/protocol/disco#info" /* XEP-0030 */ +#define XMLNS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" /* XEP-0030 */ #define XMLNS_MUC "http://jabber.org/protocol/muc" /* XEP-0045 */ #define XMLNS_MUC_USER "http://jabber.org/protocol/muc#user" /* XEP-0045 */ #define XMLNS_FEATURE "http://jabber.org/protocol/feature-neg" /* XEP-0020 */ @@ -198,6 +210,8 @@ int jabber_get_roster( struct im_connection *ic ); int jabber_get_vcard( struct im_connection *ic, char *bare_jid ); int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); int jabber_remove_from_roster( struct im_connection *ic, char *handle ); +xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ); +xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns ); /* si.c */ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index de173d19..4770feda 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -29,8 +29,8 @@ struct bs_transfer { struct jabber_transfer *tf; - /* element and elements */ - struct xt_node *qnode, *shnode; + jabber_streamhost_t *sh; + GSList *streamhosts; enum { @@ -73,7 +73,7 @@ struct socks5_message gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ); void jabber_bs_canceled( file_transfer_t *ft , char *reason ); -void jabber_bs_free_transfer( file_transfer_t *ft); +void jabber_bs_free_transfer( file_transfer_t *ft ); gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ); gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ); gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ); @@ -83,12 +83,14 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ); gboolean jabber_bs_recv_write_request( file_transfer_t *ft ); gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ); gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ); -int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode ); gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error ); -gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *port ); +gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ); gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ); gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ); +static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); +void jabber_bs_send_activate( struct bs_transfer *bt ); /* * Frees a bs_transfer struct and calls the SI free function @@ -96,6 +98,7 @@ gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage void jabber_bs_free_transfer( file_transfer_t *ft) { struct jabber_transfer *tf = ft->data; struct bs_transfer *bt = tf->streamhandle; + jabber_streamhost_t *sh; if ( tf->watch_in ) b_event_remove( tf->watch_in ); @@ -104,12 +107,25 @@ void jabber_bs_free_transfer( file_transfer_t *ft) { b_event_remove( tf->watch_out ); g_free( bt->pseudoadr ); - xt_free_node( bt->qnode ); + + while( bt->streamhosts ) + { + sh = bt->streamhosts->data; + bt->streamhosts = g_slist_remove( bt->streamhosts, sh ); + g_free( sh->jid ); + g_free( sh->host ); + g_free( sh ); + } + g_free( bt ); jabber_si_free_transfer( ft ); } +/* + * Checks if buflen data is available on the socket and + * writes it to buffer if that's the case. + */ gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ) { int ret; @@ -146,6 +162,10 @@ gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition co return FALSE; } +/* + * Polls the socket, checks for errors and removes a connect timer + * if there is one. + */ gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ) { struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; @@ -180,6 +200,9 @@ gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ) return TRUE; } +/* + * Used for receive and send path. + */ gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ) { va_list params; @@ -190,9 +213,9 @@ gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ) sprintf( error, "internal error parsing error string (BUG)" ); va_end( params ); if( bt->tf->ft->sending ) - return jabber_bs_recv_handshake_abort( bt, error ); - else return jabber_bs_send_handshake_abort( bt, error ); + else + return jabber_bs_recv_handshake_abort( bt, error ); } /* Bad luck */ @@ -213,6 +236,8 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru struct jabber_transfer *tf = NULL; GSList *tflist; struct bs_transfer *bt; + GSList *shlist=NULL; + struct xt_node *shnode; sha1_state_t sha; char hash_hex[41]; @@ -235,6 +260,30 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru return XT_HANDLED; } + shnode = qnode->children; + while( ( shnode = xt_find_node( shnode, "streamhost" ) ) ) + { + char *jid, *host; + int port; + if( ( jid = xt_find_attr( shnode, "jid" ) ) && + ( host = xt_find_attr( shnode, "host" ) ) && + ( ( port = atoi( xt_find_attr( shnode, "port" ) ) ) ) ) + { + jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup(jid); + sh->host = g_strdup(host); + sprintf( sh->port, "%u", port ); + shlist = g_slist_append( shlist, sh ); + } + shnode = shnode->next; + } + + if( !shlist ) + { + imcb_log( ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries"); + return XT_HANDLED; + } + /* Let's see if we can find out what this bytestream should be for... */ for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) @@ -273,8 +322,8 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru bt = g_new0( struct bs_transfer, 1 ); bt->tf = tf; - bt->qnode = xt_dup( qnode ); - bt->shnode = bt->qnode->children; + bt->streamhosts = shlist; + bt->sh = shlist->data; bt->phase = BS_PHASE_CONNECT; bt->pseudoadr = g_strdup( hash_hex ); tf->streamhandle = bt; @@ -284,6 +333,7 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru return XT_HANDLED; } + /* * This is what a protocol handshake can look like in cooperative multitasking :) * Might be confusing at first because it's called from different places and is recursing. @@ -304,45 +354,35 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con { case BS_PHASE_CONNECT: { - struct xt_node *c; - char *host, *port; struct addrinfo hints, *rp; - if( ( c = bt->shnode = xt_find_node( bt->shnode, "streamhost" ) ) && - ( port = xt_find_attr( c, "port" ) ) && - ( host = xt_find_attr( c, "host" ) ) && - xt_find_attr( c, "jid" ) ) - { - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; - if ( getaddrinfo( host, port, &hints, &rp ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) ); + if ( getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) ); - ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); + ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); - sock_make_nonblocking( fd ); + sock_make_nonblocking( fd ); - imcb_log( bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, host, port ); + imcb_log( bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host, bt->sh->port ); - if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && - ( errno != EINPROGRESS ) ) - return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) ); + if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && + ( errno != EINPROGRESS ) ) + return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) ); - freeaddrinfo( rp ); + freeaddrinfo( rp ); - bt->phase = BS_PHASE_CONNECTED; - - bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); + bt->phase = BS_PHASE_CONNECTED; + + bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); - /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ - bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ + bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); - bt->tf->watch_in = 0; - return FALSE; - } else - return jabber_bs_abort( bt, c ? "incomplete streamhost entry: host=%s port=%s jid=%s" : NULL, - host, port, xt_find_attr( c, "jid" ) ); + bt->tf->watch_in = 0; + return FALSE; } case BS_PHASE_CONNECTED: { @@ -421,7 +461,10 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con socks5_reply.atyp, socks5_reply.addrlen); - jabber_bs_recv_answer_request( bt ); + if( bt->tf->ft->sending ) + jabber_bs_send_activate( bt ); + else + jabber_bs_recv_answer_request( bt ); return FALSE; } @@ -446,24 +489,24 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) { struct jabber_transfer *tf = bt->tf; struct xt_node *reply, *iqnode; + GSList *shlist; + + imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", + tf->ft->file_name, + bt->sh->host, + bt->sh->port, + error ); - if( bt->shnode ) + /* Alright, this streamhost failed, let's try the next... */ + bt->phase = BS_PHASE_CONNECT; + shlist = g_slist_find( bt->streamhosts, bt->sh ); + if( shlist && shlist->next ) { - imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", - tf->ft->file_name, - xt_find_attr( bt->shnode, "host" ), - xt_find_attr( bt->shnode, "port" ), - error ); - - /* Alright, this streamhost failed, let's try the next... */ - bt->phase = BS_PHASE_CONNECT; - bt->shnode = bt->shnode->next; - - /* the if is not neccessary but saves us one recursion */ - if( bt->shnode ) - return jabber_bs_recv_handshake( bt, 0, 0 ); + bt->sh = shlist->next->data; + return jabber_bs_recv_handshake( bt, 0, 0 ); } + /* out of stream hosts */ iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); @@ -494,16 +537,15 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) imcb_log( tf->ic, "File %s: established SOCKS5 connection to %s:%s", tf->ft->file_name, - xt_find_attr( bt->shnode, "host" ), - xt_find_attr( bt->shnode, "port" ) ); + bt->sh->host, + bt->sh->port ); tf->ft->data = tf; - tf->ft->started = time( NULL ); tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); tf->ft->write_request = jabber_bs_recv_write_request; reply = xt_new_node( "streamhost-used", NULL, NULL ); - xt_add_attr( reply, "jid", xt_find_attr( bt->shnode, "jid" ) ); + xt_add_attr( reply, "jid", bt->sh->jid ); reply = xt_new_node( "query", NULL, reply ); xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); @@ -550,6 +592,9 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) if( ret == 0 ) return jabber_bs_abort( bt, "Remote end closed connection" ); + if( tf->bytesread == 0 ) + tf->ft->started = time( NULL ); + tf->bytesread += ret; tf->ft->write( tf->ft, tf->ft->buffer, ret ); @@ -602,8 +647,12 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l if( tf->watch_out ) return jabber_bs_abort( bt, "BUG: write() called while watching " ); + /* TODO: catch broken pipe */ ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" ); + if( tf->byteswritten == 0 ) + tf->ft->started = time( NULL ); + tf->byteswritten += ret; /* TODO: this should really not be fatal */ @@ -664,24 +713,110 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt tf->accepted = TRUE; - if( bt->phase == BS_PHASE_REPLY ) + if( strcmp( jid, tf->ini_jid ) == 0 ) { - /* handshake went through, let's start transferring */ - tf->ft->started = time( NULL ); - tf->ft->write_request( tf->ft ); + /* we're streamhost and target */ + if( bt->phase == BS_PHASE_REPLY ) + { + /* handshake went through, let's start transferring */ + tf->ft->write_request( tf->ft ); + } + } else + { + /* using a proxy */ + GSList *shlist; + for( shlist = jd->streamhosts ; shlist ; shlist = g_slist_next( shlist ) ) + { + jabber_streamhost_t *sh = shlist->data; + if( strcmp( sh->jid, jid ) == 0 ) + { + bt->sh = sh; + jabber_bs_recv_handshake( bt, 0, 0 ); + return XT_HANDLED; + } + } + + imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid ); } return XT_HANDLED; } +/* + * Tell the proxy to activate the stream. Looks like this: + * + * + * + * tgt_jid + * + * + */ +void jabber_bs_send_activate( struct bs_transfer *bt ) +{ + struct xt_node *node; + + node = xt_new_node( "activate", bt->tf->tgt_jid, NULL ); + node = xt_new_node( "query", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_BYTESTREAMS ); + xt_add_attr( node, "sid", bt->tf->sid ); + node = jabber_make_packet( "iq", "set", bt->sh->jid, node ); + + jabber_cache_add( bt->tf->ic, node, jabber_bs_send_handle_activate ); + + jabber_write_packet( bt->tf->ic, node ); +} + +/* + * The proxy has activated the bytestream. + * We can finally start pushing some data out. + */ +static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ + char *sid; + GSList *tflist; + struct jabber_transfer *tf; + struct xt_node *query; + struct jabber_data *jd = ic->proto_data; + + query = xt_find_node( orig->children, "query" ); + sid = xt_find_attr( query, "sid" ); + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if( !tf ) + { + imcb_log( ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream" ); + return XT_HANDLED; + } + + /* handshake went through, let's start transferring */ + tf->ft->write_request( tf->ft ); + + return XT_HANDLED; +} + +/* + * Starts a bytestream. + */ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) { - char host[INET6_ADDRSTRLEN], port[6]; + char host[INET6_ADDRSTRLEN]; struct bs_transfer *bt; sha1_state_t sha; char hash_hex[41]; unsigned char hash[20]; - int i; + int i,ret; + struct jabber_data *jd = tf->ic->proto_data; + jabber_streamhost_t sh; + GSList *streamhosts = jd->streamhosts; /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ sha1_init( &sha ); @@ -701,28 +836,43 @@ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) tf->ft->free = jabber_bs_free_transfer; tf->ft->canceled = jabber_bs_canceled; - if ( !jabber_bs_send_listen( bt, &tf->saddr, host, port ) ) + if ( !jabber_bs_send_listen( bt, &tf->saddr, sh.host = host, sh.port ) ) return FALSE; bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); - return jabber_bs_send_request( tf, host, port ); + + sh.jid = tf->ini_jid; + + /* temporarily add listen address to streamhosts, send the request and remove it */ + streamhosts = g_slist_prepend( streamhosts, &sh ); + ret = jabber_bs_send_request( tf, streamhosts); + streamhosts = g_slist_remove( streamhosts, &sh ); + + return ret; } -gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *port ) +gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ) { - struct xt_node *sh, *query, *iq; - - sh = xt_new_node( "streamhost", NULL, NULL ); - xt_add_attr( sh, "jid", tf->ini_jid ); - xt_add_attr( sh, "host", host ); - xt_add_attr( sh, "port", port ); + struct xt_node *shnode, *query, *iq; query = xt_new_node( "query", NULL, NULL ); xt_add_attr( query, "xmlns", XMLNS_BYTESTREAMS ); xt_add_attr( query, "sid", tf->sid ); xt_add_attr( query, "mode", "tcp" ); - xt_add_child( query, sh ); + + while( streamhosts ) { + jabber_streamhost_t *sh = streamhosts->data; + shnode = xt_new_node( "streamhost", NULL, NULL ); + xt_add_attr( shnode, "jid", sh->jid ); + xt_add_attr( shnode, "host", sh->host ); + xt_add_attr( shnode, "port", sh->port ); + + xt_add_child( query, shnode ); + + streamhosts = g_slist_next( streamhosts ); + } + iq = jabber_make_packet( "iq", "set", tf->tgt_jid, query ); xt_add_attr( iq, "from", tf->ini_jid ); @@ -738,6 +888,7 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) { struct jabber_transfer *tf = bt->tf; + /* TODO: did the receiver get here somehow??? */ imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", tf->ft->file_name, error ); @@ -900,7 +1051,6 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con if( tf->accepted ) { /* streamhost-used message came already in(possible?), let's start sending */ - tf->ft->started = time( NULL ); tf->ft->write_request( tf->ft ); } diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 0b94f81b..1d649da3 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -82,6 +82,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, { struct jabber_transfer *tf; struct jabber_data *jd = ic->proto_data; + char *server = jd->server; imcb_log( ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who ); @@ -96,8 +97,17 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); + /* query the buddy's features */ + jabber_iq_query_features( ic, who ); + + /* query proxies from the server */ + if( !jd->have_streamhosts ) + jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); + + /* send the request to our buddy */ jabber_si_send_request( ic, who, tf ); + /* and start the receive logic */ imcb_file_recv_start( ft ); } -- cgit v1.2.3 From eded1f703a8f5d2272b9d294d8e3dfb48fa302b4 Mon Sep 17 00:00:00 2001 From: kenobi Date: Wed, 19 Dec 2007 00:59:35 +0100 Subject: Merged in 280..288 from upstream (e.g. PING) --- account.c | 7 ++++++- doc/CREDITS | 1 + doc/user-guide/commands.xml | 12 ++++++------ doc/user-guide/quickstart.xml | 4 ++-- irc_commands.c | 2 +- lib/misc.c | 10 ++++++++++ lib/misc.h | 2 ++ lib/ssl_gnutls.c | 6 ++++-- nick.c | 19 ++++++++++++++----- protocols/jabber/conference.c | 21 +++++++++++++++++++++ protocols/jabber/io.c | 4 ++-- protocols/jabber/iq.c | 12 +++++++++++- protocols/jabber/jabber.c | 16 +++++++++++++++- protocols/jabber/jabber.h | 2 ++ protocols/msn/msn.c | 2 +- protocols/oscar/aim.h | 2 +- protocols/oscar/oscar.c | 28 +++++----------------------- protocols/yahoo/yahoo.c | 2 +- root_commands.c | 22 +++++++++------------- tests/check_nick.c | 12 +++++++++--- 20 files changed, 123 insertions(+), 63 deletions(-) diff --git a/account.c b/account.c index 388d04d0..4eb78faa 100644 --- a/account.c +++ b/account.c @@ -94,10 +94,15 @@ char *set_eval_account( set_t *set, char *value ) { g_free( acc->server ); if( *value ) + { acc->server = g_strdup( value ); + return value; + } else + { acc->server = NULL; - return value; + return g_strdup( set->def ); + } } else if( strcmp( set->key, "auto_connect" ) == 0 ) { diff --git a/doc/CREDITS b/doc/CREDITS index f805c251..d53f6de0 100644 --- a/doc/CREDITS +++ b/doc/CREDITS @@ -54,6 +54,7 @@ The authors thank the following people: - Ed Schouten, for reporting bugs. - Greg (gropeep.org), for updating the Yahoo! module to fix some issues that were there for quite some time already. +- misc@mandriva.org for lots of Jabber contributions. - And all other users who help us by sending useful bug reports, positive feedback, nice patches and cool addons. Mentioning you all would make diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 8c874014..090acff3 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -16,7 +16,7 @@ - account add <protocol> <username> <password> [<server>] + account add <protocol> <username> <password> @@ -25,7 +25,7 @@ - account add jabber <handle@server.tld> <password> [<servertag>] + account add jabber <handle@server.tld> <password> @@ -49,16 +49,16 @@ - account add oscar <handle> <password> [<servername>] + account add oscar <handle> <password> - Specifying a server is required for OSCAR, since OSCAR can be used for both ICQ- and AIM-connections. Although these days it's supposed to be possible to connect to ICQ via AIM-servers and vice versa, we like to stick with this separation for now. For ICQ connections, the servername is login.icq.com, for AIM connections it's login.oscar.aol.com. + OSCAR is the protocol used to connect to AIM and/or ICQ. The servers will automatically detect if you're using a numeric or non-numeric username so there's no need to tell which network you want to connect to. - account add oscar 72696705 hobbelmeeuw login.icq.com + account add oscar 72696705 hobbelmeeuw Account successfully added @@ -594,7 +594,7 @@ - Can be set for Jabber- and OSCAR-connections. For OSCAR, this must be set to login.icq.com if it's an ICQ connection, or login.oscar.aol.com if it's an AIM connection. For Jabber, you have to set this if the servername isn't equal to the part after the @ in the Jabber handle. + Can be set for Jabber- and OSCAR-connections. For Jabber, you might have to set this if the servername isn't equal to the part after the @ in the Jabber handle. For OSCAR this shouldn't be necessary anymore in recent BitlBee versions. diff --git a/doc/user-guide/quickstart.xml b/doc/user-guide/quickstart.xml index 520f240f..fcb06c6b 100644 --- a/doc/user-guide/quickstart.xml +++ b/doc/user-guide/quickstart.xml @@ -37,12 +37,12 @@ For instance, suppose you have an ICQ account with UIN 72696705 - account add oscar 72696705 QuickStart login.icq.com + account add jabber bitlbee@jabber.org QuickStart Account successfully added -Other available IM protocols are jabber, msn, and yahoo. Oscar is the protocol used by ICQ and AOL. For oscar, you need to specify the IM-server as a fourth argument (for msn and yahoo there is no fourth argument). For AOL Instant Messenger, the server name is login.oscar.aol.com. For ICQ, the server name is login.icq.com. +Other available IM protocols are msn, oscar, and yahoo. Oscar is the protocol used by ICQ and AOL. diff --git a/irc_commands.c b/irc_commands.c index 287a126f..65f0d6c6 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -206,7 +206,7 @@ static void irc_cmd_invite( irc_t *irc, char **cmd ) if( u && c && ( u->ic == c->ic ) ) if( c->ic && c->ic->acc->prpl->chat_invite ) { - c->ic->acc->prpl->chat_invite( c, "", u->handle ); + c->ic->acc->prpl->chat_invite( c, u->handle, NULL ); irc_reply( irc, 341, "%s %s", nick, channel ); return; } diff --git a/lib/misc.c b/lib/misc.c index c977029f..d6795ec9 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -44,6 +44,8 @@ #include #endif +#include "ssl_client.h" + void strip_linefeed(gchar *text) { int i, j; @@ -590,3 +592,11 @@ char *word_wrap( char *msg, int line_len ) return g_string_free( ret, FALSE ); } + +gboolean ssl_sockerr_again( void *ssl ) +{ + if( ssl ) + return ssl_errno == SSL_AGAIN; + else + return sockerr_again(); +} diff --git a/lib/misc.h b/lib/misc.h index 1d76f7f2..e0468d73 100644 --- a/lib/misc.h +++ b/lib/misc.h @@ -65,4 +65,6 @@ G_MODULE_EXPORT struct ns_srv_reply *srv_lookup( char *service, char *protocol, G_MODULE_EXPORT char *word_wrap( char *msg, int line_len ); +G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl ); + #endif diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index 221a2862..b964ab49 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -222,8 +222,10 @@ void ssl_disconnect( void *conn_ ) closesocket( conn->fd ); - gnutls_deinit( conn->session ); - gnutls_certificate_free_credentials( conn->xcred ); + if( conn->session ) + gnutls_deinit( conn->session ); + if( conn->xcred ) + gnutls_certificate_free_credentials( conn->xcred ); g_free( conn ); } diff --git a/nick.c b/nick.c index 88c3faea..4b05f4a7 100644 --- a/nick.c +++ b/nick.c @@ -153,10 +153,10 @@ void nick_del( account_t *acc, const char *handle ) /* Character maps, _lc_[x] == _uc_[x] (but uppercase), according to the RFC's. With one difference, we allow dashes. */ -static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^-_|"; -static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~-_\\"; +static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^`-_|"; +static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~`-_\\"; -void nick_strip( char * nick ) +void nick_strip( char *nick ) { int i, j; @@ -169,6 +169,15 @@ void nick_strip( char * nick ) j++; } } + if( isdigit( nick[0] ) ) + { + char *orig; + + orig = g_strdup( nick ); + g_snprintf( nick, MAX_NICK_LENGTH, "_%s", orig ); + g_free( orig ); + j ++; + } while( j <= MAX_NICK_LENGTH ) nick[j++] = '\0'; } @@ -177,8 +186,8 @@ int nick_ok( const char *nick ) { const char *s; - /* Empty/long nicks are not allowed */ - if( !*nick || strlen( nick ) > MAX_NICK_LENGTH ) + /* Empty/long nicks are not allowed, nor numbers at [0] */ + if( !*nick || isdigit( nick[0] ) || strlen( nick ) > MAX_NICK_LENGTH ) return( 0 ); for( s = nick; *s; s ++ ) diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index c5bc0e68..074412ec 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -175,6 +175,27 @@ int jabber_chat_leave( struct groupchat *c, const char *reason ) return 1; } +void jabber_chat_invite( struct groupchat *c, char *who, char *message ) +{ + struct xt_node *node; + struct im_connection *ic = c->ic; + struct jabber_chat *jc = c->data; + + node = xt_new_node( "reason", message, NULL ); + + node = xt_new_node( "invite", NULL, node ); + xt_add_attr( node, "to", who ); + + node = xt_new_node( "x", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_MUC_USER ); + + node = jabber_make_packet( "message", NULL, jc->name, node ); + + jabber_write_packet( ic, node ); + + xt_free_node( node ); +} + /* Not really the same syntax as the normal pkt_ functions, but this isn't called by the xmltree parser directly and this way I can add some extra parameters so we won't have to repeat too many things done by the caller diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index 61cd142e..29561b86 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -119,7 +119,7 @@ static gboolean jabber_write_queue( struct im_connection *ic ) return TRUE; } - else if( st == 0 || ( st < 0 && !sockerr_again() ) ) + else if( st == 0 || ( st < 0 && !ssl_sockerr_again( jd->ssl ) ) ) { /* Set fd to -1 to make sure we won't write to it anymore. */ closesocket( jd->fd ); /* Shouldn't be necessary after errors? */ @@ -230,7 +230,7 @@ static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition } } } - else if( st == 0 || ( st < 0 && !sockerr_again() ) ) + else if( st == 0 || ( st < 0 && !ssl_sockerr_again( jd->ssl ) ) ) { closesocket( jd->fd ); jd->fd = -1; diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 8bd1111a..2f0959b0 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -49,7 +49,8 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) } else if( strcmp( type, "get" ) == 0 ) { - if( !( c = xt_find_node( node->children, "query" ) ) || + if( !( ( c = xt_find_node( node->children, "query" ) ) || + ( c = xt_find_node( node->children, "ping" ) ) ) || /* O_o WHAT is wrong with just ????? */ !( s = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-%s packet", type ); @@ -80,12 +81,21 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) ); xt_add_child( reply, xt_new_node( "tz", buf, NULL ) ); } + else if( strcmp( s, XMLNS_PING ) == 0 ) + { + xt_free_node( reply ); + reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), NULL ); + if( ( s = xt_find_attr( node, "id" ) ) ) + xt_add_attr( reply, "id", s ); + pack = 0; + } else if( strcmp( s, XMLNS_DISCO_INFO ) == 0 ) { const char *features[] = { XMLNS_VERSION, XMLNS_TIME, XMLNS_CHATSTATES, XMLNS_MUC, + XMLNS_PING, XMLNS_SI, XMLNS_BYTESTREAMS, XMLNS_FILETRANSFER, diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 98d2dadf..1d3225dd 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -422,6 +422,20 @@ static void jabber_chat_leave_( struct groupchat *c ) jabber_chat_leave( c, NULL ); } +static void jabber_chat_invite_( struct groupchat *c, char *who, char *msg ) +{ + struct jabber_chat *jc = c->data; + gchar *msg_alt = NULL; + + if( msg == NULL ) + msg_alt = g_strdup_printf( "%s invited you to %s", c->ic->acc->user, jc->name ); + + if( c && who ) + jabber_chat_invite( c, who, msg ? msg : msg_alt ); + + g_free( msg_alt ); +} + static void jabber_keepalive( struct im_connection *ic ) { /* Just any whitespace character is enough as a keepalive for XMPP sessions. */ @@ -493,7 +507,7 @@ void jabber_initmodule() ret->remove_buddy = jabber_remove_buddy; ret->chat_msg = jabber_chat_msg_; ret->chat_topic = jabber_chat_topic_; -// ret->chat_invite = jabber_chat_invite; + ret->chat_invite = jabber_chat_invite_; ret->chat_leave = jabber_chat_leave_; ret->chat_join = jabber_chat_join_; ret->keepalive = jabber_keepalive; diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 9edb844e..3251b49b 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -188,6 +188,7 @@ struct jabber_transfer #define XMLNS_AUTH "jabber:iq:auth" /* XEP-0078 */ #define XMLNS_VERSION "jabber:iq:version" /* XEP-0092 */ #define XMLNS_TIME "jabber:iq:time" /* XEP-0090 */ +#define XMLNS_PING "urn:xmpp:ping" /* XEP-0199 */ #define XMLNS_VCARD "vcard-temp" /* XEP-0054 */ #define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */ #define XMLNS_XDATA "jabber:x:data" /* XEP-0004 */ @@ -292,5 +293,6 @@ int jabber_chat_topic( struct groupchat *c, char *topic ); int jabber_chat_leave( struct groupchat *c, const char *reason ); void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ); void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ); +void jabber_chat_invite( struct groupchat *c, char *who, char *message ); #endif diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index df04e30d..aa05dbdd 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -240,7 +240,7 @@ static void msn_chat_msg( struct groupchat *c, char *message, int flags ) already severely broken) disappeared here! */ } -static void msn_chat_invite( struct groupchat *c, char *msg, char *who ) +static void msn_chat_invite( struct groupchat *c, char *who, char *message ) { struct msn_switchboard *sb = msn_sb_by_chat( c ); char buf[1024]; diff --git a/protocols/oscar/aim.h b/protocols/oscar/aim.h index 81ea5f9e..9516996c 100644 --- a/protocols/oscar/aim.h +++ b/protocols/oscar/aim.h @@ -93,7 +93,7 @@ typedef guint16 flap_seqnum_t; * the client to connect to it. * */ -#define AIM_DEFAULT_LOGIN_SERVER "login.oscar.aol.com" +#define AIM_DEFAULT_LOGIN_SERVER "login.messaging.aol.com" #define AIM_LOGIN_PORT 5190 /* diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 96983738..c4683046 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -340,7 +340,7 @@ static void oscar_init(account_t *acc) { set_t *s; - s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); + s = set_add( &acc->set, "server", AIM_DEFAULT_LOGIN_SERVER, set_eval_account, acc ); s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; if (isdigit(acc->user[0])) { @@ -355,15 +355,7 @@ static void oscar_login(account_t *acc) { struct im_connection *ic = imcb_new(acc); struct oscar_data *odata = ic->proto_data = g_new0(struct oscar_data, 1); - if (isdigit(acc->user[0])) { - odata->icq = TRUE; - /* This is odd but it's necessary for a proper do_import and do_export. - We don't do those anymore, but let's stick with it, just in case - it accidentally fixes something else too... */ - /* ic->acc->pass[8] = 0; - Not touching this anymore now that it belongs to account_t! - Let's hope nothing will break. ;-) */ - } else { + if (!isdigit(acc->user[0])) { ic->flags |= OPT_DOES_HTML; } @@ -384,24 +376,14 @@ static void oscar_login(account_t *acc) { return; } - if (acc->server == NULL) { - imcb_error(ic, "No servername specified"); - imc_logout(ic, FALSE); - return; - } - - if (g_strcasecmp(acc->server, "login.icq.com") != 0 && - g_strcasecmp(acc->server, "login.oscar.aol.com") != 0) { - imcb_log(ic, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.",acc->server); - } - imcb_log(ic, _("Signon: %s"), ic->acc->user); aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0); aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0); conn->status |= AIM_CONN_STATUS_INPROGRESS; - conn->fd = proxy_connect(acc->server, AIM_LOGIN_PORT, oscar_login_connect, ic); + conn->fd = proxy_connect(set_getstr(&acc->set, "server"), + AIM_LOGIN_PORT, oscar_login_connect, ic); if (conn->fd < 0) { imcb_error(ic, _("Couldn't connect to host")); imc_logout(ic, TRUE); @@ -2508,7 +2490,7 @@ void oscar_chat_msg(struct groupchat *c, char *message, int msgflags) /* return (ret >= 0); */ } -void oscar_chat_invite(struct groupchat *c, char *message, char *who) +void oscar_chat_invite(struct groupchat *c, char *who, char *message) { struct im_connection *ic = c->ic; struct oscar_data * od = (struct oscar_data *)ic->proto_data; diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 28a72877..625f3d1c 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -305,7 +305,7 @@ static void byahoo_chat_msg( struct groupchat *c, char *message, int flags ) yahoo_conference_message( yd->y2_id, NULL, c->data, c->title, message, 1 ); } -static void byahoo_chat_invite( struct groupchat *c, char *msg, char *who ) +static void byahoo_chat_invite( struct groupchat *c, char *who, char *msg ) { struct byahoo_data *yd = (struct byahoo_data *) c->ic->proto_data; diff --git a/root_commands.c b/root_commands.c index 2da77519..eea16178 100644 --- a/root_commands.c +++ b/root_commands.c @@ -198,7 +198,7 @@ static void cmd_drop( irc_t *irc, char **cmd ) irc_usermsg( irc, "Account `%s' removed", irc->nick ); break; default: - irc_usermsg( irc, "Error: '%d'", status ); + irc_usermsg( irc, "Error: `%d'", status ); break; } } @@ -233,7 +233,11 @@ static void cmd_account( irc_t *irc, char **cmd ) a = account_add( irc, prpl, cmd[3], cmd[4] ); if( cmd[5] ) + { + irc_usermsg( irc, "Warning: Passing a servername/other flags to `account add' " + "is now deprecated. Use `account set' instead." ); set_setstr( &a->set, "server", cmd[5] ); + } irc_usermsg( irc, "Account successfully added" ); } @@ -316,7 +320,7 @@ static void cmd_account( irc_t *irc, char **cmd ) } else { - irc_usermsg( irc, "No accounts known. Use 'account add' to add one." ); + irc_usermsg( irc, "No accounts known. Use `account add' to add one." ); } } } @@ -402,9 +406,7 @@ static void cmd_account( irc_t *irc, char **cmd ) return; } - if( ( strcmp( cmd[3], "=" ) ) == 0 && cmd[4] ) - irc_usermsg( irc, "Warning: Correct syntax: \002account set \002 (without =)" ); - else if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 ) + if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 ) set_reset( &a->set, set_name ); else set_setstr( &a->set, set_name, cmd[3] ); @@ -744,16 +746,11 @@ static void cmd_yesno( irc_t *irc, char **cmd ) static void cmd_set( irc_t *irc, char **cmd ) { - char *set_name = NULL; + char *set_name = cmd[1]; if( cmd[1] && cmd[2] ) { - if( ( strcmp( cmd[2], "=" ) ) == 0 && cmd[3] ) - { - irc_usermsg( irc, "Warning: Correct syntax: \002set \002 (without =)" ); - return; - } - else if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 ) + if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 ) { set_reset( &irc->set, cmd[2] ); set_name = cmd[2]; @@ -761,7 +758,6 @@ static void cmd_set( irc_t *irc, char **cmd ) else { set_setstr( &irc->set, cmd[1], cmd[2] ); - set_name = cmd[1]; } } if( set_name ) /* else 'forgotten' on purpose.. Must show new value after changing */ diff --git a/tests/check_nick.c b/tests/check_nick.c index 714c4fdc..6c4267cd 100644 --- a/tests/check_nick.c +++ b/tests/check_nick.c @@ -14,11 +14,17 @@ START_TEST(test_nick_strip) "thisisaveryveryveryverylongnick", "thisisave:ryveryveryverylongnick", "t::::est", + "test123", + "123test", + "123", NULL }; const char *expected[] = { "test", "test", "test", "thisisaveryveryveryveryl", "thisisaveryveryveryveryl", "test", + "test123", + "_123test", + "_123", NULL }; for (i = 0; get[i]; i++) { @@ -34,8 +40,8 @@ END_TEST START_TEST(test_nick_ok_ok) { - const char *nicks[] = { "foo", "bar", "bla[", "blie]", - "BreEZaH", "\\od^~", NULL }; + const char *nicks[] = { "foo", "bar123", "bla[", "blie]", "BreEZaH", + "\\od^~", "_123", "_123test", NULL }; int i; for (i = 0; nicks[i]; i++) { @@ -48,7 +54,7 @@ END_TEST START_TEST(test_nick_ok_notok) { const char *nicks[] = { "thisisaveryveryveryveryveryveryverylongnick", - "\nillegalchar", "", "nick%", NULL }; + "\nillegalchar", "", "nick%", "123test", NULL }; int i; for (i = 0; nicks[i]; i++) { -- cgit v1.2.3 From 506e61b853ba969bc2d4d2878e87b975bd9e431c Mon Sep 17 00:00:00 2001 From: ulim Date: Sun, 17 Feb 2008 13:34:47 +0100 Subject: Removes the word "dcc" from 2 comments and one message. Thanks to vmiklos for this! --- protocols/ft.h | 4 ++-- protocols/jabber/si.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/ft.h b/protocols/ft.h index d35580d0..42333c90 100644 --- a/protocols/ft.h +++ b/protocols/ft.h @@ -47,7 +47,7 @@ typedef enum { * imcb_file_send_start() method, which will initialize most of the fields. The data field and the various * methods are zero-initialized. Instances will automatically be deleted once the transfer is completed, * canceled, or the connection to the irc client has been lost (note that also if only the irc connection - * and not the dcc connection is lost, the file transfer will still be canceled and freed). + * and not the file transfer connection is lost, the file transfer will still be canceled and freed). * * The following (poor ascii-art) diagram illustrates what methods are called for which status-changes: * @@ -159,7 +159,7 @@ typedef struct file_transfer { } file_transfer_t; /* - * This starts a file transfer from bitlbee to the user (currently via DCC). + * This starts a file transfer from bitlbee to the user. */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ); diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 1d649da3..aab71c8b 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -211,7 +211,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", xt_find_attr( node, "from" ), name, size/1024 ); - imcb_log( ic, "Accept the DCC transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); + imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); tf = g_new0( struct jabber_transfer, 1 ); -- cgit v1.2.3 From 2625d6de47df4054f793990118fa99f3d04d694f Mon Sep 17 00:00:00 2001 From: ulim Date: Wed, 20 Feb 2008 21:55:38 +0100 Subject: some fixes related to connection timeout --- protocols/jabber/s5bytestream.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 4770feda..0abc2c1b 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -100,6 +100,12 @@ void jabber_bs_free_transfer( file_transfer_t *ft) { struct bs_transfer *bt = tf->streamhandle; jabber_streamhost_t *sh; + if ( bt->connect_timeout ) + { + b_event_remove( bt->connect_timeout ); + bt->connect_timeout = 0; + } + if ( tf->watch_in ) b_event_remove( tf->watch_in ); @@ -347,7 +353,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con struct bs_transfer *bt = data; short revents; - if ( !jabber_bs_poll( bt, fd, &revents ) ) + if ( ( fd != 0 ) && !jabber_bs_poll( bt, fd, &revents ) ) return FALSE; switch( bt->phase ) @@ -723,7 +729,17 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt } } else { - /* using a proxy */ + /* using a proxy, abort listen */ + + closesocket( tf->fd ); + tf->fd = 0; + + if ( bt->connect_timeout ) + { + b_event_remove( bt->connect_timeout ); + bt->connect_timeout = 0; + } + GSList *shlist; for( shlist = jd->streamhosts ; shlist ; shlist = g_slist_next( shlist ) ) { @@ -887,13 +903,15 @@ gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) { struct jabber_transfer *tf = bt->tf; + struct jabber_data *jd = tf->ic->proto_data; /* TODO: did the receiver get here somehow??? */ imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", tf->ft->file_name, error ); - imcb_file_canceled( tf->ft, error ); + if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */ + imcb_file_canceled( tf->ft, error ); return FALSE; } -- cgit v1.2.3 From 6cac643f6933e431b90fcb937dec505f989e6a53 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 14 Apr 2008 14:59:15 +0200 Subject: more verbose error logging --- dcc.c | 24 ++++++++++++++++++------ protocols/jabber/s5bytestream.c | 11 ++++++----- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/dcc.c b/dcc.c index 2fa3f745..fa5a0f76 100644 --- a/dcc.c +++ b/dcc.c @@ -583,11 +583,17 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) regex_t re; file_transfer_t *ft; dcc_file_transfer_t *df; - - if( regcomp( &re, pattern, REG_EXTENDED ) ) - return NULL; - if( regexec( &re, line, 9, pmatch, 0 ) ) + char errbuf[256]; + int regerrcode, gret; + + if( ( regerrcode = regcomp( &re, pattern, REG_EXTENDED ) ) || + ( regerrcode = regexec( &re, line, 9, pmatch, 0 ) ) ) { + regerror( regerrcode,&re,errbuf,sizeof( errbuf ) ); + imcb_log( ic, + "DCC: error parsing 'DCC SEND': %s, line: %s", + errbuf, line ); return NULL; + } if( ( pmatch[1].rm_so > 0 ) && ( pmatch[4].rm_so > 0 ) && @@ -615,7 +621,7 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) /* number means ipv4, something else means ipv6 */ if ( pmatch[5].rm_so > 0 ) { - struct in_addr ipaddr = { htonl( atoi( input + pmatch[5].rm_so ) ) }; + struct in_addr ipaddr = { .s_addr = htonl( atoi( input + pmatch[5].rm_so ) ) }; host = inet_ntoa( ipaddr ); } else { @@ -630,9 +636,13 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) filesize = atoll( input + pmatch[8].rm_so ); memset( &hints, 0, sizeof ( struct addrinfo ) ); - if ( getaddrinfo( host, port, &hints, &rp ) ) + if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) ) { g_free( input ); + imcb_log( ic, "DCC: getaddrinfo() failed with %s " + "when parsing incoming 'DCC SEND': " + "host %s, port %s", + gai_strerror( gret ), host, port ); return NULL; } @@ -649,6 +659,8 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) return ft; } + imcb_log( ic, "DCC: couldnt parse 'DCC SEND' line: %s", line ); + return NULL; } diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 0abc2c1b..1008f162 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -352,6 +352,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con struct bs_transfer *bt = data; short revents; + int gret; if ( ( fd != 0 ) && !jabber_bs_poll( bt, fd, &revents ) ) return FALSE; @@ -365,8 +366,8 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con memset( &hints, 0, sizeof( struct addrinfo ) ); hints.ai_socktype = SOCK_STREAM; - if ( getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) ); + if ( ( gret = getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo() failed: %s", gai_strerror( gret ) ); ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); @@ -922,7 +923,7 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ) { struct jabber_transfer *tf = bt->tf; - int fd; + int fd,gret; char hostname[ HOST_NAME_MAX + 1 ]; struct addrinfo hints, *rp; socklen_t ssize = sizeof( struct sockaddr_storage ); @@ -935,8 +936,8 @@ gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; - if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo()" ); + if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo() failed: %s", gai_strerror( gret ) ); memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); -- cgit v1.2.3 From 0cab3888888c7c6b58af9560a0ae2c74a795727f Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 14 Apr 2008 17:33:13 +0200 Subject: more verbose error reporting --- dcc.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/dcc.c b/dcc.c index fa5a0f76..d93eef87 100644 --- a/dcc.c +++ b/dcc.c @@ -217,7 +217,7 @@ gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_pt { file_transfer_t *file = df->ft; struct sockaddr_storage *saddr; - int fd; + int fd,gret; char hostname[ HOST_NAME_MAX + 1 ]; struct addrinfo hints, *rp; socklen_t ssize = sizeof( struct sockaddr_storage ); @@ -230,8 +230,8 @@ gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_pt hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; - if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) - return dcc_abort( df, "getaddrinfo()" ); + if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) ) + return dcc_abort( df, "getaddrinfo(): %s", gai_strerror( gret ) ); saddr = g_new( struct sockaddr_storage, 1 ); @@ -389,11 +389,12 @@ gboolean dccs_recv_start( file_transfer_t *ft ) dcc_file_transfer_t *df = ft->priv; struct sockaddr_storage *saddr = &df->saddr; int fd; + char ipaddr[INET6_ADDRSTRLEN]; socklen_t sa_len = saddr->ss_family == AF_INET ? sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 ); if( !ft->write ) - return dcc_abort( df, "Protocol didn't register write()" ); + return dcc_abort( df, "BUG: protocol didn't register write()" ); ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" ); @@ -401,7 +402,17 @@ gboolean dccs_recv_start( file_transfer_t *ft ) if( ( connect( fd, (struct sockaddr *)saddr, sa_len ) == -1 ) && ( errno != EINPROGRESS ) ) - return dcc_abort( df, "Connecting" ); + return dcc_abort( df, "Connecting to %s:%d : %s", + inet_ntop( saddr->ss_family, + saddr->ss_family == AF_INET ? + ( void* ) &( ( struct sockaddr_in *) saddr )->sin_addr.s_addr : + ( void* ) &( ( struct sockaddr_in6 *) saddr )->sin6_addr.s6_addr, + ipaddr, + sizeof( ipaddr ) ), + ntohs( saddr->ss_family == AF_INET ? + ( ( struct sockaddr_in *) saddr )->sin_port : + ( ( struct sockaddr_in6 *) saddr )->sin6_port ), + strerror( errno ) ); ft->status = FT_STATUS_CONNECTING; -- cgit v1.2.3 From 4358b10c11410a27af9458c92067549cafbc4c0b Mon Sep 17 00:00:00 2001 From: ulim Date: Sun, 4 May 2008 15:32:15 +0200 Subject: ulibc support, fixes "Invalid SOCKS5 Connect message" problem --- dcc.c | 14 ++++++++++++++ irc.c | 3 --- protocols/jabber/iq.c | 5 +++-- protocols/jabber/s5bytestream.c | 16 +++++++++++++++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/dcc.c b/dcc.c index d93eef87..cd70c480 100644 --- a/dcc.c +++ b/dcc.c @@ -29,6 +29,20 @@ #include #include +/* Some ifdefs for ulibc (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + /* * Since that might be confusing a note on naming: * diff --git a/irc.c b/irc.c index e8b08e71..c61e289b 100644 --- a/irc.c +++ b/irc.c @@ -29,9 +29,6 @@ #include "ipc.h" #include "dcc.h" -#include -#include - static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); GSList *irc_connection_list = NULL; diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 22aa3e7c..eacc85af 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -611,7 +611,7 @@ xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL ) { /* Who cares about the unknown... */ - imcb_log( ic, "Couldnt find the man: %s", bare_jid); + imcb_log( ic, "Couldn't find buddy: %s", bare_jid); return 0; } @@ -625,6 +625,7 @@ xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) { imcb_log( ic, "WARNING: Couldn't generate feature query" ); xt_free_node( node ); + return 0; } jabber_cache_add( ic, query, jabber_iq_parse_features ); @@ -647,7 +648,7 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no if( ( bud = jabber_buddy_by_jid( ic, xt_find_attr( node, "from") , 0 ) ) == NULL ) { /* Who cares about the unknown... */ - imcb_log( ic, "Couldnt find the man: %s", xt_find_attr( node, "from")); + imcb_log( ic, "Couldn't find buddy: %s", xt_find_attr( node, "from")); return 0; } diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 1008f162..15696501 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -25,6 +25,16 @@ #include "sha1.h" #include +/* Some ifdefs for ulibc (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + struct bs_transfer { struct jabber_transfer *tf; @@ -1047,10 +1057,14 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con { struct socks5_message socks5_connect; int msgsize = sizeof( struct socks5_message ); + int ret; - if( !jabber_bs_peek( bt, &socks5_connect, msgsize ) ) + if( !( ret = jabber_bs_peek( bt, &socks5_connect, msgsize ) ) ) return FALSE; + if( ret < msgsize ) + return TRUE; + if( !( socks5_connect.ver == 5) || !( socks5_connect.cmdrep.cmd == 1 ) || !( socks5_connect.atyp == 3 ) || -- cgit v1.2.3 From 29c1456dcadec0d239ffc9d88ea06695b66c435c Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 6 May 2008 02:13:37 +0200 Subject: SOCKS5 bytestream related changes. * allow the SOCKS5 server to not include the pseudo address in its reply(including it is an rfc-style SHOULD in XEP-0065) * ignore if the SOCKS5 server's reply is too short (as is the one from the jabber.cz proxy [apparently using the proxy65 code]) --- protocols/jabber/s5bytestream.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 15696501..996d1f24 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -465,18 +465,28 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con if ( !( ret = jabber_bs_peek( bt, &socks5_reply, sizeof( struct socks5_message ) ) ) ) return FALSE; - if ( ret < sizeof( socks5_reply ) ) + if ( ret < 5 ) /* header up to address length */ return TRUE; + else if( ret < sizeof( struct socks5_message ) ) + { + /* Either a buggy proxy or just one that doesnt regard the SHOULD in XEP-0065 + * saying the reply SHOULD contain the address */ + + ASSERTSOCKOP( ret = recv( fd, &socks5_reply, ret, 0 ), "Dequeuing after MSG_PEEK" ); + } if( !( socks5_reply.ver == 5 ) || - !( socks5_reply.cmdrep.rep == 0 ) || - !( socks5_reply.atyp == 3 ) || - !( socks5_reply.addrlen == 40 ) ) + !( socks5_reply.cmdrep.rep == 0 ) ) return jabber_bs_abort( bt, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", socks5_reply.ver, socks5_reply.cmdrep.rep, socks5_reply.atyp, socks5_reply.addrlen); + + /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz) + * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect). + * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy + * is sending, it shouldnt matter */ if( bt->tf->ft->sending ) jabber_bs_send_activate( bt ); @@ -742,6 +752,12 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt { /* using a proxy, abort listen */ + if ( tf->watch_in ) + { + b_event_remove( tf->watch_in ); + tf->watch_in = 0; + } + closesocket( tf->fd ); tf->fd = 0; @@ -824,6 +840,8 @@ static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struc return XT_HANDLED; } + imcb_log( tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...", tf->ft->file_name ); + /* handshake went through, let's start transferring */ tf->ft->write_request( tf->ft ); -- cgit v1.2.3 From d56ee38d444fb9a4bc0fbf7f699eaf675e019591 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 6 May 2008 02:20:11 +0200 Subject: timeout of transfers after 120 seconds of no progress (bytes received/sent). --- dcc.c | 31 +++++++++++++++++++++++++++++-- dcc.h | 8 ++++++++ protocols/jabber/si.c | 3 ++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/dcc.c b/dcc.c index cd70c480..a6eb511c 100644 --- a/dcc.c +++ b/dcc.c @@ -84,6 +84,7 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc gboolean dccs_recv_start( file_transfer_t *ft ); gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); gboolean dccs_recv_write_request( file_transfer_t *ft ); +gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ); /* As defined in ft.h */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) @@ -151,6 +152,8 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file ); + df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); + return file; } @@ -167,8 +170,8 @@ gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ) if( file->canceled ) file->canceled( file, msg ); - else - imcb_log( df->ic, "DCC transfer aborted: %s", msg ); + + imcb_log( df->ic, "File %s: DCC transfer aborted: %s", file->file_name, msg ); g_free( msg ); @@ -177,6 +180,25 @@ gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ) return FALSE; } +gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ) +{ + struct dcc_file_transfer *df = data; + + if( df->ft->bytes_transferred == df->progress_bytes_last ) + { + /* no progress. cancel */ + if( df->bytes_sent == 0 ) + return dcc_abort( df, "Couldnt establish transfer within %d seconds", DCC_MAX_STALL ); + else + return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->ft->bytes_transferred / 1024 ); + + } + + df->progress_bytes_last = df->ft->bytes_transferred; + + return TRUE; +} + /* used extensively for socket operations */ #define ASSERTSOCKOP(op, msg) \ if( (op) == -1 ) \ @@ -434,6 +456,8 @@ gboolean dccs_recv_start( file_transfer_t *ft ) df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df ); ft->write_request = dccs_recv_write_request; + df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); + return TRUE; } @@ -573,6 +597,9 @@ static void dcc_close( file_transfer_t *file ) if( df->watch_out ) b_event_remove( df->watch_out ); + if( df->progress_timeout ) + b_event_remove( df->progress_timeout ); + df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file ); g_free( df ); diff --git a/dcc.h b/dcc.h index e53823e7..de24dac0 100644 --- a/dcc.h +++ b/dcc.h @@ -46,6 +46,10 @@ /* Send an ACK after receiving this amount of data */ #define DCC_PACKET_SIZE 1024 +/* Time in seconds that a DCC transfer can be stalled before being aborted. + * By handling this here individual protocols don't have to think about this. */ +#define DCC_MAX_STALL 120 + typedef struct dcc_file_transfer { struct im_connection *ic; @@ -63,6 +67,10 @@ typedef struct dcc_file_transfer { gint watch_in; /* readable */ gint watch_out; /* writable */ + /* the progress watcher cancels any file transfer if nothing happens within DCC_MAX_STALL */ + gint progress_timeout; + size_t progress_bytes_last; + /* * The total amount of bytes that have been sent to the irc client. */ diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index aab71c8b..d47cf7a4 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -54,8 +54,9 @@ void jabber_si_free_transfer( file_transfer_t *ft) void jabber_si_finished( file_transfer_t *ft ) { struct jabber_transfer *tf = ft->data; + time_t diff = time( NULL ) - ft->started; - imcb_log( tf->ic, "File %s transferred successfully!" , ft->file_name ); + imcb_log( tf->ic, "File %s transferred successfully at %d kb/s!" , ft->file_name, (int) ( ft->bytes_transferred / 1024 / diff ) ); } /* file_transfer canceled() callback */ -- cgit v1.2.3 From 44961cb326fde3cb681a79eb28becb74a921a29d Mon Sep 17 00:00:00 2001 From: ulim Date: Wed, 7 May 2008 00:06:22 +0200 Subject: fix bug in new kb/s display for transfers of less than one second. --- protocols/jabber/si.c | 2 +- root_commands.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index d47cf7a4..6e62f684 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -54,7 +54,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) void jabber_si_finished( file_transfer_t *ft ) { struct jabber_transfer *tf = ft->data; - time_t diff = time( NULL ) - ft->started; + time_t diff = time( NULL ) - ft->started ? : 1; imcb_log( tf->ic, "File %s transferred successfully at %d kb/s!" , ft->file_name, (int) ( ft->bytes_transferred / 1024 / diff ) ); } diff --git a/root_commands.c b/root_commands.c index 9dfbc998..b3a77a10 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1075,7 +1075,7 @@ static void cmd_transfers( irc_t *irc, char **cmd ) else { int kb_per_s = 0; - time_t diff = time( NULL ) - file->started; + time_t diff = time( NULL ) - file->started ? : 1; if ( ( file->started > 0 ) && ( file->bytes_transferred > 0 ) ) kb_per_s = file->bytes_transferred / 1024 / diff; -- cgit v1.2.3 From b043ad5e69d91730145ea49305a1fab588c83a93 Mon Sep 17 00:00:00 2001 From: ulim Date: Thu, 8 May 2008 23:20:36 +0200 Subject: allow dcc optimization in progress watcher. Apparently, irssi doesn't send any DCC ACKs if it "feels" that the sender doesn't expect any(no idea how exactly it does that). Anyway, the progress watcher used to check whether the ACKed bytes have increased which it shouldn't do if there aren't any ACKs. --- dcc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dcc.c b/dcc.c index a6eb511c..0ee894b7 100644 --- a/dcc.c +++ b/dcc.c @@ -184,17 +184,17 @@ gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ) { struct dcc_file_transfer *df = data; - if( df->ft->bytes_transferred == df->progress_bytes_last ) + if( df->bytes_sent == df->progress_bytes_last ) { /* no progress. cancel */ if( df->bytes_sent == 0 ) return dcc_abort( df, "Couldnt establish transfer within %d seconds", DCC_MAX_STALL ); else - return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->ft->bytes_transferred / 1024 ); + return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->bytes_sent / 1024 ); } - df->progress_bytes_last = df->ft->bytes_transferred; + df->progress_bytes_last = df->bytes_sent; return TRUE; } -- cgit v1.2.3 From b5cfc2bdad8a68011bf9215c63293299e4e8dc5c Mon Sep 17 00:00:00 2001 From: ulim Date: Sat, 10 May 2008 12:09:19 +0200 Subject: some changes for sending. * not only query but also respect peer's features (i.e. abort ft if an important feature is not advertised) * wait for proxy discovery to complete before starting the transfer (important for sending to people with auto accept) --- protocols/jabber/jabber.h | 6 +++ protocols/jabber/si.c | 106 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 102 insertions(+), 10 deletions(-) diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index fc315710..bc848170 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -153,8 +153,14 @@ struct jabber_transfer /* the stream's private handle */ gpointer streamhandle; + /* timeout for discover queries */ + gint disco_timeout; + gint disco_timeout_fired; + struct im_connection *ic; + struct jabber_buddy *bud; + int watch_in; int watch_out; diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 6e62f684..3afa30ff 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -44,6 +44,9 @@ void jabber_si_free_transfer( file_transfer_t *ft) tf->fd = 0; } + if( tf->disco_timeout ) + b_event_remove( tf->disco_timeout ); + g_free( tf->ini_jid ); g_free( tf->tgt_jid ); g_free( tf->iq_id ); @@ -79,12 +82,90 @@ void jabber_si_canceled( file_transfer_t *ft, char *reason ) } +int jabber_si_check_features( struct jabber_transfer *tf, GSList *features ) { + int foundft = FALSE, foundbt = FALSE, foundsi = FALSE; + + while ( features ) + { + if( !strcmp( features->data, XMLNS_FILETRANSFER ) ) + foundft = TRUE; + if( !strcmp( features->data, XMLNS_BYTESTREAMS ) ) + foundbt = TRUE; + if( !strcmp( features->data, XMLNS_SI ) ) + foundsi = TRUE; + + features = g_slist_next(features); + } + + if( !foundft ) + imcb_file_canceled( tf->ft, "Buddy's client doesn't feature file transfers" ); + else if( !foundbt ) + imcb_file_canceled( tf->ft, "Buddy's client doesn't feature byte streams (required)" ); + else if( !foundsi ) + imcb_file_canceled( tf->ft, "Buddy's client doesn't feature stream initiation (required)" ); + + return foundft && foundbt && foundsi; +} + +void jabber_si_transfer_start( struct jabber_transfer *tf ) { + + if( !jabber_si_check_features( tf, tf->bud->features ) ) + return; + + /* send the request to our buddy */ + jabber_si_send_request( tf->ic, tf->bud->full_jid, tf ); + + /* and start the receive logic */ + imcb_file_recv_start( tf->ft ); + +} + +gboolean jabber_si_waitfor_disco( gpointer data, gint fd, b_input_condition cond ) +{ + struct jabber_transfer *tf = data; + struct jabber_data *jd = tf->ic->proto_data; + + tf->disco_timeout_fired++; + + if( tf->bud->features && jd->have_streamhosts==1 ) { + jabber_si_transfer_start( tf ); + tf->disco_timeout = 0; + return FALSE; + } + + /* 8 seconds should be enough for server and buddy to respond */ + if ( tf->disco_timeout_fired < 16 ) + return TRUE; + + if( !tf->bud->features && jd->have_streamhosts!=1 ) + imcb_file_canceled( tf->ft, "Couldn't get buddy's features or the server's" ); + else if( !tf->bud->features ) + imcb_file_canceled( tf->ft, "Couldn't get buddy's features" ); + else + imcb_file_canceled( tf->ft, "Couldn't get server's features" ); + + tf->disco_timeout = 0; + return FALSE; +} + void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ) { struct jabber_transfer *tf; struct jabber_data *jd = ic->proto_data; - char *server = jd->server; + struct jabber_buddy *bud; + char *server = jd->server, *s; + + if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) ) + bud = jabber_buddy_by_ext_jid( ic, who, 0 ); + else + bud = jabber_buddy_by_jid( ic, who, 0 ); + if( bud == NULL ) + { + imcb_file_canceled( tf->ft, "Couldn't find buddy (BUG?)" ); + return; + } + imcb_log( ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who ); tf = g_new0( struct jabber_transfer, 1 ); @@ -94,22 +175,27 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, tf->ft->data = tf; tf->ft->free = jabber_si_free_transfer; tf->ft->finished = jabber_si_finished; + tf->bud = bud; ft->write = jabber_bs_send_write; jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); - /* query the buddy's features */ - jabber_iq_query_features( ic, who ); + /* query buddy's features and server's streaming proxies if neccessary */ - /* query proxies from the server */ - if( !jd->have_streamhosts ) - jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); + if( !tf->bud->features ) + jabber_iq_query_features( ic, bud->full_jid ); - /* send the request to our buddy */ - jabber_si_send_request( ic, who, tf ); + if( jd->have_streamhosts!=1 ) { + jd->have_streamhosts = 0; + jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); + } - /* and start the receive logic */ - imcb_file_recv_start( ft ); + /* if we had to do a query, wait for the result. + * Otherwise fire away. */ + if( !tf->bud->features || jd->have_streamhosts!=1 ) + tf->disco_timeout = b_timeout_add( 500, jabber_si_waitfor_disco, tf ); + else + jabber_si_transfer_start( tf ); } /* -- cgit v1.2.3 From cce0450184b4358ef06d91cca985fa3ca389fcd6 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 2 Jun 2008 14:49:33 +0200 Subject: Added textual SOCKS5 error messages. --- protocols/jabber/s5bytestream.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 996d1f24..41bae40a 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -71,6 +71,18 @@ struct socks5_message in_port_t port; } __attribute__ ((packed)); +char *socks5_reply_code[] = { + "succeeded", + "general SOCKS server failure", + "connection not allowed by ruleset", + "Network unreachable", + "Host unreachable", + "Connection refused", + "TTL expired", + "Command not supported", + "Address type not supported", + "unassigned"}; + /* connect() timeout in seconds. */ #define JABBER_BS_CONTIMEOUT 15 /* listen timeout */ @@ -476,12 +488,19 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con } if( !( socks5_reply.ver == 5 ) || - !( socks5_reply.cmdrep.rep == 0 ) ) - return jabber_bs_abort( bt, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", + !( socks5_reply.cmdrep.rep == 0 ) ) { + char errstr[128] = ""; + if( ( socks5_reply.ver == 5 ) && ( socks5_reply.cmdrep.rep < + ( sizeof( socks5_reply_code ) / sizeof( socks5_reply_code[0] ) ) ) ) { + sprintf( errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ] ); + } + return jabber_bs_abort( bt, "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)", + errstr, socks5_reply.ver, socks5_reply.cmdrep.rep, socks5_reply.atyp, socks5_reply.addrlen); + } /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz) * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect). -- cgit v1.2.3 From 3355a82eae40c89024233ccafc5f0c25dee2dc5c Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 2 Jun 2008 15:26:38 +0200 Subject: added a #define for AI_NUMERICSERV in s5bytestream.c (missing in ulibc). --- protocols/jabber/s5bytestream.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 41bae40a..809fbb22 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -35,6 +35,10 @@ #endif #endif +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + struct bs_transfer { struct jabber_transfer *tf; -- cgit v1.2.3 From 9afeeface6363ea81c5f80a7274a691f18550a21 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 22 Jul 2008 14:36:56 +0200 Subject: fixes bug in handling file names with spaces. --- dcc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dcc.c b/dcc.c index 0ee894b7..d815e9b7 100644 --- a/dcc.c +++ b/dcc.c @@ -627,11 +627,11 @@ void dcc_finish( file_transfer_t *file ) file_transfer_t *dcc_request( struct im_connection *ic, char *line ) { char *pattern = "SEND" - " (([^\"][^ ]*)|\"([^\"]|\\\")*\")" + " (([^\"][^ ]*)|\"(([^\"]|\\\")*)\")" " (([0-9]*)|([^ ]*))" " ([0-9]*)" " ([0-9]*)\001"; - regmatch_t pmatch[9]; + regmatch_t pmatch[10]; regex_t re; file_transfer_t *ft; dcc_file_transfer_t *df; @@ -639,7 +639,7 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) int regerrcode, gret; if( ( regerrcode = regcomp( &re, pattern, REG_EXTENDED ) ) || - ( regerrcode = regexec( &re, line, 9, pmatch, 0 ) ) ) { + ( regerrcode = regexec( &re, line, 10, pmatch, 0 ) ) ) { regerror( regerrcode,&re,errbuf,sizeof( errbuf ) ); imcb_log( ic, "DCC: error parsing 'DCC SEND': %s, line: %s", @@ -648,9 +648,9 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) } if( ( pmatch[1].rm_so > 0 ) && - ( pmatch[4].rm_so > 0 ) && - ( pmatch[7].rm_so > 0 ) && - ( pmatch[8].rm_so > 0 ) ) + ( pmatch[5].rm_so > 0 ) && + ( pmatch[8].rm_so > 0 ) && + ( pmatch[9].rm_so > 0 ) ) { char *input = g_strdup( line ); char *filename, *host, *port; @@ -668,24 +668,24 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) filename = input + pmatch[3].rm_so; } - input[pmatch[4].rm_eo] = '\0'; + input[pmatch[5].rm_eo] = '\0'; /* number means ipv4, something else means ipv6 */ - if ( pmatch[5].rm_so > 0 ) + if ( pmatch[6].rm_so > 0 ) { struct in_addr ipaddr = { .s_addr = htonl( atoi( input + pmatch[5].rm_so ) ) }; host = inet_ntoa( ipaddr ); } else { /* Contains non-numbers, hopefully an IPV6 address */ - host = input + pmatch[6].rm_so; + host = input + pmatch[7].rm_so; } - input[pmatch[7].rm_eo] = '\0'; input[pmatch[8].rm_eo] = '\0'; + input[pmatch[9].rm_eo] = '\0'; - port = input + pmatch[7].rm_so; - filesize = atoll( input + pmatch[8].rm_so ); + port = input + pmatch[8].rm_so; + filesize = atoll( input + pmatch[9].rm_so ); memset( &hints, 0, sizeof ( struct addrinfo ) ); if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) ) -- cgit v1.2.3 From 8a90001ab2cde971abc64b8d5e4174dc4c4b0ae2 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 22 Jul 2008 14:37:49 +0200 Subject: Added an account setting 'proxy'. Note that this is only used for sending. The default ; means let the receiver try a direct connection first and then the proxy discovered from the server (if any). If you know you're firewalled you can remove the . If you want to provide your own proxy try something like ";JID,HOST,PORT". E.g. ";proxy.somewhere.org,123.123.123.123,7777". --- protocols/jabber/jabber.c | 2 + protocols/jabber/s5bytestream.c | 101 ++++++++++++++++++++++++++++++---------- protocols/jabber/si.c | 22 +++++---- 3 files changed, 93 insertions(+), 32 deletions(-) diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 04d6f906..8b1d78db 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -61,6 +61,8 @@ static void jabber_init( account_t *acc ) s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; + + s = set_add( &acc->set, "proxy", ";", NULL, acc ); } static void jabber_generate_id_hash( struct jabber_data *jd ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 809fbb22..c492491b 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -361,7 +361,7 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru tf->streamhandle = bt; tf->ft->free = jabber_bs_free_transfer; - jabber_bs_recv_handshake( bt, 0, 0 ); + jabber_bs_recv_handshake( bt, -1, 0 ); return XT_HANDLED; } @@ -380,7 +380,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con short revents; int gret; - if ( ( fd != 0 ) && !jabber_bs_poll( bt, fd, &revents ) ) + if ( ( fd != -1 ) && !jabber_bs_poll( bt, fd, &revents ) ) return FALSE; switch( bt->phase ) @@ -553,7 +553,7 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) if( shlist && shlist->next ) { bt->sh = shlist->next->data; - return jabber_bs_recv_handshake( bt, 0, 0 ); + return jabber_bs_recv_handshake( bt, -1, 0 ); } @@ -619,7 +619,7 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) struct bs_transfer *bt = data; struct jabber_transfer *tf = bt->tf; - if( fd != 0 ) /* called via event thread */ + if( fd != -1 ) /* called via event thread */ { tf->watch_in = 0; ASSERTSOCKOP( ret = recv( fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) , "Receiving" ); @@ -665,7 +665,7 @@ gboolean jabber_bs_recv_write_request( file_transfer_t *ft ) return FALSE; } - jabber_bs_recv_read( tf->streamhandle, 0 , 0 ); + jabber_bs_recv_read( tf->streamhandle, -1 , 0 ); return TRUE; } @@ -775,14 +775,16 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt { /* using a proxy, abort listen */ - if ( tf->watch_in ) + if( tf->watch_in ) { b_event_remove( tf->watch_in ); tf->watch_in = 0; } - closesocket( tf->fd ); - tf->fd = 0; + if( tf->fd != -1 ) { + closesocket( tf->fd ); + tf->fd = -1; + } if ( bt->connect_timeout ) { @@ -797,7 +799,7 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt if( strcmp( sh->jid, jid ) == 0 ) { bt->sh = sh; - jabber_bs_recv_handshake( bt, 0, 0 ); + jabber_bs_recv_handshake( bt, -1, 0 ); return XT_HANDLED; } } @@ -871,20 +873,80 @@ static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struc return XT_HANDLED; } +jabber_streamhost_t *jabber_si_parse_proxy( struct im_connection *ic, char *proxy ) +{ + char *host, *port, *jid; + jabber_streamhost_t *sh; + + if( ( ( host = strchr( proxy, ',' ) ) == 0 ) || + ( ( port = strchr( host+1, ',' ) ) == 0 ) ) { + imcb_log( ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy ); + return NULL; + } + + jid = proxy; + *host++ = '\0'; + *port++ = '\0'; + + sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( jid ); + sh->host = g_strdup( host ); + strcpy( sh->port, port ); + + return sh; +} + +void jabber_si_set_proxies( struct bs_transfer *bt ) +{ + struct jabber_transfer *tf = bt->tf; + struct jabber_data *jd = tf->ic->proto_data; + char *proxysetting = g_strdup ( set_getstr( &tf->ic->acc->set, "proxy" ) ); + char *proxy,*next; + char port[6]; + char host[INET6_ADDRSTRLEN]; + jabber_streamhost_t *sh, *sh2; + GSList *streamhosts = jd->streamhosts; + + proxy = proxysetting; + while ( proxy && ( *proxy!='\0' ) ) { + if( ( next = strchr( proxy, ';' ) ) ) + *next++ = '\0'; + + if( ( strcmp( proxy, "" ) == 0 ) && jabber_bs_send_listen( bt, &tf->saddr, host, port ) ) { + sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( tf->ini_jid ); + sh->host = g_strdup( host ); + strcpy( sh->port, port ); + bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + + bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + } else if( strcmp( proxy, "" ) == 0 ) { + while ( streamhosts ) { + sh = g_new0( jabber_streamhost_t, 1 ); + sh2 = streamhosts->data; + sh->jid = g_strdup( sh2->jid ); + sh->host = g_strdup( sh2->host ); + strcpy( sh->port, sh2->port ); + bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + streamhosts = g_slist_next( streamhosts ); + } + } else if( ( sh = jabber_si_parse_proxy( tf->ic, proxy ) ) ) + bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + proxy = next; + } +} + /* * Starts a bytestream. */ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) { - char host[INET6_ADDRSTRLEN]; struct bs_transfer *bt; sha1_state_t sha; char hash_hex[41]; unsigned char hash[20]; int i,ret; - struct jabber_data *jd = tf->ic->proto_data; - jabber_streamhost_t sh; - GSList *streamhosts = jd->streamhosts; /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ sha1_init( &sha ); @@ -904,18 +966,9 @@ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) tf->ft->free = jabber_bs_free_transfer; tf->ft->canceled = jabber_bs_canceled; - if ( !jabber_bs_send_listen( bt, &tf->saddr, sh.host = host, sh.port ) ) - return FALSE; - - bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); - bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); - - sh.jid = tf->ini_jid; + jabber_si_set_proxies( bt ); - /* temporarily add listen address to streamhosts, send the request and remove it */ - streamhosts = g_slist_prepend( streamhosts, &sh ); - ret = jabber_bs_send_request( tf, streamhosts); - streamhosts = g_slist_remove( streamhosts, &sh ); + ret = jabber_bs_send_request( tf, bt->streamhosts); return ret; } diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 3afa30ff..e7cb2a17 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -38,10 +38,10 @@ void jabber_si_free_transfer( file_transfer_t *ft) jd->filetransfers = g_slist_remove( jd->filetransfers, tf ); - if( tf->fd ) + if( tf->fd != -1 ) { close( tf->fd ); - tf->fd = 0; + tf->fd = -1; } if( tf->disco_timeout ) @@ -128,8 +128,8 @@ gboolean jabber_si_waitfor_disco( gpointer data, gint fd, b_input_condition cond tf->disco_timeout_fired++; if( tf->bud->features && jd->have_streamhosts==1 ) { - jabber_si_transfer_start( tf ); tf->disco_timeout = 0; + jabber_si_transfer_start( tf ); return FALSE; } @@ -138,13 +138,14 @@ gboolean jabber_si_waitfor_disco( gpointer data, gint fd, b_input_condition cond return TRUE; if( !tf->bud->features && jd->have_streamhosts!=1 ) - imcb_file_canceled( tf->ft, "Couldn't get buddy's features or the server's" ); + imcb_log( tf->ic, "Couldn't get buddy's features nor discover all services of the server" ); else if( !tf->bud->features ) - imcb_file_canceled( tf->ft, "Couldn't get buddy's features" ); + imcb_log( tf->ic, "Couldn't get buddy's features" ); else - imcb_file_canceled( tf->ft, "Couldn't get server's features" ); + imcb_log( tf->ic, "Couldn't discover some of the server's services" ); tf->disco_timeout = 0; + jabber_si_transfer_start( tf ); return FALSE; } @@ -172,6 +173,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, tf->ic = ic; tf->ft = ft; + tf->fd = -1; tf->ft->data = tf; tf->ft->free = jabber_si_free_transfer; tf->ft->finished = jabber_si_finished; @@ -185,10 +187,13 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, if( !tf->bud->features ) jabber_iq_query_features( ic, bud->full_jid ); - if( jd->have_streamhosts!=1 ) { + /* If is not set don't check for proxies */ + if( ( jd->have_streamhosts!=1 ) && ( jd->streamhosts==NULL ) && + ( strstr( set_getstr( &ic->acc->set, "proxy" ), "" ) != NULL ) ) { jd->have_streamhosts = 0; jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); - } + } else if ( jd->streamhosts!=NULL ) + jd->have_streamhosts = 1; /* if we had to do a query, wait for the result. * Otherwise fire away. */ @@ -308,6 +313,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st tf->sid = g_strdup( sid ); tf->ic = ic; tf->ft = ft; + tf->fd = -1; tf->ft->data = tf; tf->ft->accept = jabber_si_answer_request; tf->ft->free = jabber_si_free_transfer; -- cgit v1.2.3 From 4ac647dbcef152bebde7209f7c9cbbf8a5e0fc37 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 4 Aug 2008 16:21:49 +0200 Subject: moved some stuff around in preperation for MSN merge. * both ends (proto&dcc) need to finish a transfer now for it to be finished * moved throughput calc. and some messages to dcc (no need to implement in protocols) --- dcc.c | 39 ++++++++++++++++++++++++++++++++++----- dcc.h | 4 ++++ protocols/ft.h | 2 ++ protocols/jabber/jabber.c | 12 ++++++++++++ protocols/jabber/s5bytestream.c | 14 +++++++------- protocols/jabber/si.c | 15 --------------- 6 files changed, 59 insertions(+), 27 deletions(-) diff --git a/dcc.c b/dcc.c index d815e9b7..909fedad 100644 --- a/dcc.c +++ b/dcc.c @@ -113,6 +113,17 @@ gboolean imcb_file_recv_start( file_transfer_t *ft ) return dccs_recv_start( ft ); } +/* As defined in ft.h */ +void imcb_file_finished( file_transfer_t *file ) +{ + dcc_file_transfer_t *df = file->priv; + + if( file->bytes_transferred >= file->file_size ) + dcc_finish( file ); + else + df->proto_finished = TRUE; +} + dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic ) { file_transfer_t *file = g_new0( file_transfer_t, 1 ); @@ -154,6 +165,10 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); + imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", user_nick, file_name, file_size/1024 ); + + imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); + return file; } @@ -410,7 +425,8 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) file->bytes_transferred = bytes_received; if( file->bytes_transferred >= file->file_size ) { - dcc_finish( file ); + if( df->proto_finished ) + dcc_finish( file ); return FALSE; } @@ -492,6 +508,9 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) if( ret == 0 ) return dcc_abort( df, "Remote end closed connection" ); + if( !ft->write( df->ft, ft->buffer, ret ) ) + return FALSE; + df->bytes_sent += ret; done = df->bytes_sent >= ft->file_size; @@ -508,13 +527,16 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret ); } - if( !ft->write( df->ft, ft->buffer, ret ) ) - return FALSE; + if( df->bytes_sent == ret ) + ft->started = time( NULL ); if( done ) { - closesocket( fd ); - dcc_finish( ft ); + if( df->watch_out ) + b_event_remove( df->watch_out ); + + if( df->proto_finished ) + dcc_finish( ft ); df->watch_in = 0; return FALSE; @@ -571,6 +593,9 @@ gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_l if( ret < data_len ) return dcc_abort( df, "send() sent %d instead of %d", ret, data_len ); + if( df->bytes_sent == 0 ) + file->started = time( NULL ); + df->bytes_sent += ret; if( df->bytes_sent < df->ft->file_size ) @@ -609,11 +634,15 @@ static void dcc_close( file_transfer_t *file ) void dcc_finish( file_transfer_t *file ) { + dcc_file_transfer_t *df = file->priv; + time_t diff = time( NULL ) - file->started ? : 1; + file->status |= FT_STATUS_FINISHED; if( file->finished ) file->finished( file ); + imcb_log( df->ic, "File %s transferred successfully at %d kb/s!" , file->file_name, (int) ( file->bytes_transferred / 1024 / diff ) ); dcc_close( file ); } diff --git a/dcc.h b/dcc.h index de24dac0..4be5a338 100644 --- a/dcc.h +++ b/dcc.h @@ -82,6 +82,10 @@ typedef struct dcc_file_transfer { /* if we're receiving, this is the sender's socket address */ struct sockaddr_storage saddr; + /* set to true if the protocol has finished + * (i.e. called imcb_file_finished) + */ + int proto_finished; } dcc_file_transfer_t; file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ); diff --git a/protocols/ft.h b/protocols/ft.h index 42333c90..1155f06f 100644 --- a/protocols/ft.h +++ b/protocols/ft.h @@ -170,4 +170,6 @@ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *user_nick void imcb_file_canceled( file_transfer_t *file, char *reason ); gboolean imcb_file_recv_start( file_transfer_t *ft ); + +void imcb_file_finished( file_transfer_t *file ); #endif diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 8b1d78db..987ef96e 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -244,6 +244,18 @@ static void jabber_logout( struct im_connection *ic ) { struct jabber_data *jd = ic->proto_data; + while( jd->filetransfers ) + imcb_file_canceled( ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" ); + + while( jd->streamhosts ) + { + jabber_streamhost_t *sh = jd->streamhosts->data; + jd->streamhosts = g_slist_remove( jd->streamhosts, sh ); + g_free( sh->jid ); + g_free( sh->host ); + g_free( sh ); + } + if( jd->fd >= 0 ) jabber_end_stream( ic ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index c492491b..7e4ca2ab 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -642,11 +642,11 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) if( ret == 0 ) return jabber_bs_abort( bt, "Remote end closed connection" ); - if( tf->bytesread == 0 ) - tf->ft->started = time( NULL ); - tf->bytesread += ret; + if( tf->bytesread >= tf->ft->file_size ) + imcb_file_finished( tf->ft ); + tf->ft->write( tf->ft, tf->ft->buffer, ret ); return FALSE; @@ -700,16 +700,16 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l /* TODO: catch broken pipe */ ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" ); - if( tf->byteswritten == 0 ) - tf->ft->started = time( NULL ); - tf->byteswritten += ret; /* TODO: this should really not be fatal */ if( ret < len ) return jabber_bs_abort( bt, "send() sent %d instead of %d (send buffer too big!)", ret, len ); - bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); + if( tf->byteswritten >= ft->file_size ) + imcb_file_finished( ft ); + else + bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); return TRUE; } diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index e7cb2a17..e7aeffc9 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -53,15 +53,6 @@ void jabber_si_free_transfer( file_transfer_t *ft) g_free( tf->sid ); } -/* file_transfer finished() callback */ -void jabber_si_finished( file_transfer_t *ft ) -{ - struct jabber_transfer *tf = ft->data; - time_t diff = time( NULL ) - ft->started ? : 1; - - imcb_log( tf->ic, "File %s transferred successfully at %d kb/s!" , ft->file_name, (int) ( ft->bytes_transferred / 1024 / diff ) ); -} - /* file_transfer canceled() callback */ void jabber_si_canceled( file_transfer_t *ft, char *reason ) { @@ -176,7 +167,6 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, tf->fd = -1; tf->ft->data = tf; tf->ft->free = jabber_si_free_transfer; - tf->ft->finished = jabber_si_finished; tf->bud = bud; ft->write = jabber_bs_send_write; @@ -301,10 +291,6 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st /* Request is fine. */ - imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", xt_find_attr( node, "from" ), name, size/1024 ); - - imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); - tf = g_new0( struct jabber_transfer, 1 ); tf->ini_jid = g_strdup( ini_jid ); @@ -317,7 +303,6 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st tf->ft->data = tf; tf->ft->accept = jabber_si_answer_request; tf->ft->free = jabber_si_free_transfer; - tf->ft->finished = jabber_si_finished; tf->ft->canceled = jabber_si_canceled; jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); -- cgit v1.2.3 From a2b99ec7a1a02c57b2ef44663e56bdfab6063a4f Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 11 Aug 2008 00:17:58 +0200 Subject: Added MSN file transfer of type MSNFTP. Transfer is direct and the sender can not be firewalled. --- protocols/msn/Makefile | 2 +- protocols/msn/invitation.c | 666 +++++++++++++++++++++++++++++++++++++++++++++ protocols/msn/invitation.h | 82 ++++++ protocols/msn/msn.c | 5 + protocols/msn/msn.h | 4 + protocols/msn/sb.c | 95 +++---- 6 files changed, 797 insertions(+), 57 deletions(-) create mode 100644 protocols/msn/invitation.c create mode 100644 protocols/msn/invitation.h diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 6a588613..dd5d46e2 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = msn.o msn_util.o ns.o passport.o sb.o tables.o +objects = msn.o msn_util.o ns.o passport.o sb.o tables.o invitation.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c new file mode 100644 index 00000000..4e24d54c --- /dev/null +++ b/protocols/msn/invitation.c @@ -0,0 +1,666 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway * +* * +* Copyright 2008 Uli Meis * +* Copyright 2006 Marijn Kruisselbrink and others * +\********************************************************************/ + +/* MSN module - File transfer support */ + +/* + 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 + */ + +#include "bitlbee.h" +#include "invitation.h" +#include "msn.h" + +#ifdef debug +#undef debug +#endif +#define debug(msg...) log_message( LOGLVL_INFO, msg ) + +static void msn_ftp_free( file_transfer_t *file ); +static void msn_ftpr_accept( file_transfer_t *file ); +static void msn_ftp_finished( file_transfer_t *file ); +static void msn_ftp_canceled( file_transfer_t *file, char *reason ); +static gboolean msn_ftpr_write_request( file_transfer_t *file ); + +static gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ); +static gboolean msn_ftp_read( gpointer data, gint fd, b_input_condition cond ); +gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ); + +/* + * Vararg wrapper for imcb_file_canceled(). + */ +gboolean msn_ftp_abort( file_transfer_t *file, char *format, ... ) +{ + va_list params; + va_start( params, format ); + char error[128]; + + if( vsnprintf( error, 128, format, params ) < 0 ) + sprintf( error, "internal error parsing error string (BUG)" ); + va_end( params ); + imcb_file_canceled( file, error ); + return FALSE; +} + +/* very useful */ +#define ASSERTSOCKOP(op, msg) \ + if( (op) == -1 ) \ + return msn_ftp_abort( file , msg ": %s", strerror( errno ) ); + +/* + * Creates a listening socket and returns its address in host, port. + */ +gboolean msn_ftp_listen( msn_filetransfer_t *msn_file, char *host, char *port ) +{ + file_transfer_t *file = msn_file->dcc; + int fd,gret; + char hostname[ HOST_NAME_MAX + 1 ]; + struct addrinfo hints, *rp; + struct sockaddr_storage saddrst, *saddr = &saddrst; + socklen_t ssize = sizeof( struct sockaddr_storage ); + + /* won't be long till someone asks for this to be configurable :) */ + + ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) ) != 0 ) + return msn_ftp_abort( file, "getaddrinfo() failed: %s", gai_strerror( gret ) ); + + memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); + + ASSERTSOCKOP( fd = msn_file->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); + + ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); + + freeaddrinfo( rp ); + + ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + + if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? + ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr + , host, INET6_ADDRSTRLEN ) ) + return msn_ftp_abort( file, "inet_ntop failed on listening socket" ); + + ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + + if( saddr->ss_family == AF_INET ) + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); + else + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); + + return TRUE; +} + +void msn_ftp_invitation_cmd( struct im_connection *ic, char *who, int cookie, char *icmd, + char *trailer ) +{ + struct msn_message *m = g_new0( struct msn_message, 1 ); + + m->text = g_strdup_printf( "%s" + "Invitation-Command: %s\r\n" + "Invitation-Cookie: %u\r\n" + "%s", + MSN_INVITE_HEADERS, + icmd, + cookie, + trailer); + + m->who = g_strdup( who ); + + msn_sb_write_msg( ic, m ); +} + +void msn_ftp_cancel_invite( struct im_connection *ic, char *who, int cookie, char *code ) +{ + char buf[64]; + + g_snprintf( buf, sizeof( buf ), "Cancel-Code: %s\r\n", code ); + msn_ftp_invitation_cmd( ic, who, cookie, "CANCEL", buf ); +} + +void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *file, char *who ) +{ + unsigned int cookie = time( NULL ); /* TODO: randomize */ + char buf[2048]; + + msn_filetransfer_t *msn_file = g_new0( msn_filetransfer_t, 1 ); + file->data = msn_file; + file->free = msn_ftp_free; + file->canceled = msn_ftp_canceled; + file->write = msn_ftps_write; + msn_file->md = ic->proto_data; + msn_file->invite_cookie = cookie; + msn_file->handle = g_strdup( who ); + msn_file->dcc = file; + msn_file->md->filetransfers = g_slist_prepend( msn_file->md->filetransfers, msn_file->dcc ); + msn_file->fd = -1; + msn_file->sbufpos = 3; + + g_snprintf( buf, sizeof( buf ), + "Application-Name: File Transfer\r\n" + "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n" + "Application-File: %s\r\n" + "Application-FileSize: %zd\r\n", + file->file_name, + file->file_size); + + msn_ftp_invitation_cmd( msn_file->md->ic, msn_file->handle, cookie, "INVITE", buf ); + + imcb_file_recv_start( file ); +} + +void msn_invitation_invite( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ + char *itype = msn_findheader( body, "Application-GUID:", blen ); + char *name, *size, *invitecookie, *reject = NULL; + user_t *u; + size_t isize; + file_transfer_t *file; + + if( !itype || strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) != 0 ) { + /* Don't know what that is - don't care */ + char *iname = msn_findheader( body, "Application-Name:", blen ); + imcb_log( sb->ic, "Received unknown MSN invitation %s (%s) from %s", + itype ? : "with no GUID", iname ? iname : "no application name", handle ); + g_free( iname ); + reject = "REJECT_NOT_INSTALLED"; + } else if ( + !( name = msn_findheader( body, "Application-File:", blen )) || + !( size = msn_findheader( body, "Application-FileSize:", blen )) || + !( invitecookie = msn_findheader( body, "Invitation-Cookie:", blen)) || + !( isize = atoll( size ) ) ) { + imcb_log( sb->ic, "Received corrupted transfer request from %s" + "(name=%s, size=%s, invitecookie=%s)", + handle, name, size, invitecookie ); + reject = "REJECT"; + } else if ( !( u = user_findhandle( sb->ic, handle ) ) ) { + imcb_log( sb->ic, "Error in parsing transfer request, User '%s'" + "is not in contact list", handle ); + reject = "REJECT"; + } else if ( !( file = imcb_file_send_start( sb->ic, handle, name, isize ) ) ) { + imcb_log( sb->ic, "Error initiating transfer for request from %s for %s", + handle, name ); + reject = "REJECT"; + } else { + msn_filetransfer_t *msn_file = g_new0( msn_filetransfer_t, 1 ); + file->data = msn_file; + file->accept = msn_ftpr_accept; + file->free = msn_ftp_free; + file->finished = msn_ftp_finished; + file->canceled = msn_ftp_canceled; + file->write_request = msn_ftpr_write_request; + msn_file->md = sb->ic->proto_data; + msn_file->invite_cookie = cookie; + msn_file->handle = g_strdup( handle ); + msn_file->dcc = file; + msn_file->md->filetransfers = g_slist_prepend( msn_file->md->filetransfers, msn_file->dcc ); + msn_file->fd = -1; + } + + if( reject ) + msn_ftp_cancel_invite( sb->ic, sb->who, cookie, reject ); + + g_free( name ); + g_free( size ); + g_free( invitecookie ); + g_free( itype ); +} + +msn_filetransfer_t* msn_find_filetransfer( struct msn_data *md, unsigned int cookie, char *handle ) +{ + GSList *l; + + for( l = md->filetransfers; l; l = l->next ) { + msn_filetransfer_t *file = ( (file_transfer_t*) l->data )->data; + if( file->invite_cookie == cookie && strcmp( handle, file->handle ) == 0 ) { + return file; + } + } + return NULL; +} + +gboolean msn_ftps_connected( gpointer data, gint fd, b_input_condition cond ) +{ + file_transfer_t *file = data; + msn_filetransfer_t *msn_file = file->data; + struct sockaddr_storage clt_addr; + socklen_t ssize = sizeof( clt_addr ); + + debug( "Connected to MSNFTP client" ); + + ASSERTSOCKOP( msn_file->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + + closesocket( fd ); + fd = msn_file->fd; + sock_make_nonblocking( fd ); + + msn_file->r_event_id = b_input_add( fd, GAIM_INPUT_READ, msn_ftp_read, file ); + + return FALSE; +} + +void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ + file_transfer_t *file = msn_file->dcc; + char buf[1024]; + unsigned int acookie = time ( NULL ); + char host[INET6_ADDRSTRLEN]; + char port[6]; + + msn_file->auth_cookie = acookie; + + if( !msn_ftp_listen( msn_file, host, port ) ) + return; + + msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftps_connected, file ); + + g_snprintf( buf, sizeof( buf ), + "IP-Address: %s\r\n" + "Port: %s\r\n" + "AuthCookie: %d\r\n" + "Launch-Application: FALSE\r\n" + "Request-Data: IP-Address:\r\n\r\n", + host, + port, + msn_file->auth_cookie ); + + msn_ftp_invitation_cmd( msn_file->md->ic, handle, msn_file->invite_cookie, "ACCEPT", buf ); +} + +void msn_invitationr_accept( msn_filetransfer_t *msn_file, struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) { + file_transfer_t *file = msn_file->dcc; + char *authcookie, *ip, *port; + + if( !( authcookie = msn_findheader( body, "AuthCookie:", blen ) ) || + !( ip = msn_findheader( body, "IP-Address:", blen ) ) || + !( port = msn_findheader( body, "Port:", blen ) ) ) { + msn_ftp_abort( file, "Received invalid accept reply" ); + } else if( + ( msn_file->fd = proxy_connect( ip, atoi( port ), msn_ftp_connected, file ) ) + < 0 ) { + msn_ftp_abort( file, "Error connecting to MSN client" ); + } else + msn_file->auth_cookie = strtoul( authcookie, NULL, 10 ); + + g_free( authcookie ); + g_free( ip ); + g_free( port ); +} + +void msn_invitation_accept( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ + msn_filetransfer_t *msn_file = msn_find_filetransfer( sb->ic->proto_data, cookie, handle ); + file_transfer_t *file = msn_file ? msn_file->dcc : NULL; + + if( !msn_file ) + imcb_log( sb->ic, "Received invitation ACCEPT message for unknown invitation (already aborted?)" ); + else if( file->sending ) + msn_invitations_accept( msn_file, sb, handle, cookie, body, blen ); + else + msn_invitationr_accept( msn_file, sb, handle, cookie, body, blen ); +} + +void msn_invitation_cancel( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ + msn_filetransfer_t *msn_file = msn_find_filetransfer( sb->ic->proto_data, cookie, handle ); + + if( !msn_file ) + imcb_log( sb->ic, "Received invitation CANCEL message for unknown invitation (already aborted?)" ); + else + msn_ftp_abort( msn_file->dcc, msn_findheader( body, "Cancel-Code:", blen ) ); +} + +int msn_ftp_write( file_transfer_t *file, char *format, ... ) +{ + msn_filetransfer_t *msn_file = file->data; + va_list params; + int st; + char *s; + + va_start( params, format ); + s = g_strdup_vprintf( format, params ); + va_end( params ); + + st = write( msn_file->fd, s, strlen( s ) ); + if( st != strlen( s ) ) + return msn_ftp_abort( file, "Error sending data over MSNFTP connection: %s", + strerror( errno ) ); + + g_free( s ); + return 1; +} + +gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ) +{ + file_transfer_t *file = data; + msn_filetransfer_t *msn_file = file->data; + + debug( "Connected to MSNFTP server, starting authentication" ); + if( !msn_ftp_write( file, "VER MSNFTP\r\n" ) ) + return FALSE; + + sock_make_nonblocking( msn_file->fd ); + msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftp_read, file ); + + return FALSE; +} + +gboolean msn_ftp_handle_command( file_transfer_t *file, char* line ) +{ + msn_filetransfer_t *msn_file = file->data; + char **cmd = msn_linesplit( line ); + int count = 0; + if( cmd[0] ) while( cmd[++count] ); + + if( count < 1 ) + return msn_ftp_abort( file, "Missing command in MSNFTP communication" ); + + if( strcmp( cmd[0], "VER" ) == 0 ) { + if( strcmp( cmd[1], "MSNFTP" ) != 0 ) + return msn_ftp_abort( file, "Unsupported filetransfer protocol: %s", cmd[1] ); + if( file->sending ) + msn_ftp_write( file, "VER MSNFTP\r\n" ); + else + msn_ftp_write( file, "USR %s %u\r\n", msn_file->md->ic->acc->user, msn_file->auth_cookie ); + } else if( strcmp( cmd[0], "FIL" ) == 0 ) { + if( strtoul( cmd[1], NULL, 10 ) != file->file_size ) + return msn_ftp_abort( file, "FIL reply contains a different file size than the size in the invitation" ); + msn_ftp_write( file, "TFR\r\n" ); + msn_file->status |= MSN_TRANSFER_RECEIVING; + } else if( strcmp( cmd[0], "USR" ) == 0 ) { + if( ( strcmp( cmd[1], msn_file->handle ) != 0 ) || + ( strtoul( cmd[2], NULL, 10 ) != msn_file->auth_cookie ) ) + msn_ftp_abort( file, "Authentication failed. " + "Expected handle: %s (got %s), cookie: %u (got %s)", + msn_file->handle, cmd[1], + msn_file->auth_cookie, cmd[2] ); + msn_ftp_write( file, "FIL %zu\r\n", file->file_size); + } else if( strcmp( cmd[0], "TFR" ) == 0 ) { + file->write_request( file ); + } else if( strcmp( cmd[0], "BYE" ) == 0 ) { + unsigned int retcode = count > 1 ? atoi(cmd[1]) : 1; + + if( ( retcode==16777989 ) || ( retcode==16777987 ) ) + imcb_file_finished( file ); + else if( retcode==2147942405 ) + imcb_file_canceled( file, "Failure: receiver is out of disk space" ); + else if( retcode==2164261682 ) + imcb_file_canceled( file, "Failure: receiver cancelled the transfer" ); + else if( retcode==2164261683 ) + imcb_file_canceled( file, "Failure: sender has cancelled the transfer" ); + else if( retcode==2164261694 ) + imcb_file_canceled( file, "Failure: connection is blocked" ); + else { + char buf[128]; + + sprintf( buf, "Failure: unknown BYE code: %d", retcode); + imcb_file_canceled( file, buf ); + } + } else if( strcmp( cmd[0], "CCL" ) == 0 ) { + imcb_file_canceled( file, "Failure: receiver cancelled the transfer" ); + } else { + msn_ftp_abort( file, "Received invalid command %s from msn client", cmd[0] ); + } + return TRUE; +} + +gboolean msn_ftp_send( gpointer data, gint fd, b_input_condition cond ) +{ + file_transfer_t *file = data; + msn_filetransfer_t *msn_file = file->data; + + msn_file->w_event_id = 0; + + file->write_request( file ); + + return FALSE; +} + +/* + * This should only be called if we can write, so just do it. + * Add a write watch so we can write more during the next cycle (if possible). + * This got a bit complicated because (at least) amsn expects packets of size 2045. + */ +gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) +{ + msn_filetransfer_t *msn_file = file->data; + int ret, overflow; + + /* what we can't send now */ + overflow = msn_file->sbufpos + len - MSNFTP_PSIZE; + + /* append what we can do the send buffer */ + memcpy( msn_file->sbuf + msn_file->sbufpos, buffer, MIN( len, MSNFTP_PSIZE - msn_file->sbufpos ) ); + msn_file->sbufpos += MIN( len, MSNFTP_PSIZE - msn_file->sbufpos ); + + /* if we don't have enough for a full packet and there's more wait for it */ + if( ( msn_file->sbufpos < MSNFTP_PSIZE ) && + ( msn_file->data_sent + msn_file->sbufpos - 3 < file->file_size ) ) { + if( !msn_file->w_event_id ) + msn_file->w_event_id = b_input_add( msn_file->fd, GAIM_INPUT_WRITE, msn_ftp_send, file ); + return TRUE; + } + + /* Accumulated enough data, lets send something out */ + + msn_file->sbuf[0] = 0; + msn_file->sbuf[1] = ( msn_file->sbufpos - 3 ) & 0xff; + msn_file->sbuf[2] = ( ( msn_file->sbufpos - 3 ) >> 8 ) & 0xff; + + ASSERTSOCKOP( ret = send( msn_file->fd, msn_file->sbuf, msn_file->sbufpos, 0 ), "Sending" ); + + msn_file->data_sent += ret - 3; + + /* TODO: this should really not be fatal */ + if( ret < msn_file->sbufpos ) + return msn_ftp_abort( file, "send() sent %d instead of %d (send buffer full!)", ret, msn_file->sbufpos ); + + msn_file->sbufpos = 3; + + if( overflow > 0 ) { + while( overflow > ( MSNFTP_PSIZE - 3 ) ) { + if( !msn_ftps_write( file, buffer + len - overflow, MSNFTP_PSIZE - 3 ) ) + return FALSE; + overflow -= MSNFTP_PSIZE - 3; + } + return msn_ftps_write( file, buffer + len - overflow, overflow ); + } + + if( msn_file->data_sent == file->file_size ) { + if( msn_file->w_event_id ) { + b_event_remove( msn_file->w_event_id ); + msn_file->w_event_id = 0; + } + } else { + /* we might already be listening if this is data from an overflow */ + if( !msn_file->w_event_id ) + msn_file->w_event_id = b_input_add( msn_file->fd, GAIM_INPUT_WRITE, msn_ftp_send, file ); + } + + return TRUE; +} + +/* Binary part of the file transfer protocol */ +gboolean msn_ftpr_read( file_transfer_t *file ) +{ + msn_filetransfer_t *msn_file = file->data; + int st; + unsigned char buf[3]; + + if( msn_file->data_remaining ) { + msn_file->r_event_id = 0; + + ASSERTSOCKOP( st = read( msn_file->fd, file->buffer, MIN( sizeof( file->buffer ), msn_file->data_remaining ) ), "Receiving" ); + + if( st == 0 ) + return msn_ftp_abort( file, "Remote end closed connection"); + + msn_file->data_sent += st; + + msn_file->data_remaining -= st; + + file->write( file, file->buffer, st ); + + if( msn_file->data_sent >= file->file_size ) + imcb_file_finished( file ); + + return FALSE; + } else { + ASSERTSOCKOP( st = read( msn_file->fd, buf, 1 ), "Receiving" ); + if( st == 0 ) { + return msn_ftp_abort( file, "read returned EOF while reading data header from msn client" ); + } else if( buf[0] == '\r' || buf[0] == '\n' ) { + debug( "Discarding extraneous newline" ); + } else if( buf[0] != 0 ) { + msn_ftp_abort( file, "Remote end canceled the transfer"); + /* don't really care about these last 2 (should be 0,0) */ + read( msn_file->fd, buf, 2 ); + return FALSE; + } else { + unsigned int size; + ASSERTSOCKOP( st = read( msn_file->fd, buf, 2 ), "Receiving" ); + if( st < 2 ) + return msn_ftp_abort( file, "read returned EOF while reading data header from msn client" ); + + size = buf[0] + ((unsigned int) buf[1] << 8); + msn_file->data_remaining = size; + } + } + return TRUE; +} + +/* Text mode part of the file transfer protocol */ +gboolean msn_ftp_txtproto( file_transfer_t *file ) +{ + msn_filetransfer_t *msn_file = file->data; + int i = msn_file->tbufpos, st; + char *tbuf = msn_file->tbuf; + + ASSERTSOCKOP( st = read( msn_file->fd, + tbuf + msn_file->tbufpos, + sizeof( msn_file->tbuf ) - msn_file->tbufpos ), + "Receiving" ); + + if( st == 0 ) + return msn_ftp_abort( file, "read returned EOF while reading text from msn client" ); + + msn_file->tbufpos += st; + + do { + for( ;i < msn_file->tbufpos; i++ ) { + if( tbuf[i] == '\n' || tbuf[i] == '\r' ) { + tbuf[i] = '\0'; + if( i > 0 ) + msn_ftp_handle_command( file, tbuf ); + else + while( tbuf[i] == '\n' || tbuf[i] == '\r' ) i++; + memmove( tbuf, tbuf + i + 1, msn_file->tbufpos - i - 1 ); + msn_file->tbufpos -= i + 1; + i = 0; + break; + } + } + } while ( i < msn_file->tbufpos ); + + if( msn_file->tbufpos == sizeof( msn_file->tbuf ) ) + return msn_ftp_abort( file, + "Line exceeded %d bytes in text protocol", + sizeof( msn_file->tbuf ) ); + return TRUE; +} + +gboolean msn_ftp_read( gpointer data, gint fd, b_input_condition cond ) +{ + file_transfer_t *file = data; + msn_filetransfer_t *msn_file = file->data; + + if( msn_file->status & MSN_TRANSFER_RECEIVING ) + return msn_ftpr_read( file ); + else + return msn_ftp_txtproto( file ); +} + +void msn_ftp_free( file_transfer_t *file ) +{ + msn_filetransfer_t *msn_file = file->data; + + if( msn_file->r_event_id ) + b_event_remove( msn_file->r_event_id ); + + if( msn_file->w_event_id ) + b_event_remove( msn_file->w_event_id ); + + if( msn_file->fd != -1 ) + closesocket( msn_file->fd ); + + msn_file->md->filetransfers = g_slist_remove( msn_file->md->filetransfers, msn_file->dcc ); + + g_free( msn_file->handle ); + + g_free( msn_file ); +} + +void msn_ftpr_accept( file_transfer_t *file ) +{ + msn_filetransfer_t *msn_file = file->data; + + msn_ftp_invitation_cmd( msn_file->md->ic, msn_file->handle, msn_file->invite_cookie, "ACCEPT", + "Launch-Application: FALSE\r\n" + "Request-Data: IP-Address:\r\n"); +} + +void msn_ftp_finished( file_transfer_t *file ) +{ + msn_ftp_write( file, "BYE 16777989\r\n" ); +} + +void msn_ftp_canceled( file_transfer_t *file, char *reason ) +{ + msn_filetransfer_t *msn_file = file->data; + + msn_ftp_cancel_invite( msn_file->md->ic, msn_file->handle, + msn_file->invite_cookie, + file->status & FT_STATUS_TRANSFERRING ? + "FTTIMEOUT" : + "FAIL" ); + + imcb_log( msn_file->md->ic, "File transfer aborted: %s", reason ); +} + +gboolean msn_ftpr_write_request( file_transfer_t *file ) +{ + msn_filetransfer_t *msn_file = file->data; + if( msn_file->r_event_id != 0 ) { + msn_ftp_abort( file, + "BUG in MSN file transfer:" + "write_request called when" + "already watching for input" ); + return FALSE; + } + + msn_file->r_event_id = + b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftp_read, file ); + + return TRUE; +} diff --git a/protocols/msn/invitation.h b/protocols/msn/invitation.h new file mode 100644 index 00000000..289efd7b --- /dev/null +++ b/protocols/msn/invitation.h @@ -0,0 +1,82 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway * +* * +* Copyright 2006 Marijn Kruisselbrink and others * +\********************************************************************/ + +/* MSN module - File transfer support */ + +/* + 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 + */ + +#ifndef _MSN_INVITATION_H +#define _MSN_INVITATION_H + +#include "msn.h" + +#define MSN_INVITE_HEADERS "MIME-Version: 1.0\r\n" \ + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" \ + "\r\n" + +#define MSNFTP_PSIZE 2048 + +typedef enum { + MSN_TRANSFER_RECEIVING = 1, + MSN_TRANSFER_SENDING = 2 +} msn_filetransfer_status_t; + +typedef struct msn_filetransfer +{ +/* Generic invitation data */ + /* msn_data instance this invitation was received with. */ + struct msn_data *md; + /* Cookie specifying this invitation. */ + unsigned int invite_cookie; + /* Handle of user that started this invitation. */ + char *handle; + +/* File transfer specific data */ + /* Current status of the file transfer. */ + msn_filetransfer_status_t status; + /* Pointer to the dcc structure for this transfer. */ + file_transfer_t *dcc; + /* Socket the transfer is taking place over. */ + int fd; + /* Cookie received in the original invitation, this must be sent as soon as + a connection has been established. */ + unsigned int auth_cookie; + /* Data remaining to be received in the current packet. */ + unsigned int data_remaining; + /* Buffer containing received, but unprocessed text. */ + char tbuf[256]; + unsigned int tbufpos; + + unsigned int data_sent; + + gint r_event_id; + gint w_event_id; + + unsigned char sbuf[2048]; + int sbufpos; + +} msn_filetransfer_t; + +void msn_invitation_invite( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); +void msn_invitation_accept( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); +void msn_invitation_cancel( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); + +#endif diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 046b2772..590a1382 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -76,6 +76,10 @@ static void msn_logout( struct im_connection *ic ) if( md ) { + while( md->filetransfers ) { + imcb_file_canceled( md->filetransfers->data, "Closing msn connection" ); + } + if( md->fd >= 0 ) closesocket( md->fd ); @@ -337,6 +341,7 @@ void msn_initmodule() ret->rem_deny = msn_rem_deny; ret->send_typing = msn_send_typing; ret->handle_cmp = g_strcasecmp; + ret->transfer_request = msn_ftp_transfer_request; register_protocol(ret); } diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 7c849acf..e2badbf9 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -68,6 +68,7 @@ struct msn_data GSList *switchboards; int sb_failures; time_t first_sb_failure; + GSList *filetransfers; const struct msn_away_state *away_state; int buddycount; @@ -180,4 +181,7 @@ void msn_sb_destroy( struct msn_switchboard *sb ); gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond ); int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ); +/* invitation.c */ +void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ); + #endif //_MSN_H diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index e9526234..b60a9a8b 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -28,6 +28,7 @@ #include "msn.h" #include "passport.h" #include "md5.h" +#include "invitation.h" static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond ); static int msn_sb_command( gpointer data, char **cmd, int num_parts ); @@ -167,7 +168,16 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) int i, j; /* Build the message. Convert LF to CR-LF for normal messages. */ - if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 ) + if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) == 0 ) + { + i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user ); + buf = g_new0( char, i ); + i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user ); + } else if( strncmp( text, MSN_INVITE_HEADERS, sizeof( MSN_INVITE_HEADERS ) - 1 ) == 0 ) + { + buf = g_strdup( text ); + i = strlen( buf ); + } else { buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 + 1 ); i = strlen( MSN_MESSAGE_HEADERS ); @@ -181,12 +191,6 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) buf[i++] = text[j]; } } - else - { - i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user ); - buf = g_new0( char, i ); - i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user ); - } /* Build the final packet (MSG command + the message). */ packet = g_strdup_printf( "MSG %d N %d\r\n%s", ++sb->trId, i, buf ); @@ -686,62 +690,41 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int } else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 ) { - char *itype = msn_findheader( body, "Application-GUID:", blen ); - char buf[1024]; + char *command = msn_findheader( body, "Invitation-Command:", blen ); + char *cookie = msn_findheader( body, "Invitation-Cookie:", blen ); + unsigned int icookie; g_free( ct ); - *buf = 0; - - if( !itype ) - return( 1 ); - - /* File transfer. */ - if( strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) == 0 ) - { - char *name = msn_findheader( body, "Application-File:", blen ); - char *size = msn_findheader( body, "Application-FileSize:", blen ); - - if( name && size ) - { - g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Filetransfer: `%s', %s bytes >>\n" - "Filetransfers are not supported by BitlBee for now...", name, size ); - } - else - { - strcpy( buf, "<< \x02""BitlBee\x02"" - Corrupted MSN filetransfer invitation message >>" ); - } - - if( name ) g_free( name ); - if( size ) g_free( size ); - } - else - { - char *iname = msn_findheader( body, "Application-Name:", blen ); - - g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Unknown MSN invitation - %s (%s) >>", - itype, iname ? iname : "no name" ); - - if( iname ) g_free( iname ); + /* Every invite should have both a Command and Cookie header */ + if( !command || !cookie ) { + g_free( command ); + g_free( cookie ); + imcb_log( ic, "Warning: No command or cookie from %s", sb->who ); + return 1; } - g_free( itype ); + icookie = strtoul( cookie, NULL, 10 ); + g_free( cookie ); - if( !*buf ) - return( 1 ); - - if( sb->who ) - { - imcb_buddy_msg( ic, cmd[1], buf, 0, 0 ); - } - else if( sb->chat ) - { - imcb_chat_msg( sb->chat, cmd[1], buf, 0, 0 ); - } - else - { - /* PANIC! */ + if( g_strncasecmp( command, "INVITE", 6 ) == 0 ) { + msn_invitation_invite( sb, cmd[1], icookie, body, blen ); + } else if( g_strncasecmp( command, "ACCEPT", 6 ) == 0 ) { + msn_invitation_accept( sb, cmd[1], icookie, body, blen ); + } else if( g_strncasecmp( command, "CANCEL", 6 ) == 0 ) { + msn_invitation_cancel( sb, cmd[1], icookie, body, blen ); + } else { + imcb_log( ic, "Warning: Received invalid invitation with " + "command %s from %s", command, sb->who ); } + + g_free( command ); + } + else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0 ) + { + imcb_error( sb->ic, "Cannot receive file from %s: BitlBee does not " + "support msnmsgrp2p yet.", sb->who ); + g_free( ct ); } else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 ) { -- cgit v1.2.3 From 92f4ec5d9d480247281c50c163c19b1ea438c1b3 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 11 Aug 2008 01:51:48 +0200 Subject: fixes bug on 32bit archs in DCC code. Shouldn't atoi be the same on 64 and 32bits? Strange. --- dcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dcc.c b/dcc.c index 909fedad..2bb85ad4 100644 --- a/dcc.c +++ b/dcc.c @@ -702,7 +702,7 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) /* number means ipv4, something else means ipv6 */ if ( pmatch[6].rm_so > 0 ) { - struct in_addr ipaddr = { .s_addr = htonl( atoi( input + pmatch[5].rm_so ) ) }; + struct in_addr ipaddr = { .s_addr = htonl( strtoul( input + pmatch[5].rm_so, NULL, 10 ) ) }; host = inet_ntoa( ipaddr ); } else { -- cgit v1.2.3 From 66be7849ef6b74c39bc8f1dc1d96bc4788eb50a0 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 11 Aug 2008 16:31:03 +0200 Subject: copied ulibc/BSD ifdefs over to invitation.c. I'll have to move these to a generic header file eventually. --- protocols/msn/invitation.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index 4e24d54c..021764d2 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -28,6 +28,20 @@ #include "invitation.h" #include "msn.h" +/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + #ifdef debug #undef debug #endif -- cgit v1.2.3 From a02f34fb047af728f991ced3688c5e804c130878 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 12 Aug 2008 01:07:12 +0200 Subject: Added conf entries and lib/ftutil.[ch]. ft_listen = :;: to specify listening addresses for the bitlbee<->client connection and the bitlbee<->IM peer connection, respectively. ft_max_size should be obvious. ft_max_kbps should limit the kilobits per second per transfer (not implemented yet). --- Makefile | 2 +- conf.c | 28 ++++++++++++- conf.h | 4 +- dcc.c | 79 +++++++------------------------------ lib/Makefile | 2 +- protocols/jabber/s5bytestream.c | 87 ++++++++--------------------------------- protocols/msn/invitation.c | 70 +++------------------------------ 7 files changed, 69 insertions(+), 203 deletions(-) diff --git a/Makefile b/Makefile index 0c07b91b..2aae8c83 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # Program variables objects = account.o bitlbee.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o dcc.o -headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h protocols/ft.h +headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h protocols/ft.h lib/ftutil.h subdirs = lib protocols ifeq ($(TARGET),i586-mingw32msvc) diff --git a/conf.c b/conf.c index 02b1eb98..19fe7020 100644 --- a/conf.c +++ b/conf.c @@ -62,7 +62,9 @@ conf_t *conf_load( int argc, char *argv[] ) conf->ping_interval = 180; conf->ping_timeout = 300; conf->user = NULL; - conf->max_filetransfer_size = G_MAXUINT; + conf->ft_max_size = SIZE_MAX; + conf->ft_max_kbps = G_MAXUINT; + conf->ft_listen = NULL; proxytype = 0; i = conf_loadini( conf, global.conf_file ); @@ -306,6 +308,30 @@ static int conf_loadini( conf_t *conf, char *file ) g_free( conf->user ); conf->user = g_strdup( ini->value ); } + else if( g_strcasecmp( ini->key, "ft_max_size" ) == 0 ) + { + size_t ft_max_size; + if( sscanf( ini->value, "%zu", &ft_max_size ) != 1 ) + { + fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); + return 0; + } + conf->ft_max_size = ft_max_size; + } + else if( g_strcasecmp( ini->key, "ft_max_kbps" ) == 0 ) + { + if( sscanf( ini->value, "%d", &i ) != 1 ) + { + fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); + return 0; + } + conf->ft_max_kbps = i; + } + else if( g_strcasecmp( ini->key, "ft_listen" ) == 0 ) + { + g_free( conf->ft_listen ); + conf->ft_listen = g_strdup( ini->value ); + } else { fprintf( stderr, "Error: Unknown setting `%s` in configuration file.\n", ini->key ); diff --git a/conf.h b/conf.h index 7fcd0ede..b39845ad 100644 --- a/conf.h +++ b/conf.h @@ -49,7 +49,9 @@ typedef struct conf int ping_interval; int ping_timeout; char *user; - size_t max_filetransfer_size; + size_t ft_max_size; + int ft_max_kbps; + char *ft_listen; } conf_t; G_GNUC_MALLOC conf_t *conf_load( int argc, char *argv[] ); diff --git a/dcc.c b/dcc.c index 2bb85ad4..c54e9169 100644 --- a/dcc.c +++ b/dcc.c @@ -28,20 +28,7 @@ #include #include #include - -/* Some ifdefs for ulibc (Thanks to Whoopie) */ -#ifndef HOST_NAME_MAX -#include -#ifdef MAXHOSTNAMELEN -#define HOST_NAME_MAX MAXHOSTNAMELEN -#else -#define HOST_NAME_MAX 255 -#endif -#endif - -#ifndef AI_NUMERICSERV -#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ -#endif +#include "lib/ftutil.h" /* * Since that might be confusing a note on naming: @@ -79,12 +66,12 @@ int max_packet_size = 0; static void dcc_finish( file_transfer_t *file ); static void dcc_close( file_transfer_t *file ); gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); -gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ); int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); gboolean dccs_recv_start( file_transfer_t *ft ); gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); gboolean dccs_recv_write_request( file_transfer_t *ft ); gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ); +gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ); /* As defined in ft.h */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) @@ -142,9 +129,12 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha { file_transfer_t *file; dcc_file_transfer_t *df; - struct sockaddr_storage *saddr; + struct sockaddr_storage saddr; + char *errmsg; + char host[INET6_ADDRSTRLEN]; + char port[6]; - if( file_size > global.conf->max_filetransfer_size ) + if( file_size > global.conf->ft_max_size ) return NULL; df = dcc_alloc_transfer( file_name, file_size, ic ); @@ -152,11 +142,16 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha file->write = dccs_send_write; /* listen and request */ - if( !dcc_listen( df, &saddr ) || - !dccs_send_request( df, user_nick, saddr ) ) + + if( ( df->fd = ft_listen( &saddr, host, port, TRUE, &errmsg ) ) == -1 ) { + dcc_abort( df, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg ); return NULL; + } - g_free( saddr ); + file->status = FT_STATUS_LISTENING; + + if( !dccs_send_request( df, user_nick, &saddr ) ) + return NULL; /* watch */ df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); @@ -261,50 +256,6 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc return TRUE; } -/* - * Creates a listening socket and returns it in saddr_ptr. - */ -gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ) -{ - file_transfer_t *file = df->ft; - struct sockaddr_storage *saddr; - int fd,gret; - char hostname[ HOST_NAME_MAX + 1 ]; - struct addrinfo hints, *rp; - socklen_t ssize = sizeof( struct sockaddr_storage ); - - /* won't be long till someone asks for this to be configurable :) */ - - ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); - - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - - if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) ) - return dcc_abort( df, "getaddrinfo(): %s", gai_strerror( gret ) ); - - saddr = g_new( struct sockaddr_storage, 1 ); - - *saddr_ptr = saddr; - - memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); - - ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); - - ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); - - freeaddrinfo( rp ); - - ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); - - ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); - - file->status = FT_STATUS_LISTENING; - - return TRUE; -} - /* * Checks poll(), same for receiving and sending */ diff --git a/lib/Makefile b/lib/Makefile index 03fef1ab..3d128b5a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o +objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o ftutil.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 7e4ca2ab..3c5ce503 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -23,22 +23,9 @@ #include "jabber.h" #include "sha1.h" +#include "lib/ftutil.h" #include -/* Some ifdefs for ulibc (Thanks to Whoopie) */ -#ifndef HOST_NAME_MAX -#include -#ifdef MAXHOSTNAMELEN -#define HOST_NAME_MAX MAXHOSTNAMELEN -#else -#define HOST_NAME_MAX 255 -#endif -#endif - -#ifndef AI_NUMERICSERV -#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ -#endif - struct bs_transfer { struct jabber_transfer *tf; @@ -114,7 +101,6 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error ); gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ); gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ); -gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ); static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); void jabber_bs_send_activate( struct bs_transfer *bt ); @@ -901,7 +887,7 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) struct jabber_transfer *tf = bt->tf; struct jabber_data *jd = tf->ic->proto_data; char *proxysetting = g_strdup ( set_getstr( &tf->ic->acc->set, "proxy" ) ); - char *proxy,*next; + char *proxy, *next, *errmsg = NULL; char port[6]; char host[INET6_ADDRSTRLEN]; jabber_streamhost_t *sh, *sh2; @@ -912,15 +898,21 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) if( ( next = strchr( proxy, ';' ) ) ) *next++ = '\0'; - if( ( strcmp( proxy, "" ) == 0 ) && jabber_bs_send_listen( bt, &tf->saddr, host, port ) ) { - sh = g_new0( jabber_streamhost_t, 1 ); - sh->jid = g_strdup( tf->ini_jid ); - sh->host = g_strdup( host ); - strcpy( sh->port, port ); - bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + if( strcmp( proxy, "" ) == 0 ) { + if( ( tf->fd = ft_listen( &tf->saddr, host, port, FALSE, &errmsg ) ) != -1 ) { + sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( tf->ini_jid ); + sh->host = g_strdup( host ); + strcpy( sh->port, port ); + bt->streamhosts = g_slist_append( bt->streamhosts, sh ); - bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); - bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + } else { + imcb_log( tf->ic, "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s", + tf->ft->file_name, + errmsg ); + } } else if( strcmp( proxy, "" ) == 0 ) { while ( streamhosts ) { sh = g_new0( jabber_streamhost_t, 1 ); @@ -1021,53 +1013,6 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) return FALSE; } -/* - * Creates a listening socket and returns it in saddr_ptr. - */ -gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ) -{ - struct jabber_transfer *tf = bt->tf; - int fd,gret; - char hostname[ HOST_NAME_MAX + 1 ]; - struct addrinfo hints, *rp; - socklen_t ssize = sizeof( struct sockaddr_storage ); - - /* won't be long till someone asks for this to be configurable :) */ - - ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); - - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - - if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo() failed: %s", gai_strerror( gret ) ); - - memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); - - ASSERTSOCKOP( fd = tf->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); - - ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); - - freeaddrinfo( rp ); - - ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); - - if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? - ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr - , host, INET6_ADDRSTRLEN ) ) - return jabber_bs_abort( bt, "inet_ntop failed on listening socket" ); - - ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); - - if( saddr->ss_family == AF_INET ) - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); - else - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); - - return TRUE; -} - /* * SOCKS5BYTESTREAM protocol for the sender */ diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index 021764d2..f44155fa 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -27,20 +27,7 @@ #include "bitlbee.h" #include "invitation.h" #include "msn.h" - -/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ -#ifndef HOST_NAME_MAX -#include -#ifdef MAXHOSTNAMELEN -#define HOST_NAME_MAX MAXHOSTNAMELEN -#else -#define HOST_NAME_MAX 255 -#endif -#endif - -#ifndef AI_NUMERICSERV -#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ -#endif +#include "lib/ftutil.h" #ifdef debug #undef debug @@ -78,54 +65,6 @@ gboolean msn_ftp_abort( file_transfer_t *file, char *format, ... ) if( (op) == -1 ) \ return msn_ftp_abort( file , msg ": %s", strerror( errno ) ); -/* - * Creates a listening socket and returns its address in host, port. - */ -gboolean msn_ftp_listen( msn_filetransfer_t *msn_file, char *host, char *port ) -{ - file_transfer_t *file = msn_file->dcc; - int fd,gret; - char hostname[ HOST_NAME_MAX + 1 ]; - struct addrinfo hints, *rp; - struct sockaddr_storage saddrst, *saddr = &saddrst; - socklen_t ssize = sizeof( struct sockaddr_storage ); - - /* won't be long till someone asks for this to be configurable :) */ - - ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); - - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - - if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) ) != 0 ) - return msn_ftp_abort( file, "getaddrinfo() failed: %s", gai_strerror( gret ) ); - - memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); - - ASSERTSOCKOP( fd = msn_file->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); - - ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); - - freeaddrinfo( rp ); - - ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); - - if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? - ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr - , host, INET6_ADDRSTRLEN ) ) - return msn_ftp_abort( file, "inet_ntop failed on listening socket" ); - - ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); - - if( saddr->ss_family == AF_INET ) - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); - else - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); - - return TRUE; -} - void msn_ftp_invitation_cmd( struct im_connection *ic, char *who, int cookie, char *icmd, char *trailer ) { @@ -281,11 +220,14 @@ void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboar unsigned int acookie = time ( NULL ); char host[INET6_ADDRSTRLEN]; char port[6]; + char *errmsg; msn_file->auth_cookie = acookie; - if( !msn_ftp_listen( msn_file, host, port ) ) - return; + if( ( msn_file->fd = ft_listen( NULL, host, port, FALSE, &errmsg ) ) == -1 ) { + msn_ftp_abort( file, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg ); + return; + } msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftps_connected, file ); -- cgit v1.2.3 From 5d550c51a5e9c9f48f26283f0ea3fee2d4945feb Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 12 Aug 2008 11:24:09 +0200 Subject: Added lib/ftutil.[ch]. yes, that was supposed to happen in the last commit ;) --- lib/ftutil.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ftutil.h | 24 +++++++++++ 2 files changed, 159 insertions(+) create mode 100644 lib/ftutil.c create mode 100644 lib/ftutil.h diff --git a/lib/ftutil.c b/lib/ftutil.c new file mode 100644 index 00000000..cfe06006 --- /dev/null +++ b/lib/ftutil.c @@ -0,0 +1,135 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Utility functions for file transfer * +* * +* Copyright 2008 Uli Meis * +* * +* 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 along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include +#include +#include "lib/ftutil.h" + +/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + +#define ASSERTSOCKOP(op, msg) \ + if( (op) == -1 ) {\ + sprintf( errmsg , msg ": %s", strerror( errno ) ); \ + return -1; } + +/* + * Creates a listening socket and returns it in saddr_ptr. + */ +int ft_listen( struct sockaddr_storage *saddr_ptr, char *hostp, char *port, int for_bitlbee_client, char **errptr ) +{ + int fd,gret,saddrlen; + struct addrinfo hints, *rp; + socklen_t ssize = sizeof( struct sockaddr_storage ); + struct sockaddr_storage saddrs, *saddr = &saddrs; + static char errmsg[1024]; + char host[ HOST_NAME_MAX + 1 ]; + char *ftlisten = global.conf->ft_listen; + + *errptr = errmsg; + + sprintf( port, "0" ); + + /* Format is [:];[:] where + * A is for connections with the bitlbee client (DCC) + * and B is for connections with IM peers. + */ + if( ftlisten ) { + char *scolon = strchr( ftlisten, ';' ); + char *colon; + + if( scolon ) { + if( for_bitlbee_client ) { + *scolon = '\0'; + sprintf( host, ftlisten ); + *scolon = ';'; + } else { + sprintf( host, scolon + 1 ); + } + } else { + sprintf( host, ftlisten ); + } + + if( ( colon = strchr( host, ':' ) ) ) { + *colon = '\0'; + sprintf( port, colon + 1 ); + } + } else { + ASSERTSOCKOP( gethostname( host, HOST_NAME_MAX + 1 ), "gethostname()" ); + } + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) != 0 ) { + sprintf( errmsg, "getaddrinfo() failed: %s", gai_strerror( gret ) ); + return -1; + } + + saddrlen = rp->ai_addrlen; + + memcpy( saddr, rp->ai_addr, saddrlen ); + + freeaddrinfo( rp ); + + ASSERTSOCKOP( fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); + + ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, saddrlen ), "Binding socket" ); + + ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + + if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? + ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr + , host, INET6_ADDRSTRLEN ) ) { + sprintf( errmsg, "inet_ntop failed on listening socket" ); + return -1; + } + + ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + + if( saddr->ss_family == AF_INET ) + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); + else + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); + + if( saddr_ptr ) + memcpy( saddr_ptr, saddr, saddrlen ); + + strcpy( hostp, host ); + + return fd; +} diff --git a/lib/ftutil.h b/lib/ftutil.h new file mode 100644 index 00000000..5f2780ef --- /dev/null +++ b/lib/ftutil.h @@ -0,0 +1,24 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Utility functions for file transfer * +* * +* Copyright 2008 Uli Meis * +* * +* 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 along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int for_bitlbee_client, char **errptr ); -- cgit v1.2.3 From aac40178a6669e20855b7f5d3cc6a82cba10042e Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 12 Aug 2008 13:04:37 +0200 Subject: More hints for getaddrinfo(). Hopefully solves a problem on FreeBSD. --- dcc.c | 3 +++ lib/ftutil.c | 4 ---- lib/ftutil.h | 4 ++++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dcc.c b/dcc.c index c54e9169..b6345a92 100644 --- a/dcc.c +++ b/dcc.c @@ -668,6 +668,9 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) filesize = atoll( input + pmatch[9].rm_so ); memset( &hints, 0, sizeof ( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) ) { g_free( input ); diff --git a/lib/ftutil.c b/lib/ftutil.c index cfe06006..169a6480 100644 --- a/lib/ftutil.c +++ b/lib/ftutil.c @@ -37,10 +37,6 @@ #endif #endif -#ifndef AI_NUMERICSERV -#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ -#endif - #define ASSERTSOCKOP(op, msg) \ if( (op) == -1 ) {\ sprintf( errmsg , msg ": %s", strerror( errno ) ); \ diff --git a/lib/ftutil.h b/lib/ftutil.h index 5f2780ef..636fcbd0 100644 --- a/lib/ftutil.h +++ b/lib/ftutil.h @@ -21,4 +21,8 @@ * * \***************************************************************************/ +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int for_bitlbee_client, char **errptr ); -- cgit v1.2.3 From 796da03f9f54f8fb193529288592571b371bf0cd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 5 Oct 2009 00:28:11 +0100 Subject: Something that compiles and runs, but otherwise utterly useless. Added a protocols/purple/ module and included it in the build system. Already picks up all the supported protocols and adds them individually. --- configure | 12 +++ protocols/nogaim.c | 5 + protocols/purple/Makefile | 41 ++++++++ protocols/purple/purple.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 protocols/purple/Makefile create mode 100644 protocols/purple/purple.c diff --git a/configure b/configure index b3a98086..65eb9e89 100755 --- a/configure +++ b/configure @@ -25,6 +25,7 @@ msn=1 jabber=1 oscar=1 yahoo=1 +purple=0 debug=0 strip=1 @@ -66,6 +67,8 @@ Option Description Default --oscar=0/1 Disable/enable Oscar part (ICQ, AIM) $oscar --yahoo=0/1 Disable/enable Yahoo part $yahoo +--purple=0/1 Disable/enable libpurple support $purple + --debug=0/1 Disable/enable debugging $debug --strip=0/1 Disable/enable binary stripping $strip --gcov=0/1 Disable/enable test coverage reporting $gcov @@ -478,6 +481,15 @@ else protoobjs=$protoobjs'yahoo_mod.o ' fi +if [ "$purple" = 0 ]; then + echo '#undef WITH_PURPLE' >> config.h +else + echo '#define WITH_PURPLE' >> config.h + echo 'EFLAGS += $$(pkg-config purple --libs)' >> Makefile.settings + protocols=$protocols'purple ' + protoobjs=$protoobjs'purple_mod.o ' +fi + if [ "$protocols" = "PROTOCOLS = " ]; then echo "Warning: You haven't selected any communication protocol to compile!" echo " BitlBee will run, but you will be unable to connect to IM servers!" diff --git a/protocols/nogaim.c b/protocols/nogaim.c index fd445324..8eae178d 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -117,6 +117,7 @@ void nogaim_init() extern void oscar_initmodule(); extern void byahoo_initmodule(); extern void jabber_initmodule(); + extern void purple_initmodule(); #ifdef WITH_MSN msn_initmodule(); @@ -133,6 +134,10 @@ void nogaim_init() #ifdef WITH_JABBER jabber_initmodule(); #endif + +#ifdef WITH_PURPLE + purple_initmodule(); +#endif #ifdef WITH_PLUGINS load_plugins(); diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile new file mode 100644 index 00000000..bdefbd5f --- /dev/null +++ b/protocols/purple/Makefile @@ -0,0 +1,41 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../../Makefile.settings + +# [SH] Program variables +objects = purple.o + +CFLAGS += -Wall $$(pkg-config purple --cflags) +LFLAGS += -r + +# [SH] Phony targets +all: purple_mod.o +check: all +lcov: check +gcov: + gcov *.c + +.PHONY: all clean distclean + +clean: + rm -f *.o core + +distclean: clean + +### MAIN PROGRAM + +$(objects): ../../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +purple_mod.o: $(objects) + @echo '*' Linking purple_mod.o + $(LD) $(LFLAGS) $(objects) -o purple_mod.o diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c new file mode 100644 index 00000000..333f6674 --- /dev/null +++ b/protocols/purple/purple.c @@ -0,0 +1,245 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* libpurple module - Main file * +* * +* Copyright 2009 Wilmer van der Gaast * +* * +* 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 along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include +#include + +#include "bitlbee.h" + +GSList *purple_connections; + +#undef g_io_add_watch +#undef g_io_add_watch_full +#undef g_timeout_add +#undef g_source_remove + +/** + * The following eventloop functions are used in both pidgin and purple-text. If your + * application uses glib mainloop, you can safely use this verbatim. + */ +#define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +typedef struct _PurpleGLibIOClosure { + PurpleInputFunction function; + guint result; + gpointer data; +} PurpleGLibIOClosure; + +static void purple_glib_io_destroy(gpointer data) +{ + g_free(data); +} + +static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ + PurpleGLibIOClosure *closure = data; + PurpleInputCondition purple_cond = 0; + + if (condition & PURPLE_GLIB_READ_COND) + purple_cond |= PURPLE_INPUT_READ; + if (condition & PURPLE_GLIB_WRITE_COND) + purple_cond |= PURPLE_INPUT_WRITE; + + closure->function(closure->data, g_io_channel_unix_get_fd(source), + purple_cond); + + return TRUE; +} + +static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function, + gpointer data) +{ + PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1); + GIOChannel *channel; + GIOCondition cond = 0; + + closure->function = function; + closure->data = data; + + if (condition & PURPLE_INPUT_READ) + cond |= PURPLE_GLIB_READ_COND; + if (condition & PURPLE_INPUT_WRITE) + cond |= PURPLE_GLIB_WRITE_COND; + + channel = g_io_channel_unix_new(fd); + closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, + purple_glib_io_invoke, closure, purple_glib_io_destroy); + + g_io_channel_unref(channel); + return closure->result; +} + +static PurpleEventLoopUiOps glib_eventloops = +{ + g_timeout_add, + g_source_remove, + glib_input_add, + g_source_remove, + NULL, +#if GLIB_CHECK_VERSION(2,14,0) + g_timeout_add_seconds, +#else + NULL, +#endif + + /* padding */ + NULL, + NULL, + NULL +}; + +static PurpleCoreUiOps bee_core_uiops = +{ + NULL, + NULL, + NULL, //null_ui_init, + NULL, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static PurpleConversationUiOps bee_conv_uiops = +{ + NULL, /* create_conversation */ + NULL, /* destroy_conversation */ + NULL, /* write_chat */ + NULL, /* write_im */ + NULL, //null_write_conv, /* write_conv */ + NULL, /* chat_add_users */ + NULL, /* chat_rename_user */ + NULL, /* chat_remove_users */ + NULL, /* chat_update_user */ + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL +}; + +static void purple_init( account_t *acc ) +{ + set_t *s; + char str[16]; + +} + +static void purple_login( account_t *acc ) +{ + struct im_connection *ic = imcb_new( acc ); + struct ns_srv_reply *srv = NULL; + char *connect_to, *s; + int i; + + /* For now this is needed in the _connected() handlers if using + GLib event handling, to make sure we're not handling events + on dead connections. */ + purple_connections = g_slist_prepend( purple_connections, ic ); + +} + +static void purple_logout( struct im_connection *ic ) +{ + purple_connections = g_slist_remove( purple_connections, ic ); +} + +static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) +{ +} + +static GList *purple_away_states( struct im_connection *ic ) +{ +} + +static void purple_set_away( struct im_connection *ic, char *state_txt, char *message ) +{ +} + +static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) +{ +} + +static void purple_remove_buddy( struct im_connection *ic, char *who, char *group ) +{ +} + +static void purple_keepalive( struct im_connection *ic ) +{ +} + +static int purple_send_typing( struct im_connection *ic, char *who, int typing ) +{ +} + +void purple_initmodule() +{ + GList *prots; + + purple_util_set_user_dir("/tmp"); + purple_debug_set_enabled(FALSE); + purple_core_set_ui_ops(&bee_core_uiops); + purple_eventloop_set_ui_ops(&glib_eventloops); + if( !purple_core_init( "BitlBee") ) + { + /* Initializing the core failed. Terminate. */ + fprintf( stderr, "libpurple initialization failed.\n" ); + abort(); + } + + /* This seems like stateful shit we don't want... */ + purple_set_blist(purple_blist_new()); + purple_blist_load(); + + /* Meh? */ + purple_prefs_load(); + + for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) + { + struct prpl *ret = g_new0( struct prpl, 1 ); + PurplePlugin *prot = prots->data; + + ret->name = prot->info->id; + ret->login = purple_login; + ret->init = purple_init; + ret->logout = purple_logout; + ret->buddy_msg = purple_buddy_msg; + ret->away_states = purple_away_states; + ret->set_away = purple_set_away; + ret->add_buddy = purple_add_buddy; + ret->remove_buddy = purple_remove_buddy; + ret->keepalive = purple_keepalive; + ret->send_typing = purple_send_typing; + ret->handle_cmp = g_strcasecmp; + + register_protocol( ret ); + } +} -- cgit v1.2.3 From 860ba6aaeabb25cd27ec70fb4a37d910dd5b3746 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Oct 2009 00:32:34 +0100 Subject: Moved some stuff around, got something that logs in and reports status now. --- protocols/purple/purple.c | 152 +++++++++++++++++++++++++++++++++------------- 1 file changed, 109 insertions(+), 43 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 333f6674..5817373b 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -46,6 +46,18 @@ typedef struct _PurpleGLibIOClosure { gpointer data; } PurpleGLibIOClosure; +static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) +{ + PurpleAccount *pa = purple_connection_get_account( gc ); + GSList *i; + + for( i = purple_connections; i; i = i->next ) + if( ((struct im_connection *)i->data)->proto_data == pa ) + return i->data; + + return NULL; +} + static void purple_glib_io_destroy(gpointer data) { g_free(data); @@ -109,62 +121,31 @@ static PurpleEventLoopUiOps glib_eventloops = NULL }; -static PurpleCoreUiOps bee_core_uiops = -{ - NULL, - NULL, - NULL, //null_ui_init, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurpleConversationUiOps bee_conv_uiops = -{ - NULL, /* create_conversation */ - NULL, /* destroy_conversation */ - NULL, /* write_chat */ - NULL, /* write_im */ - NULL, //null_write_conv, /* write_conv */ - NULL, /* chat_add_users */ - NULL, /* chat_rename_user */ - NULL, /* chat_remove_users */ - NULL, /* chat_update_user */ - NULL, /* present */ - NULL, /* has_focus */ - NULL, /* custom_smiley_add */ - NULL, /* custom_smiley_write */ - NULL, /* custom_smiley_close */ - NULL, /* send_confirm */ - NULL, - NULL, - NULL, - NULL -}; - static void purple_init( account_t *acc ) { - set_t *s; - char str[16]; - + /* TODO: Figure out variables to export via set. */ } static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); - struct ns_srv_reply *srv = NULL; - char *connect_to, *s; - int i; + PurpleAccount *pa; + PurpleSavedStatus *ps; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events on dead connections. */ purple_connections = g_slist_prepend( purple_connections, ic ); + pa = purple_account_new( acc->user, acc->prpl->name ); + purple_account_set_password( pa, acc->pass ); + + ic->proto_data = pa; + + purple_account_set_enabled( pa, "BitlBee", TRUE ); + + //ps = purple_savedstatus_new( NULL, PURPLE_STATUS_AVAILABLE ); + //purple_savedstatus_activate_for_account( ps, pa ); } static void purple_logout( struct im_connection *ic ) @@ -178,6 +159,7 @@ static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, static GList *purple_away_states( struct im_connection *ic ) { + return NULL; } static void purple_set_away( struct im_connection *ic, char *state_txt, char *message ) @@ -200,6 +182,90 @@ static int purple_send_typing( struct im_connection *ic, char *who, int typing ) { } +static void purple_ui_init(); + +static PurpleCoreUiOps bee_core_uiops = +{ + NULL, + NULL, + purple_ui_init, + NULL, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) +{ + imcb_log( purple_ic_by_gc( gc ), "%s", text ); +} + +static void prplcb_conn_connected( PurpleConnection *gc ) +{ + imcb_connected( purple_ic_by_gc( gc ) ); +} + +static void prplcb_conn_disconnected( PurpleConnection *gc ) +{ + imc_logout( purple_ic_by_gc( gc ), TRUE ); +} + +static void prplcb_conn_notice( PurpleConnection *gc, const char *text ) +{ + imcb_log( purple_ic_by_gc( gc ), "%s", text ); +} + +static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text ) +{ + /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login, + should probably handle that. */ + imcb_error( purple_ic_by_gc( gc ), "%s", text ); +} + +static PurpleConnectionUiOps bee_conn_uiops = +{ + prplcb_conn_progress, + prplcb_conn_connected, + prplcb_conn_disconnected, + prplcb_conn_notice, + NULL, + NULL, + NULL, + prplcb_conn_report_disconnect_reason, +}; + +static PurpleConversationUiOps bee_conv_uiops = +{ + NULL, /* create_conversation */ + NULL, /* destroy_conversation */ + NULL, /* write_chat */ + NULL, /* write_im */ + NULL, //null_write_conv, /* write_conv */ + NULL, /* chat_add_users */ + NULL, /* chat_rename_user */ + NULL, /* chat_remove_users */ + NULL, /* chat_update_user */ + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL +}; + +static void purple_ui_init() +{ + purple_connections_set_ui_ops( &bee_conn_uiops ); + purple_conversations_set_ui_ops( &bee_conv_uiops ); +} + void purple_initmodule() { GList *prots; -- cgit v1.2.3 From 7da726b12a546a5022d8f91fa3a34764335ba037 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Oct 2009 22:49:42 +0100 Subject: Getting a contact list and online status now. Time to handle messages. --- protocols/purple/purple.c | 51 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 5817373b..5807d01a 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -46,9 +46,8 @@ typedef struct _PurpleGLibIOClosure { gpointer data; } PurpleGLibIOClosure; -static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) +static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { - PurpleAccount *pa = purple_connection_get_account( gc ); GSList *i; for( i = purple_connections; i; i = i->next ) @@ -58,6 +57,11 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) return NULL; } +static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) +{ + return purple_ic_by_pa( purple_connection_get_account( gc ) ); +} + static void purple_glib_io_destroy(gpointer data) { g_free(data); @@ -237,6 +241,48 @@ static PurpleConnectionUiOps bee_conn_uiops = prplcb_conn_report_disconnect_reason, }; +static void prplcb_blist_new( PurpleBlistNode *node ) +{ + PurpleBuddy *bud = (PurpleBuddy*) node; + struct im_connection *ic = purple_ic_by_pa( bud->account ); + + if( node->type == PURPLE_BLIST_BUDDY_NODE ) + { + imcb_add_buddy( ic, bud->name, NULL ); + if( bud->server_alias ) + imcb_buddy_nick_hint( ic, bud->name, bud->server_alias ); + } +} + +static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) +{ + PurpleBuddy *bud = (PurpleBuddy*) node; + + if( node->type == PURPLE_BLIST_BUDDY_NODE ) + { + imcb_buddy_status( purple_ic_by_pa( bud->account ), bud->name, + purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0, + NULL, NULL ); + } +} + +static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) +{ + PurpleBuddy *bud = (PurpleBuddy*) node; + + if( node->type == PURPLE_BLIST_BUDDY_NODE ) + imcb_remove_buddy( purple_ic_by_pa( bud->account ), bud->name, NULL ); +} + +static PurpleBlistUiOps bee_blist_uiops = +{ + NULL, + prplcb_blist_new, + NULL, + prplcb_blist_update, + prplcb_blist_remove, +}; + static PurpleConversationUiOps bee_conv_uiops = { NULL, /* create_conversation */ @@ -262,6 +308,7 @@ static PurpleConversationUiOps bee_conv_uiops = static void purple_ui_init() { + purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); } -- cgit v1.2.3 From d250b2a5fa95724613d0176ce85a6c6859407ce6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Oct 2009 23:26:01 +0100 Subject: Receive messages. --- protocols/purple/purple.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 5807d01a..9edba87e 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -209,7 +209,12 @@ static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t static void prplcb_conn_connected( PurpleConnection *gc ) { - imcb_connected( purple_ic_by_gc( gc ) ); + struct im_connection *ic = purple_ic_by_gc( gc ); + + imcb_connected( ic ); + + if( gc->flags & PURPLE_CONNECTION_HTML ) + ic->flags |= OPT_DOES_HTML; } static void prplcb_conn_disconnected( PurpleConnection *gc ) @@ -283,12 +288,19 @@ static PurpleBlistUiOps bee_blist_uiops = prplcb_blist_remove, }; +static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) +{ + struct im_connection *ic = purple_ic_by_pa( conv->account ); + + imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); +} + static PurpleConversationUiOps bee_conv_uiops = { NULL, /* create_conversation */ NULL, /* destroy_conversation */ NULL, /* write_chat */ - NULL, /* write_im */ + prplcb_conv_im, /* write_im */ NULL, //null_write_conv, /* write_conv */ NULL, /* chat_add_users */ NULL, /* chat_rename_user */ -- cgit v1.2.3 From 389f7bed787168abcc8aa5d01cdd49946cb863b6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Oct 2009 23:55:46 +0100 Subject: Support for sending messages. --- protocols/purple/purple.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 9edba87e..3c86490d 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -159,6 +159,16 @@ static void purple_logout( struct im_connection *ic ) static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) { + PurpleConversation *conv; + + if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM, + who, ic->proto_data ) ) == NULL ) + { + conv = purple_conversation_new( PURPLE_CONV_TYPE_IM, + ic->proto_data, who ); + } + + purple_conv_im_send( purple_conversation_get_im_data( conv ), message ); } static GList *purple_away_states( struct im_connection *ic ) @@ -292,7 +302,9 @@ static void prplcb_conv_im( PurpleConversation *conv, const char *who, const cha { struct im_connection *ic = purple_ic_by_pa( conv->account ); - imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); + /* ..._SEND means it's an outgoing message, no need to echo those. */ + if( !( flags & PURPLE_MESSAGE_SEND ) ) + imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); } static PurpleConversationUiOps bee_conv_uiops = -- cgit v1.2.3 From 0cbef26bd1f82787a8107e92b14839a59187e0c2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Oct 2009 00:37:32 +0100 Subject: Added some debugging stuff and handling (better said, ignoring) of events for closed connections where necessary. --- protocols/purple/purple.c | 60 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 3c86490d..cd908832 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -128,13 +128,15 @@ static PurpleEventLoopUiOps glib_eventloops = static void purple_init( account_t *acc ) { /* TODO: Figure out variables to export via set. */ + } static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); PurpleAccount *pa; - PurpleSavedStatus *ps; + //PurpleSavedStatus *ps; + GList *i; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events @@ -148,6 +150,15 @@ static void purple_login( account_t *acc ) purple_account_set_enabled( pa, "BitlBee", TRUE ); + /* + for( i = ((PurplePluginProtocolInfo *)pa->gc->prpl->info->extra_info)->protocol_options; i; i = i->next ) + { + PurpleAccountOption *o = i->data; + + printf( "%s\n", o->pref_name ); + } + */ + //ps = purple_savedstatus_new( NULL, PURPLE_STATUS_AVAILABLE ); //purple_savedstatus_activate_for_account( ps, pa ); } @@ -169,6 +180,8 @@ static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, } purple_conv_im_send( purple_conversation_get_im_data( conv ), message ); + + return 1; } static GList *purple_away_states( struct im_connection *ic ) @@ -194,6 +207,7 @@ static void purple_keepalive( struct im_connection *ic ) static int purple_send_typing( struct im_connection *ic, char *who, int typing ) { + return 1; } static void purple_ui_init(); @@ -214,7 +228,9 @@ static PurpleCoreUiOps bee_core_uiops = static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) { - imcb_log( purple_ic_by_gc( gc ), "%s", text ); + struct im_connection *ic = purple_ic_by_gc( gc ); + + imcb_log( ic, "%s", text ); } static void prplcb_conn_connected( PurpleConnection *gc ) @@ -229,19 +245,28 @@ static void prplcb_conn_connected( PurpleConnection *gc ) static void prplcb_conn_disconnected( PurpleConnection *gc ) { - imc_logout( purple_ic_by_gc( gc ), TRUE ); + struct im_connection *ic = purple_ic_by_gc( gc ); + + if( ic != NULL ) + imc_logout( ic, TRUE ); } static void prplcb_conn_notice( PurpleConnection *gc, const char *text ) { - imcb_log( purple_ic_by_gc( gc ), "%s", text ); + struct im_connection *ic = purple_ic_by_gc( gc ); + + if( ic != NULL ) + imcb_log( ic, "%s", text ); } static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text ) { + struct im_connection *ic = purple_ic_by_gc( gc ); + /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login, should probably handle that. */ - imcb_error( purple_ic_by_gc( gc ), "%s", text ); + if( ic != NULL ) + imcb_error( ic, "%s", text ); } static PurpleConnectionUiOps bee_conn_uiops = @@ -261,7 +286,7 @@ static void prplcb_blist_new( PurpleBlistNode *node ) PurpleBuddy *bud = (PurpleBuddy*) node; struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE ) + if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) { imcb_add_buddy( ic, bud->name, NULL ); if( bud->server_alias ) @@ -272,10 +297,11 @@ static void prplcb_blist_new( PurpleBlistNode *node ) static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; + struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE ) + if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) { - imcb_buddy_status( purple_ic_by_pa( bud->account ), bud->name, + imcb_buddy_status( ic, bud->name, purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0, NULL, NULL ); } @@ -284,9 +310,12 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; + struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE ) - imcb_remove_buddy( purple_ic_by_pa( bud->account ), bud->name, NULL ); + if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) + { + imcb_remove_buddy( ic, bud->name, NULL ); + } } static PurpleBlistUiOps bee_blist_uiops = @@ -330,11 +359,22 @@ static PurpleConversationUiOps bee_conv_uiops = NULL }; +static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) +{ + printf( "DEBUG %s: %s", category, arg_s ); +} + +static PurpleDebugUiOps bee_debug_uiops = +{ + prplcb_debug_print, +}; + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); + //purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From e046390da36e369c94af607fdedfe7b9f99d9e47 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 00:25:54 +0100 Subject: Make purple use BitlBee's event handling API. Since the APIs never really diverged too much this is fairly transparent. I did rename and redefine GAIM_INPUT_* variables to really make it work without adding another stupid layer in between. One problem left, the new libpurple input API doesn't care about return values. Fixing that in the next CL. --- bitlbee.c | 6 ++-- ipc.c | 6 ++-- irc.c | 8 +++--- lib/events.h | 4 +-- lib/events_glib.c | 8 +++--- lib/events_libevent.c | 12 ++++---- lib/http_client.c | 6 ++-- lib/proxy.c | 64 ++++++++++++++++++++--------------------- lib/ssl_bogus.c | 2 +- lib/ssl_client.h | 2 +- lib/ssl_gnutls.c | 4 +-- lib/ssl_nss.c | 2 +- lib/ssl_openssl.c | 4 +-- lib/ssl_sspi.c | 2 +- protocols/jabber/io.c | 4 +-- protocols/msn/ns.c | 2 +- protocols/msn/sb.c | 2 +- protocols/oscar/oscar.c | 14 ++++----- protocols/purple/purple.c | 72 +++++++++++------------------------------------ protocols/yahoo/yahoo.c | 4 +-- 20 files changed, 95 insertions(+), 133 deletions(-) diff --git a/bitlbee.c b/bitlbee.c index b31c31fe..0feb88fc 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -92,7 +92,7 @@ int bitlbee_daemon_init() return( -1 ); } - global.listen_watch_source_id = b_input_add( global.listen_socket, GAIM_INPUT_READ, bitlbee_io_new_client, NULL ); + 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 ) @@ -283,7 +283,7 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition 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, GAIM_INPUT_READ, ipc_master_read, child ); + 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 ); @@ -311,7 +311,7 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition /* We can store the IPC fd there now. */ global.listen_socket = fds[1]; - global.listen_watch_source_id = b_input_add( fds[1], GAIM_INPUT_READ, ipc_child_read, irc ); + global.listen_watch_source_id = b_input_add( fds[1], B_EV_IO_READ, ipc_child_read, irc ); close( fds[0] ); diff --git a/ipc.c b/ipc.c index d6b850f1..54053322 100644 --- a/ipc.c +++ b/ipc.c @@ -513,7 +513,7 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio return TRUE; } - child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + 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 ); @@ -551,7 +551,7 @@ int ipc_master_listen_socket() return 0; } - b_input_add( serversock, GAIM_INPUT_READ, new_ipc_client, NULL ); + b_input_add( serversock, B_EV_IO_READ, new_ipc_client, NULL ); return 1; } @@ -596,7 +596,7 @@ int ipc_master_load_state( char *statefile ) fclose( fp ); return 0; } - child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + 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 ); } diff --git a/irc.c b/irc.c index 2dcc625d..ea0f7db7 100644 --- a/irc.c +++ b/irc.c @@ -89,7 +89,7 @@ irc_t *irc_new( int fd ) irc->fd = fd; sock_make_nonblocking( irc->fd ); - irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc ); + irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); irc->status = USTATUS_OFFLINE; irc->last_pong = gettime(); @@ -652,10 +652,10 @@ void irc_vawrite( irc_t *irc, char *format, va_list params ) the queue. If it's FALSE, we emptied the buffer and saved ourselves some work in the event queue. */ /* Really can't be done as long as the code doesn't do error checking very well: - if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */ + if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */ /* So just always do it via the event handler. */ - irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc ); + irc->w_watch_source_id = b_input_add( irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc ); } return; @@ -681,7 +681,7 @@ void irc_write_all( int now, char *format, ... ) irc_vawrite( temp->data, format, params ); if( now ) { - bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ); + bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ); } temp = temp->next; } diff --git a/lib/events.h b/lib/events.h index 4baea7b6..5bf2cfd8 100644 --- a/lib/events.h +++ b/lib/events.h @@ -47,8 +47,8 @@ /* The conditions you can pass to b_input_add()/that will be passed to the given callback function. */ typedef enum { - GAIM_INPUT_READ = 1 << 1, - GAIM_INPUT_WRITE = 1 << 2 + B_EV_IO_READ = 1 << 0, + B_EV_IO_WRITE = 1 << 1 } b_input_condition; typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond); diff --git a/lib/events_glib.c b/lib/events_glib.c index 3e194e98..2260ec4e 100644 --- a/lib/events_glib.c +++ b/lib/events_glib.c @@ -75,9 +75,9 @@ static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpoin gboolean st; if (condition & GAIM_READ_COND) - gaim_cond |= GAIM_INPUT_READ; + gaim_cond |= B_EV_IO_READ; if (condition & GAIM_WRITE_COND) - gaim_cond |= GAIM_INPUT_WRITE; + gaim_cond |= B_EV_IO_WRITE; event_debug( "gaim_io_invoke( %d, %d, 0x%x )\n", g_io_channel_unix_get_fd(source), condition, data ); @@ -105,9 +105,9 @@ gint b_input_add(gint source, b_input_condition condition, b_event_handler funct closure->function = function; closure->data = data; - if (condition & GAIM_INPUT_READ) + if (condition & B_EV_IO_READ) cond |= GAIM_READ_COND; - if (condition & GAIM_INPUT_WRITE) + if (condition & B_EV_IO_WRITE) cond |= GAIM_WRITE_COND; channel = g_io_channel_unix_new(source); diff --git a/lib/events_libevent.c b/lib/events_libevent.c index cf616576..b52a5dd3 100644 --- a/lib/events_libevent.c +++ b/lib/events_libevent.c @@ -125,9 +125,9 @@ static void b_event_passthrough( int fd, short event, void *data ) if( fd >= 0 ) { if( event & EV_READ ) - cond |= GAIM_INPUT_READ; + cond |= B_EV_IO_READ; if( event & EV_WRITE ) - cond |= GAIM_INPUT_WRITE; + cond |= B_EV_IO_WRITE; } event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id ); @@ -173,8 +173,8 @@ gint b_input_add( gint fd, b_input_condition condition, b_event_handler function event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data ); - if( ( condition & GAIM_INPUT_READ && ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) || - ( condition & GAIM_INPUT_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) ) + if( ( condition & B_EV_IO_READ && ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) || + ( condition & B_EV_IO_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) ) { /* We'll stick with this libevent entry, but give it a new BitlBee id. */ g_hash_table_remove( id_hash, &b_ev->id ); @@ -197,9 +197,9 @@ gint b_input_add( gint fd, b_input_condition condition, b_event_handler function b_ev->data = data; out_cond = EV_PERSIST; - if( condition & GAIM_INPUT_READ ) + if( condition & B_EV_IO_READ ) out_cond |= EV_READ; - if( condition & GAIM_INPUT_WRITE ) + if( condition & B_EV_IO_WRITE ) out_cond |= EV_WRITE; event_set( &b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev ); diff --git a/lib/http_client.c b/lib/http_client.c index aae5645b..e9d3c1bb 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -148,10 +148,10 @@ static gboolean http_connected( gpointer data, int source, b_input_condition con if( req->bytes_written < req->request_length ) req->inpa = b_input_add( source, - req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE, + req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_WRITE, http_connected, req ); else - req->inpa = b_input_add( source, GAIM_INPUT_READ, http_incoming_data, req ); + req->inpa = b_input_add( source, B_EV_IO_READ, http_incoming_data, req ); return FALSE; @@ -233,7 +233,7 @@ static gboolean http_incoming_data( gpointer data, int source, b_input_condition /* There will be more! */ req->inpa = b_input_add( req->fd, - req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ, + req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ, http_incoming_data, req ); return FALSE; diff --git a/lib/proxy.c b/lib/proxy.c index e52837fe..baf5823a 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -90,9 +90,9 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition closesocket(source); b_event_remove(phb->inpa); if( phb->proxy_func ) - phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ); + phb->proxy_func(phb->proxy_data, -1, B_EV_IO_READ); else { - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb); } return FALSE; @@ -101,9 +101,9 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition sock_make_blocking(source); b_event_remove(phb->inpa); if( phb->proxy_func ) - phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ); + phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ); else { - phb->func(phb->data, source, GAIM_INPUT_READ); + phb->func(phb->data, source, B_EV_IO_READ); g_free(phb); } @@ -146,7 +146,7 @@ static int proxy_connect_none(const char *host, unsigned short port, struct PHB return -1; } else { - phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb); + phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb); phb->fd = fd; return fd; @@ -178,14 +178,14 @@ static gboolean http_canread(gpointer data, gint source, b_input_condition cond) if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { - phb->func(phb->data, source, GAIM_INPUT_READ); + phb->func(phb->data, source, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); @@ -203,7 +203,7 @@ static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -214,7 +214,7 @@ static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond phb->host, phb->port); if (send(source, cmd, strlen(cmd), 0) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -229,7 +229,7 @@ static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond g_free(t2); if (send(source, cmd, strlen(cmd), 0) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -239,13 +239,13 @@ static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond g_snprintf(cmd, sizeof(cmd), "\r\n"); if (send(source, cmd, strlen(cmd), 0) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, http_canread, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, http_canread, phb); return FALSE; } @@ -272,14 +272,14 @@ static gboolean s4_canread(gpointer data, gint source, b_input_condition cond) memset(packet, 0, sizeof(packet)); if (read(source, packet, 9) >= 4 && packet[1] == 90) { - phb->func(phb->data, source, GAIM_INPUT_READ); + phb->func(phb->data, source, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); @@ -298,7 +298,7 @@ static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -308,7 +308,7 @@ static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) /* XXX does socks4 not support host name lookups by the proxy? */ if (!(hp = gethostbyname(phb->host))) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -325,13 +325,13 @@ static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) packet[8] = 0; if (write(source, packet, 9) != 9) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, s4_canread, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, s4_canread, phb); return FALSE; } @@ -358,20 +358,20 @@ static gboolean s5_canread_again(gpointer data, gint source, b_input_condition c if (read(source, buf, 10) < 10) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } if ((buf[0] != 0x05) || (buf[1] != 0x00)) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->func(phb->data, source, GAIM_INPUT_READ); + phb->func(phb->data, source, B_EV_IO_READ); g_free(phb->host); g_free(phb); @@ -395,13 +395,13 @@ static void s5_sendconnect(gpointer data, gint source) if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread_again, phb); } static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) @@ -413,7 +413,7 @@ static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) if (read(source, buf, 2) < 2) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -421,7 +421,7 @@ static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) if ((buf[0] != 0x01) || (buf[1] != 0x00)) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -441,7 +441,7 @@ static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) if (read(source, buf, 2) < 2) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -449,7 +449,7 @@ static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) if ((buf[0] != 0x05) || (buf[1] == 0xff)) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -464,13 +464,13 @@ static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) memcpy(buf + 2 + i + 1, proxypass, j); if (write(source, buf, 3 + i + j) < 3 + i + j) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_readauth, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_readauth, phb); } else { s5_sendconnect(phb, source); } @@ -490,7 +490,7 @@ static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond) len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -512,13 +512,13 @@ static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond) if (write(source, buf, i) < i) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread, phb); return FALSE; } diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c index a07ea752..9c368c66 100644 --- a/lib/ssl_bogus.c +++ b/lib/ssl_bogus.c @@ -58,7 +58,7 @@ void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) b_input_condition ssl_getdirection( void *conn ) { - return GAIM_INPUT_READ; + return B_EV_IO_READ; } int ssl_pending( void *conn ) diff --git a/lib/ssl_client.h b/lib/ssl_client.h index f91d0d70..0a8e82d8 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -70,7 +70,7 @@ G_MODULE_EXPORT void ssl_disconnect( void *conn_ ); handling. */ G_MODULE_EXPORT int ssl_getfd( void *conn ); -/* This function returns GAIM_INPUT_READ/WRITE. With SSL connections it's +/* This function returns B_EV_IO_READ/WRITE. With SSL connections it's possible that something has to be read while actually were trying to write something (think about key exchange/refresh/etc). So when an SSL operation returned SSL_AGAIN, *always* use this function when diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index f5945442..5a14b825 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -105,7 +105,7 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition { struct scd *conn = data; - return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); + return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); } static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) @@ -243,5 +243,5 @@ int ssl_getfd( void *conn ) b_input_condition ssl_getdirection( void *conn ) { return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ? - GAIM_INPUT_WRITE : GAIM_INPUT_READ ); + B_EV_IO_WRITE : B_EV_IO_READ ); } diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index eba3c441..de6e7ec6 100644 --- a/lib/ssl_nss.c +++ b/lib/ssl_nss.c @@ -192,5 +192,5 @@ int ssl_getfd( void *conn ) b_input_condition ssl_getdirection( void *conn ) { /* Just in case someone calls us, let's return the most likely case: */ - return GAIM_INPUT_READ; + return B_EV_IO_READ; } diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index fc6d433e..8abff390 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -101,7 +101,7 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition { struct scd *conn = data; - return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); + return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); } static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) @@ -269,5 +269,5 @@ int ssl_getfd( void *conn ) b_input_condition ssl_getdirection( void *conn ) { - return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ ); + return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ ); } diff --git a/lib/ssl_sspi.c b/lib/ssl_sspi.c index a16423b1..e14c451e 100644 --- a/lib/ssl_sspi.c +++ b/lib/ssl_sspi.c @@ -274,5 +274,5 @@ int ssl_getfd(void *conn) GaimInputCondition ssl_getdirection( void *conn ) { - return GAIM_INPUT_WRITE; /* FIXME: or GAIM_INPUT_READ */ + return B_EV_IO_WRITE; /* FIXME: or B_EV_IO_READ */ } diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index 10efad37..3b0ef3ef 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -63,7 +63,7 @@ int jabber_write( struct im_connection *ic, char *buf, int len ) it via the event handler. If not, add the handler. (In most cases it probably won't be necessary.) */ if( ( ret = jabber_write_queue( ic ) ) && jd->tx_len > 0 ) - jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, ic ); + jd->w_inpa = b_input_add( jd->fd, B_EV_IO_WRITE, jabber_write_callback, ic ); } else { @@ -528,7 +528,7 @@ gboolean jabber_start_stream( struct im_connection *ic ) jd->xt = xt_new( jabber_handlers, ic ); if( jd->r_inpa <= 0 ) - jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic ); + jd->r_inpa = b_input_add( jd->fd, B_EV_IO_READ, jabber_read_callback, ic ); greet = g_strdup_printf( "" "trId ); if( msn_write( ic, s, strlen( s ) ) ) { - ic->inpa = b_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, ic ); + ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic ); imcb_log( ic, "Connected to server, waiting for reply" ); } diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index e9526234..b0f7a2c5 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -308,7 +308,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond ) g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session ); if( msn_sb_write( sb, buf, strlen( buf ) ) ) - sb->inp = b_input_add( sb->fd, GAIM_INPUT_READ, msn_sb_callback, sb ); + sb->inp = b_input_add( sb->fd, B_EV_IO_READ, msn_sb_callback, sb ); else debug( "Error %d while connecting to switchboard server", 2 ); diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 1118c26d..06b8100f 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -290,7 +290,7 @@ static gboolean oscar_callback(gpointer data, gint source, odata = (struct oscar_data *)ic->proto_data; - if (condition & GAIM_INPUT_READ) { + if (condition & B_EV_IO_READ) { if (aim_get_command(odata->sess, conn) >= 0) { aim_rxdispatch(odata->sess); if (odata->killme) @@ -362,7 +362,7 @@ static gboolean oscar_login_connect(gpointer data, gint source, b_input_conditio } aim_conn_completeconnect(sess, conn); - ic->inpa = b_input_add(conn->fd, GAIM_INPUT_READ, + ic->inpa = b_input_add(conn->fd, B_EV_IO_READ, oscar_callback, conn); return FALSE; @@ -486,7 +486,7 @@ static gboolean oscar_bos_connect(gpointer data, gint source, b_input_condition } aim_conn_completeconnect(sess, bosconn); - ic->inpa = b_input_add(bosconn->fd, GAIM_INPUT_READ, + ic->inpa = b_input_add(bosconn->fd, B_EV_IO_READ, oscar_callback, bosconn); imcb_log(ic, _("Connection established, cookie sent")); @@ -662,7 +662,7 @@ static gboolean straight_to_hell(gpointer data, gint source, b_input_condition c write(pos->fd, buf, strlen(buf)); if (pos->modname) g_free(pos->modname); - pos->inpa = b_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos); + pos->inpa = b_input_add(pos->fd, B_EV_IO_READ, damn_you, pos); return FALSE; } @@ -831,7 +831,7 @@ static gboolean oscar_chatnav_connect(gpointer data, gint source, b_input_condit } aim_conn_completeconnect(sess, tstconn); - odata->cnpa = b_input_add(tstconn->fd, GAIM_INPUT_READ, + odata->cnpa = b_input_add(tstconn->fd, B_EV_IO_READ, oscar_callback, tstconn); return FALSE; @@ -859,7 +859,7 @@ static gboolean oscar_auth_connect(gpointer data, gint source, b_input_condition } aim_conn_completeconnect(sess, tstconn); - odata->paspa = b_input_add(tstconn->fd, GAIM_INPUT_READ, + odata->paspa = b_input_add(tstconn->fd, B_EV_IO_READ, oscar_callback, tstconn); return FALSE; @@ -895,7 +895,7 @@ static gboolean oscar_chat_connect(gpointer data, gint source, b_input_condition aim_conn_completeconnect(sess, ccon->conn); ccon->inpa = b_input_add(tstconn->fd, - GAIM_INPUT_READ, + B_EV_IO_READ, oscar_callback, tstconn); odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon); diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index cd908832..08c14edf 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -62,67 +62,22 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) return purple_ic_by_pa( purple_connection_get_account( gc ) ); } -static void purple_glib_io_destroy(gpointer data) +static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata ) { - g_free(data); + return b_timeout_add( interval, (b_event_handler) func, udata ); } -static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) { - PurpleGLibIOClosure *closure = data; - PurpleInputCondition purple_cond = 0; - - if (condition & PURPLE_GLIB_READ_COND) - purple_cond |= PURPLE_INPUT_READ; - if (condition & PURPLE_GLIB_WRITE_COND) - purple_cond |= PURPLE_INPUT_WRITE; - - closure->function(closure->data, g_io_channel_unix_get_fd(source), - purple_cond); - - return TRUE; -} - -static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function, - gpointer data) -{ - PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1); - GIOChannel *channel; - GIOCondition cond = 0; - - closure->function = function; - closure->data = data; - - if (condition & PURPLE_INPUT_READ) - cond |= PURPLE_GLIB_READ_COND; - if (condition & PURPLE_INPUT_WRITE) - cond |= PURPLE_GLIB_WRITE_COND; - - channel = g_io_channel_unix_new(fd); - closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, - purple_glib_io_invoke, closure, purple_glib_io_destroy); - - g_io_channel_unref(channel); - return closure->result; + return (guint) b_input_add( fd, cond, (b_event_handler) func, udata ); } static PurpleEventLoopUiOps glib_eventloops = { - g_timeout_add, - g_source_remove, - glib_input_add, - g_source_remove, - NULL, -#if GLIB_CHECK_VERSION(2,14,0) - g_timeout_add_seconds, -#else - NULL, -#endif - - /* padding */ - NULL, - NULL, - NULL + prplcb_ev_timeout_add, + b_event_remove, + prplcb_ev_input_add, + b_event_remove, }; static void purple_init( account_t *acc ) @@ -136,7 +91,7 @@ static void purple_login( account_t *acc ) struct im_connection *ic = imcb_new( acc ); PurpleAccount *pa; //PurpleSavedStatus *ps; - GList *i; + //GList *i; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events @@ -342,7 +297,7 @@ static PurpleConversationUiOps bee_conv_uiops = NULL, /* destroy_conversation */ NULL, /* write_chat */ prplcb_conv_im, /* write_im */ - NULL, //null_write_conv, /* write_conv */ + NULL, /* write_conv */ NULL, /* chat_add_users */ NULL, /* chat_rename_user */ NULL, /* chat_remove_users */ @@ -381,6 +336,13 @@ void purple_initmodule() { GList *prots; + if( B_EV_IO_READ != PURPLE_INPUT_READ || + B_EV_IO_WRITE != PURPLE_INPUT_WRITE ) + { + /* FIXME FIXME FIXME FIXME FIXME :-) */ + exit( 1 ); + } + purple_util_set_user_dir("/tmp"); purple_debug_set_enabled(FALSE); purple_core_set_ui_ops(&bee_core_uiops); diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 3e844c55..65993d9d 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -681,7 +681,7 @@ int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *dat d->data = data; inp->d = d; - d->tag = inp->h = b_input_add( fd, GAIM_INPUT_READ, (b_event_handler) byahoo_read_ready_callback, (gpointer) d ); + d->tag = inp->h = b_input_add( fd, B_EV_IO_READ, (b_event_handler) byahoo_read_ready_callback, (gpointer) d ); } else if( cond == YAHOO_INPUT_WRITE ) { @@ -692,7 +692,7 @@ int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *dat d->data = data; inp->d = d; - d->tag = inp->h = b_input_add( fd, GAIM_INPUT_WRITE, (b_event_handler) byahoo_write_ready_callback, (gpointer) d ); + d->tag = inp->h = b_input_add( fd, B_EV_IO_WRITE, (b_event_handler) byahoo_write_ready_callback, (gpointer) d ); } else { -- cgit v1.2.3 From c5c18c155cfdc3edcbd764633761d33e3c5992a3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 00:57:26 +0100 Subject: Hacked up a B_EV_FLAG_FORCE_REPEAT event handler flag to make libpurple happy. --- lib/events.h | 4 +++- lib/events_glib.c | 9 ++++++++- lib/events_libevent.c | 4 +++- protocols/purple/purple.c | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/events.h b/lib/events.h index 5bf2cfd8..fa30cf27 100644 --- a/lib/events.h +++ b/lib/events.h @@ -48,7 +48,9 @@ the given callback function. */ typedef enum { B_EV_IO_READ = 1 << 0, - B_EV_IO_WRITE = 1 << 1 + B_EV_IO_WRITE = 1 << 1, + B_EV_FLAG_FORCE_ONCE = 1 << 16, + B_EV_FLAG_FORCE_REPEAT = 1 << 17, } b_input_condition; typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond); diff --git a/lib/events_glib.c b/lib/events_glib.c index 2260ec4e..d6ac82cc 100644 --- a/lib/events_glib.c +++ b/lib/events_glib.c @@ -48,6 +48,7 @@ typedef struct _GaimIOClosure { b_event_handler function; gpointer data; + guint flags; } GaimIOClosure; static GMainLoop *loop = NULL; @@ -86,7 +87,12 @@ static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpoin if( !st ) event_debug( "Returned FALSE, cancelling.\n" ); - return st; + if (closure->flags & B_EV_FLAG_FORCE_ONCE) + return FALSE; + else if (closure->flags & B_EV_FLAG_FORCE_REPEAT) + return TRUE; + else + return st; } static void gaim_io_destroy(gpointer data) @@ -104,6 +110,7 @@ gint b_input_add(gint source, b_input_condition condition, b_event_handler funct closure->function = function; closure->data = data; + closure->flags = condition; if (condition & B_EV_IO_READ) cond |= GAIM_READ_COND; diff --git a/lib/events_libevent.c b/lib/events_libevent.c index b52a5dd3..43d770ea 100644 --- a/lib/events_libevent.c +++ b/lib/events_libevent.c @@ -59,6 +59,7 @@ struct b_event_data gint timeout; b_event_handler function; void *data; + guint flags; }; void b_main_init() @@ -149,7 +150,7 @@ static void b_event_passthrough( int fd, short event, void *data ) /* This event was killed already, don't touch it! */ return; } - else if( !st ) + else if( !st && !( b_ev->flags & B_EV_FLAG_FORCE_REPEAT ) ) { event_debug( "Handler returned FALSE: " ); b_event_remove( id_cur ); @@ -211,6 +212,7 @@ gint b_input_add( gint fd, b_input_condition condition, b_event_handler function g_hash_table_insert( write_hash, &b_ev->evinfo.ev_fd, b_ev ); } + b_ev->flags = condition; g_hash_table_insert( id_hash, &b_ev->id, b_ev ); return b_ev->id; } diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 08c14edf..9ef70bd3 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -69,7 +69,7 @@ static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer u static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) { - return (guint) b_input_add( fd, cond, (b_event_handler) func, udata ); + return (guint) b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata ); } static PurpleEventLoopUiOps glib_eventloops = -- cgit v1.2.3 From 4164e620b4f593a427a89d9292f4aef5c33e9def Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 11:40:40 +0100 Subject: Fixing a few compiler warnings and cleaning up the last remains of GLib-specific code. --- protocols/purple/purple.c | 60 +++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 9ef70bd3..0a70b194 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -28,24 +28,6 @@ GSList *purple_connections; -#undef g_io_add_watch -#undef g_io_add_watch_full -#undef g_timeout_add -#undef g_source_remove - -/** - * The following eventloop functions are used in both pidgin and purple-text. If your - * application uses glib mainloop, you can safely use this verbatim. - */ -#define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) -#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) - -typedef struct _PurpleGLibIOClosure { - PurpleInputFunction function; - guint result; - gpointer data; -} PurpleGLibIOClosure; - static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { GSList *i; @@ -62,24 +44,6 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) return purple_ic_by_pa( purple_connection_get_account( gc ) ); } -static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata ) -{ - return b_timeout_add( interval, (b_event_handler) func, udata ); -} - -static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) -{ - return (guint) b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata ); -} - -static PurpleEventLoopUiOps glib_eventloops = -{ - prplcb_ev_timeout_add, - b_event_remove, - prplcb_ev_input_add, - b_event_remove, -}; - static void purple_init( account_t *acc ) { /* TODO: Figure out variables to export via set. */ @@ -324,6 +288,30 @@ static PurpleDebugUiOps bee_debug_uiops = prplcb_debug_print, }; +static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata ) +{ + return b_timeout_add( interval, (b_event_handler) func, udata ); +} + +static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) +{ + return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata ); +} + +static gboolean prplcb_ev_remove( guint id ) +{ + b_event_remove( (gint) id ); + return TRUE; +} + +static PurpleEventLoopUiOps glib_eventloops = +{ + prplcb_ev_timeout_add, + prplcb_ev_remove, + prplcb_ev_input_add, + prplcb_ev_remove, +}; + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); -- cgit v1.2.3 From 4f103ea401bb6b1ed8963ea33d4924f95e10473b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 12:26:09 +0100 Subject: Added handing of away states/messages of contacts. --- protocols/purple/purple.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 0a70b194..f1355e25 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -137,12 +137,6 @@ static PurpleCoreUiOps bee_core_uiops = NULL, purple_ui_init, NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL }; static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) @@ -220,9 +214,16 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) { - imcb_buddy_status( ic, bud->name, - purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0, - NULL, NULL ); + PurpleStatus *as; + int flags = 0; + + flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0; + flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY; + + as = purple_presence_get_active_status( bud->presence ); + + imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ), + purple_status_get_attr_string( as, "message" ) ); } } -- cgit v1.2.3 From db4cd40374ade33ccb1feae113f12a1dd0b6bf37 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 13:22:23 +0100 Subject: Some valgrind cleaning/type safety fixes. --- protocols/purple/purple.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f1355e25..c9de15cd 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -84,7 +84,11 @@ static void purple_login( account_t *acc ) static void purple_logout( struct im_connection *ic ) { + PurpleAccount *pa = ic->proto_data; + + purple_account_set_enabled( pa, "BitlBee", FALSE ); purple_connections = g_slist_remove( purple_connections, ic ); + purple_account_destroy( pa ); } static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) @@ -197,10 +201,14 @@ static PurpleConnectionUiOps bee_conn_uiops = static void prplcb_blist_new( PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; - struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) + if( node->type == PURPLE_BLIST_BUDDY_NODE ) { + struct im_connection *ic = purple_ic_by_pa( bud->account ); + + if( ic == NULL ) + return; + imcb_add_buddy( ic, bud->name, NULL ); if( bud->server_alias ) imcb_buddy_nick_hint( ic, bud->name, bud->server_alias ); @@ -210,13 +218,16 @@ static void prplcb_blist_new( PurpleBlistNode *node ) static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; - struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) + if( node->type == PURPLE_BLIST_BUDDY_NODE ) { + struct im_connection *ic = purple_ic_by_pa( bud->account ); PurpleStatus *as; int flags = 0; + if( ic == NULL ) + return; + flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0; flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY; @@ -230,10 +241,14 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; - struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) + if( node->type == PURPLE_BLIST_BUDDY_NODE ) { + struct im_connection *ic = purple_ic_by_pa( bud->account ); + + if( ic == NULL ) + return; + imcb_remove_buddy( ic, bud->name, NULL ); } } -- cgit v1.2.3 From 0f7ee7e53f6bcb2d1d262a94c278440413c0103a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 13:57:29 +0100 Subject: Copy all the string/bool/int account settings with their defaults to "account set". They can be changed, but changes don't yet have any effect. --- protocols/purple/purple.c | 58 +++++++++++++++++++++++++++++++++++------------ set.c | 2 +- set.h | 2 +- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index c9de15cd..f3d8f0f4 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -46,16 +46,56 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) static void purple_init( account_t *acc ) { - /* TODO: Figure out variables to export via set. */ + PurplePlugin *prpl = purple_plugins_find_with_id( acc->prpl->name ); + PurplePluginProtocolInfo *pi = prpl->info->extra_info; + GList *i; + for( i = pi->protocol_options; i; i = i->next ) + { + PurpleAccountOption *o = i->data; + const char *name; + char *def = NULL; + set_eval eval = NULL; + set_t *s; + + name = purple_account_option_get_setting( o ); + + switch( purple_account_option_get_type( o ) ) + { + case PURPLE_PREF_STRING: + def = g_strdup( purple_account_option_get_default_string( o ) ); + break; + + case PURPLE_PREF_INT: + def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) ); + eval = set_eval_int; + break; + + case PURPLE_PREF_BOOLEAN: + if( purple_account_option_get_default_bool( o ) ) + def = g_strdup( "true" ); + else + def = g_strdup( "false" ); + eval = set_eval_bool; + break; + + default: + fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) ); + } + + if( def != NULL ) + { + s = set_add( &acc->set, name, def, eval, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; + g_free( def ); + } + } } static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); PurpleAccount *pa; - //PurpleSavedStatus *ps; - //GList *i; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events @@ -68,18 +108,6 @@ static void purple_login( account_t *acc ) ic->proto_data = pa; purple_account_set_enabled( pa, "BitlBee", TRUE ); - - /* - for( i = ((PurplePluginProtocolInfo *)pa->gc->prpl->info->extra_info)->protocol_options; i; i = i->next ) - { - PurpleAccountOption *o = i->data; - - printf( "%s\n", o->pref_name ); - } - */ - - //ps = purple_savedstatus_new( NULL, PURPLE_STATUS_AVAILABLE ); - //purple_savedstatus_activate_for_account( ps, pa ); } static void purple_logout( struct im_connection *ic ) diff --git a/set.c b/set.c index 18d5a50d..f72e0ace 100644 --- a/set.c +++ b/set.c @@ -28,7 +28,7 @@ /* Used to use NULL for this, but NULL is actually a "valid" value. */ char *SET_INVALID = "nee"; -set_t *set_add( set_t **head, char *key, char *def, set_eval eval, void *data ) +set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, void *data ) { set_t *s = set_find( head, key ); diff --git a/set.h b/set.h index 19ea73fb..d20a58d3 100644 --- a/set.h +++ b/set.h @@ -72,7 +72,7 @@ typedef struct set } set_t; /* Should be pretty clear. */ -set_t *set_add( set_t **head, char *key, char *def, set_eval eval, void *data ); +set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, void *data ); /* Returns the raw set_t. Might be useful sometimes. */ set_t *set_find( set_t **head, char *key ); -- cgit v1.2.3 From b74b287af7ee980b01b89e911e21ec8f163d24b3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 22:08:26 +0100 Subject: Fixed account cleanup (use remove, not destroy) and now using user's account settings. --- protocols/purple/purple.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- set.c | 16 ++++++++-------- set.h | 16 ++++++++-------- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f3d8f0f4..82978dc4 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -92,6 +92,43 @@ static void purple_init( account_t *acc ) } } +static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) +{ + PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); + PurplePluginProtocolInfo *pi = prpl->info->extra_info; + GList *i; + + for( i = pi->protocol_options; i; i = i->next ) + { + PurpleAccountOption *o = i->data; + const char *name; + set_t *s; + + name = purple_account_option_get_setting( o ); + s = set_find( &acc->set, name ); + if( s->value == NULL ) + continue; + + switch( purple_account_option_get_type( o ) ) + { + case PURPLE_PREF_STRING: + purple_account_set_string( pa, name, set_getstr( &acc->set, name ) ); + break; + + case PURPLE_PREF_INT: + purple_account_set_int( pa, name, set_getint( &acc->set, name ) ); + break; + + case PURPLE_PREF_BOOLEAN: + purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) ); + break; + + default: + break; + } + } +} + static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); @@ -102,10 +139,9 @@ static void purple_login( account_t *acc ) on dead connections. */ purple_connections = g_slist_prepend( purple_connections, ic ); - pa = purple_account_new( acc->user, acc->prpl->name ); + ic->proto_data = pa = purple_account_new( acc->user, acc->prpl->name ); purple_account_set_password( pa, acc->pass ); - - ic->proto_data = pa; + purple_sync_settings( acc, pa ); purple_account_set_enabled( pa, "BitlBee", TRUE ); } @@ -116,7 +152,7 @@ static void purple_logout( struct im_connection *ic ) purple_account_set_enabled( pa, "BitlBee", FALSE ); purple_connections = g_slist_remove( purple_connections, ic ); - purple_account_destroy( pa ); + purple_accounts_remove( pa ); } static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) @@ -193,7 +229,9 @@ static void prplcb_conn_disconnected( PurpleConnection *gc ) struct im_connection *ic = purple_ic_by_gc( gc ); if( ic != NULL ) + { imc_logout( ic, TRUE ); + } } static void prplcb_conn_notice( PurpleConnection *gc, const char *text ) diff --git a/set.c b/set.c index f72e0ace..7abae37a 100644 --- a/set.c +++ b/set.c @@ -62,7 +62,7 @@ set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, v return s; } -set_t *set_find( set_t **head, char *key ) +set_t *set_find( set_t **head, const char *key ) { set_t *s = *head; @@ -76,7 +76,7 @@ set_t *set_find( set_t **head, char *key ) return s; } -char *set_getstr( set_t **head, char *key ) +char *set_getstr( set_t **head, const char *key ) { set_t *s = set_find( head, key ); @@ -86,7 +86,7 @@ char *set_getstr( set_t **head, char *key ) return s->value ? s->value : s->def; } -int set_getint( set_t **head, char *key ) +int set_getint( set_t **head, const char *key ) { char *s = set_getstr( head, key ); int i = 0; @@ -100,7 +100,7 @@ int set_getint( set_t **head, char *key ) return i; } -int set_getbool( set_t **head, char *key ) +int set_getbool( set_t **head, const char *key ) { char *s = set_getstr( head, key ); @@ -110,7 +110,7 @@ int set_getbool( set_t **head, char *key ) return bool2int( s ); } -int set_setstr( set_t **head, char *key, char *value ) +int set_setstr( set_t **head, const char *key, char *value ) { set_t *s = set_find( head, key ); char *nv = value; @@ -149,7 +149,7 @@ int set_setstr( set_t **head, char *key, char *value ) return 1; } -int set_setint( set_t **head, char *key, int value ) +int set_setint( set_t **head, const char *key, int value ) { char s[24]; /* Not quite 128-bit clean eh? ;-) */ @@ -157,7 +157,7 @@ int set_setint( set_t **head, char *key, int value ) return set_setstr( head, key, s ); } -void set_del( set_t **head, char *key ) +void set_del( set_t **head, const char *key ) { set_t *s = *head, *t = NULL; @@ -181,7 +181,7 @@ void set_del( set_t **head, char *key ) } } -int set_reset( set_t **head, char *key ) +int set_reset( set_t **head, const char *key ) { set_t *s; diff --git a/set.h b/set.h index d20a58d3..42522506 100644 --- a/set.h +++ b/set.h @@ -75,23 +75,23 @@ typedef struct set set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, void *data ); /* Returns the raw set_t. Might be useful sometimes. */ -set_t *set_find( set_t **head, char *key ); +set_t *set_find( set_t **head, const char *key ); /* Returns a pointer to the string value of this setting. Don't modify the returned string, and don't free() it! */ -G_MODULE_EXPORT char *set_getstr( set_t **head, char *key ); +G_MODULE_EXPORT char *set_getstr( set_t **head, const char *key ); /* Get an integer. In previous versions set_getint() was also used to read boolean values, but this SHOULD be done with set_getbool() now! */ -G_MODULE_EXPORT int set_getint( set_t **head, char *key ); -G_MODULE_EXPORT int set_getbool( set_t **head, char *key ); +G_MODULE_EXPORT int set_getint( set_t **head, const char *key ); +G_MODULE_EXPORT int set_getbool( set_t **head, const char *key ); /* set_setstr() strdup()s the given value, so after using this function you can free() it, if you want. */ -int set_setstr( set_t **head, char *key, char *value ); -int set_setint( set_t **head, char *key, int value ); -void set_del( set_t **head, char *key ); -int set_reset( set_t **head, char *key ); +int set_setstr( set_t **head, const char *key, char *value ); +int set_setint( set_t **head, const char *key, int value ); +void set_del( set_t **head, const char *key ); +int set_reset( set_t **head, const char *key ); /* Two very useful generic evaluators. */ char *set_eval_int( set_t *set, char *value ); -- cgit v1.2.3 From ec5e57d6f165a462ac686341f9075243f0a4586a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Oct 2009 01:00:24 +0100 Subject: Support for setting away states. Somewhat hackish but this stuff is hopelessly complicated in libpurple anyway.. --- protocols/purple/purple.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 82978dc4..c78b15e7 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -173,11 +173,34 @@ static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, static GList *purple_away_states( struct im_connection *ic ) { - return NULL; + PurpleAccount *pa = ic->proto_data; + GList *st, *ret = NULL; + + for( st = purple_account_get_status_types( pa ); st; st = st->next ) + { + printf( "%s\n", purple_status_type_get_name( st->data ) ); + ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); + } + + return ret; } static void purple_set_away( struct im_connection *ic, char *state_txt, char *message ) { + PurpleAccount *pa = ic->proto_data; + GList *status_types = purple_account_get_status_types( pa ), *st; + PurpleStatusType *pst = NULL; + + for( st = status_types; st; st = st->next ) + { + pst = st->data; + + if( g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 ) + break; + } + + purple_account_set_status( pa, st ? purple_status_type_get_id( pst ) : "away", + TRUE, "message", message, NULL ); } static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) @@ -354,10 +377,6 @@ static PurpleConversationUiOps bee_conv_uiops = NULL, /* custom_smiley_write */ NULL, /* custom_smiley_close */ NULL, /* send_confirm */ - NULL, - NULL, - NULL, - NULL }; static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) -- cgit v1.2.3 From dd0d57b10a8c2d07001ca2d4228232962ed8b95d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Oct 2009 23:19:41 +0100 Subject: Oops, forgot to drop a printf() of all away states for debugging. --- protocols/purple/purple.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index c78b15e7..0e4a2f01 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -177,10 +177,7 @@ static GList *purple_away_states( struct im_connection *ic ) GList *st, *ret = NULL; for( st = purple_account_get_status_types( pa ); st; st = st->next ) - { - printf( "%s\n", purple_status_type_get_name( st->data ) ); ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); - } return ret; } -- cgit v1.2.3 From e248c7ff061e1582ed4c2919de6d615c1813e87a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Oct 2009 23:23:49 +0100 Subject: Automatically try prpl-$proto if $proto doesn't exist, and disable native protocol modules if purple is enabled; they don't go together very well. --- configure | 25 ++++++++++++++++--------- protocols/nogaim.c | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/configure b/configure index 65eb9e89..a87b3697 100755 --- a/configure +++ b/configure @@ -449,6 +449,22 @@ EOF protocols='' protoobjs='' +if [ "$purple" = 0 ]; then + echo '#undef WITH_PURPLE' >> config.h +else + echo '#define WITH_PURPLE' >> config.h + echo 'EFLAGS += $$(pkg-config purple --libs)' >> Makefile.settings + protocols=$protocols'purple ' + protoobjs=$protoobjs'purple_mod.o ' + + # Having both libpurple and native IM modules in one binary may + # do strange things. Let's not do that. + msn=0 + jabber=0 + oscar=0 + yahoo=0 +fi + if [ "$msn" = 0 ]; then echo '#undef WITH_MSN' >> config.h else @@ -481,15 +497,6 @@ else protoobjs=$protoobjs'yahoo_mod.o ' fi -if [ "$purple" = 0 ]; then - echo '#undef WITH_PURPLE' >> config.h -else - echo '#define WITH_PURPLE' >> config.h - echo 'EFLAGS += $$(pkg-config purple --libs)' >> Makefile.settings - protocols=$protocols'purple ' - protoobjs=$protoobjs'purple_mod.o ' -fi - if [ "$protocols" = "PROTOCOLS = " ]; then echo "Warning: You haven't selected any communication protocol to compile!" echo " BitlBee will run, but you will be unable to connect to IM servers!" diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 8eae178d..7e8782ac 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -101,12 +101,23 @@ void register_protocol (struct prpl *p) struct prpl *find_protocol(const char *name) { GList *gl; - for (gl = protocols; gl; gl = gl->next) + + for( gl = protocols; gl; gl = gl->next ) { struct prpl *proto = gl->data; - if(!g_strcasecmp(proto->name, name)) + + if( g_strcasecmp( proto->name, name ) == 0 ) return proto; + +#ifdef WITH_PURPLE + /* I know, hardcoding is evil, but that doesn't make it + impossible. :-) */ + if( g_strncasecmp( proto->name, "prpl-", 5 ) == 0 && + g_strcasecmp( proto->name + 5, name ) == 0 ) + return proto; +#endif } + return NULL; } -- cgit v1.2.3 From 4524f664357f9f15e7535fc6f251c5be98d162da Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Oct 2009 23:37:28 +0100 Subject: Store real names in /whois. --- protocols/purple/purple.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 0e4a2f01..aff4f3fa 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -297,7 +297,10 @@ static void prplcb_blist_new( PurpleBlistNode *node ) imcb_add_buddy( ic, bud->name, NULL ); if( bud->server_alias ) + { + imcb_rename_buddy( ic, bud->name, bud->server_alias ); imcb_buddy_nick_hint( ic, bud->name, bud->server_alias ); + } } } @@ -314,6 +317,9 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) if( ic == NULL ) return; + if( bud->server_alias ) + imcb_rename_buddy( ic, bud->name, bud->server_alias ); + flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0; flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY; -- cgit v1.2.3 From 6967d012be48db989ce2723a6ecc2b10b537c8f7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Oct 2009 22:36:09 +0100 Subject: I think daemon mode and libpurple won't go together very well for now since libpurple seems to keep track of a merged contact list. For now people shouldn't be trying this combination. --- protocols/purple/purple.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index aff4f3fa..ca2a49f1 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -132,8 +132,16 @@ static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); + static void *irc_check = NULL; PurpleAccount *pa; + if( irc_check != NULL && irc_check != acc->irc ) + { + irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! Please use inetd or ForkDaemon mode instead." ); + return; + } + irc_check = acc->irc; + /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events on dead connections. */ @@ -384,7 +392,7 @@ static PurpleConversationUiOps bee_conv_uiops = static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) { - printf( "DEBUG %s: %s", category, arg_s ); + fprintf( stderr, "DEBUG %s: %s", category, arg_s ); } static PurpleDebugUiOps bee_debug_uiops = -- cgit v1.2.3 From b3117f2524775ff7c61ead7c3bdb3799064ed97f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 23 Nov 2009 22:58:20 +0000 Subject: Adding/removing contacts now works. --- protocols/purple/purple.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index ca2a49f1..6f1e6ae9 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -21,11 +21,11 @@ * * \***************************************************************************/ +#include "bitlbee.h" + #include #include -#include "bitlbee.h" - GSList *purple_connections; static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) @@ -81,9 +81,10 @@ static void purple_init( account_t *acc ) default: fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) ); + name = NULL; } - if( def != NULL ) + if( name != NULL ) { s = set_add( &acc->set, name, def, eval, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; @@ -210,10 +211,23 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) { + PurpleBuddy *pb; + + pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL ); + purple_blist_add_buddy( pb, NULL, NULL, NULL ); + purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb ); } static void purple_remove_buddy( struct im_connection *ic, char *who, char *group ) { + PurpleBuddy *pb; + + pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who ); + if( pb != NULL ) + { + purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL ); + purple_blist_remove_buddy( pb ); + } } static void purple_keepalive( struct im_connection *ic ) @@ -342,6 +356,7 @@ static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; + /* if( node->type == PURPLE_BLIST_BUDDY_NODE ) { struct im_connection *ic = purple_ic_by_pa( bud->account ); @@ -351,6 +366,7 @@ static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) imcb_remove_buddy( ic, bud->name, NULL ); } + */ } static PurpleBlistUiOps bee_blist_uiops = -- cgit v1.2.3 From cd741d8e2bb0b7d08cf36d90f5332a639f190281 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 23 Nov 2009 23:23:37 +0000 Subject: Fixed compatibility with non-libpurple version: oscar is now recognized as a protocol name, and removed prpl- hack from nogaim.c. --- protocols/nogaim.c | 8 -------- protocols/nogaim.h | 1 + protocols/purple/purple.c | 43 ++++++++++++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index c0d4a953..f80653ff 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -110,14 +110,6 @@ struct prpl *find_protocol(const char *name) if( g_strcasecmp( proto->name, name ) == 0 ) return proto; - -#ifdef WITH_PURPLE - /* I know, hardcoding is evil, but that doesn't make it - impossible. :-) */ - if( g_strncasecmp( proto->name, "prpl-", 5 ) == 0 && - g_strcasecmp( proto->name + 5, name ) == 0 ) - return proto; -#endif } return NULL; diff --git a/protocols/nogaim.h b/protocols/nogaim.h index dc6154e2..ae329b91 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -132,6 +132,7 @@ struct prpl { /* You should set this to the name of your protocol. * - The user sees this name ie. when imcb_log() is used. */ const char *name; + void *data; /* Added this one to be able to add per-account settings, don't think * it should be used for anything else. You are supposed to use the diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 6f1e6ae9..a8733f5d 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -148,7 +148,7 @@ static void purple_login( account_t *acc ) on dead connections. */ purple_connections = g_slist_prepend( purple_connections, ic ); - ic->proto_data = pa = purple_account_new( acc->user, acc->prpl->name ); + ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data ); purple_account_set_password( pa, acc->pass ); purple_sync_settings( acc, pa ); @@ -450,6 +450,7 @@ static void purple_ui_init() void purple_initmodule() { + struct prpl funcs; GList *prots; if( B_EV_IO_READ != PURPLE_INPUT_READ || @@ -477,24 +478,36 @@ void purple_initmodule() /* Meh? */ purple_prefs_load(); + memset( &funcs, 0, sizeof( funcs ) ); + funcs.login = purple_login; + funcs.init = purple_init; + funcs.logout = purple_logout; + funcs.buddy_msg = purple_buddy_msg; + funcs.away_states = purple_away_states; + funcs.set_away = purple_set_away; + funcs.add_buddy = purple_add_buddy; + funcs.remove_buddy = purple_remove_buddy; + funcs.keepalive = purple_keepalive; + funcs.send_typing = purple_send_typing; + funcs.handle_cmp = g_strcasecmp; + for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) { - struct prpl *ret = g_new0( struct prpl, 1 ); PurplePlugin *prot = prots->data; + struct prpl *ret; - ret->name = prot->info->id; - ret->login = purple_login; - ret->init = purple_init; - ret->logout = purple_logout; - ret->buddy_msg = purple_buddy_msg; - ret->away_states = purple_away_states; - ret->set_away = purple_set_away; - ret->add_buddy = purple_add_buddy; - ret->remove_buddy = purple_remove_buddy; - ret->keepalive = purple_keepalive; - ret->send_typing = purple_send_typing; - ret->handle_cmp = g_strcasecmp; - + ret = g_memdup( &funcs, sizeof( funcs ) ); + ret->name = ret->data = prot->info->id; + if( strncmp( ret->name, "prpl-", 5 ) == 0 ) + ret->name += 5; register_protocol( ret ); + + if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 ) + { + ret = g_memdup( &funcs, sizeof( funcs ) ); + ret->name = "oscar"; + ret->data = prot->info->id; + register_protocol( ret ); + } } } -- cgit v1.2.3 From 45a19e543cc567981df92b62f428e0f89f94cb74 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 23 Nov 2009 23:38:31 +0000 Subject: Oops, forgot to change one protocol name string pointer.. --- protocols/purple/purple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index a8733f5d..f7b859ff 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -46,7 +46,7 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) static void purple_init( account_t *acc ) { - PurplePlugin *prpl = purple_plugins_find_with_id( acc->prpl->name ); + PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data ); PurplePluginProtocolInfo *pi = prpl->info->extra_info; GList *i; -- cgit v1.2.3 From 0ac1a37573f966d7a03b85816c583bd6976c402f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 25 Nov 2009 00:19:45 +0000 Subject: Added enough code to handle one class of queries (action-based), enough to make the "Please accept this SSL certificate" question work. Need to extend the BitlBee API a bit to *really* support this well though (yes/no is not enough). --- protocols/purple/purple.c | 90 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f7b859ff..33b19a67 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -23,11 +23,18 @@ #include "bitlbee.h" +#include + #include #include GSList *purple_connections; +/* This makes me VERY sad... :-( But some libpurple callbacks come in without + any context so this is the only way to get that. Don't want to support + libpurple in daemon mode anyway. */ +static irc_t *local_irc; + static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { GSList *i; @@ -133,15 +140,15 @@ static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); - static void *irc_check = NULL; PurpleAccount *pa; - if( irc_check != NULL && irc_check != acc->irc ) + if( local_irc != NULL && local_irc != acc->irc ) { - irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! Please use inetd or ForkDaemon mode instead." ); + irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! " + "Please use inetd or ForkDaemon mode instead." ); return; } - irc_check = acc->irc; + local_irc = acc->irc; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events @@ -406,6 +413,80 @@ static PurpleConversationUiOps bee_conv_uiops = NULL, /* send_confirm */ }; +struct prplcb_request_action_data +{ + void *user_data, *bee_data; + PurpleRequestActionCb yes, no; + int yes_i, no_i; +}; + +static void prplcb_request_action_yes( void *data ) +{ + struct prplcb_request_action_data *pqad = data; + + pqad->yes( pqad->user_data, pqad->yes_i ); + g_free( pqad ); +} + +static void prplcb_request_action_no( void *data ) +{ + struct prplcb_request_action_data *pqad = data; + + pqad->no( pqad->user_data, pqad->no_i ); + g_free( pqad ); +} + +static void *prplcb_request_action( const char *title, const char *primary, const char *secondary, + int default_action, PurpleAccount *account, const char *who, + PurpleConversation *conv, void *user_data, size_t action_count, + va_list actions ) +{ + struct prplcb_request_action_data *pqad; + int i; + char *q; + + pqad = g_new0( struct prplcb_request_action_data, 1 ); + + for( i = 0; i < action_count; i ++ ) + { + char *caption; + void *fn; + + caption = va_arg( actions, char* ); + fn = va_arg( actions, void* ); + + if( strcmp( caption, "Accept" ) == 0 ) + { + pqad->yes = fn; + pqad->yes_i = i; + } + else if( strcmp( caption, "Reject" ) == 0 ) + { + pqad->no = fn; + pqad->no_i = i; + } + } + + pqad->user_data = user_data; + + q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary ); + pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q, + prplcb_request_action_yes, prplcb_request_action_no, pqad ); + + g_free( q ); +} + +static PurpleRequestUiOps bee_request_uiops = +{ + NULL, + NULL, + prplcb_request_action, + NULL, + NULL, + NULL, + NULL, +}; + static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) { fprintf( stderr, "DEBUG %s: %s", category, arg_s ); @@ -445,6 +526,7 @@ static void purple_ui_init() purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); + purple_request_set_ui_ops( &bee_request_uiops ); //purple_debug_set_ui_ops( &bee_debug_uiops ); } -- cgit v1.2.3 From e5d8d21fd20516be53f873d269b469be109eca91 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 25 Nov 2009 00:45:27 +0000 Subject: Added in-memory help info, which I wanted to implement for ages already. Sadly the way I'm using it now doesn't work yet since nogaim_init() is called before help_init(). I'll fix that later. (Have to do that anyway to at least make ForkDaemon mode work..) --- help.c | 26 +++++++++++++++++++++++++- help.h | 1 + protocols/purple/purple.c | 11 +++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/help.c b/help.c index 587b9940..c43d0459 100644 --- a/help.c +++ b/help.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2005 Wilmer van der Gaast and others * + * Copyright 2002-2009 Wilmer van der Gaast and others * \********************************************************************/ /* Help file control */ @@ -168,3 +168,27 @@ char *help_get( help_t **help, char *title ) return NULL; } + +int help_add_mem( help_t **help, const char *title, const char *content ) +{ + help_t *h, *l = NULL; + + for( h = *help; h; h = h->next ) + { + if( g_strcasecmp( h->title, title ) == 0 ) + return 0; + + l = h; + } + + if( l ) + h = l->next = g_new0( struct help, 1 ); + else + *help = h = g_new0( struct help, 1 ); + h->fd = -1; + h->title = g_strdup( title ); + h->length = strlen( content ); + h->offset.mem_offset = g_strdup( content ); + + return 1; +} diff --git a/help.h b/help.h index 5421220c..e689a93d 100644 --- a/help.h +++ b/help.h @@ -45,5 +45,6 @@ typedef struct help G_GNUC_MALLOC help_t *help_init( help_t **help, const char *helpfile ); void help_free( help_t **help ); char *help_get( help_t **help, char *title ); +int help_add_mem( help_t **help, const char *title, const char *content_ ); #endif diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 33b19a67..50770187 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -22,6 +22,7 @@ \***************************************************************************/ #include "bitlbee.h" +#include "help.h" #include @@ -474,6 +475,8 @@ static void *prplcb_request_action( const char *title, const char *primary, cons prplcb_request_action_yes, prplcb_request_action_no, pqad ); g_free( q ); + + return pqad; } static PurpleRequestUiOps bee_request_uiops = @@ -534,6 +537,7 @@ void purple_initmodule() { struct prpl funcs; GList *prots; + GString *help; if( B_EV_IO_READ != PURPLE_INPUT_READ || B_EV_IO_WRITE != PURPLE_INPUT_WRITE ) @@ -573,6 +577,8 @@ void purple_initmodule() funcs.send_typing = purple_send_typing; funcs.handle_cmp = g_strcasecmp; + help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); + for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) { PurplePlugin *prot = prots->data; @@ -584,6 +590,8 @@ void purple_initmodule() ret->name += 5; register_protocol( ret ); + g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name ); + if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 ) { ret = g_memdup( &funcs, sizeof( funcs ) ); @@ -592,4 +600,7 @@ void purple_initmodule() register_protocol( ret ); } } + + help_add_mem( &global.help, "purple", help->str ); + g_string_free( help, TRUE ); } -- cgit v1.2.3 From 487f555c687571cee39d69b3954b4d1f82811d29 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 26 Nov 2009 00:04:40 +0000 Subject: Support for sending zomg-im-typing notifications. --- protocols/purple/purple.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 50770187..6bb8fc99 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -242,9 +242,26 @@ static void purple_keepalive( struct im_connection *ic ) { } -static int purple_send_typing( struct im_connection *ic, char *who, int typing ) +static int purple_send_typing( struct im_connection *ic, char *who, int flags ) { - return 1; + PurpleTypingState state = PURPLE_NOT_TYPING; + PurpleConversation *conv; + + if( flags & OPT_TYPING ) + state = PURPLE_TYPING; + else if( flags & OPT_THINKING ) + state = PURPLE_TYPED; + + if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM, + who, ic->proto_data ) ) == NULL ) + { + purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state ); + return 1; + } + else + { + return 0; + } } static void purple_ui_init(); -- cgit v1.2.3 From 3e7b640db6ae2d77122d93dcf5f1a0989ef0b3f1 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 28 Nov 2009 00:47:20 +0000 Subject: Look up a buddy in the contact list on incoming msgs. This seems to be the best way to "normalize" handles (i.e. chopping off the resource part of JIDs). --- protocols/purple/purple.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 6bb8fc99..f28e5cf0 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -379,9 +379,9 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { + /* PurpleBuddy *bud = (PurpleBuddy*) node; - /* if( node->type == PURPLE_BLIST_BUDDY_NODE ) { struct im_connection *ic = purple_ic_by_pa( bud->account ); @@ -406,10 +406,17 @@ static PurpleBlistUiOps bee_blist_uiops = static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) { struct im_connection *ic = purple_ic_by_pa( conv->account ); + PurpleBuddy *buddy; /* ..._SEND means it's an outgoing message, no need to echo those. */ - if( !( flags & PURPLE_MESSAGE_SEND ) ) - imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); + if( flags & PURPLE_MESSAGE_SEND ) + return; + + buddy = purple_find_buddy( conv->account, who ); + if( buddy != NULL ) + who = purple_buddy_get_contact_alias( buddy ); + + imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); } static PurpleConversationUiOps bee_conv_uiops = -- cgit v1.2.3 From 5674207b09a856149c110040f7f672182a04dee5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 28 Nov 2009 19:51:39 +0000 Subject: Reshuffled initialization sequence a little bit. Most important change: nogaim_init() should be done after fork() to make ForkDaemon mode work again. Also, doing help_init() earlie makes "help purple" work. --- irc.c | 2 ++ unix.c | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/irc.c b/irc.c index ea0f7db7..9a5c96d8 100644 --- a/irc.c +++ b/irc.c @@ -171,6 +171,8 @@ irc_t *irc_new( int fd ) /* Evaluator sets the iconv/oconv structures. */ set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) ); + nogaim_init(); + return( irc ); } diff --git a/unix.c b/unix.c index 56b0ab46..18b2f83f 100644 --- a/unix.c +++ b/unix.c @@ -46,16 +46,26 @@ int main( int argc, char *argv[] ) struct sigaction sig, old; log_init(); + global.conf_file = g_strdup( CONF_FILE_DEF ); global.conf = conf_load( argc, argv ); if( global.conf == NULL ) return( 1 ); b_main_init(); - nogaim_init(); srand( time( NULL ) ^ getpid() ); + global.helpfile = g_strdup( HELP_FILE ); + if( help_init( &global.help, global.helpfile ) == NULL ) + log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); + + global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); + if( global.storage == NULL ) + { + log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); + return( 1 ); + } if( global.conf->runmode == RUNMODE_INETD ) { @@ -104,13 +114,6 @@ int main( int argc, char *argv[] ) setuid( pw->pw_uid ); } } - - global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); - if( global.storage == NULL ) - { - log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); - return( 1 ); - } /* Catch some signals to tell the user what's happening before quitting */ memset( &sig, 0, sizeof( sig ) ); @@ -129,8 +132,6 @@ int main( int argc, char *argv[] ) if( !getuid() || !geteuid() ) log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); - if( help_init( &global.help, global.helpfile ) == NULL ) - log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); b_main_run(); -- cgit v1.2.3 From 1c3008ac0b2b29f7e14ec9b874af3277c511c7a4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 13 Dec 2009 14:48:56 +0000 Subject: No functional changes, just some style "fixes". Although I admit I'm somewhat growing out of my own coding style, I do try to keep things consistent at least within files. Comments are now in reviewboard: http://code.bitlbee.org/rb/r/13/ --- dcc.c | 32 ++++++++++++++++++-------------- protocols/jabber/Makefile | 2 +- protocols/jabber/iq.c | 38 +++++++++++++++++++++++--------------- protocols/jabber/jabber.c | 4 ++-- protocols/jabber/jabber.h | 2 +- protocols/msn/Makefile | 2 +- protocols/msn/msn.c | 2 +- protocols/msn/sb.c | 6 ++++-- 8 files changed, 51 insertions(+), 37 deletions(-) diff --git a/dcc.c b/dcc.c index b6345a92..655c98b9 100644 --- a/dcc.c +++ b/dcc.c @@ -114,7 +114,8 @@ void imcb_file_finished( file_transfer_t *file ) dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic ) { file_transfer_t *file = g_new0( file_transfer_t, 1 ); - dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1); + dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1 ); + file->file_size = file_size; file->file_name = g_strdup( file_name ); file->local_id = local_transfer_id++; @@ -143,7 +144,8 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha /* listen and request */ - if( ( df->fd = ft_listen( &saddr, host, port, TRUE, &errmsg ) ) == -1 ) { + if( ( df->fd = ft_listen( &saddr, host, port, TRUE, &errmsg ) ) == -1 ) + { dcc_abort( df, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg ); return NULL; } @@ -160,9 +162,10 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); - imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", user_nick, file_name, file_size/1024 ); - - imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); + imcb_log( ic, "File transfer request from %s for %s (%zd kb).\n" + "Accept the file transfer if you'd like the file. If you don't, " + "issue the 'transfers reject' command.", + user_nick, file_name, file_size / 1024 ); return file; } @@ -198,7 +201,7 @@ gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ) { /* no progress. cancel */ if( df->bytes_sent == 0 ) - return dcc_abort( df, "Couldnt establish transfer within %d seconds", DCC_MAX_STALL ); + return dcc_abort( df, "Couldn't establish transfer within %d seconds", DCC_MAX_STALL ); else return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->bytes_sent / 1024 ); @@ -229,7 +232,8 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc sprintf( ipaddr, "%d", ntohl( saddr_ipv4->sin_addr.s_addr ) ); port = saddr_ipv4->sin_port; - } else + } + else { struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr; @@ -249,7 +253,7 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc df->ft->file_name, ipaddr, port, df->ft->file_size ); if ( !irc_msgfrom( df->ic->irc, user_nick, cmd ) ) - return dcc_abort( df, "couldn't send 'DCC SEND' message to %s", user_nick ); + return dcc_abort( df, "Couldn't send `DCC SEND' message to %s.", user_nick ); g_free( cmd ); @@ -312,7 +316,7 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) file_transfer_t *file = df->ft; short revents; - if( !dcc_poll( df, fd, &revents) ) + if( !dcc_poll( df, fd, &revents ) ) return FALSE; if( ( revents & POLLIN ) && @@ -348,18 +352,18 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) int bytes_received; int ret; - ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), MSG_PEEK ), "Receiving" ); + ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), MSG_PEEK ), "Receiving" ); if( ret == 0 ) return dcc_abort( df, "Remote end closed connection" ); if( ret < 4 ) { - imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only 2 bytes instead of 4, shouldn't happen too often!" ); + imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only %d bytes instead of 4, shouldn't happen too often!", ret ); return TRUE; } - ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), 0 ), "Receiving" ); + ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), 0 ), "Receiving" ); if( ret != 4 ) return dcc_abort( df, "MSG_PEEK'ed 4, but can only dequeue %d bytes", ret ); @@ -399,7 +403,7 @@ gboolean dccs_recv_start( file_transfer_t *ft ) if( !ft->write ) return dcc_abort( df, "BUG: protocol didn't register write()" ); - ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" ); + ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening Socket" ); sock_make_nonblocking( fd ); @@ -428,7 +432,7 @@ gboolean dccs_recv_start( file_transfer_t *ft ) return TRUE; } -gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) +gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) { dcc_file_transfer_t *df = data; file_transfer_t *ft = df->ft; diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index 891df5eb..78a02696 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o si.o s5bytestream.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index f17a7bb5..e0658a9e 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -98,9 +98,9 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) XMLNS_CHATSTATES, XMLNS_MUC, XMLNS_PING, - XMLNS_SI, - XMLNS_BYTESTREAMS, - XMLNS_FILETRANSFER, + XMLNS_SI, + XMLNS_BYTESTREAMS, + XMLNS_FILETRANSFER, NULL }; const char **f; @@ -126,16 +126,18 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) } else if( strcmp( type, "set" ) == 0 ) { - if( ( c = xt_find_node( node->children, "si" ) ) && - ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) ) + if( ( c = xt_find_node( node->children, "si" ) ) && + ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) ) { return jabber_si_handle_request( ic, node, c ); - } else if( !( c = xt_find_node( node->children, "query" ) ) || - !( s = xt_find_attr( c, "xmlns" ) ) ) + } + else if( !( c = xt_find_node( node->children, "query" ) ) || + !( s = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "Warning: Received incomplete IQ-%s packet", type ); return XT_HANDLED; - } else if( strcmp( s, XMLNS_ROSTER ) == 0 ) + } + else if( strcmp( s, XMLNS_ROSTER ) == 0 ) { /* This is a roster push. XMPP servers send this when someone was added to (or removed from) the buddy list. AFAIK they're @@ -159,11 +161,13 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL ); pack = 0; } - } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) + } + else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) { - /* Bytestream Request (stage 2 of file transfer) */ + /* Bytestream Request (stage 2 of file transfer) */ return jabber_bs_recv_request( ic, node, c ); - } else + } + else { xt_free_node( reply ); reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL ); @@ -654,9 +658,10 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no } c = c->children; - while( ( c = xt_find_node( c, "feature" ) ) ) { + while( ( c = xt_find_node( c, "feature" ) ) ) + { feature = xt_find_attr( c, "var" ); - bud->features = g_slist_append(bud->features, g_strdup(feature) ); + bud->features = g_slist_append( bud->features, g_strdup( feature ) ); c = c->next; } @@ -718,7 +723,8 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n c = c->next; } - } else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) + } + else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) { char *category, *type; @@ -736,7 +742,8 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n c = c->next; } - } else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_BYTESTREAMS ) == 0 ) + } + else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_BYTESTREAMS ) == 0 ) { char *host, *jid; int port; @@ -760,5 +767,6 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n if( jd->have_streamhosts == 0 ) jd->have_streamhosts++; + return XT_HANDLED; } diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 15341c9b..02461101 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -62,6 +62,8 @@ static void jabber_init( account_t *acc ) s->flags |= ACC_SET_OFFLINE_ONLY; s = set_add( &acc->set, "priority", "0", set_eval_priority, acc ); + + s = set_add( &acc->set, "proxy", ";", NULL, acc ); s = set_add( &acc->set, "resource", "BitlBee", NULL, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; @@ -79,8 +81,6 @@ static void jabber_init( account_t *acc ) s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; - - s = set_add( &acc->set, "proxy", ";", NULL, acc ); } static void jabber_generate_id_hash( struct jabber_data *jd ); diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 9f101f83..898c4978 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -59,7 +59,7 @@ typedef enum have a real JID. */ } jabber_buddy_flags_t; -/* Stores a streamhost's(a.k.a. proxy) data */ +/* Stores a streamhost's (a.k.a. proxy) data */ typedef struct { char *jid; diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index dd5d46e2..5d199b9e 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = msn.o msn_util.o ns.o passport.o sb.o tables.o invitation.o +objects = invitation.o msn.o msn_util.o ns.o passport.o sb.o tables.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 590a1382..b17a0bfa 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -77,7 +77,7 @@ static void msn_logout( struct im_connection *ic ) if( md ) { while( md->filetransfers ) { - imcb_file_canceled( md->filetransfers->data, "Closing msn connection" ); + imcb_file_canceled( md->filetransfers->data, "Closing connection" ); } if( md->fd >= 0 ) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index b60a9a8b..c3302e57 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -173,11 +173,13 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user ); buf = g_new0( char, i ); i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user ); - } else if( strncmp( text, MSN_INVITE_HEADERS, sizeof( MSN_INVITE_HEADERS ) - 1 ) == 0 ) + } + else if( strncmp( text, MSN_INVITE_HEADERS, sizeof( MSN_INVITE_HEADERS ) - 1 ) == 0 ) { buf = g_strdup( text ); i = strlen( buf ); - } else + } + else { buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 + 1 ); i = strlen( MSN_MESSAGE_HEADERS ); -- cgit v1.2.3 From a19ea7a21e082d0e28aea7198bea0f3bd3e2eb4f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 26 Dec 2009 16:14:52 +0100 Subject: Use purple_buddy_get_name, not purple_buddy_get_contact_alias. Makes sense, but I'm actually not sure if this function *is* the right one. Fixes issues with messages coming from the wrong handle. --- protocols/purple/purple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f28e5cf0..ce7f0e56 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -414,7 +414,7 @@ static void prplcb_conv_im( PurpleConversation *conv, const char *who, const cha buddy = purple_find_buddy( conv->account, who ); if( buddy != NULL ) - who = purple_buddy_get_contact_alias( buddy ); + who = purple_buddy_get_name( buddy ); imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); } -- cgit v1.2.3 From e08e53c9398700309000c6e6b7ff895185d567a9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 4 Jan 2010 12:16:18 +0000 Subject: Fixed build system: Run pkg-config at configure-time instead of just failing mysteriously at build time if libpurple-dev is missing. --- configure | 10 +++++++++- protocols/purple/Makefile | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/configure b/configure index a87b3697..b7f9a464 100755 --- a/configure +++ b/configure @@ -452,8 +452,16 @@ protoobjs='' if [ "$purple" = 0 ]; then echo '#undef WITH_PURPLE' >> config.h else + if ! $PKG_CONFIG purple; then + echo + echo 'Cannot find libpurple development libraries, aborting. (Install libpurple-dev?)' + exit 1 + fi echo '#define WITH_PURPLE' >> config.h - echo 'EFLAGS += $$(pkg-config purple --libs)' >> Makefile.settings + cat<>Makefile.settings +EFLAGS += $($PKG_CONFIG purple --libs) +PURPLE_CFLAGS += $($PKG_CONFIG purple --cflags) +EOF protocols=$protocols'purple ' protoobjs=$protoobjs'purple_mod.o ' diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile index bdefbd5f..15460529 100644 --- a/protocols/purple/Makefile +++ b/protocols/purple/Makefile @@ -11,7 +11,7 @@ # [SH] Program variables objects = purple.o -CFLAGS += -Wall $$(pkg-config purple --cflags) +CFLAGS += -Wall $(PURPLE_CFLAGS) LFLAGS += -r # [SH] Phony targets -- cgit v1.2.3 From 279607e52513bba31b7dfdd508d51e9ac24950c2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 7 Mar 2010 22:35:00 +0000 Subject: Fixed purple module to work with the new away interface. --- protocols/purple/purple.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index ce7f0e56..0873b6f5 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -194,7 +194,11 @@ static GList *purple_away_states( struct im_connection *ic ) GList *st, *ret = NULL; for( st = purple_account_get_status_types( pa ); st; st = st->next ) - ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); + { + PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data ); + if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE ) + ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); + } return ret; } @@ -204,17 +208,31 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me PurpleAccount *pa = ic->proto_data; GList *status_types = purple_account_get_status_types( pa ), *st; PurpleStatusType *pst = NULL; + GList *args = NULL; for( st = status_types; st; st = st->next ) { pst = st->data; - if( g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 ) + if( state_txt == NULL && + purple_status_type_get_primitive( st->data ) == PURPLE_STATUS_AVAILABLE ) + break; + + if( state_txt != NULL && + g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 ) break; } - purple_account_set_status( pa, st ? purple_status_type_get_id( pst ) : "away", - TRUE, "message", message, NULL ); + if( message ) + { + args = g_list_append( args, "message" ); + args = g_list_append( args, message ); + } + + purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away", + TRUE, args ); + + g_list_free( args ); } static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) -- cgit v1.2.3 From 52cae01137057f2cc3def802a661fef92cedbcae Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 7 Mar 2010 23:08:40 +0000 Subject: Set the ACC_FLAG_*_MESSAGE flags correctly depending on the prpl. --- protocols/purple/purple.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 0873b6f5..6a9472dc 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -3,7 +3,7 @@ * BitlBee - An IRC to IM gateway * * libpurple module - Main file * * * -* Copyright 2009 Wilmer van der Gaast * +* Copyright 2010 Wilmer van der Gaast * * * * 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 * @@ -56,8 +56,10 @@ static void purple_init( account_t *acc ) { PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data ); PurplePluginProtocolInfo *pi = prpl->info->extra_info; - GList *i; + PurpleAccount *pa; + GList *i, *st; + /* Convert all protocol_options into per-account setting variables. */ for( i = pi->protocol_options; i; i = i->next ) { PurpleAccountOption *o = i->data; @@ -99,6 +101,26 @@ static void purple_init( account_t *acc ) g_free( def ); } } + + /* Go through all away states to figure out if away/status messages + are possible. */ + pa = purple_account_new( acc->user, (char*) acc->prpl->data ); + for( st = purple_account_get_status_types( pa ); st; st = st->next ) + { + PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data ); + + if( prim == PURPLE_STATUS_AVAILABLE ) + { + if( purple_status_type_get_attr( st->data, "message" ) ) + acc->flags |= ACC_FLAG_STATUS_MESSAGE; + } + else if( prim != PURPLE_STATUS_OFFLINE ) + { + if( purple_status_type_get_attr( st->data, "message" ) ) + acc->flags |= ACC_FLAG_AWAY_MESSAGE; + } + } + purple_accounts_remove( pa ); } static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) @@ -223,7 +245,7 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me break; } - if( message ) + if( message && purple_status_type_get_attr( st, "message" ) ) { args = g_list_append( args, "message" ); args = g_list_append( args, message ); -- cgit v1.2.3 From bab1c86e9e07553f58ab4ebdaad7f74018052b5e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 8 Mar 2010 01:21:08 +0000 Subject: Mail notifications, partially from http://irc.nfx.cz/patches/notify.patch written by sd@ircnet. --- protocols/purple/purple.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 6a9472dc..3c1f505c 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -3,7 +3,7 @@ * BitlBee - An IRC to IM gateway * * libpurple module - Main file * * * -* Copyright 2010 Wilmer van der Gaast * +* Copyright 2009-2010 Wilmer van der Gaast * * * * 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 * @@ -58,6 +58,7 @@ static void purple_init( account_t *acc ) PurplePluginProtocolInfo *pi = prpl->info->extra_info; PurpleAccount *pa; GList *i, *st; + set_t *s; /* Convert all protocol_options into per-account setting variables. */ for( i = pi->protocol_options; i; i = i->next ) @@ -66,7 +67,6 @@ static void purple_init( account_t *acc ) const char *name; char *def = NULL; set_eval eval = NULL; - set_t *s; name = purple_account_option_get_setting( o ); @@ -102,6 +102,12 @@ static void purple_init( account_t *acc ) } } + if( pi->options & OPT_PROTO_MAIL_CHECK ) + { + s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; + } + /* Go through all away states to figure out if away/status messages are possible. */ pa = purple_account_new( acc->user, (char*) acc->prpl->data ); @@ -158,6 +164,9 @@ static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) break; } } + + if( pi->options & OPT_PROTO_MAIL_CHECK ) + purple_account_set_check_mail( pa, set_getbool( &acc->set, "mail_notifications" ) ); } static void purple_login( account_t *acc ) @@ -237,7 +246,7 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me pst = st->data; if( state_txt == NULL && - purple_status_type_get_primitive( st->data ) == PURPLE_STATUS_AVAILABLE ) + purple_status_type_get_primitive( pst ) == PURPLE_STATUS_AVAILABLE ) break; if( state_txt != NULL && @@ -245,7 +254,7 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me break; } - if( message && purple_status_type_get_attr( st, "message" ) ) + if( message && purple_status_type_get_attr( pst, "message" ) ) { args = g_list_append( args, "message" ); args = g_list_append( args, message ); @@ -588,12 +597,29 @@ static PurpleEventLoopUiOps glib_eventloops = prplcb_ev_remove, }; +static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from, + const char *to, const char *url ) +{ + struct im_connection *ic = purple_ic_by_gc( gc ); + + imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url ); + + return NULL; +} + +static PurpleNotifyUiOps bee_notify_uiops = +{ + NULL, + prplcb_notify_email, +}; + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); purple_request_set_ui_ops( &bee_request_uiops ); + purple_notify_set_ui_ops(&bee_notify_uiops); //purple_debug_set_ui_ops( &bee_debug_uiops ); } @@ -643,6 +669,8 @@ void purple_initmodule() help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); + /* Add a protocol entry to BitlBee's structures for every protocol + supported by this libpurple instance. */ for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) { PurplePlugin *prot = prots->data; @@ -656,6 +684,8 @@ void purple_initmodule() g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name ); + /* libpurple doesn't define a protocol called OSCAR, but we + need it to be compatible with normal BitlBee. */ if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 ) { ret = g_memdup( &funcs, sizeof( funcs ) ); @@ -665,6 +695,8 @@ void purple_initmodule() } } + /* Add a simple dynamically-generated help item listing all + the supported protocols. */ help_add_mem( &global.help, "purple", help->str ); g_string_free( help, TRUE ); } -- cgit v1.2.3 From 56244c0a88b603a683da9c0cc6abfccdc7616265 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 12 Mar 2010 00:38:40 +0000 Subject: Added set_eval_list which I will need for the Purple module now to make QQ (and others) work. --- set.c | 15 +++++++++++++++ set.h | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/set.c b/set.c index 7abae37a..baf85261 100644 --- a/set.c +++ b/set.c @@ -212,6 +212,21 @@ char *set_eval_bool( set_t *set, char *value ) return is_bool( value ) ? value : SET_INVALID; } +char *set_eval_list( set_t *set, char *value ) +{ + GSList *options = set->eval_data, *opt; + + for( opt = options; opt; opt = opt->next ) + if( strcmp( value, opt->data ) == 0 ) + return value; + + /* TODO: It'd be nice to show the user a list of allowed values, + but we don't have enough context here to do that. May + want to fix that. */ + + return NULL; +} + char *set_eval_to_char( set_t *set, char *value ) { char *s = g_new( char, 3 ); diff --git a/set.h b/set.h index 42522506..7ff1f985 100644 --- a/set.h +++ b/set.h @@ -68,6 +68,7 @@ typedef struct set the passed value variable. When returning a corrected value, set_setstr() should be able to free() the returned string! */ set_eval eval; + void *eval_data; struct set *next; } set_t; @@ -97,6 +98,9 @@ int set_reset( set_t **head, const char *key ); char *set_eval_int( set_t *set, char *value ); char *set_eval_bool( set_t *set, char *value ); +/* Another more complicated one. */ +char *set_eval_list( set_t *set, char *value ); + /* Some not very generic evaluators that really shouldn't be here... */ char *set_eval_to_char( set_t *set, char *value ); char *set_eval_ops( set_t *set, char *value ); -- cgit v1.2.3 From 4dc6b8d10786baafd3ead9a2ecb22d7065b9c4b9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 12 Mar 2010 01:05:21 +0000 Subject: Added support for PURPLE_PREF_STRING_LIST style settings, this makes the QQ module (and maybe others) work. --- protocols/purple/purple.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 3c1f505c..9a6556b0 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -67,6 +67,9 @@ static void purple_init( account_t *acc ) const char *name; char *def = NULL; set_eval eval = NULL; + void *eval_data = NULL; + GList *io = NULL; + GSList *opts = NULL; name = purple_account_option_get_setting( o ); @@ -89,8 +92,20 @@ static void purple_init( account_t *acc ) eval = set_eval_bool; break; + case PURPLE_PREF_STRING_LIST: + def = g_strdup( purple_account_option_get_default_list_value( o ) ); + for( io = purple_account_option_get_list( o ); io; io = io->next ) + { + PurpleKeyValuePair *kv = io->data; + opts = g_slist_append( opts, kv->key ); + } + eval = set_eval_list; + eval_data = opts; + break; + default: - fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) ); + irc_usermsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n", + name, purple_account_option_get_type( o ) ); name = NULL; } @@ -98,6 +113,7 @@ static void purple_init( account_t *acc ) { s = set_add( &acc->set, name, def, eval, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; + s->eval_data = eval_data; g_free( def ); } } @@ -149,6 +165,7 @@ static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) switch( purple_account_option_get_type( o ) ) { case PURPLE_PREF_STRING: + case PURPLE_PREF_STRING_LIST: purple_account_set_string( pa, name, set_getstr( &acc->set, name ) ); break; -- cgit v1.2.3 From 7c5affcabd08f23e36719afefe736f266b80912b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 12 Mar 2010 01:47:44 +0000 Subject: Add some simple information about available settings to the online help command. --- protocols/purple/purple.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 9a6556b0..b336b108 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -59,6 +59,12 @@ static void purple_init( account_t *acc ) PurpleAccount *pa; GList *i, *st; set_t *s; + char help_title[64]; + GString *help; + + help = g_string_new( "" ); + g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:", + (char*) acc->prpl->name, prpl->info->name ); /* Convert all protocol_options into per-account setting variables. */ for( i = pi->protocol_options; i; i = i->next ) @@ -77,11 +83,21 @@ static void purple_init( account_t *acc ) { case PURPLE_PREF_STRING: def = g_strdup( purple_account_option_get_default_string( o ) ); + + g_string_append_printf( help, "\n* %s (%s), %s, default: %s", + name, purple_account_option_get_text( o ), + "string", def ); + break; case PURPLE_PREF_INT: def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) ); eval = set_eval_int; + + g_string_append_printf( help, "\n* %s (%s), %s, default: %s", + name, purple_account_option_get_text( o ), + "integer", def ); + break; case PURPLE_PREF_BOOLEAN: @@ -90,17 +106,31 @@ static void purple_init( account_t *acc ) else def = g_strdup( "false" ); eval = set_eval_bool; + + g_string_append_printf( help, "\n* %s (%s), %s, default: %s", + name, purple_account_option_get_text( o ), + "boolean", def ); + break; case PURPLE_PREF_STRING_LIST: def = g_strdup( purple_account_option_get_default_list_value( o ) ); + + g_string_append_printf( help, "\n* %s (%s), %s, default: %s", + name, purple_account_option_get_text( o ), + "list", def ); + g_string_append( help, "\n Possible values: " ); + for( io = purple_account_option_get_list( o ); io; io = io->next ) { PurpleKeyValuePair *kv = io->data; opts = g_slist_append( opts, kv->key ); + g_string_append_printf( help, "%s, ", kv->key ); } + g_string_truncate( help, help->len - 2 ); eval = set_eval_list; eval_data = opts; + break; default: @@ -118,6 +148,10 @@ static void purple_init( account_t *acc ) } } + g_snprintf( help_title, sizeof( help_title ), "purple %s", (char*) acc->prpl->name ); + help_add_mem( &global.help, help_title, help->str ); + g_string_free( help, TRUE ); + if( pi->options & OPT_PROTO_MAIL_CHECK ) { s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); @@ -712,6 +746,9 @@ void purple_initmodule() } } + g_string_append( help, "\n\nFor used protocols, more information about available " + "settings can be found using \x02help purple \x02" ); + /* Add a simple dynamically-generated help item listing all the supported protocols. */ help_add_mem( &global.help, "purple", help->str ); -- cgit v1.2.3 From 60e4df367e5c3af0eb1aada19f9c39ef7079e8e6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Mar 2010 23:23:27 +0000 Subject: Small cleanup. The max_packet_size variable doesn't seem to be read anywhere, and reworked string handling in ft_listen() a little bit. --- dcc.c | 27 +-------------- lib/ftutil.c | 77 +++++++++++++++++++++-------------------- lib/ftutil.h | 12 +++++++ protocols/jabber/s5bytestream.c | 2 +- protocols/msn/invitation.c | 2 +- 5 files changed, 55 insertions(+), 65 deletions(-) diff --git a/dcc.c b/dcc.c index 655c98b9..d28c3b34 100644 --- a/dcc.c +++ b/dcc.c @@ -61,8 +61,6 @@ unsigned int local_transfer_id=1; */ unsigned int receivedchunks=0, receiveddata=0; -int max_packet_size = 0; - static void dcc_finish( file_transfer_t *file ); static void dcc_close( file_transfer_t *file ); gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); @@ -132,7 +130,7 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha dcc_file_transfer_t *df; struct sockaddr_storage saddr; char *errmsg; - char host[INET6_ADDRSTRLEN]; + char host[HOST_NAME_MAX]; char port[6]; if( file_size > global.conf->ft_max_size ) @@ -288,24 +286,6 @@ gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents ) return TRUE; } -/* - * fills max_packet_size with twice the TCP maximum segment size - */ -gboolean dcc_check_maxseg( dcc_file_transfer_t *df, int fd ) -{ - /* - * use twice the maximum segment size as a maximum for calls to send(). - */ - if( max_packet_size == 0 ) - { - unsigned int mpslen = sizeof( max_packet_size ); - if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) ) - return dcc_abort( df, "getsockopt() failed" ); - max_packet_size *= 2; - } - return TRUE; -} - /* * After setup, the transfer itself is handled entirely by this function. * There are basically four things to handle: connect, receive, send, and error. @@ -334,9 +314,6 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) file->status = FT_STATUS_TRANSFERRING; sock_make_nonblocking( fd ); - if ( !dcc_check_maxseg( df, fd ) ) - return FALSE; - /* IM protocol callback */ if( file->accept ) file->accept( file ); @@ -445,8 +422,6 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) ( ft->status & FT_STATUS_CONNECTING ) ) { ft->status = FT_STATUS_TRANSFERRING; - if ( !dcc_check_maxseg( df, fd ) ) - return FALSE; //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); diff --git a/lib/ftutil.c b/lib/ftutil.c index 169a6480..d59dd4e0 100644 --- a/lib/ftutil.c +++ b/lib/ftutil.c @@ -27,63 +27,63 @@ #include #include "lib/ftutil.h" -/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ -#ifndef HOST_NAME_MAX -#include -#ifdef MAXHOSTNAMELEN -#define HOST_NAME_MAX MAXHOSTNAMELEN -#else -#define HOST_NAME_MAX 255 -#endif -#endif - #define ASSERTSOCKOP(op, msg) \ if( (op) == -1 ) {\ - sprintf( errmsg , msg ": %s", strerror( errno ) ); \ + g_snprintf( errmsg, sizeof( errmsg ), msg ": %s", strerror( errno ) ); \ return -1; } /* * Creates a listening socket and returns it in saddr_ptr. */ -int ft_listen( struct sockaddr_storage *saddr_ptr, char *hostp, char *port, int for_bitlbee_client, char **errptr ) +int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int for_bitlbee_client, char **errptr ) { - int fd,gret,saddrlen; + int fd, gret, saddrlen; struct addrinfo hints, *rp; socklen_t ssize = sizeof( struct sockaddr_storage ); struct sockaddr_storage saddrs, *saddr = &saddrs; static char errmsg[1024]; - char host[ HOST_NAME_MAX + 1 ]; char *ftlisten = global.conf->ft_listen; - *errptr = errmsg; + if( errptr ) + *errptr = errmsg; - sprintf( port, "0" ); + strcpy( port, "0" ); /* Format is [:];[:] where * A is for connections with the bitlbee client (DCC) * and B is for connections with IM peers. */ - if( ftlisten ) { + if( ftlisten ) + { char *scolon = strchr( ftlisten, ';' ); char *colon; - if( scolon ) { - if( for_bitlbee_client ) { + if( scolon ) + { + if( for_bitlbee_client ) + { *scolon = '\0'; - sprintf( host, ftlisten ); + strncpy( host, ftlisten, HOST_NAME_MAX ); *scolon = ';'; - } else { - sprintf( host, scolon + 1 ); } - } else { - sprintf( host, ftlisten ); + else + { + strncpy( host, scolon + 1, HOST_NAME_MAX ); + } + } + else + { + strncpy( host, ftlisten, HOST_NAME_MAX ); } - if( ( colon = strchr( host, ':' ) ) ) { + if( ( colon = strchr( host, ':' ) ) ) + { *colon = '\0'; - sprintf( port, colon + 1 ); + strncpy( port, colon + 1, 5 ); } - } else { + } + else + { ASSERTSOCKOP( gethostname( host, HOST_NAME_MAX + 1 ), "gethostname()" ); } @@ -91,7 +91,8 @@ int ft_listen( struct sockaddr_storage *saddr_ptr, char *hostp, char *port, int hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; - if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) != 0 ) { + if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) != 0 ) + { sprintf( errmsg, "getaddrinfo() failed: %s", gai_strerror( gret ) ); return -1; } @@ -103,29 +104,31 @@ int ft_listen( struct sockaddr_storage *saddr_ptr, char *hostp, char *port, int freeaddrinfo( rp ); ASSERTSOCKOP( fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); - ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, saddrlen ), "Binding socket" ); - ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? - ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr - , host, INET6_ADDRSTRLEN ) ) { - sprintf( errmsg, "inet_ntop failed on listening socket" ); + ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : + ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr, + host, HOST_NAME_MAX ) ) + { + strcpy( errmsg, "inet_ntop failed on listening socket" ); return -1; } ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); if( saddr->ss_family == AF_INET ) - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); + g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in *) saddr )->sin_port ) ); else - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); + g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in6 *) saddr )->sin6_port ) ); if( saddr_ptr ) memcpy( saddr_ptr, saddr, saddrlen ); - strcpy( hostp, host ); - + /* I hate static-length strings.. */ + host[HOST_NAME_MAX] = '\0'; + port[5] = '\0'; + return fd; } diff --git a/lib/ftutil.h b/lib/ftutil.h index 636fcbd0..c4a5b02b 100644 --- a/lib/ftutil.h +++ b/lib/ftutil.h @@ -25,4 +25,16 @@ #define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ #endif +/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + +/* This function should be used with care. host should be AT LEAST a + char[HOST_NAME_MAX+1] and port AT LEAST a char[6]. */ int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int for_bitlbee_client, char **errptr ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 3c5ce503..c292e61e 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -889,7 +889,7 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) char *proxysetting = g_strdup ( set_getstr( &tf->ic->acc->set, "proxy" ) ); char *proxy, *next, *errmsg = NULL; char port[6]; - char host[INET6_ADDRSTRLEN]; + char host[HOST_NAME_MAX+1]; jabber_streamhost_t *sh, *sh2; GSList *streamhosts = jd->streamhosts; diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index f44155fa..d2b2a5c8 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -218,7 +218,7 @@ void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboar file_transfer_t *file = msn_file->dcc; char buf[1024]; unsigned int acookie = time ( NULL ); - char host[INET6_ADDRSTRLEN]; + char host[HOST_NAME_MAX+1]; char port[6]; char *errmsg; -- cgit v1.2.3 From c1a3c27575ac6ae77cbffb1e48d02ebee3f83152 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Mar 2010 23:41:07 +0000 Subject: Avoid some NULL pointer dereferences on malformed XMPP packets. --- protocols/jabber/iq.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index d690d336..22f97b2a 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -657,18 +657,20 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no { struct xt_node *c; struct jabber_buddy *bud; - char *feature; + char *feature, *xmlns, *from; if( !( c = xt_find_node( node->children, "query" ) ) || - !( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) ) + !( from = xt_find_attr( c, "from" ) ) || + !( xmlns = xt_find_attr( c, "xmlns" ) ) || + !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); return XT_HANDLED; } - if( ( bud = jabber_buddy_by_jid( ic, xt_find_attr( node, "from") , 0 ) ) == NULL ) + if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL ) { /* Who cares about the unknown... */ - imcb_log( ic, "Couldn't find buddy: %s", xt_find_attr( node, "from")); + imcb_log( ic, "Couldn't find buddy: %s", from ); return 0; } @@ -676,7 +678,8 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no while( ( c = xt_find_node( c, "feature" ) ) ) { feature = xt_find_attr( c, "var" ); - bud->features = g_slist_append( bud->features, g_strdup( feature ) ); + if( feature ) + bud->features = g_slist_append( bud->features, g_strdup( feature ) ); c = c->next; } @@ -712,9 +715,11 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n { struct xt_node *c; struct jabber_data *jd = ic->proto_data; + char *xmlns, *from; if( !( c = xt_find_node( node->children, "query" ) ) || - !xt_find_attr( node, "from" ) ) + !( from = xt_find_attr( node, "from" ) ) || + !( xmlns = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); return XT_HANDLED; @@ -722,24 +727,24 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n jd->have_streamhosts++; - if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_ITEMS ) == 0 ) + if( strcmp( xmlns, XMLNS_DISCO_ITEMS ) == 0 ) { - char *item, *itemjid; + char *itemjid; /* answer from server */ c = c->children; while( ( c = xt_find_node( c, "item" ) ) ) { - item = xt_find_attr( c, "name" ); itemjid = xt_find_attr( c, "jid" ); - - jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO ); + + if( itemjid ) + jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO ); c = c->next; } } - else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) + else if( xmlns, XMLNS_DISCO_INFO ) == 0 ) { char *category, *type; @@ -753,27 +758,29 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n if( type && ( strcmp( type, "bytestreams" ) == 0 ) && category && ( strcmp( category, "proxy" ) == 0 ) ) - jabber_iq_query_server( ic, xt_find_attr( node, "from" ), XMLNS_BYTESTREAMS ); + jabber_iq_query_server( ic, from, XMLNS_BYTESTREAMS ); c = c->next; } } - else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_BYTESTREAMS ) == 0 ) + else if( xmlns, XMLNS_BYTESTREAMS ) == 0 ) { - char *host, *jid; + char *host, *jid, *port_s; int port; /* answer from proxy */ if( ( c = xt_find_node( c->children, "streamhost" ) ) && ( host = xt_find_attr( c, "host" ) ) && - ( port = atoi( xt_find_attr( c, "port" ) ) ) && + ( port_s = xt_find_attr( c, "port" ) ) && + ( sscanf( port_s, "%d", &port ) == 1 ) && ( jid = xt_find_attr( c, "jid" ) ) ) { jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( jid ); sh->host = g_strdup( host ); - sprintf( sh->port, "%u", port ); + g_snprintf( sh->port, sizeof( sh->port ), "%u", port ); imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port ); jd->streamhosts = g_slist_append( jd->streamhosts, sh ); -- cgit v1.2.3 From 42fc5b6cfed51ac011df8877cf5e24f00828e8be Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Mar 2010 23:47:35 +0000 Subject: Ouch, committing stuff without even checking if it compiles is bad style. :-) --- protocols/jabber/iq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 22f97b2a..e3c77d3d 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -744,7 +744,7 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n c = c->next; } } - else if( xmlns, XMLNS_DISCO_INFO ) == 0 ) + else if( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) { char *category, *type; @@ -763,7 +763,7 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n c = c->next; } } - else if( xmlns, XMLNS_BYTESTREAMS ) == 0 ) + else if( strcmp( xmlns, XMLNS_BYTESTREAMS ) == 0 ) { char *host, *jid, *port_s; int port; -- cgit v1.2.3 From 78d254f110d47f9e0c3a8f12259f93a4faa2130d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 18 Mar 2010 00:07:16 +0000 Subject: More small fixes. (NULL derefs and s/close/disconnect/) --- protocols/jabber/s5bytestream.c | 5 +++-- protocols/jabber/si.c | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index c292e61e..5fcbef6a 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -281,11 +281,12 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru shnode = qnode->children; while( ( shnode = xt_find_node( shnode, "streamhost" ) ) ) { - char *jid, *host; + char *jid, *host, *port_s; int port; if( ( jid = xt_find_attr( shnode, "jid" ) ) && ( host = xt_find_attr( shnode, "host" ) ) && - ( ( port = atoi( xt_find_attr( shnode, "port" ) ) ) ) ) + ( port_s = xt_find_attr( shnode, "port" ) ) && + ( sscanf( port_s, "%d", &port ) == 1 ) ) { jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); sh->jid = g_strdup(jid); diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index e7aeffc9..424db8b3 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -40,7 +40,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) if( tf->fd != -1 ) { - close( tf->fd ); + disconnect( tf->fd ); tf->fd = -1; } @@ -51,6 +51,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) g_free( tf->tgt_jid ); g_free( tf->iq_id ); g_free( tf->sid ); + g_free( tf ); } /* file_transfer canceled() callback */ @@ -203,7 +204,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode) { struct xt_node *c, *d, *reply; - char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid; + char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s; struct jabber_buddy *bud; int requestok = FALSE; char *name; @@ -229,7 +230,8 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st !( d = xt_find_node( sinode->children, "file" ) ) || !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || !( name = xt_find_attr( d, "name" ) ) || - !( size = (size_t) atoll( xt_find_attr( d, "size" ) ) ) || + !( size_s = xt_find_attr( d, "size" ) ) || + !( 1 == sscanf( size_s, "%lld", &size ) ) || !( d = xt_find_node( sinode->children, "feature" ) ) || !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || !( d = xt_find_node( d->children, "x" ) ) || -- cgit v1.2.3 From b8a491db597ba2d82cc8eddc727509197747f0d5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 18 Mar 2010 00:30:38 +0000 Subject: Some compiler warning fixes and renamed "transfers" command to just "transfer" since all root commands are singular so far. --- protocols/jabber/s5bytestream.c | 2 +- protocols/jabber/si.c | 21 +++++++++++---------- root_commands.c | 13 +++++-------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 5fcbef6a..bb1d9e2e 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -829,7 +829,7 @@ static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struc { char *sid; GSList *tflist; - struct jabber_transfer *tf; + struct jabber_transfer *tf = NULL; struct xt_node *query; struct jabber_data *jd = ic->proto_data; diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 424db8b3..a87f7b8b 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -40,7 +40,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) if( tf->fd != -1 ) { - disconnect( tf->fd ); + closesocket( tf->fd ); tf->fd = -1; } @@ -155,10 +155,10 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, if( bud == NULL ) { - imcb_file_canceled( tf->ft, "Couldn't find buddy (BUG?)" ); + imcb_file_canceled( ft, "Couldn't find buddy (BUG?)" ); return; } - + imcb_log( ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who ); tf = g_new0( struct jabber_transfer, 1 ); @@ -231,7 +231,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || !( name = xt_find_attr( d, "name" ) ) || !( size_s = xt_find_attr( d, "size" ) ) || - !( 1 == sscanf( size_s, "%lld", &size ) ) || + !( 1 == sscanf( size_s, "%zd", &size ) ) || !( d = xt_find_node( sinode->children, "feature" ) ) || !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || !( d = xt_find_node( d->children, "x" ) ) || @@ -241,14 +241,15 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) ) { imcb_log( ic, "WARNING: Received incomplete Stream Initiation request" ); - } else + } + else { /* Check if we support one of the options */ c = d->children; while( ( c = xt_find_node( c, "option" ) ) ) - if( ( d = xt_find_node( c->children, "value" ) ) && - ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) + if( ( d = xt_find_node( c->children, "value" ) ) && + ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) { requestok = TRUE; break; @@ -258,10 +259,11 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); } - if ( requestok ) + if( requestok ) { /* Figure out who the transfer should come frome... */ + ext_jid = ini_jid; if( ( s = strchr( ini_jid, '/' ) ) ) { if( ( bud = jabber_buddy_by_jid( ic, ini_jid, GET_BUDDY_EXACT ) ) ) @@ -281,8 +283,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st *s = '/'; } - - if ( !requestok ) + else { reply = jabber_make_error_packet( node, "item-not-found", "cancel", NULL ); if (!jabber_write_packet( ic, reply )) diff --git a/root_commands.c b/root_commands.c index b39cb1ba..c60092f0 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1127,7 +1127,7 @@ static void cmd_chat( irc_t *irc, char **cmd ) } } -static void cmd_transfers( irc_t *irc, char **cmd ) +static void cmd_transfer( irc_t *irc, char **cmd ) { GSList *files = irc->file_transfers; enum { LIST, REJECT, CANCEL }; @@ -1140,15 +1140,12 @@ static void cmd_transfers( irc_t *irc, char **cmd ) return; } - if( cmd[1] && - ( strcmp( cmd[1], "reject" ) == 0 ) ) + if( cmd[1] && ( strcmp( cmd[1], "reject" ) == 0 ) ) { subcmd = REJECT; } - else if( cmd[1] && - ( strcmp( cmd[1], "cancel" ) == 0 ) && - cmd[2] && - ( fid = atoi( cmd[2] ) ) ) + else if( cmd[1] && ( strcmp( cmd[1], "cancel" ) == 0 ) && + cmd[2] && ( sscanf( cmd[2], "%d", &fid ) == 1 ) ) { subcmd = CANCEL; } @@ -1213,6 +1210,6 @@ const command_t commands[] = { { "qlist", 0, cmd_qlist, 0 }, { "join_chat", 2, cmd_join_chat, 0 }, { "chat", 1, cmd_chat, 0 }, - { "transfers", 0, cmd_transfers, 0 }, + { "transfer", 0, cmd_transfer, 0 }, { NULL } }; -- cgit v1.2.3 From 699376f7c3b3d6aff18af0601fa1f1ac6c5a2892 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 00:39:45 +0000 Subject: Read the from attribute from the main stanza, not the inside query. Also fixing another potential NULL pointer dereference. --- protocols/jabber/iq.c | 4 ++-- protocols/jabber/si.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index e3c77d3d..dfdc6887 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -659,8 +659,8 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no struct jabber_buddy *bud; char *feature, *xmlns, *from; - if( !( c = xt_find_node( node->children, "query" ) ) || - !( from = xt_find_attr( c, "from" ) ) || + if( !( from = xt_find_attr( node, "from" ) ) || + !( c = xt_find_node( node->children, "query" ) ) || !( xmlns = xt_find_attr( c, "xmlns" ) ) || !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) ) { diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index a87f7b8b..b76fb3e0 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -249,6 +249,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st c = d->children; while( ( c = xt_find_node( c, "option" ) ) ) if( ( d = xt_find_node( c->children, "value" ) ) && + ( d->text != NULL ) && ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) { requestok = TRUE; -- cgit v1.2.3 From 54a20149778028bff730511c6cc8027f61634124 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 00:46:18 +0000 Subject: xt_status functions should always return an XT_ status, it's not a boolean. --- protocols/jabber/iq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index dfdc6887..3e2eb870 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -632,7 +632,7 @@ xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) { /* Who cares about the unknown... */ imcb_log( ic, "Couldn't find buddy: %s", bare_jid); - return 0; + return XT_HANDLED; } if( bud->features ) /* been here already */ @@ -645,12 +645,12 @@ xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) { imcb_log( ic, "WARNING: Couldn't generate feature query" ); xt_free_node( node ); - return 0; + return XT_HANDLED; } jabber_cache_add( ic, query, jabber_iq_parse_features ); - return jabber_write_packet( ic, query ); + return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT; } xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) @@ -671,7 +671,7 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no { /* Who cares about the unknown... */ imcb_log( ic, "Couldn't find buddy: %s", from ); - return 0; + return XT_HANDLED; } c = c->children; @@ -705,7 +705,7 @@ xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xml jd->have_streamhosts--; jabber_cache_add( ic, query, jabber_iq_parse_server_features ); - return jabber_write_packet( ic, query ); + return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT; } /* -- cgit v1.2.3 From aed152f005a70e04f7d833dc4fb468e400e54fb3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 01:02:03 +0000 Subject: Killed the last str*cmp(xml_find_attr) combos. --- protocols/jabber/iq.c | 3 ++- protocols/jabber/si.c | 39 ++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 3e2eb870..f5fbdc13 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -127,7 +127,8 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) else if( strcmp( type, "set" ) == 0 ) { if( ( c = xt_find_node( node->children, "si" ) ) && - ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) ) + ( s = xt_find_attr( c, "xmlns" ) ) && + ( strcmp( s, XMLNS_SI ) == 0 ) ) { return jabber_si_handle_request( ic, node, c ); } diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index b76fb3e0..eff4aad7 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -207,7 +207,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s; struct jabber_buddy *bud; int requestok = FALSE; - char *name; + char *name, *cmp; size_t size; struct jabber_transfer *tf; struct jabber_data *jd = ic->proto_data; @@ -226,19 +226,25 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st !( tgt_jid = xt_find_attr( node, "to" ) ) || !( iq_id = xt_find_attr( node, "id" ) ) || !( sid = xt_find_attr( sinode, "id" ) ) || - !( strcmp( xt_find_attr( sinode, "profile" ), XMLNS_FILETRANSFER ) == 0 ) || + !( cmp = xt_find_attr( sinode, "profile" ) ) || + !( 0 == strcmp( cmp, XMLNS_FILETRANSFER ) ) || !( d = xt_find_node( sinode->children, "file" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( 0 == strcmp( cmp, XMLNS_FILETRANSFER ) ) || !( name = xt_find_attr( d, "name" ) ) || !( size_s = xt_find_attr( d, "size" ) ) || !( 1 == sscanf( size_s, "%zd", &size ) ) || !( d = xt_find_node( sinode->children, "feature" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( 0 == strcmp( cmp, XMLNS_FEATURE ) ) || !( d = xt_find_node( d->children, "x" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_XDATA ) == 0 ) || - !( strcmp( xt_find_attr( d, "type" ), "form" ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( 0 == strcmp( cmp, XMLNS_XDATA ) ) || + !( cmp = xt_find_attr( d, "type" ) ) || + !( 0 == strcmp( cmp, "form" ) ) || !( d = xt_find_node( d->children, "field" ) ) || - !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) ) + !( cmp = xt_find_attr( d, "var" ) ) || + !( 0 == strcmp( cmp, "stream-method" ) ) ) { imcb_log( ic, "WARNING: Received incomplete Stream Initiation request" ); } @@ -366,7 +372,7 @@ void jabber_si_answer_request( file_transfer_t *ft ) { static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct xt_node *c, *d; - char *ini_jid, *tgt_jid, *iq_id; + char *ini_jid, *tgt_jid, *iq_id, *cmp; GSList *tflist; struct jabber_transfer *tf=NULL; struct jabber_data *jd = ic->proto_data; @@ -391,16 +397,19 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n !( ini_jid = xt_find_attr( node, "to" ) ) || !( iq_id = xt_find_attr( node, "id" ) ) || !( c = xt_find_node( node->children, "si" ) ) || - !( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) || -/* !( d = xt_find_node( c->children, "file" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || */ + !( cmp = xt_find_attr( c, "xmlns" ) ) || + !( strcmp( cmp, XMLNS_SI ) == 0 ) || !( d = xt_find_node( c->children, "feature" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( strcmp( cmp, XMLNS_FEATURE ) == 0 ) || !( d = xt_find_node( d->children, "x" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_XDATA ) == 0 ) || - !( strcmp( xt_find_attr( d, "type" ), "submit" ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( strcmp( cmp, XMLNS_XDATA ) == 0 ) || + !( cmp = xt_find_attr( d, "type" ) ) || + !( strcmp( cmp, "submit" ) == 0 ) || !( d = xt_find_node( d->children, "field" ) ) || - !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) || + !( cmp = xt_find_attr( d, "var" ) ) || + !( strcmp( cmp, "stream-method" ) == 0 ) || !( d = xt_find_node( d->children, "value" ) ) ) { imcb_log( ic, "WARNING: Received incomplete Stream Initiation response" ); -- cgit v1.2.3 From 2e89256f2265676d5d37b92433352ceccf079a7a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 13:20:20 +0000 Subject: Remove dcc_poll() and just use the cond variable passed to I/O events. --- dcc.c | 45 ++++----------------------------------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/dcc.c b/dcc.c index d28c3b34..dcfeb190 100644 --- a/dcc.c +++ b/dcc.c @@ -25,7 +25,6 @@ #include "bitlbee.h" #include "ft.h" #include "dcc.h" -#include #include #include #include "lib/ftutil.h" @@ -258,34 +257,6 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc return TRUE; } -/* - * Checks poll(), same for receiving and sending - */ -gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents ) -{ - struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT }; - - ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" ) - - if( pfd.revents & POLLERR ) - { - int sockerror; - socklen_t errlen = sizeof( sockerror ); - - if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) - return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" ); - - return dcc_abort( df, "Socket error: %s", strerror( sockerror ) ); - } - - if( pfd.revents & POLLHUP ) - return dcc_abort( df, "Remote end closed connection" ); - - *revents = pfd.revents; - - return TRUE; -} - /* * After setup, the transfer itself is handled entirely by this function. * There are basically four things to handle: connect, receive, send, and error. @@ -294,12 +265,8 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) { dcc_file_transfer_t *df = data; file_transfer_t *file = df->ft; - short revents; - if( !dcc_poll( df, fd, &revents ) ) - return FALSE; - - if( ( revents & POLLIN ) && + if( ( cond & GAIM_INPUT_READ ) && ( file->status & FT_STATUS_LISTENING ) ) { struct sockaddr *clt_addr; @@ -324,7 +291,7 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return FALSE; } - if( revents & POLLIN ) + if( cond & GAIM_INPUT_READ ) { int bytes_received; int ret; @@ -413,12 +380,8 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) { dcc_file_transfer_t *df = data; file_transfer_t *ft = df->ft; - short revents; - if( !dcc_poll( df, fd, &revents ) ) - return FALSE; - - if( ( revents & POLLOUT ) && + if( ( cond & GAIM_INPUT_WRITE ) && ( ft->status & FT_STATUS_CONNECTING ) ) { ft->status = FT_STATUS_TRANSFERRING; @@ -429,7 +392,7 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) return FALSE; } - if( revents & POLLIN ) + if( cond & GAIM_INPUT_READ ) { int ret, done; -- cgit v1.2.3 From 4ed9c8c11adb8d1010ed4bb7b3ff9af962f91237 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 15:10:07 +0000 Subject: Fixed 100% CPU usage bug in dcc.c. --- dcc.c | 33 ++++++++++++++------------------- dcc.h | 6 ++++++ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/dcc.c b/dcc.c index dcfeb190..558d923a 100644 --- a/dcc.c +++ b/dcc.c @@ -293,35 +293,30 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) if( cond & GAIM_INPUT_READ ) { - int bytes_received; int ret; - ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), MSG_PEEK ), "Receiving" ); + ASSERTSOCKOP( ret = recv( fd, ( (char*) &df->acked ) + df->acked_len, + sizeof( df->acked ) - df->acked_len, 0 ), "Receiving" ); if( ret == 0 ) return dcc_abort( df, "Remote end closed connection" ); - - if( ret < 4 ) - { - imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only %d bytes instead of 4, shouldn't happen too often!", ret ); + + /* How likely is it that a 32-bit integer gets split accross + packet boundaries? Chances are rarely 0 so let's be sure. */ + if( ( df->acked_len = ( df->acked_len + ret ) % 4 ) > 0 ) return TRUE; - } - - ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), 0 ), "Receiving" ); - if( ret != 4 ) - return dcc_abort( df, "MSG_PEEK'ed 4, but can only dequeue %d bytes", ret ); - bytes_received = ntohl( bytes_received ); + df->acked = ntohl( df->acked ); /* If any of this is actually happening, the receiver should buy a new IRC client */ - if ( bytes_received > df->bytes_sent ) - return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", bytes_received, df->bytes_sent ); + if ( df->acked > df->bytes_sent ) + return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", df->acked, df->bytes_sent ); - if ( bytes_received < file->bytes_transferred ) - return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", bytes_received, file->bytes_transferred ); + if ( df->acked < file->bytes_transferred ) + return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", df->acked, file->bytes_transferred ); - file->bytes_transferred = bytes_received; + file->bytes_transferred = df->acked; if( file->bytes_transferred >= file->file_size ) { if( df->proto_finished ) @@ -411,8 +406,8 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) if( ( ( df->bytes_sent - ft->bytes_transferred ) > DCC_PACKET_SIZE ) || done ) { - int ack, ackret; - ack = htonl( ft->bytes_transferred = df->bytes_sent ); + guint32 ack = htonl( ft->bytes_transferred = df->bytes_sent ); + int ackret; ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" ); diff --git a/dcc.h b/dcc.h index 4be5a338..cc784478 100644 --- a/dcc.h +++ b/dcc.h @@ -76,6 +76,12 @@ typedef struct dcc_file_transfer { */ size_t bytes_sent; + /* + * Handle the wonderful sadly-not-deprecated ACKs. + */ + guint32 acked; + int acked_len; + /* imc's handle */ file_transfer_t *ft; -- cgit v1.2.3 From a81d679654e36055ce9913cd7541885b41380947 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 15:56:59 +0000 Subject: Fixed jabber_bs_peek() to deal with incomplete packets as well. --- protocols/jabber/s5bytestream.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index bb1d9e2e..58a6c2e4 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -45,6 +45,9 @@ struct bs_transfer { char *pseudoadr; gint connect_timeout; + + char peek_buf[64]; + int peek_buf_len; }; struct socks5_message @@ -149,20 +152,26 @@ gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ) int ret; int fd = bt->tf->fd; - ASSERTSOCKOP( ret = recv( fd, buffer, buflen, MSG_PEEK ), "MSG_PEEK'ing" ); + if( buflen > sizeof( bt->peek_buf ) ) + return jabber_bs_abort( bt, "BUG: %d > sizeof(peek_buf)", buflen ); + + ASSERTSOCKOP( ret = recv( fd, bt->peek_buf + bt->peek_buf_len, + buflen - bt->peek_buf_len, 0 ), "recv() on SOCKS5 connection" ); if( ret == 0 ) return jabber_bs_abort( bt, "Remote end closed connection" ); - - if( ret < buflen ) - return ret; - - ASSERTSOCKOP( ret = recv( fd, buffer, buflen, 0 ), "Dequeuing after MSG_PEEK" ); - - if( ret != buflen ) - return jabber_bs_abort( bt, "recv returned less than previous recv with MSG_PEEK" ); - return ret; + bt->peek_buf_len += ret; + memcpy( buffer, bt->peek_buf, bt->peek_buf_len ); + + if( bt->peek_buf_len == buflen ) + { + /* If we have everything the caller wanted, reset the peek buffer. */ + bt->peek_buf_len = 0; + return buflen; + } + else + return bt->peek_buf_len; } @@ -559,6 +568,7 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); bt->tf->watch_in = 0; + /* MUST always return FALSE! */ return FALSE; } @@ -1011,6 +1021,7 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */ imcb_file_canceled( tf->ft, error ); + /* MUST always return FALSE! */ return FALSE; } -- cgit v1.2.3 From 1cc0df34f742f93f995b68210de3d1f2eac2b5ac Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 16:52:22 +0000 Subject: s/last_act/last_msg/ now. --- protocols/jabber/si.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index eff4aad7..bfb64f11 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -275,7 +275,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st { if( ( bud = jabber_buddy_by_jid( ic, ini_jid, GET_BUDDY_EXACT ) ) ) { - bud->last_act = time( NULL ); + bud->last_msg = time( NULL ); ext_jid = bud->ext_jid ? : bud->bare_jid; } else -- cgit v1.2.3 From 0cb71a67163cbca49827855a4a56642f919330fd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 16:57:20 +0000 Subject: I renamed GAIM_INPUT_* to something else in this branch. --- dcc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dcc.c b/dcc.c index 558d923a..1f8ec611 100644 --- a/dcc.c +++ b/dcc.c @@ -153,7 +153,7 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha return NULL; /* watch */ - df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); + df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_send_proto, df ); df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file ); @@ -266,7 +266,7 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) dcc_file_transfer_t *df = data; file_transfer_t *file = df->ft; - if( ( cond & GAIM_INPUT_READ ) && + if( ( cond & B_EV_IO_READ ) && ( file->status & FT_STATUS_LISTENING ) ) { struct sockaddr *clt_addr; @@ -286,12 +286,12 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) file->accept( file ); /* reschedule for reading on new fd */ - df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df ); + df->watch_in = b_input_add( fd, B_EV_IO_READ, dccs_send_proto, df ); return FALSE; } - if( cond & GAIM_INPUT_READ ) + if( cond & B_EV_IO_READ ) { int ret; @@ -363,7 +363,7 @@ gboolean dccs_recv_start( file_transfer_t *ft ) ft->status = FT_STATUS_CONNECTING; /* watch */ - df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df ); + df->watch_out = b_input_add( df->fd, B_EV_IO_WRITE, dccs_recv_proto, df ); ft->write_request = dccs_recv_write_request; df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); @@ -376,18 +376,18 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) dcc_file_transfer_t *df = data; file_transfer_t *ft = df->ft; - if( ( cond & GAIM_INPUT_WRITE ) && + if( ( cond & B_EV_IO_WRITE ) && ( ft->status & FT_STATUS_CONNECTING ) ) { ft->status = FT_STATUS_TRANSFERRING; - //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + //df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_recv_proto, df ); df->watch_out = 0; return FALSE; } - if( cond & GAIM_INPUT_READ ) + if( cond & B_EV_IO_READ ) { int ret, done; @@ -444,7 +444,7 @@ gboolean dccs_recv_write_request( file_transfer_t *ft ) if( df->watch_in ) return dcc_abort( df, "BUG: write_request() called while watching" ); - df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_recv_proto, df ); return TRUE; } @@ -487,7 +487,7 @@ gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_l df->bytes_sent += ret; if( df->bytes_sent < df->ft->file_size ) - df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_can_write, df ); + df->watch_out = b_input_add( df->fd, B_EV_IO_WRITE, dccs_send_can_write, df ); return TRUE; } -- cgit v1.2.3 From 437bd9b726339c44aa1a048cd84c2539bfa6cab5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 21:38:42 +0000 Subject: Enough code to make an incoming transfer show up properly and accept it. Not enough yet to handle the incoming data. --- protocols/purple/purple.c | 65 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b336b108..f0fc736e 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -580,12 +580,12 @@ static void *prplcb_request_action( const char *title, const char *primary, cons caption = va_arg( actions, char* ); fn = va_arg( actions, void* ); - if( strcmp( caption, "Accept" ) == 0 ) + if( strstr( caption, "Accept" ) ) { pqad->yes = fn; pqad->yes_i = i; } - else if( strcmp( caption, "Reject" ) == 0 ) + else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) ) { pqad->no = fn; pqad->no_i = i; @@ -658,20 +658,75 @@ static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, con return NULL; } -static PurpleNotifyUiOps bee_notify_uiops = +static PurpleNotifyUiOps bee_notify_uiops = { NULL, prplcb_notify_email, }; +static void prplcb_xfer( PurpleXfer *xfer ) +{ + fprintf( stderr, "ft bla: 0x%p\n", xfer ); +} + +static void prpl_xfer_accept( struct file_transfer *ft ) +{ + purple_xfer_request_accepted( ft->data, NULL ); + purple_xfer_ui_ready( ft->data ); +} + +static void prpl_xfer_reject( struct file_transfer *ft ) +{ + purple_xfer_request_denied( ft->data ); +} + +static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition cond ) +{ + PurpleXfer *xfer = data; + struct im_connection *ic = purple_ic_by_pa( xfer->account ); + file_transfer_t *ft; + + ft = imcb_file_send_start( ic, xfer->who, xfer->filename, xfer->size ); + ft->data = xfer; + xfer->ui_data = ft; + + ft->accept = prpl_xfer_accept; + + return FALSE; +} + +static void prplcb_xfer_new( PurpleXfer *xfer ) +{ + purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + + fprintf( stderr, "ft_new bla: 0x%p\n", xfer ); + + b_timeout_add( 0, prplcb_xfer_new_cb, xfer ); +} + +static PurpleXferUiOps bee_xfer_uiops = +{ + prplcb_xfer_new, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, +}; + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); purple_request_set_ui_ops( &bee_request_uiops ); - purple_notify_set_ui_ops(&bee_notify_uiops); - //purple_debug_set_ui_ops( &bee_debug_uiops ); + purple_notify_set_ui_ops( &bee_notify_uiops ); + purple_xfers_set_ui_ops( &bee_xfer_uiops ); + purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From c735200e7727a7b17161c2a205ba6639d61e9b54 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 22 Mar 2010 01:20:40 +0000 Subject: Incoming file transfers can now be accepted (and should work) and/or rejected. Tested with Jabber and msn/msn-pecan so far. --- protocols/purple/purple.c | 90 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f0fc736e..383ed55f 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -664,58 +664,102 @@ static PurpleNotifyUiOps bee_notify_uiops = prplcb_notify_email, }; -static void prplcb_xfer( PurpleXfer *xfer ) +struct prpl_xfer_data { - fprintf( stderr, "ft bla: 0x%p\n", xfer ); + PurpleXfer *xfer; + file_transfer_t *ft; + gint ready_timer; +}; + +/* Glorious hack: We seem to have to remind at least some libpurple plugins + that we're ready because this info may get lost if we give it too early. + So just do it ten times a second. :-/ */ +static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ + purple_xfer_ui_ready( data ); + return TRUE; +} + +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px->xfer ); + return TRUE; +} + +static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + gboolean st; + + b_event_remove( px->ready_timer ); + px->ready_timer = 0; + + st = px->ft->write( px->ft, (char*) buffer, size ); + + if( st && xfer->bytes_remaining == size ) + imcb_file_finished( px->ft ); + + return st ? size : 0; } static void prpl_xfer_accept( struct file_transfer *ft ) { - purple_xfer_request_accepted( ft->data, NULL ); - purple_xfer_ui_ready( ft->data ); + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_accepted( px->xfer, NULL ); + prpl_xfer_write_request( ft ); } -static void prpl_xfer_reject( struct file_transfer *ft ) +static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) { - purple_xfer_request_denied( ft->data ); + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_denied( px->xfer ); } static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition cond ) { PurpleXfer *xfer = data; struct im_connection *ic = purple_ic_by_pa( xfer->account ); - file_transfer_t *ft; + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + PurpleBuddy *buddy; + const char *who; + + buddy = purple_find_buddy( xfer->account, xfer->who ); + who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; - ft = imcb_file_send_start( ic, xfer->who, xfer->filename, xfer->size ); - ft->data = xfer; - xfer->ui_data = ft; + /* TODO(wilmer): After spreading some more const goodness in BitlBee, + remove the evil cast below. */ + px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); + px->ft->data = px; + px->xfer = data; + px->xfer->ui_data = px; - ft->accept = prpl_xfer_accept; + px->ft->accept = prpl_xfer_accept; + px->ft->canceled = prpl_xfer_canceled; + px->ft->write_request = prpl_xfer_write_request; return FALSE; } static void prplcb_xfer_new( PurpleXfer *xfer ) { + /* This should suppress the stupid file dialog. */ purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); - fprintf( stderr, "ft_new bla: 0x%p\n", xfer ); - + /* Sadly the xfer struct is still empty ATM so come back after + the caller is done. */ b_timeout_add( 0, prplcb_xfer_new_cb, xfer ); } static PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, + NULL, + NULL, + NULL, + NULL, + NULL, + prplcb_xfer_write, }; static void purple_ui_init() @@ -726,7 +770,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - purple_debug_set_ui_ops( &bee_debug_uiops ); + //purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From edfc6db1415558b7f202cc3fa2654ad58defea78 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 23 Mar 2010 01:06:25 +0000 Subject: Frankenstein, it lives! This stuff can send files but it has troubles with certain protocol modules, don't rely on this yet. It's also getting too messy and should be split off into a separate file. --- protocols/purple/purple.c | 133 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 15 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 383ed55f..997b09f7 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -364,6 +364,8 @@ static int purple_send_typing( struct im_connection *ic, char *who, int flags ) } } +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); + static void purple_ui_init(); static PurpleCoreUiOps bee_core_uiops = @@ -664,26 +666,34 @@ static PurpleNotifyUiOps bee_notify_uiops = prplcb_notify_email, }; + struct prpl_xfer_data { PurpleXfer *xfer; file_transfer_t *ft; gint ready_timer; + char *buf; + int buf_len; }; +static file_transfer_t *next_ft; + /* Glorious hack: We seem to have to remind at least some libpurple plugins that we're ready because this info may get lost if we give it too early. So just do it ten times a second. :-/ */ static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) { - purple_xfer_ui_ready( data ); - return TRUE; + struct prpl_xfer_data *px = data; + + purple_xfer_ui_ready( px->xfer ); + + return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; } static gboolean prpl_xfer_write_request( struct file_transfer *ft ) { struct prpl_xfer_data *px = ft->data; - px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px->xfer ); + px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); return TRUE; } @@ -703,6 +713,19 @@ static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize return st ? size : 0; } +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ + struct prpl_xfer_data *px = ft->data; + + px->buf = g_memdup( buffer, len ); + px->buf_len = len; + + //purple_xfer_ui_ready( px->xfer ); + px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); + + return TRUE; +} + static void prpl_xfer_accept( struct file_transfer *ft ) { struct prpl_xfer_data *px = ft->data; @@ -716,7 +739,7 @@ static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) purple_xfer_request_denied( px->xfer ); } -static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition cond ) +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) { PurpleXfer *xfer = data; struct im_connection *ic = purple_ic_by_pa( xfer->account ); @@ -743,25 +766,103 @@ static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition co static void prplcb_xfer_new( PurpleXfer *xfer ) { - /* This should suppress the stupid file dialog. */ - purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) + { + /* This should suppress the stupid file dialog. */ + purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + + /* Sadly the xfer struct is still empty ATM so come back after + the caller is done. */ + b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); + } + else + { + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + px->ft = next_ft; + px->ft->data = px; + px->xfer = xfer; + px->xfer->ui_data = px; + + purple_xfer_set_filename( xfer, px->ft->file_name ); + purple_xfer_set_size( xfer, px->ft->file_size ); + + next_ft = NULL; + } +} + +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); +} + +gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; - /* Sadly the xfer struct is still empty ATM so come back after - the caller is done. */ - b_timeout_add( 0, prplcb_xfer_new_cb, xfer ); + fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + + if( px->buf ) + { + *buffer = px->buf; + px->buf = NULL; + + px->ft->write_request( px->ft ); + + return px->buf_len; + } + + return 0; } static PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, - NULL, - NULL, - NULL, - NULL, - NULL, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_dbg, prplcb_xfer_write, + prplcb_xfer_read, + prplcb_xfer_dbg, }; +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +{ + PurpleAccount *pa = ic->proto_data; + struct prpl_xfer_data *px; + + /* xfer_new() will pick up this variable. It's a hack but we're not + multi-threaded anyway. */ + next_ft = ft; + serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); + + ft->write = prpl_xfer_write; + + px = ft->data; + imcb_file_recv_start( ft ); + + px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); +} + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + if( px->ft->status & FT_STATUS_TRANSFERRING ) + { + fprintf( stderr, "The ft, it is ready...\n" ); + px->ft->write_request( px->ft ); + + return FALSE; + } + + return TRUE; +} + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); @@ -770,7 +871,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - //purple_debug_set_ui_ops( &bee_debug_uiops ); + purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() @@ -816,6 +917,8 @@ void purple_initmodule() funcs.keepalive = purple_keepalive; funcs.send_typing = purple_send_typing; funcs.handle_cmp = g_strcasecmp; + /* TODO(wilmer): Set this one only for protocols that support it? */ + funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From ba7d16f3c90de2744243efe6373ccebe51cfcb5a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 24 Mar 2010 17:12:53 +0000 Subject: Now seems like a pretty good time to finally kill crypting.c and storage_text. This means people won't be able to upgrade from BitlBee 1.0 to this version anymore but only via 1.2. 1.0 is old enough that I don't really expect this to be a problem. --- Makefile | 4 +- configure | 2 +- crypting.c | 133 ----------------------------------------------- crypting.h | 29 ----------- irc.c | 1 - root_commands.c | 1 - storage.c | 2 - storage_text.c | 157 -------------------------------------------------------- win32.c | 1 - 9 files changed, 3 insertions(+), 327 deletions(-) delete mode 100644 crypting.c delete mode 100644 crypting.h delete mode 100644 storage_text.c diff --git a/Makefile b/Makefile index 2007a11d..ef34f123 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,8 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o chat.o crypting.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o -headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h +objects = account.o bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o +headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols ifeq ($(TARGET),i586-mingw32msvc) diff --git a/configure b/configure index 6bd2995a..7ffd225b 100755 --- a/configure +++ b/configure @@ -395,7 +395,7 @@ if detect_resolv_dynamic || detect_resolv_static; then echo '#define HAVE_RESOLV_A' >> config.h fi -STORAGES="text xml" +STORAGES="xml" if [ "$ldap" = "auto" ]; then detect_ldap diff --git a/crypting.c b/crypting.c deleted file mode 100644 index 0a5c937e..00000000 --- a/crypting.c +++ /dev/null @@ -1,133 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Sjoerd Hemminga and others * - \********************************************************************/ - -/* A little bit of encryption for the users' passwords */ - -/* - 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 -*/ - -/* [WvG] This file can also be compiled into a stand-alone program - which can encode/decode BitlBee account files. The main() will be - included if CRYPTING_MAIN is defined. Or just do "make decode" and - the programs will be built. */ - -#include -#include "md5.h" -#include "crypting.h" - -/*\ - * [SH] Do _not_ call this if it's not entirely sure that it will not cause - * harm to another users file, since this does not check the password for - * correctness. -\*/ - -int checkpass (const char *pass, const char *md5sum) -{ - md5_state_t md5state; - md5_byte_t digest[16]; - int i, j; - char digits[3]; - - md5_init (&md5state); - md5_append (&md5state, (unsigned char *)pass, strlen (pass)); - md5_finish (&md5state, digest); - - for (i = 0, j = 0; i < 16; i++, j += 2) { - /* Check password for correctness */ - g_snprintf (digits, sizeof (digits), "%02x\n", digest[i]); - - if (digits[0] != md5sum[j]) return (-1); - if (digits[1] != md5sum[j + 1]) return (-1); - } - - return( 0 ); -} - - -char *hashpass (const char *password) -{ - md5_state_t md5state; - md5_byte_t digest[16]; - int i; - char digits[3]; - char *rv; - - if (password == NULL) return (NULL); - - rv = g_new0 (char, 33); - - md5_init (&md5state); - md5_append (&md5state, (const unsigned char *)password, strlen (password)); - md5_finish (&md5state, digest); - - for (i = 0; i < 16; i++) { - /* Build a hash of the pass */ - g_snprintf (digits, sizeof (digits), "%02x", digest[i]); - strcat (rv, digits); - } - - return (rv); -} - -char *obfucrypt (char *line, const char *password) -{ - int i, j; - char *rv; - - if (password == NULL) return (NULL); - - rv = g_new0 (char, strlen (line) + 1); - - i = j = 0; - while (*line) { - /* Encrypt/obfuscate the line, using the password */ - if (*(signed char*)line < 0) *line = - (*line); - - rv[j] = *line + password[i]; /* Overflow intended */ - - line++; - if (!password[++i]) i = 0; - j++; - } - - return (rv); -} - -char *deobfucrypt (char *line, const char *password) -{ - int i, j; - char *rv; - - if (password == NULL) return (NULL); - - rv = g_new0 (char, strlen (line) + 1); - - i = j = 0; - while (*line) { - /* Decrypt/deobfuscate the line, using the pass */ - rv[j] = *line - password[i]; /* Overflow intended */ - - line++; - if (!password[++i]) i = 0; - j++; - } - - return (rv); -} diff --git a/crypting.h b/crypting.h deleted file mode 100644 index e13b0433..00000000 --- a/crypting.h +++ /dev/null @@ -1,29 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Sjoerd Hemminga and others * - \********************************************************************/ - -/* A little bit of encryption for the users' passwords */ - -/* - 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 -*/ - -int checkpass (const char *password, const char *md5sum); -G_GNUC_MALLOC char *hashpass (const char *password); -G_GNUC_MALLOC char *obfucrypt (char *line, const char *password); -G_GNUC_MALLOC char *deobfucrypt (char *line, const char *password); diff --git a/irc.c b/irc.c index e23239f4..edb42dde 100644 --- a/irc.c +++ b/irc.c @@ -26,7 +26,6 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "sock.h" -#include "crypting.h" #include "ipc.h" #include "dcc.h" diff --git a/root_commands.c b/root_commands.c index e42268b5..d3b0c7d3 100644 --- a/root_commands.c +++ b/root_commands.c @@ -25,7 +25,6 @@ #define BITLBEE_CORE #include "commands.h" -#include "crypting.h" #include "bitlbee.h" #include "help.h" #include "chat.h" diff --git a/storage.c b/storage.c index f011ade2..ad1833fc 100644 --- a/storage.c +++ b/storage.c @@ -27,7 +27,6 @@ #define BITLBEE_CORE #include "bitlbee.h" -#include "crypting.h" extern storage_t storage_text; extern storage_t storage_xml; @@ -65,7 +64,6 @@ GList *storage_init(const char *primary, char **migrate) int i; storage_t *storage; - register_storage_backend(&storage_text); register_storage_backend(&storage_xml); storage = storage_init_single(primary); diff --git a/storage_text.c b/storage_text.c deleted file mode 100644 index 8ce4edcf..00000000 --- a/storage_text.c +++ /dev/null @@ -1,157 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Storage backend that uses the same file format as <=1.0 */ - -/* - 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 "crypting.h" -#ifdef _WIN32 -# define umask _umask -# define mode_t int -#endif - -#ifndef F_OK -#define F_OK 0 -#endif - -static void text_init (void) -{ - /* Don't complain about the configuration directory anymore, leave it - up to the XML storage module, which uses the same directory for it - anyway. Nobody should be using just the text plugin anymore since - it's read only! */ -} - -static storage_status_t text_load( irc_t *irc, const char* password ) -{ - char s[512]; - char *line; - int proto; - char nick[MAX_NICK_LENGTH+1]; - FILE *fp; - user_t *ru = user_find( irc, ROOT_NICK ); - account_t *acc, *acc_lookup[9]; - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); - fp = fopen( s, "r" ); - if( !fp ) return STORAGE_NO_SUCH_USER; - - fscanf( fp, "%32[^\n]s", s ); - - if( checkpass( password, s ) != 0 ) - { - fclose( fp ); - return STORAGE_INVALID_PASSWORD; - } - - while( fscanf( fp, "%511[^\n]s", s ) > 0 ) - { - fgetc( fp ); - line = deobfucrypt( s, password ); - if (line == NULL) return STORAGE_OTHER_ERROR; - root_command_string( irc, ru, line, 0 ); - g_free( line ); - } - fclose( fp ); - - /* Build a list with the first listed account of every protocol - number. So if the user had nicks defined for a second account on - the same IM network, those nicks will be added to the wrong - account, and the user should rename those buddies again. But at - least from now on things will be saved properly. */ - memset( acc_lookup, 0, sizeof( acc_lookup ) ); - for( acc = irc->accounts; acc; acc = acc->next ) - { - if( acc_lookup[0] == NULL && strcmp( acc->prpl->name, "oscar" ) == 0 ) - acc_lookup[0] = acc_lookup[1] = acc_lookup[3] = acc; - else if( acc_lookup[2] == NULL && strcmp( acc->prpl->name, "yahoo" ) == 0 ) - acc_lookup[2] = acc; - else if( acc_lookup[4] == NULL && strcmp( acc->prpl->name, "msn" ) == 0 ) - acc_lookup[4] = acc; - else if( acc_lookup[8] == NULL && strcmp( acc->prpl->name, "jabber" ) == 0 ) - acc_lookup[8] = acc; - } - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); - fp = fopen( s, "r" ); - if( !fp ) return STORAGE_NO_SUCH_USER; - while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 ) - { - if( proto < 0 || proto > 8 || ( acc = acc_lookup[proto] ) == NULL ) - continue; - - http_decode( s ); - nick_set( acc, s, nick ); - } - fclose( fp ); - - return STORAGE_OK; -} - -static storage_status_t text_check_pass( const char *nick, const char *password ) -{ - char s[512]; - FILE *fp; - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" ); - fp = fopen( s, "r" ); - if (!fp) - return STORAGE_NO_SUCH_USER; - - fscanf( fp, "%32[^\n]s", s ); - fclose( fp ); - - if (checkpass( password, s) == -1) - return STORAGE_INVALID_PASSWORD; - - return STORAGE_OK; -} - -static storage_status_t text_remove( const char *nick, const char *password ) -{ - char s[512]; - storage_status_t status; - - status = text_check_pass( nick, password ); - if (status != STORAGE_OK) - return status; - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" ); - if (unlink( s ) == -1) - return STORAGE_OTHER_ERROR; - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".nicks" ); - if (unlink( s ) == -1) - return STORAGE_OTHER_ERROR; - - return STORAGE_OK; -} - -storage_t storage_text = { - .name = "text", - .init = text_init, - .check_pass = text_check_pass, - .remove = text_remove, - .load = text_load -}; diff --git a/win32.c b/win32.c index 4ab1d522..99d2a8ca 100644 --- a/win32.c +++ b/win32.c @@ -26,7 +26,6 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "commands.h" -#include "crypting.h" #include "protocols/nogaim.h" #include "help.h" #include -- cgit v1.2.3 From 3ddb7477f51d3cf1632e2a8b6f7da4c0609a52cb Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 26 Mar 2010 08:14:37 -0400 Subject: One total mess that doesn't do much yet, but reorganised some stuff and untying the IRC and the core parts a little bit. Lots of work left to do. --- Makefile | 3 +- account.c | 359 ------------------- account.h | 72 ---- bitlbee.h | 3 +- chat.c | 192 ----------- chat.h | 51 --- conf.c | 2 +- ipc.c | 12 +- irc.c | 969 +++++++--------------------------------------------- irc.h | 93 +++-- irc_commands.c | 115 ++++--- nick.c | 4 +- protocols/Makefile | 4 +- protocols/account.c | 359 +++++++++++++++++++ protocols/account.h | 72 ++++ protocols/chat.c | 192 +++++++++++ protocols/chat.h | 51 +++ protocols/user.c | 106 ++++++ protocols/user.h | 40 +++ set.c | 2 + unix.c | 4 +- user.c | 231 ------------- user.h | 62 ---- 23 files changed, 1067 insertions(+), 1931 deletions(-) delete mode 100644 account.c delete mode 100644 account.h delete mode 100644 chat.c delete mode 100644 chat.h create mode 100644 protocols/account.c create mode 100644 protocols/account.h create mode 100644 protocols/chat.c create mode 100644 protocols/chat.h create mode 100644 protocols/user.c create mode 100644 protocols/user.h delete mode 100644 user.c delete mode 100644 user.h diff --git a/Makefile b/Makefile index ef34f123..ecd133e2 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,8 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o +#objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) +objects = bitlbee.o help.o ipc.o irc.o irc_commands.o irc_send.o irc_user.o nick.o set.o headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols diff --git a/account.c b/account.c deleted file mode 100644 index a844d229..00000000 --- a/account.c +++ /dev/null @@ -1,359 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2010 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Account management functions */ - -/* - 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 "account.h" -#include "chat.h" - -account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) -{ - account_t *a; - set_t *s; - - if( irc->accounts ) - { - for( a = irc->accounts; a->next; a = a->next ); - a = a->next = g_new0( account_t, 1 ); - } - else - { - irc->accounts = a = g_new0 ( account_t, 1 ); - } - - a->prpl = prpl; - a->user = g_strdup( user ); - a->pass = g_strdup( pass ); - a->auto_connect = 1; - a->irc = irc; - - s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); - s->flags |= ACC_SET_NOSAVE; - - s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); - - s = set_add( &a->set, "nick_source", "handle", NULL, a ); - - s = set_add( &a->set, "password", NULL, set_eval_account, a ); - s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; - - s = set_add( &a->set, "username", NULL, set_eval_account, a ); - s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; - set_setstr( &a->set, "username", user ); - - a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); - - /* This function adds some more settings (and might want to do more - things that have to be done now, although I can't think of anything. */ - if( prpl->init ) - prpl->init( a ); - - s = set_add( &a->set, "away", NULL, set_eval_account, a ); - s->flags |= SET_NULL_OK; - - if( a->flags & ACC_FLAG_STATUS_MESSAGE ) - { - s = set_add( &a->set, "status", NULL, set_eval_account, a ); - s->flags |= SET_NULL_OK; - } - - return a; -} - -char *set_eval_account( set_t *set, char *value ) -{ - account_t *acc = set->data; - - /* Double-check: We refuse to edit on-line accounts. */ - if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic ) - return SET_INVALID; - - if( strcmp( set->key, "server" ) == 0 ) - { - g_free( acc->server ); - if( value && *value ) - { - acc->server = g_strdup( value ); - return value; - } - else - { - acc->server = g_strdup( set->def ); - return g_strdup( set->def ); - } - } - else if( strcmp( set->key, "username" ) == 0 ) - { - g_free( acc->user ); - acc->user = g_strdup( value ); - return value; - } - else if( strcmp( set->key, "password" ) == 0 ) - { - if( value ) - { - g_free( acc->pass ); - acc->pass = g_strdup( value ); - return NULL; /* password shouldn't be visible in plaintext! */ - } - else - { - /* NULL can (should) be stored in the set_t - variable, but is otherwise not correct. */ - return SET_INVALID; - } - } - else if( strcmp( set->key, "auto_connect" ) == 0 ) - { - if( !is_bool( value ) ) - return SET_INVALID; - - acc->auto_connect = bool2int( value ); - return value; - } - else if( strcmp( set->key, "away" ) == 0 || - strcmp( set->key, "status" ) == 0 ) - { - if( acc->ic && acc->ic->flags & OPT_LOGGED_IN ) - { - /* If we're currently on-line, set the var now already - (bit of a hack) and send an update. */ - g_free( set->value ); - set->value = g_strdup( value ); - - imc_away_send_update( acc->ic ); - } - - return value; - } - - return SET_INVALID; -} - -account_t *account_get( irc_t *irc, char *id ) -{ - account_t *a, *ret = NULL; - char *handle, *s; - int nr; - - /* This checks if the id string ends with (...) */ - if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 ) - { - struct prpl *proto; - - *s = *handle = 0; - handle ++; - - if( ( proto = find_protocol( id ) ) ) - { - for( a = irc->accounts; a; a = a->next ) - if( a->prpl == proto && - a->prpl->handle_cmp( handle, a->user ) == 0 ) - ret = a; - } - - /* Restore the string. */ - handle --; - *handle = '('; - *s = ')'; - - if( ret ) - return ret; - } - - if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) - { - for( a = irc->accounts; a; a = a->next ) - if( ( nr-- ) == 0 ) - return( a ); - - return( NULL ); - } - - for( a = irc->accounts; a; a = a->next ) - { - if( g_strcasecmp( id, a->prpl->name ) == 0 ) - { - if( !ret ) - ret = a; - else - return( NULL ); /* We don't want to match more than one... */ - } - else if( strstr( a->user, id ) ) - { - if( !ret ) - ret = a; - else - return( NULL ); - } - } - - return( ret ); -} - -void account_del( irc_t *irc, account_t *acc ) -{ - account_t *a, *l = NULL; - struct chat *c, *nc; - - if( acc->ic ) - /* Caller should have checked, accounts still in use can't be deleted. */ - return; - - for( a = irc->accounts; a; a = (l=a)->next ) - if( a == acc ) - { - if( l ) - l->next = a->next; - else - irc->accounts = a->next; - - for( c = irc->chatrooms; c; c = nc ) - { - nc = c->next; - if( acc == c->acc ) - chat_del( irc, c ); - } - - while( a->set ) - set_del( &a->set, a->set->key ); - - g_hash_table_destroy( a->nicks ); - - g_free( a->user ); - g_free( a->pass ); - g_free( a->server ); - if( a->reconnect ) /* This prevents any reconnect still queued to happen */ - cancel_auto_reconnect( a ); - g_free( a ); - - break; - } -} - -void account_on( irc_t *irc, account_t *a ) -{ - if( a->ic ) - { - /* Trying to enable an already-enabled account */ - return; - } - - cancel_auto_reconnect( a ); - - a->reconnect = 0; - a->prpl->login( a ); -} - -void account_off( irc_t *irc, account_t *a ) -{ - imc_logout( a->ic, FALSE ); - a->ic = NULL; - if( a->reconnect ) - { - /* Shouldn't happen */ - cancel_auto_reconnect( a ); - } -} - -struct account_reconnect_delay -{ - int start; - char op; - int step; - int max; -}; - -int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p ) -{ - memset( p, 0, sizeof( *p ) ); - /* A whole day seems like a sane "maximum maximum". */ - p->max = 86400; - - /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */ - while( *value && isdigit( *value ) ) - p->start = p->start * 10 + *value++ - '0'; - - /* Sure, call me evil for implementing my own fscanf here, but it's - dead simple and I immediately know where to continue parsing. */ - - if( *value == 0 ) - /* If the string ends now, the delay is constant. */ - return 1; - else if( *value != '+' && *value != '*' ) - /* Otherwise allow either a + or a * */ - return 0; - - p->op = *value++; - - /* + or * the delay by this number every time. */ - while( *value && isdigit( *value ) ) - p->step = p->step * 10 + *value++ - '0'; - - if( *value == 0 ) - /* Use the default maximum (one day). */ - return 1; - else if( *value != '<' ) - return 0; - - p->max = 0; - value ++; - while( *value && isdigit( *value ) ) - p->max = p->max * 10 + *value++ - '0'; - - return p->max > 0; -} - -char *set_eval_account_reconnect_delay( set_t *set, char *value ) -{ - struct account_reconnect_delay p; - - return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID; -} - -int account_reconnect_delay( account_t *a ) -{ - char *setting = set_getstr( &a->irc->set, "auto_reconnect_delay" ); - struct account_reconnect_delay p; - - if( account_reconnect_delay_parse( setting, &p ) ) - { - if( a->auto_reconnect_delay == 0 ) - a->auto_reconnect_delay = p.start; - else if( p.op == '+' ) - a->auto_reconnect_delay += p.step; - else if( p.op == '*' ) - a->auto_reconnect_delay *= p.step; - - if( a->auto_reconnect_delay > p.max ) - a->auto_reconnect_delay = p.max; - } - else - { - a->auto_reconnect_delay = 0; - } - - return a->auto_reconnect_delay; -} diff --git a/account.h b/account.h deleted file mode 100644 index 984dcfe6..00000000 --- a/account.h +++ /dev/null @@ -1,72 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Account management functions */ - -/* - 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 -*/ - -#ifndef _ACCOUNT_H -#define _ACCOUNT_H - -typedef struct account -{ - struct prpl *prpl; - char *user; - char *pass; - char *server; - - int auto_connect; - int auto_reconnect_delay; - int reconnect; - int flags; - - set_t *set; - GHashTable *nicks; - - struct irc *irc; - struct im_connection *ic; - struct account *next; -} account_t; - -account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ); -account_t *account_get( irc_t *irc, char *id ); -void account_del( irc_t *irc, account_t *acc ); -void account_on( irc_t *irc, account_t *a ); -void account_off( irc_t *irc, account_t *a ); - -char *set_eval_account( set_t *set, char *value ); -char *set_eval_account_reconnect_delay( set_t *set, char *value ); -int account_reconnect_delay( account_t *a ); - -typedef enum -{ - ACC_SET_NOSAVE = 0x01, /* Don't save this setting (i.e. stored elsewhere). */ - ACC_SET_OFFLINE_ONLY = 0x02, /* Allow changes only if the acct is offline. */ - ACC_SET_ONLINE_ONLY = 0x04, /* Allow changes only if the acct is online. */ -} account_set_flag_t; - -typedef enum -{ - ACC_FLAG_AWAY_MESSAGE = 0x01, /* Supports away messages instead of just states. */ - ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */ -} account_flag_t; - -#endif diff --git a/bitlbee.h b/bitlbee.h index 5f98deef..a40c48e6 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -123,6 +123,7 @@ #define HELP_FILE VARDIR "help.txt" #define CONF_FILE_DEF ETCDIR "bitlbee.conf" +#include "bee.h" #include "irc.h" #include "storage.h" #include "set.h" @@ -157,7 +158,7 @@ int bitlbee_inetd_init( void ); gboolean bitlbee_io_current_client_read( gpointer data, gint source, b_input_condition cond ); gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_condition cond ); -void root_command_string( irc_t *irc, user_t *u, char *command, int flags ); +//void root_command_string( irc_t *irc, user_t *u, char *command, int flags ); void root_command( irc_t *irc, char *command[] ); gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond ); diff --git a/chat.c b/chat.c deleted file mode 100644 index 8c5ce0bc..00000000 --- a/chat.c +++ /dev/null @@ -1,192 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2008 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Keep track of chatrooms the user is interested in */ - -/* - 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 -*/ - -#include "bitlbee.h" -#include "chat.h" - -struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ) -{ - struct chat *c, *l; - set_t *s; - - if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) || - chat_chancmp( channel, irc->channel ) == 0 ) - { - return NULL; - } - - for( c = irc->chatrooms; c; c = c->next ) - { - if( chat_chancmp( channel, c->channel ) == 0 ) - return NULL; - - if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) - return NULL; - - l = c; - } - - if( irc->chatrooms == NULL ) - irc->chatrooms = c = g_new0( struct chat, 1 ); - else - l->next = c = g_new0( struct chat, 1 ); - - c->acc = acc; - c->handle = g_strdup( handle ); - c->channel = g_strdup( channel ); - - s = set_add( &c->set, "auto_join", "false", set_eval_bool, c ); - /* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */ - s = set_add( &c->set, "nick", NULL, NULL, c ); - s->flags |= SET_NULL_OK; - - return c; -} - -struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ) -{ - struct chat *c; - - for( c = irc->chatrooms; c; c = c->next ) - { - if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) - break; - } - - return c; -} - -struct chat *chat_bychannel( irc_t *irc, char *channel ) -{ - struct chat *c; - - for( c = irc->chatrooms; c; c = c->next ) - { - if( chat_chancmp( channel, c->channel ) == 0 ) - break; - } - - return c; -} - -struct chat *chat_get( irc_t *irc, char *id ) -{ - struct chat *c, *ret = NULL; - int nr; - - if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) - { - for( c = irc->chatrooms; c; c = c->next ) - if( ( nr-- ) == 0 ) - return c; - - return NULL; - } - - for( c = irc->chatrooms; c; c = c->next ) - { - if( strstr( c->handle, id ) ) - { - if( !ret ) - ret = c; - else - return NULL; - } - else if( strstr( c->channel, id ) ) - { - if( !ret ) - ret = c; - else - return NULL; - } - } - - return ret; -} - -int chat_del( irc_t *irc, struct chat *chat ) -{ - struct chat *c, *l = NULL; - - for( c = irc->chatrooms; c; c = (l=c)->next ) - if( c == chat ) - break; - - if( c == NULL ) - return 0; - else if( l == NULL ) - irc->chatrooms = c->next; - else - l->next = c->next; - - while( c->set ) - set_del( &c->set, c->set->key ); - - g_free( c->handle ); - g_free( c->channel ); - g_free( c ); - - return 1; -} - -int chat_chancmp( char *a, char *b ) -{ - if( !chat_chanok( a ) || !chat_chanok( b ) ) - return 0; - - if( a[0] == b[0] ) - return nick_cmp( a + 1, b + 1 ); - else - return -1; -} - -int chat_chanok( char *a ) -{ - if( strchr( CTYPES, a[0] ) != NULL ) - return nick_ok( a + 1 ); - else - return 0; -} - -int chat_join( irc_t *irc, struct chat *c, const char *password ) -{ - struct groupchat *gc; - char *nick = set_getstr( &c->set, "nick" ); - - if( c->acc->ic == NULL || c->acc->prpl->chat_join == NULL ) - return 0; - - if( nick == NULL ) - nick = irc->nick; - - if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, password ) ) ) - { - g_free( gc->channel ); - gc->channel = g_strdup( c->channel ); - return 1; - } - - return 0; -} diff --git a/chat.h b/chat.h deleted file mode 100644 index 7196aea8..00000000 --- a/chat.h +++ /dev/null @@ -1,51 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2008 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Keep track of chatrooms the user is interested in */ - -/* - 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 -*/ - -#ifndef _CHAT_H -#define _CHAT_H - -struct chat -{ - account_t *acc; - - char *handle; - char *channel; - set_t *set; - - struct chat *next; -}; - -struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ); -struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ); -struct chat *chat_bychannel( irc_t *irc, char *channel ); -struct chat *chat_get( irc_t *irc, char *id ); -int chat_del( irc_t *irc, struct chat *chat ); - -int chat_chancmp( char *a, char *b ); -int chat_chanok( char *a ); - -int chat_join( irc_t *irc, struct chat *c, const char *password ); - -#endif diff --git a/conf.c b/conf.c index 8687afea..d4e482cb 100644 --- a/conf.c +++ b/conf.c @@ -368,7 +368,7 @@ void conf_loaddefaults( irc_t *irc ) { if( g_strcasecmp( ini->section, "defaults" ) == 0 ) { - set_t *s = set_find( &irc->set, ini->key ); + set_t *s = set_find( &irc->b->set, ini->key ); if( s ) { diff --git a/ipc.c b/ipc.c index d6b850f1..2d833354 100644 --- a/ipc.c +++ b/ipc.c @@ -137,7 +137,7 @@ static void ipc_child_cmd_wallops( irc_t *irc, char **cmd ) return; if( strchr( irc->umode, 'w' ) ) - irc_write( irc, ":%s WALLOPS :%s", irc->myhost, cmd[1] ); + irc_write( irc, ":%s WALLOPS :%s", irc->root->host, cmd[1] ); } static void ipc_child_cmd_wall( irc_t *irc, char **cmd ) @@ -146,7 +146,7 @@ static void ipc_child_cmd_wall( irc_t *irc, char **cmd ) return; if( strchr( irc->umode, 's' ) ) - irc_write( irc, ":%s NOTICE %s :%s", irc->myhost, irc->nick, cmd[1] ); + irc_write( irc, ":%s NOTICE %s :%s", irc->root->host, irc->user->nick, cmd[1] ); } static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd ) @@ -155,7 +155,7 @@ static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd ) return; if( strchr( irc->umode, 'o' ) ) - irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->myhost, irc->nick, cmd[1] ); + irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->root->host, irc->user->nick, cmd[1] ); } static void ipc_child_cmd_rehash( irc_t *irc, char **cmd ) @@ -175,10 +175,10 @@ static void ipc_child_cmd_kill( irc_t *irc, char **cmd ) if( !( irc->status & USTATUS_LOGGED_IN ) ) return; - if( nick_cmp( cmd[1], irc->nick ) != 0 ) + if( nick_cmp( cmd[1], irc->user->nick ) != 0 ) return; /* It's not for us. */ - irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->mynick, irc->mynick, irc->myhost, irc->nick, cmd[2] ); + irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick, cmd[2] ); irc_abort( irc, 0, "Killed by operator: %s", cmd[2] ); } @@ -187,7 +187,7 @@ static void ipc_child_cmd_hello( irc_t *irc, char **cmd ) if( !( irc->status & USTATUS_LOGGED_IN ) ) ipc_to_master_str( "HELLO\r\n" ); else - ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->host, irc->nick, irc->realname ); + ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); } static const command_t ipc_child_commands[] = { diff --git a/irc.c b/irc.c index edb42dde..8e44fe9e 100644 --- a/irc.c +++ b/irc.c @@ -4,7 +4,7 @@ * Copyright 2002-2004 Wilmer van der Gaast and others * \********************************************************************/ -/* The big hairy IRCd part of the project */ +/* The IRC-based UI (for now the only one) */ /* This program is free software; you can redistribute it and/or modify @@ -23,85 +23,21 @@ Suite 330, Boston, MA 02111-1307 USA */ -#define BITLBEE_CORE #include "bitlbee.h" -#include "sock.h" -#include "ipc.h" -#include "dcc.h" -static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); +GSList *irc_connection_list; -GSList *irc_connection_list = NULL; - -static char *set_eval_password( set_t *set, char *value ) -{ - irc_t *irc = set->data; - - if( irc->status & USTATUS_IDENTIFIED && value ) - { - irc_setpass( irc, value ); - return NULL; - } - else - { - return SET_INVALID; - } -} - -static char *set_eval_charset( set_t *set, char *value ) -{ - irc_t *irc = set->data; - GIConv ic, oc; - - if( g_strcasecmp( value, "none" ) == 0 ) - value = g_strdup( "utf-8" ); - - if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) - { - return NULL; - } - if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) - { - g_iconv_close( ic ); - return NULL; - } - - if( irc->iconv != (GIConv) -1 ) - g_iconv_close( irc->iconv ); - if( irc->oconv != (GIConv) -1 ) - g_iconv_close( irc->oconv ); - - irc->iconv = ic; - irc->oconv = oc; - - return value; -} - -static char *set_eval_away_status( set_t *set, char *value ) -{ - irc_t *irc = set->data; - account_t *a; - - g_free( set->value ); - set->value = g_strdup( value ); - - for( a = irc->accounts; a; a = a->next ) - { - struct im_connection *ic = a->ic; - - if( ic && ic->flags & OPT_LOGGED_IN ) - imc_away_send_update( ic ); - } - - return value; -} +static char *set_eval_charset( set_t *set, char *value ); irc_t *irc_new( int fd ) { irc_t *irc; struct sockaddr_storage sock; socklen_t socklen = sizeof( sock ); + char *host = NULL, *myhost = NULL; + irc_user_t *iu; set_t *s; + bee_t *b; irc = g_new0( irc_t, 1 ); @@ -113,19 +49,17 @@ irc_t *irc_new( int fd ) irc->status = USTATUS_OFFLINE; irc->last_pong = gettime(); - irc->userhash = g_hash_table_new( g_str_hash, g_str_equal ); + irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal ); irc->watches = g_hash_table_new( g_str_hash, g_str_equal ); strcpy( irc->umode, UMODE ); - irc->mynick = g_strdup( ROOT_NICK ); - irc->channel = g_strdup( ROOT_CHAN ); irc->iconv = (GIConv) -1; irc->oconv = (GIConv) -1; if( global.conf->hostname ) { - irc->myhost = g_strdup( global.conf->hostname ); + myhost = g_strdup( global.conf->hostname ); } else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) { @@ -134,7 +68,7 @@ irc_t *irc_new( int fd ) if( getnameinfo( (struct sockaddr *) &sock, socklen, buf, NI_MAXHOST, NULL, 0, 0 ) == 0 ) { - irc->myhost = g_strdup( ipv6_unwrap( buf ) ); + myhost = g_strdup( ipv6_unwrap( buf ) ); } } @@ -145,57 +79,58 @@ irc_t *irc_new( int fd ) if( getnameinfo( (struct sockaddr *)&sock, socklen, buf, NI_MAXHOST, NULL, 0, 0 ) == 0 ) { - irc->host = g_strdup( ipv6_unwrap( buf ) ); + host = g_strdup( ipv6_unwrap( buf ) ); } } - if( irc->host == NULL ) - irc->host = g_strdup( "localhost.localdomain" ); - if( irc->myhost == NULL ) - irc->myhost = g_strdup( "localhost.localdomain" ); + if( host == NULL ) + host = g_strdup( "localhost.localdomain" ); + if( myhost == NULL ) + myhost = g_strdup( "localhost.localdomain" ); - if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) - irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc ); - - irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" ); + //if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) + // irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc ); irc_connection_list = g_slist_append( irc_connection_list, irc ); - s = set_add( &irc->set, "away", NULL, set_eval_away_status, irc ); - s->flags |= SET_NULL_OK; - s = set_add( &irc->set, "away_devoice", "true", set_eval_away_devoice, irc ); - s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc ); - s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); - s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); - s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc ); - s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc ); - s = set_add( &irc->set, "debug", "false", set_eval_bool, irc ); - s = set_add( &irc->set, "default_target", "root", NULL, irc ); - s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); - s = set_add( &irc->set, "handle_unknown", "root", NULL, irc ); - s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "ops", "both", set_eval_ops, irc ); - s = set_add( &irc->set, "password", NULL, set_eval_password, irc ); - s->flags |= SET_NULL_OK; - s = set_add( &irc->set, "private", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "query_order", "lifo", NULL, irc ); - s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc ); - s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "status", NULL, set_eval_away_status, irc ); - s->flags |= SET_NULL_OK; - s = set_add( &irc->set, "strip_html", "true", NULL, irc ); - s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc ); - s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); - - conf_loaddefaults( irc ); + b = irc->b = bee_new(); + + s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc ); + s = set_add( &b->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); + s = set_add( &b->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); + s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); + //s = set_add( &b->set, "control_channel", irc->channel, NULL/*set_eval_control_channel*/, irc ); + s = set_add( &b->set, "default_target", "root", NULL, irc ); + s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc ); + s = set_add( &b->set, "handle_unknown", "root", NULL, irc ); + s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); + s = set_add( &b->set, "ops", "both", NULL/*set_eval_ops*/, irc ); + s = set_add( &b->set, "private", "true", set_eval_bool, irc ); + s = set_add( &b->set, "query_order", "lifo", NULL, irc ); + s = set_add( &b->set, "root_nick", ROOT_NICK, NULL/*set_eval_root_nick*/, irc ); + s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc ); + s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc ); + s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc ); + + irc->root = iu = irc_user_new( irc, ROOT_NICK ); + iu->host = g_strdup( myhost ); + iu->fullname = g_strdup( ROOT_FN ); + + iu = irc_user_new( irc, NS_NICK ); + iu->host = g_strdup( myhost ); + iu->fullname = g_strdup( ROOT_FN ); + + irc->user = g_new0( irc_user_t, 1 ); + irc->user->host = g_strdup( host ); + + conf_loaddefaults( b ); /* Evaluator sets the iconv/oconv structures. */ - set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) ); + set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) ); - return( irc ); + irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" ); + + return irc; } /* immed=1 makes this function pretty much equal to irc_free(), except that @@ -216,7 +151,7 @@ void irc_abort( irc_t *irc, int immed, char *format, ... ) irc_write( irc, "ERROR :Closing link: %s", reason ); ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", - irc->nick ? irc->nick : "(NONE)", irc->host, reason ); + irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, reason ); g_free( reason ); } @@ -226,7 +161,7 @@ void irc_abort( irc_t *irc, int immed, char *format, ... ) irc_write( irc, "ERROR :Closing link" ); ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", - irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" ); + irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, "No reason given" ); } irc->status |= USTATUS_SHUTDOWN; @@ -247,65 +182,27 @@ void irc_abort( irc_t *irc, int immed, char *format, ... ) } } -static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) -{ - g_free( key ); - - return( TRUE ); -} +static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ); -/* Because we have no garbage collection, this is quite annoying */ void irc_free( irc_t * irc ) { - user_t *user, *usertmp; - log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); - if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) ) + /* + if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) ) if( storage_save( irc, NULL, TRUE ) != STORAGE_OK ) irc_usermsg( irc, "Error while saving settings!" ); + */ irc_connection_list = g_slist_remove( irc_connection_list, irc ); - while( irc->accounts ) - { - if( irc->accounts->ic ) - imc_logout( irc->accounts->ic, FALSE ); - else if( irc->accounts->reconnect ) - cancel_auto_reconnect( irc->accounts ); - - if( irc->accounts->ic == NULL ) - account_del( irc, irc->accounts ); - else - /* Nasty hack, but account_del() doesn't work in this - case and we don't want infinite loops, do we? ;-) */ - irc->accounts = irc->accounts->next; - } - + /* while( irc->queries != NULL ) query_del( irc, irc->queries ); + */ - while( irc->set ) - set_del( &irc->set, irc->set->key ); - - if (irc->users != NULL) - { - user = irc->users; - while( user != NULL ) - { - g_free( user->nick ); - g_free( user->away ); - g_free( user->handle ); - if( user->user != user->nick ) g_free( user->user ); - if( user->host != user->nick ) g_free( user->host ); - if( user->realname != user->nick ) g_free( user->realname ); - b_event_remove( user->sendbuf_timer ); - - usertmp = user; - user = user->next; - g_free( usertmp ); - } - } + while( irc->users ) + irc_user_free( irc, irc->users->data ); if( irc->ping_source_id > 0 ) b_event_remove( irc->ping_source_id ); @@ -317,8 +214,8 @@ void irc_free( irc_t * irc ) closesocket( irc->fd ); irc->fd = -1; - g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL ); - g_hash_table_destroy( irc->userhash ); + g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL ); + g_hash_table_destroy( irc->nick_user_hash ); g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL ); g_hash_table_destroy( irc->watches ); @@ -331,19 +228,8 @@ void irc_free( irc_t * irc ) g_free( irc->sendbuffer ); g_free( irc->readbuffer ); - g_free( irc->nick ); - g_free( irc->user ); - g_free( irc->host ); - g_free( irc->realname ); g_free( irc->password ); - g_free( irc->myhost ); - g_free( irc->mynick ); - - g_free( irc->channel ); - - g_free( irc->last_target ); - g_free( irc ); if( global.conf->runmode == RUNMODE_INETD || @@ -354,19 +240,15 @@ void irc_free( irc_t * irc ) b_main_quit(); } -/* USE WITH CAUTION! - Sets pass without checking */ -void irc_setpass (irc_t *irc, const char *pass) +static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) { - g_free (irc->password); + g_free( key ); - if (pass) { - irc->password = g_strdup (pass); - } else { - irc->password = NULL; - } + return( TRUE ); } +static char **irc_splitlines( char *buffer ); + void irc_process( irc_t *irc ) { char **lines, *temp, **cmd; @@ -374,7 +256,7 @@ void irc_process( irc_t *irc ) if( irc->readbuffer != NULL ) { - lines = irc_tokenize( irc->readbuffer ); + lines = irc_splitlines( irc->readbuffer ); for( i = 0; *lines[i] != '\0'; i ++ ) { @@ -411,14 +293,14 @@ void irc_process( irc_t *irc ) "expect by changing the charset setting. See " "`help set charset' for more information. Your " "message was ignored.", - set_getstr( &irc->set, "charset" ) ); + set_getstr( &irc->b->set, "charset" ) ); g_free( conv ); conv = NULL; } else { - irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, + irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "Warning: invalid characters received at login time." ); conv = g_strdup( lines[i] ); @@ -456,9 +338,11 @@ void irc_process( irc_t *irc ) } } -/* Splits a long string into separate lines. The array is NULL-terminated and, unless the string - contains an incomplete line at the end, ends with an empty string. */ -char **irc_tokenize( char *buffer ) +/* Splits a long string into separate lines. The array is NULL-terminated + and, unless the string contains an incomplete line at the end, ends with + an empty string. Could use g_strsplit() but this one does it in-place. + (So yes, it's destructive.) */ +static char **irc_splitlines( char *buffer ) { int i, j, n = 3; char **lines; @@ -589,46 +473,45 @@ char *irc_build_line( char **cmd ) return s; } -void irc_reply( irc_t *irc, int code, char *format, ... ) +void irc_write( irc_t *irc, char *format, ... ) { - char text[IRC_MAX_LINE]; va_list params; - + va_start( params, format ); - g_vsnprintf( text, IRC_MAX_LINE, format, params ); + irc_vawrite( irc, format, params ); va_end( params ); - irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text ); - + return; } -int irc_usermsg( irc_t *irc, char *format, ... ) +void irc_write_all( int now, char *format, ... ) { - char text[1024]; va_list params; - char is_private = 0; - user_t *u; - - u = user_find( irc, irc->mynick ); - is_private = u->is_private; + GSList *temp; va_start( params, format ); - g_vsnprintf( text, sizeof( text ), format, params ); - va_end( params ); - return( irc_msgfrom( irc, u->nick, text ) ); -} - -void irc_write( irc_t *irc, char *format, ... ) -{ - va_list params; - - va_start( params, format ); - irc_vawrite( irc, format, params ); + temp = irc_connection_list; + while( temp != NULL ) + { + irc_t *irc = temp->data; + + if( now ) + { + g_free( irc->sendbuffer ); + irc->sendbuffer = g_strdup( "\r\n" ); + } + irc_vawrite( temp->data, format, params ); + if( now ) + { + bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ); + } + temp = temp->next; + } + va_end( params ); - return; -} +} void irc_vawrite( irc_t *irc, char *format, va_list params ) { @@ -685,105 +568,18 @@ void irc_vawrite( irc_t *irc, char *format, va_list params ) return; } -void irc_write_all( int now, char *format, ... ) -{ - va_list params; - GSList *temp; - - va_start( params, format ); - - temp = irc_connection_list; - while( temp != NULL ) - { - irc_t *irc = temp->data; - - if( now ) - { - g_free( irc->sendbuffer ); - irc->sendbuffer = g_strdup( "\r\n" ); - } - irc_vawrite( temp->data, format, params ); - if( now ) - { - bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ); - } - temp = temp->next; - } - - va_end( params ); - return; -} - -void irc_names( irc_t *irc, char *channel ) -{ - user_t *u; - char namelist[385] = ""; - struct groupchat *c = NULL; - char *ops = set_getstr( &irc->set, "ops" ); - - /* RFCs say there is no error reply allowed on NAMES, so when the - channel is invalid, just give an empty reply. */ - - if( g_strcasecmp( channel, irc->channel ) == 0 ) - { - for( u = irc->users; u; u = u->next ) if( u->online ) - { - if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 ) - { - irc_reply( irc, 353, "= %s :%s", channel, namelist ); - *namelist = 0; - } - - if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) ) - strcat( namelist, "+" ); - else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) || - ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ) - strcat( namelist, "@" ); - - strcat( namelist, u->nick ); - strcat( namelist, " " ); - } - } - else if( ( c = irc_chat_by_channel( irc, channel ) ) ) - { - GList *l; - - /* root and the user aren't in the channel userlist but should - show up in /NAMES, so list them first: */ - sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick, - strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick ); - - for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) ) - { - if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 ) - { - irc_reply( irc, 353, "= %s :%s", channel, namelist ); - *namelist = 0; - } - - strcat( namelist, u->nick ); - strcat( namelist, " " ); - } - } - - if( *namelist ) - irc_reply( irc, 353, "= %s :%s", channel, namelist ); - - irc_reply( irc, 366, "%s :End of /NAMES list", channel ); -} - int irc_check_login( irc_t *irc ) { - if( irc->user && irc->nick ) + if( irc->user->user && irc->user->nick ) { if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) ) { - irc_reply( irc, 464, ":This server is password-protected." ); + irc_send_num( irc, 464, ":This server is password-protected." ); return 0; } else { - irc_login( irc ); + irc_send_login( irc ); return 1; } } @@ -794,556 +590,33 @@ int irc_check_login( irc_t *irc ) } } -void irc_login( irc_t *irc ) -{ - user_t *u; - - irc_reply( irc, 1, ":Welcome to the BitlBee gateway, %s", irc->nick ); - irc_reply( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost ); - irc_reply( irc, 3, ":%s", IRCD_INFO ); - irc_reply( irc, 4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES ); - irc_reply( irc, 5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee " - "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", - CTYPES, CMODES, MAX_NICK_LENGTH - 1 ); - irc_motd( irc ); - irc->umode[0] = '\0'; - irc_umode_set( irc, "+" UMODE, 1 ); - u = user_add( irc, irc->mynick ); - u->host = g_strdup( irc->myhost ); - u->realname = g_strdup( ROOT_FN ); - u->online = 1; - u->send_handler = root_command_string; - u->is_private = 0; /* [SH] The channel is root's personal playground. */ - irc_spawn( irc, u ); - - u = user_add( irc, NS_NICK ); - u->host = g_strdup( irc->myhost ); - u->realname = g_strdup( ROOT_FN ); - u->online = 0; - u->send_handler = root_command_string; - u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */ - - u = user_add( irc, irc->nick ); - u->user = g_strdup( irc->user ); - u->host = g_strdup( irc->host ); - u->realname = g_strdup( irc->realname ); - u->online = 1; - irc_spawn( irc, u ); - - irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n" - "If you've never used BitlBee before, please do read the help " - "information using the \x02help\x02 command. Lots of FAQs are " - "answered there.\n" - "If you already have an account on this server, just use the " - "\x02identify\x02 command to identify yourself." ); - - if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) - ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname ); - - irc->status |= USTATUS_LOGGED_IN; - - /* This is for bug #209 (use PASS to identify to NickServ). */ - if( irc->password != NULL ) - { - char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; - - irc_setpass( irc, NULL ); - root_command( irc, send_cmd ); - g_free( send_cmd[1] ); - } -} -void irc_motd( irc_t *irc ) -{ - int fd; - - fd = open( global.conf->motdfile, O_RDONLY ); - if( fd == -1 ) - { - irc_reply( irc, 422, ":We don't need MOTDs." ); - } - else - { - char linebuf[80]; /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */ - char *add, max; - int len; - - linebuf[79] = len = 0; - max = sizeof( linebuf ) - 1; - - irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost ); - while( read( fd, linebuf + len, 1 ) == 1 ) - { - if( linebuf[len] == '\n' || len == max ) - { - linebuf[len] = 0; - irc_reply( irc, 372, ":- %s", linebuf ); - len = 0; - } - else if( linebuf[len] == '%' ) - { - read( fd, linebuf + len, 1 ); - if( linebuf[len] == 'h' ) - add = irc->myhost; - else if( linebuf[len] == 'v' ) - add = BITLBEE_VERSION; - else if( linebuf[len] == 'n' ) - add = irc->nick; - else - add = "%"; - - strncpy( linebuf + len, add, max - len ); - while( linebuf[++len] ); - } - else if( len < max ) - { - len ++; - } - } - irc_reply( irc, 376, ":End of MOTD" ); - close( fd ); - } -} - -void irc_topic( irc_t *irc, char *channel ) -{ - struct groupchat *c = irc_chat_by_channel( irc, channel ); - - if( c && c->topic ) - irc_reply( irc, 332, "%s :%s", channel, c->topic ); - else if( g_strcasecmp( channel, irc->channel ) == 0 ) - irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC ); - else - irc_reply( irc, 331, "%s :No topic for this channel", channel ); -} - -void irc_umode_set( irc_t *irc, char *s, int allow_priv ) -{ - /* allow_priv: Set to 0 if s contains user input, 1 if you want - to set a "privileged" mode (+o, +R, etc). */ - char m[256], st = 1, *t; - int i; - char changes[512], *p, st2 = 2; - char badflag = 0; - - memset( m, 0, sizeof( m ) ); - - for( t = irc->umode; *t; t ++ ) - m[(int)*t] = 1; - - p = changes; - for( t = s; *t; t ++ ) - { - if( *t == '+' || *t == '-' ) - st = *t == '+'; - else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) ) - { - if( m[(int)*t] != st) - { - if( st != st2 ) - st2 = st, *p++ = st ? '+' : '-'; - *p++ = *t; - } - m[(int)*t] = st; - } - else - badflag = 1; - } - *p = '\0'; - - memset( irc->umode, 0, sizeof( irc->umode ) ); - - for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ ) - if( m[i] ) - irc->umode[strlen(irc->umode)] = i; - - if( badflag ) - irc_reply( irc, 501, ":Unknown MODE flag" ); - /* Deliberately no !user@host on the prefix here */ - if( *changes ) - irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes ); -} - -void irc_spawn( irc_t *irc, user_t *u ) -{ - irc_join( irc, u, irc->channel ); -} - -void irc_join( irc_t *irc, user_t *u, char *channel ) -{ - char *nick; - - if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) ) - irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel ); - - if( nick_cmp( u->nick, irc->nick ) == 0 ) - { - irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE ); - irc_names( irc, channel ); - irc_topic( irc, channel ); - } - - nick = g_strdup( u->nick ); - nick_lc( nick ); - if( g_hash_table_lookup( irc->watches, nick ) ) - { - irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" ); - } - g_free( nick ); -} - -void irc_part( irc_t *irc, user_t *u, char *channel ) -{ - irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" ); -} - -void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker ) -{ - irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" ); -} - -void irc_kill( irc_t *irc, user_t *u ) -{ - char *nick, *s; - char reason[128]; - - if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) ) - { - if( u->ic->acc->server ) - g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, - u->ic->acc->server ); - else if( ( s = strchr( u->ic->acc->user, '@' ) ) ) - g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, - s + 1 ); - else - g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost, - u->ic->acc->prpl->name, irc->myhost ); - - /* proto_opt might contain garbage after the : */ - if( ( s = strchr( reason, ':' ) ) ) - *s = 0; - } - else - { - strcpy( reason, "Leaving..." ); - } - - irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason ); - - nick = g_strdup( u->nick ); - nick_lc( nick ); - if( g_hash_table_lookup( irc->watches, nick ) ) - { - irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" ); - } - g_free( nick ); -} - -int irc_send( irc_t *irc, char *nick, char *s, int flags ) -{ - struct groupchat *c = NULL; - user_t *u = NULL; - - if( strchr( CTYPES, *nick ) ) - { - if( !( c = irc_chat_by_channel( irc, nick ) ) ) - { - irc_reply( irc, 403, "%s :Channel does not exist", nick ); - return( 0 ); - } - } - else - { - u = user_find( irc, nick ); - - if( !u ) - { - if( irc->is_private ) - irc_reply( irc, 401, "%s :Nick does not exist", nick ); - else - irc_usermsg( irc, "Nick `%s' does not exist!", nick ); - return( 0 ); - } - } - - if( *s == 1 && s[strlen(s)-1] == 1 ) - { - if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 ) - { - if( s[7] == ' ' ) s ++; - s += 3; - *(s++) = '/'; - *(s++) = 'm'; - *(s++) = 'e'; - *(s++) = ' '; - s -= 4; - s[strlen(s)-1] = 0; - } - else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 ) - { - u = user_find( irc, irc->mynick ); - irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" ); - return( 1 ); - } - else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 ) - { - u = user_find( irc, irc->mynick ); - irc_privmsg( irc, u, "NOTICE", irc->nick, "", s ); - return( 1 ); - } - else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 ) - { - if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 ) - { - time_t current_typing_notice = time( NULL ); - - if( current_typing_notice - u->last_typing_notice >= 5 ) - { - u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 ); - u->last_typing_notice = current_typing_notice; - } - } - return( 1 ); - } - else if( g_strncasecmp( s + 1, "DCC", 3 ) == 0 ) - { - if( u && u->ic && u->ic->acc->prpl->transfer_request ) - { - file_transfer_t *ft = dcc_request( u->ic, s + 5 ); - if ( ft ) - u->ic->acc->prpl->transfer_request( u->ic, ft, u->handle ); - } - return( 1 ); - } - else - { - irc_usermsg( irc, "Supported CTCPs are ACTION, VERSION, PING, TYPING, DCC" ); - return( 0 ); - } - } - - if( u ) - { - /* For the next message, we probably do have to send new notices... */ - u->last_typing_notice = 0; - u->is_private = irc->is_private; - - if( u->is_private ) - { - if( !u->online ) - irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" ); - else if( u->away ) - irc_reply( irc, 301, "%s :%s", u->nick, u->away ); - } - - if( u->send_handler ) - { - u->send_handler( irc, u, s, flags ); - return 1; - } - } - else if( c && c->ic && c->ic->acc && c->ic->acc->prpl ) - { - return( imc_chat_msg( c, s, 0 ) ); - } - - return( 0 ); -} - -static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond ) -{ - user_t *u = data; - - /* Shouldn't happen, but just to be sure. */ - if( u->sendbuf_len < 2 ) - return FALSE; - - u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */ - imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags ); - - g_free( u->sendbuf ); - u->sendbuf = NULL; - u->sendbuf_len = 0; - u->sendbuf_timer = 0; - u->sendbuf_flags = 0; - - return FALSE; -} - -void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) -{ - if( !u || !u->ic ) return; - - if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 ) - { - int delay; - - if( u->sendbuf_len > 0 && u->sendbuf_flags != flags) - { - /* Flush the buffer */ - b_event_remove( u->sendbuf_timer ); - buddy_send_handler_delayed( u, -1, 0 ); - } - - if( u->sendbuf_len == 0 ) - { - u->sendbuf_len = strlen( msg ) + 2; - u->sendbuf = g_new( char, u->sendbuf_len ); - u->sendbuf[0] = 0; - u->sendbuf_flags = flags; - } - else - { - u->sendbuf_len += strlen( msg ) + 1; - u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len ); - } - - strcat( u->sendbuf, msg ); - strcat( u->sendbuf, "\n" ); - - delay = set_getint( &irc->set, "buddy_sendbuffer_delay" ); - if( delay <= 5 ) - delay *= 1000; - - if( u->sendbuf_timer > 0 ) - b_event_remove( u->sendbuf_timer ); - u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u ); - } - else - { - imc_buddy_msg( u->ic, u->handle, msg, flags ); - } -} - -int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ) -{ - char last = 0; - char *s = msg, *line = msg; - - /* The almighty linesplitter .. woohoo!! */ - while( !last ) - { - if( *s == '\r' && *(s+1) == '\n' ) - *(s++) = 0; - if( *s == '\n' ) - { - last = s[1] == 0; - *s = 0; - } - else - { - last = s[0] == 0; - } - if( *s == 0 ) - { - if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 ) - { - irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host, - type, to, line + 4 ); - } - else - { - irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host, - type, to, prefix ? prefix : "", line ); - } - line = s + 1; - } - s ++; - } - - return( 1 ); -} - -int irc_msgfrom( irc_t *irc, char *nick, char *msg ) +static char *set_eval_charset( set_t *set, char *value ) { - user_t *u = user_find( irc, nick ); - static char *prefix = NULL; - - if( !u ) return( 0 ); - if( prefix && *prefix ) g_free( prefix ); - - if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 ) - { - int len = strlen( irc->nick) + 3; - prefix = g_new (char, len ); - g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) ); - prefix[len-1] = 0; - } - else - { - prefix = ""; - } - - return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) ); -} + irc_t *irc = set->data; + GIConv ic, oc; -int irc_noticefrom( irc_t *irc, char *nick, char *msg ) -{ - user_t *u = user_find( irc, nick ); - - if( u ) - return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) ); - else - return( 0 ); -} + if( g_strcasecmp( value, "none" ) == 0 ) + value = g_strdup( "utf-8" ); -/* Returns 0 if everything seems to be okay, a number >0 when there was a - timeout. The number returned is the number of seconds we received no - pongs from the user. When not connected yet, we don't ping but drop the - connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */ -static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ) -{ - irc_t *irc = _irc; - int rv = 0; - - if( !( irc->status & USTATUS_LOGGED_IN ) ) + if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) { - if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) ) - rv = gettime() - irc->last_pong; + return NULL; } - else + if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) { - if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging ) - { - irc_write( irc, "PING :%s", IRC_PING_STRING ); - irc->pinging = 1; - } - else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) ) - { - rv = gettime() - irc->last_pong; - } + g_iconv_close( ic ); + return NULL; } - if( rv > 0 ) - { - irc_abort( irc, 0, "Ping Timeout: %d seconds", rv ); - return FALSE; - } + if( irc->iconv != (GIConv) -1 ) + g_iconv_close( irc->iconv ); + if( irc->oconv != (GIConv) -1 ) + g_iconv_close( irc->oconv ); - return TRUE; -} + irc->iconv = ic; + irc->oconv = oc; -struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel ) -{ - struct groupchat *c; - account_t *a; - - /* This finds the connection which has a conversation which belongs to this channel */ - for( a = irc->accounts; a; a = a->next ) - { - if( a->ic == NULL ) - continue; - - c = a->ic->groupchats; - while( c ) - { - if( c->channel && g_strcasecmp( c->channel, channel ) == 0 ) - return c; - - c = c->next; - } - } - - return NULL; + return value; } diff --git a/irc.h b/irc.h index a865bde3..482ef067 100644 --- a/irc.h +++ b/irc.h @@ -4,7 +4,7 @@ * Copyright 2002-2004 Wilmer van der Gaast and others * \********************************************************************/ -/* The big hairy IRCd part of the project */ +/* The IRC-based UI (for now the only one) */ /* This program is free software; you can redistribute it and/or modify @@ -48,6 +48,8 @@ typedef enum USTATUS_SHUTDOWN = 8 } irc_status_t; +struct irc_user; + typedef struct irc { int fd; @@ -58,86 +60,83 @@ typedef struct irc char *readbuffer; GIConv iconv, oconv; - int sentbytes; - time_t oldtime; + struct irc_user *root; + struct irc_user *user; - char *nick; - char *user; - char *host; - char *realname; char *password; /* HACK: Used to save the user's password, but before logging in, this may contain a password we should send to identify after USER/NICK are received. */ char umode[8]; - char *myhost; - char *mynick; - - char *channel; - int c_id; - - char is_private; /* Not too nice... */ - char *last_target; - struct query *queries; struct account *accounts; GSList *file_transfers; struct chat *chatrooms; - struct __USER *users; - GHashTable *userhash; + GSList *users; + GHashTable *nick_user_hash; GHashTable *watches; - struct __NICK *nicks; - struct set *set; gint r_watch_source_id; gint w_watch_source_id; gint ping_source_id; + + struct bee *b; } irc_t; +typedef struct irc_user +{ + char *nick; + char *user; + char *host; + char *fullname; + + /* Nickname in lowercase for case sensitive searches */ + char *key; + + char is_private; + + char *sendbuf; + int sendbuf_len; + guint sendbuf_timer; + int sendbuf_flags; + + //struct user *b; +} irc_user_t; + #include "user.h" +/* irc.c */ extern GSList *irc_connection_list; irc_t *irc_new( int fd ); void irc_abort( irc_t *irc, int immed, char *format, ... ) G_GNUC_PRINTF( 3, 4 ); void irc_free( irc_t *irc ); -void irc_exec( irc_t *irc, char **cmd ); void irc_process( irc_t *irc ); char **irc_parse_line( char *line ); char *irc_build_line( char **cmd ); -void irc_vawrite( irc_t *irc, char *format, va_list params ); void irc_write( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); void irc_write_all( int now, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); -void irc_reply( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 ); -G_MODULE_EXPORT int irc_usermsg( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); -char **irc_tokenize( char *buffer ); +void irc_vawrite( irc_t *irc, char *format, va_list params ); -void irc_login( irc_t *irc ); int irc_check_login( irc_t *irc ); -void irc_motd( irc_t *irc ); -void irc_names( irc_t *irc, char *channel ); -void irc_topic( irc_t *irc, char *channel ); -void irc_umode_set( irc_t *irc, char *s, int allow_priv ); -void irc_who( irc_t *irc, char *channel ); -void irc_spawn( irc_t *irc, user_t *u ); -void irc_join( irc_t *irc, user_t *u, char *channel ); -void irc_part( irc_t *irc, user_t *u, char *channel ); -void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker ); -void irc_kill( irc_t *irc, user_t *u ); -void irc_invite( irc_t *irc, char *nick, char *channel ); -void irc_whois( irc_t *irc, char *nick ); -void irc_setpass( irc_t *irc, const char *pass ); /* USE WITH CAUTION! */ - -int irc_send( irc_t *irc, char *nick, char *s, int flags ); -int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ); -int irc_msgfrom( irc_t *irc, char *nick, char *msg ); -int irc_noticefrom( irc_t *irc, char *nick, char *msg ); - -void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ); -struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel ); + +/* irc_commands.c */ +void irc_exec( irc_t *irc, char **cmd ); + +/* irc_send.c */ +void irc_send_num( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 ); +void irc_send_login( irc_t *irc ); +void irc_send_motd( irc_t *irc ); +int irc_usermsg( irc_t *irc, char *format, ... ); + +/* irc_user.c */ +irc_user_t *irc_user_new( irc_t *irc, const char *nick ); +int irc_user_free( irc_t *irc, const char *nick ); +irc_user_t *irc_user_find( irc_t *irc, const char *nick ); +int irc_user_rename( irc_t *irc, const char *old, const char *new ); #endif diff --git a/irc_commands.c b/irc_commands.c index a417e0d9..81ddd588 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2006 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* IRC commands */ @@ -38,7 +38,7 @@ static void irc_cmd_pass( irc_t *irc, char **cmd ) command last. (Possibly it won't send it at all if it turns out we don't require it, which will break this feature.) Try to identify using the given password. */ - return root_command( irc, send_cmd ); + /*return root_command( irc, send_cmd );*/ } /* Handling in pre-logged-in state, first see if this server is password-protected: */ @@ -52,48 +52,48 @@ static void irc_cmd_pass( irc_t *irc, char **cmd ) } else if( global.conf->auth_pass ) { - irc_reply( irc, 464, ":Incorrect password" ); + irc_send_num( irc, 464, ":Incorrect password" ); } else { /* Remember the password and try to identify after USER/NICK. */ - irc_setpass( irc, cmd[1] ); + /*irc_setpass( irc, cmd[1] ); */ irc_check_login( irc ); } } static void irc_cmd_user( irc_t *irc, char **cmd ) { - irc->user = g_strdup( cmd[1] ); - irc->realname = g_strdup( cmd[4] ); + irc->user->user = g_strdup( cmd[1] ); + irc->user->fullname = g_strdup( cmd[4] ); irc_check_login( irc ); } static void irc_cmd_nick( irc_t *irc, char **cmd ) { - if( irc->nick ) + if( irc->user->nick ) { - irc_reply( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" ); + irc_send_num( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" ); } - /* This is not clean, but for now it'll have to be like this... */ - else if( ( nick_cmp( cmd[1], irc->mynick ) == 0 ) || ( nick_cmp( cmd[1], NS_NICK ) == 0 ) ) + else if( irc_user_find( irc, cmd[1] ) ) { - irc_reply( irc, 433, ":This nick is already in use" ); + irc_send_num( irc, 433, ":This nick is already in use" ); } else if( !nick_ok( cmd[1] ) ) { /* [SH] Invalid characters. */ - irc_reply( irc, 432, ":This nick contains invalid characters" ); + irc_send_num( irc, 432, ":This nick contains invalid characters" ); } else { - irc->nick = g_strdup( cmd[1] ); + irc->user->nick = g_strdup( cmd[1] ); irc_check_login( irc ); } } +#if 0 static void irc_cmd_quit( irc_t *irc, char **cmd ) { if( cmd[1] && *cmd[1] ) @@ -115,11 +115,11 @@ static void irc_cmd_oper( irc_t *irc, char **cmd ) strcmp( cmd[2], global.conf->oper_pass ) == 0 ) ) { irc_umode_set( irc, "+o", 1 ); - irc_reply( irc, 381, ":Password accepted" ); + irc_send_num( irc, 381, ":Password accepted" ); } else { - irc_reply( irc, 432, ":Incorrect password" ); + irc_send_num( irc, 432, ":Incorrect password" ); } } @@ -130,12 +130,12 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) if( cmd[2] ) { if( *cmd[2] == '+' || *cmd[2] == '-' ) - irc_reply( irc, 477, "%s :Can't change channel modes", cmd[1] ); + irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] ); else if( *cmd[2] == 'b' ) - irc_reply( irc, 368, "%s :No bans possible", cmd[1] ); + irc_send_num( irc, 368, "%s :No bans possible", cmd[1] ); } else - irc_reply( irc, 324, "%s +%s", cmd[1], CMODE ); + irc_send_num( irc, 324, "%s +%s", cmd[1], CMODE ); } else { @@ -144,10 +144,10 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) if( cmd[2] ) irc_umode_set( irc, cmd[2], 0 ); else - irc_reply( irc, 221, "+%s", irc->umode ); + irc_send_num( irc, 221, "+%s", irc->umode ); } else - irc_reply( irc, 502, ":Don't touch their modes" ); + irc_send_num( irc, 502, ":Don't touch their modes" ); } } @@ -182,7 +182,7 @@ static void irc_cmd_part( irc_t *irc, char **cmd ) } else { - irc_reply( irc, 403, "%s :No such channel", cmd[1] ); + irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); } } @@ -196,11 +196,11 @@ static void irc_cmd_join( irc_t *irc, char **cmd ) struct chat *c; if( strchr( CTYPES, cmd[1][0] ) == NULL || cmd[1][1] == 0 ) - irc_reply( irc, 479, "%s :Invalid channel name", cmd[1] ); + irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] ); else if( ( c = chat_bychannel( irc, cmd[1] ) ) && c->acc && c->acc->ic ) chat_join( irc, c, cmd[2] ); else - irc_reply( irc, 403, "%s :No such channel", cmd[1] ); + irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); } } @@ -214,18 +214,18 @@ static void irc_cmd_invite( irc_t *irc, char **cmd ) if( c->ic && c->ic->acc->prpl->chat_invite ) { c->ic->acc->prpl->chat_invite( c, u->handle, NULL ); - irc_reply( irc, 341, "%s %s", nick, channel ); + irc_send_num( irc, 341, "%s %s", nick, channel ); return; } - irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); + irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); } static void irc_cmd_privmsg( irc_t *irc, char **cmd ) { if ( !cmd[2] ) { - irc_reply( irc, 412, ":No text to send" ); + irc_send_num( irc, 412, ":No text to send" ); } else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) { @@ -282,26 +282,26 @@ static void irc_cmd_who( irc_t *irc, char **cmd ) if( !channel || *channel == '0' || *channel == '*' || !*channel ) while( u ) { - irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); + irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); u = u->next; } else if( g_strcasecmp( channel, irc->channel ) == 0 ) while( u ) { if( u->online ) - irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); + irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); u = u->next; } else if( ( c = irc_chat_by_channel( irc, channel ) ) ) for( l = c->in_room; l; l = l->next ) { if( ( u = user_findhandle( c->ic, l->data ) ) ) - irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); + irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); } else if( ( u = user_find( irc, channel ) ) ) - irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); + irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); - irc_reply( irc, 315, "%s :End of /WHO list", channel?channel:"**" ); + irc_send_num( irc, 315, "%s :End of /WHO list", channel?channel:"**" ); } static void irc_cmd_userhost( irc_t *irc, char **cmd ) @@ -319,9 +319,9 @@ static void irc_cmd_userhost( irc_t *irc, char **cmd ) if( ( u = user_find( irc, cmd[i] ) ) ) { if( u->online && u->away ) - irc_reply( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host ); + irc_send_num( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host ); else - irc_reply( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host ); + irc_send_num( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host ); } } @@ -376,7 +376,7 @@ static void irc_cmd_ison( irc_t *irc, char **cmd ) if( strlen( buff ) > 0 ) buff[strlen(buff)-1] = '\0'; - irc_reply( irc, 303, ":%s", buff ); + irc_send_num( irc, 303, ":%s", buff ); } static void irc_cmd_watch( irc_t *irc, char **cmd ) @@ -405,9 +405,9 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) g_hash_table_insert( irc->watches, nick, nick ); if( u && u->online ) - irc_reply( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" ); + irc_send_num( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" ); else - irc_reply( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" ); + irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" ); } else if( cmd[i][0] == '-' ) { @@ -418,7 +418,7 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) g_hash_table_remove( irc->watches, okey ); g_free( okey ); - irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" ); + irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" ); } } } @@ -462,7 +462,7 @@ static void irc_cmd_away( irc_t *irc, char **cmd ) j ++; u->away[j] = 0; - irc_reply( irc, 306, ":You're now away: %s", u->away ); + irc_send_num( irc, 306, ":You're now away: %s", u->away ); /* irc_umode_set( irc, irc->myhost, "+a" ); */ } else @@ -470,7 +470,7 @@ static void irc_cmd_away( irc_t *irc, char **cmd ) if( u->away ) g_free( u->away ); u->away = NULL; /* irc_umode_set( irc, irc->myhost, "-a" ); */ - irc_reply( irc, 305, ":Welcome back" ); + irc_send_num( irc, 305, ":Welcome back" ); } set_setstr( &irc->set, "away", u->away ); @@ -483,27 +483,27 @@ static void irc_cmd_whois( irc_t *irc, char **cmd ) if( u ) { - irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); + irc_send_num( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); if( u->ic ) - irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user, + irc_send_num( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user, u->ic->acc->server && *u->ic->acc->server ? u->ic->acc->server : "", u->ic->acc->prpl->name ); else - irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); + irc_send_num( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); if( !u->online ) - irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" ); + irc_send_num( irc, 301, "%s :%s", u->nick, "User is offline" ); else if( u->away ) - irc_reply( irc, 301, "%s :%s", u->nick, u->away ); + irc_send_num( irc, 301, "%s :%s", u->nick, u->away ); if( u->status_msg ) - irc_reply( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); + irc_send_num( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); - irc_reply( irc, 318, "%s :End of /WHOIS list", nick ); + irc_send_num( irc, 318, "%s :End of /WHOIS list", nick ); } else { - irc_reply( irc, 401, "%s :Nick does not exist", nick ); + irc_send_num( irc, 401, "%s :Nick does not exist", nick ); } } @@ -514,8 +514,8 @@ static void irc_cmd_whowas( irc_t *irc, char **cmd ) message from irssi which is a bit annoying. So just respond with not-found and irssi users will get better error messages */ - irc_reply( irc, 406, "%s :Nick does not exist", cmd[1] ); - irc_reply( irc, 369, "%s :End of WHOWAS", cmd[1] ); + irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] ); + irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] ); } static void irc_cmd_nickserv( irc_t *irc, char **cmd ) @@ -540,7 +540,7 @@ static void irc_cmd_pong( irc_t *irc, char **cmd ) static void irc_cmd_version( irc_t *irc, char **cmd ) { - irc_reply( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU ); + irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU ); } static void irc_cmd_completions( irc_t *irc, char **cmd ) @@ -571,13 +571,15 @@ static void irc_cmd_rehash( irc_t *irc, char **cmd ) else ipc_to_master( cmd ); - irc_reply( irc, 382, "%s :Rehashing", global.conf_file ); + irc_send_num( irc, 382, "%s :Rehashing", global.conf_file ); } +#endif static const command_t irc_commands[] = { { "pass", 1, irc_cmd_pass, 0 }, { "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN }, { "nick", 1, irc_cmd_nick, 0 }, +#if 0 { "quit", 0, irc_cmd_quit, 0 }, { "ping", 0, irc_cmd_ping, 0 }, { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, @@ -609,6 +611,7 @@ static const command_t irc_commands[] = { { "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY }, { "restart", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "kill", 2, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, +#endif { NULL } }; @@ -627,19 +630,19 @@ void irc_exec( irc_t *irc, char *cmd[] ) if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN ) { - irc_reply( irc, 462, ":Only allowed before logging in" ); + irc_send_num( irc, 462, ":Only allowed before logging in" ); } else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) ) { - irc_reply( irc, 451, ":Register first" ); + irc_send_num( irc, 451, ":Register first" ); } else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) ) { - irc_reply( irc, 481, ":Permission denied - You're not an IRC operator" ); + irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" ); } else if( n_arg < irc_commands[i].required_parameters ) { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] ); } else if( irc_commands[i].flags & IRC_CMD_TO_MASTER ) { @@ -656,5 +659,5 @@ void irc_exec( irc_t *irc, char *cmd[] ) } if( irc->status >= USTATUS_LOGGED_IN ) - irc_reply( irc, 421, "%s :Unknown command", cmd[0] ); + irc_send_num( irc, 421, "%s :Unknown command", cmd[0] ); } diff --git a/nick.c b/nick.c index 5d7dc8a9..6d798bc6 100644 --- a/nick.c +++ b/nick.c @@ -77,7 +77,7 @@ char *nick_get( account_t *acc, const char *handle ) *(s++) = 0; nick_strip( nick ); - if( set_getbool( &acc->irc->set, "lcnicks" ) ) + if( set_getbool( &acc->irc->b->set, "lcnicks" ) ) nick_lc( nick ); } g_free( store_handle ); @@ -95,7 +95,7 @@ void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+ /* Now, find out if the nick is already in use at the moment, and make subtle changes to make it unique. */ - while( !nick_ok( nick ) || user_find( acc->irc, nick ) ) + while( !nick_ok( nick ) || irc_user_find( acc->irc, nick ) ) { if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) { diff --git a/protocols/Makefile b/protocols/Makefile index 18d79e8d..f1133cc9 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -9,7 +9,9 @@ -include ../Makefile.settings # [SH] Program variables -objects = nogaim.o +#objects = account.o nogaim.o user.o +objects = bee.o + # [SH] The next two lines should contain the directory name (in $(subdirs)) # and the name of the object file, which should be linked into diff --git a/protocols/account.c b/protocols/account.c new file mode 100644 index 00000000..c549c866 --- /dev/null +++ b/protocols/account.c @@ -0,0 +1,359 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Account management functions */ + +/* + 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 "account.h" +#include "chat.h" + +account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) +{ + account_t *a; + set_t *s; + + if( irc->accounts ) + { + for( a = irc->accounts; a->next; a = a->next ); + a = a->next = g_new0( account_t, 1 ); + } + else + { + irc->accounts = a = g_new0 ( account_t, 1 ); + } + + a->prpl = prpl; + a->user = g_strdup( user ); + a->pass = g_strdup( pass ); + a->auto_connect = 1; + a->irc = irc; + + s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE; + + s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); + + s = set_add( &a->set, "nick_source", "handle", NULL, a ); + + s = set_add( &a->set, "password", NULL, set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; + + s = set_add( &a->set, "username", NULL, set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; + set_setstr( &a->set, "username", user ); + + a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); + + /* This function adds some more settings (and might want to do more + things that have to be done now, although I can't think of anything. */ + if( prpl->init ) + prpl->init( a ); + + s = set_add( &a->set, "away", NULL, set_eval_account, a ); + s->flags |= SET_NULL_OK; + + if( a->flags & ACC_FLAG_STATUS_MESSAGE ) + { + s = set_add( &a->set, "status", NULL, set_eval_account, a ); + s->flags |= SET_NULL_OK; + } + + return a; +} + +char *set_eval_account( set_t *set, char *value ) +{ + account_t *acc = set->data; + + /* Double-check: We refuse to edit on-line accounts. */ + if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic ) + return SET_INVALID; + + if( strcmp( set->key, "server" ) == 0 ) + { + g_free( acc->server ); + if( value && *value ) + { + acc->server = g_strdup( value ); + return value; + } + else + { + acc->server = g_strdup( set->def ); + return g_strdup( set->def ); + } + } + else if( strcmp( set->key, "username" ) == 0 ) + { + g_free( acc->user ); + acc->user = g_strdup( value ); + return value; + } + else if( strcmp( set->key, "password" ) == 0 ) + { + if( value ) + { + g_free( acc->pass ); + acc->pass = g_strdup( value ); + return NULL; /* password shouldn't be visible in plaintext! */ + } + else + { + /* NULL can (should) be stored in the set_t + variable, but is otherwise not correct. */ + return SET_INVALID; + } + } + else if( strcmp( set->key, "auto_connect" ) == 0 ) + { + if( !is_bool( value ) ) + return SET_INVALID; + + acc->auto_connect = bool2int( value ); + return value; + } + else if( strcmp( set->key, "away" ) == 0 || + strcmp( set->key, "status" ) == 0 ) + { + if( acc->ic && acc->ic->flags & OPT_LOGGED_IN ) + { + /* If we're currently on-line, set the var now already + (bit of a hack) and send an update. */ + g_free( set->value ); + set->value = g_strdup( value ); + + imc_away_send_update( acc->ic ); + } + + return value; + } + + return SET_INVALID; +} + +account_t *account_get( irc_t *irc, char *id ) +{ + account_t *a, *ret = NULL; + char *handle, *s; + int nr; + + /* This checks if the id string ends with (...) */ + if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 ) + { + struct prpl *proto; + + *s = *handle = 0; + handle ++; + + if( ( proto = find_protocol( id ) ) ) + { + for( a = irc->accounts; a; a = a->next ) + if( a->prpl == proto && + a->prpl->handle_cmp( handle, a->user ) == 0 ) + ret = a; + } + + /* Restore the string. */ + handle --; + *handle = '('; + *s = ')'; + + if( ret ) + return ret; + } + + if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) + { + for( a = irc->accounts; a; a = a->next ) + if( ( nr-- ) == 0 ) + return( a ); + + return( NULL ); + } + + for( a = irc->accounts; a; a = a->next ) + { + if( g_strcasecmp( id, a->prpl->name ) == 0 ) + { + if( !ret ) + ret = a; + else + return( NULL ); /* We don't want to match more than one... */ + } + else if( strstr( a->user, id ) ) + { + if( !ret ) + ret = a; + else + return( NULL ); + } + } + + return( ret ); +} + +void account_del( irc_t *irc, account_t *acc ) +{ + account_t *a, *l = NULL; + struct chat *c, *nc; + + if( acc->ic ) + /* Caller should have checked, accounts still in use can't be deleted. */ + return; + + for( a = irc->accounts; a; a = (l=a)->next ) + if( a == acc ) + { + if( l ) + l->next = a->next; + else + irc->accounts = a->next; + + for( c = irc->chatrooms; c; c = nc ) + { + nc = c->next; + if( acc == c->acc ) + chat_del( irc, c ); + } + + while( a->set ) + set_del( &a->set, a->set->key ); + + g_hash_table_destroy( a->nicks ); + + g_free( a->user ); + g_free( a->pass ); + g_free( a->server ); + if( a->reconnect ) /* This prevents any reconnect still queued to happen */ + cancel_auto_reconnect( a ); + g_free( a ); + + break; + } +} + +void account_on( irc_t *irc, account_t *a ) +{ + if( a->ic ) + { + /* Trying to enable an already-enabled account */ + return; + } + + cancel_auto_reconnect( a ); + + a->reconnect = 0; + a->prpl->login( a ); +} + +void account_off( irc_t *irc, account_t *a ) +{ + imc_logout( a->ic, FALSE ); + a->ic = NULL; + if( a->reconnect ) + { + /* Shouldn't happen */ + cancel_auto_reconnect( a ); + } +} + +struct account_reconnect_delay +{ + int start; + char op; + int step; + int max; +}; + +int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p ) +{ + memset( p, 0, sizeof( *p ) ); + /* A whole day seems like a sane "maximum maximum". */ + p->max = 86400; + + /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */ + while( *value && isdigit( *value ) ) + p->start = p->start * 10 + *value++ - '0'; + + /* Sure, call me evil for implementing my own fscanf here, but it's + dead simple and I immediately know where to continue parsing. */ + + if( *value == 0 ) + /* If the string ends now, the delay is constant. */ + return 1; + else if( *value != '+' && *value != '*' ) + /* Otherwise allow either a + or a * */ + return 0; + + p->op = *value++; + + /* + or * the delay by this number every time. */ + while( *value && isdigit( *value ) ) + p->step = p->step * 10 + *value++ - '0'; + + if( *value == 0 ) + /* Use the default maximum (one day). */ + return 1; + else if( *value != '<' ) + return 0; + + p->max = 0; + value ++; + while( *value && isdigit( *value ) ) + p->max = p->max * 10 + *value++ - '0'; + + return p->max > 0; +} + +char *set_eval_account_reconnect_delay( set_t *set, char *value ) +{ + struct account_reconnect_delay p; + + return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID; +} + +int account_reconnect_delay( account_t *a ) +{ + char *setting = set_getstr( &a->irc->b->set, "auto_reconnect_delay" ); + struct account_reconnect_delay p; + + if( account_reconnect_delay_parse( setting, &p ) ) + { + if( a->auto_reconnect_delay == 0 ) + a->auto_reconnect_delay = p.start; + else if( p.op == '+' ) + a->auto_reconnect_delay += p.step; + else if( p.op == '*' ) + a->auto_reconnect_delay *= p.step; + + if( a->auto_reconnect_delay > p.max ) + a->auto_reconnect_delay = p.max; + } + else + { + a->auto_reconnect_delay = 0; + } + + return a->auto_reconnect_delay; +} diff --git a/protocols/account.h b/protocols/account.h new file mode 100644 index 00000000..984dcfe6 --- /dev/null +++ b/protocols/account.h @@ -0,0 +1,72 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Account management functions */ + +/* + 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 +*/ + +#ifndef _ACCOUNT_H +#define _ACCOUNT_H + +typedef struct account +{ + struct prpl *prpl; + char *user; + char *pass; + char *server; + + int auto_connect; + int auto_reconnect_delay; + int reconnect; + int flags; + + set_t *set; + GHashTable *nicks; + + struct irc *irc; + struct im_connection *ic; + struct account *next; +} account_t; + +account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ); +account_t *account_get( irc_t *irc, char *id ); +void account_del( irc_t *irc, account_t *acc ); +void account_on( irc_t *irc, account_t *a ); +void account_off( irc_t *irc, account_t *a ); + +char *set_eval_account( set_t *set, char *value ); +char *set_eval_account_reconnect_delay( set_t *set, char *value ); +int account_reconnect_delay( account_t *a ); + +typedef enum +{ + ACC_SET_NOSAVE = 0x01, /* Don't save this setting (i.e. stored elsewhere). */ + ACC_SET_OFFLINE_ONLY = 0x02, /* Allow changes only if the acct is offline. */ + ACC_SET_ONLINE_ONLY = 0x04, /* Allow changes only if the acct is online. */ +} account_set_flag_t; + +typedef enum +{ + ACC_FLAG_AWAY_MESSAGE = 0x01, /* Supports away messages instead of just states. */ + ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */ +} account_flag_t; + +#endif diff --git a/protocols/chat.c b/protocols/chat.c new file mode 100644 index 00000000..8c5ce0bc --- /dev/null +++ b/protocols/chat.c @@ -0,0 +1,192 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Keep track of chatrooms the user is interested in */ + +/* + 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 +*/ + +#include "bitlbee.h" +#include "chat.h" + +struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ) +{ + struct chat *c, *l; + set_t *s; + + if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) || + chat_chancmp( channel, irc->channel ) == 0 ) + { + return NULL; + } + + for( c = irc->chatrooms; c; c = c->next ) + { + if( chat_chancmp( channel, c->channel ) == 0 ) + return NULL; + + if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) + return NULL; + + l = c; + } + + if( irc->chatrooms == NULL ) + irc->chatrooms = c = g_new0( struct chat, 1 ); + else + l->next = c = g_new0( struct chat, 1 ); + + c->acc = acc; + c->handle = g_strdup( handle ); + c->channel = g_strdup( channel ); + + s = set_add( &c->set, "auto_join", "false", set_eval_bool, c ); + /* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */ + s = set_add( &c->set, "nick", NULL, NULL, c ); + s->flags |= SET_NULL_OK; + + return c; +} + +struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ) +{ + struct chat *c; + + for( c = irc->chatrooms; c; c = c->next ) + { + if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) + break; + } + + return c; +} + +struct chat *chat_bychannel( irc_t *irc, char *channel ) +{ + struct chat *c; + + for( c = irc->chatrooms; c; c = c->next ) + { + if( chat_chancmp( channel, c->channel ) == 0 ) + break; + } + + return c; +} + +struct chat *chat_get( irc_t *irc, char *id ) +{ + struct chat *c, *ret = NULL; + int nr; + + if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) + { + for( c = irc->chatrooms; c; c = c->next ) + if( ( nr-- ) == 0 ) + return c; + + return NULL; + } + + for( c = irc->chatrooms; c; c = c->next ) + { + if( strstr( c->handle, id ) ) + { + if( !ret ) + ret = c; + else + return NULL; + } + else if( strstr( c->channel, id ) ) + { + if( !ret ) + ret = c; + else + return NULL; + } + } + + return ret; +} + +int chat_del( irc_t *irc, struct chat *chat ) +{ + struct chat *c, *l = NULL; + + for( c = irc->chatrooms; c; c = (l=c)->next ) + if( c == chat ) + break; + + if( c == NULL ) + return 0; + else if( l == NULL ) + irc->chatrooms = c->next; + else + l->next = c->next; + + while( c->set ) + set_del( &c->set, c->set->key ); + + g_free( c->handle ); + g_free( c->channel ); + g_free( c ); + + return 1; +} + +int chat_chancmp( char *a, char *b ) +{ + if( !chat_chanok( a ) || !chat_chanok( b ) ) + return 0; + + if( a[0] == b[0] ) + return nick_cmp( a + 1, b + 1 ); + else + return -1; +} + +int chat_chanok( char *a ) +{ + if( strchr( CTYPES, a[0] ) != NULL ) + return nick_ok( a + 1 ); + else + return 0; +} + +int chat_join( irc_t *irc, struct chat *c, const char *password ) +{ + struct groupchat *gc; + char *nick = set_getstr( &c->set, "nick" ); + + if( c->acc->ic == NULL || c->acc->prpl->chat_join == NULL ) + return 0; + + if( nick == NULL ) + nick = irc->nick; + + if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, password ) ) ) + { + g_free( gc->channel ); + gc->channel = g_strdup( c->channel ); + return 1; + } + + return 0; +} diff --git a/protocols/chat.h b/protocols/chat.h new file mode 100644 index 00000000..7196aea8 --- /dev/null +++ b/protocols/chat.h @@ -0,0 +1,51 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Keep track of chatrooms the user is interested in */ + +/* + 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 +*/ + +#ifndef _CHAT_H +#define _CHAT_H + +struct chat +{ + account_t *acc; + + char *handle; + char *channel; + set_t *set; + + struct chat *next; +}; + +struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ); +struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ); +struct chat *chat_bychannel( irc_t *irc, char *channel ); +struct chat *chat_get( irc_t *irc, char *id ); +int chat_del( irc_t *irc, struct chat *chat ); + +int chat_chancmp( char *a, char *b ); +int chat_chanok( char *a ); + +int chat_join( irc_t *irc, struct chat *c, const char *password ); + +#endif diff --git a/protocols/user.c b/protocols/user.c new file mode 100644 index 00000000..f014586b --- /dev/null +++ b/protocols/user.c @@ -0,0 +1,106 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search buddies */ + +/* + 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" + +user_t *user_add( irc_t *irc, char *nick ) +{ + user_t *u, *lu = NULL; + char *key; + + if( !nick_ok( nick ) ) + return( NULL ); + + if( user_find( irc, nick ) != NULL ) + return( NULL ); + + return( u ); +} + +int user_del( irc_t *irc, char *nick ) +{ + user_t *u, *t; + char *key; + gpointer okey, ovalue; + + if( !nick_ok( nick ) ) + return( 0 ); + + u = irc->users; + t = NULL; + while( u ) + { + if( nick_cmp( u->nick, nick ) == 0 ) + { + /* Get this key now already, since "nick" might be free()d + at the time we start playing with the hash... */ + key = g_strdup( nick ); + nick_lc( key ); + + if( t ) + t->next = u->next; + else + irc->users = u->next; + if( u->online ) + irc_kill( irc, u ); + g_free( u->nick ); + if( u->nick != u->user ) g_free( u->user ); + if( u->nick != u->host ) g_free( u->host ); + if( u->nick != u->realname ) g_free( u->realname ); + g_free( u->group ); + g_free( u->away ); + g_free( u->handle ); + g_free( u->sendbuf ); + if( u->sendbuf_timer ) b_event_remove( u->sendbuf_timer ); + g_free( u ); + + return( 1 ); + } + u = (t=u)->next; + } + + return( 0 ); +} + +user_t *user_findhandle( struct im_connection *ic, const char *handle ) +{ + user_t *u; + char *nick; + + /* First, let's try a hash lookup. If it works, it's probably faster. */ + if( ( nick = g_hash_table_lookup( ic->acc->nicks, handle ) ) && + ( u = user_find( ic->irc, nick ) ) && + ( ic->acc->prpl->handle_cmp( handle, u->handle ) == 0 ) ) + return u; + + /* However, it doesn't always work, so in that case we'll have to dig + through the whole userlist. :-( */ + for( u = ic->irc->users; u; u = u->next ) + if( u->ic == ic && u->handle && ic->acc->prpl->handle_cmp( u->handle, handle ) == 0 ) + return u; + + return NULL; +} diff --git a/protocols/user.h b/protocols/user.h new file mode 100644 index 00000000..26697a3a --- /dev/null +++ b/protocols/user.h @@ -0,0 +1,40 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search buddies */ + +/* + 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 +*/ + +#ifndef __USER_H__ +#define __USER_H__ + +struct __USER +{ + struct im_connection *ic; + char *handle; + char *fullname; + char *group; + + char *away; + char *status_msg; +} user_t; + +#endif /* __USER_H__ */ diff --git a/set.c b/set.c index 18d5a50d..8ecc9690 100644 --- a/set.c +++ b/set.c @@ -224,6 +224,7 @@ char *set_eval_to_char( set_t *set, char *value ) return s; } +/* char *set_eval_ops( set_t *set, char *value ) { irc_t *irc = set->data; @@ -245,3 +246,4 @@ char *set_eval_ops( set_t *set, char *value ) return value; } +*/ diff --git a/unix.c b/unix.c index 7088d0f8..647418fb 100644 --- a/unix.c +++ b/unix.c @@ -61,7 +61,7 @@ int main( int argc, char *argv[] ) return( 1 ); b_main_init(); - nogaim_init(); + //nogaim_init(); srand( time( NULL ) ^ getpid() ); global.helpfile = g_strdup( HELP_FILE ); @@ -114,12 +114,14 @@ int main( int argc, char *argv[] ) } } + /* global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); if( global.storage == NULL ) { log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); return( 1 ); } + */ /* Catch some signals to tell the user what's happening before quitting */ memset( &sig, 0, sizeof( sig ) ); diff --git a/user.c b/user.c deleted file mode 100644 index 4d58f56b..00000000 --- a/user.c +++ /dev/null @@ -1,231 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Stuff to handle, save and search buddies */ - -/* - 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" - -user_t *user_add( irc_t *irc, char *nick ) -{ - user_t *u, *lu = NULL; - char *key; - - if( !nick_ok( nick ) ) - return( NULL ); - - if( user_find( irc, nick ) != NULL ) - return( NULL ); - - if( ( u = irc->users ) ) - { - while( u ) - { - if( nick_cmp( nick, u->nick ) < 0 ) - break; - - lu = u; - u = u->next; - } - - u = g_new0( user_t, 1 ); - if( lu ) - { - u->next = lu->next; - lu->next = u; - } - else - { - u->next = irc->users; - irc->users = u; - } - } - else - { - irc->users = u = g_new0( user_t, 1 ); - } - - u->user = u->realname = u->host = u->nick = g_strdup( nick ); - u->is_private = set_getbool( &irc->set, "private" ); - - key = g_strdup( nick ); - nick_lc( key ); - g_hash_table_insert( irc->userhash, key, u ); - - return( u ); -} - -int user_del( irc_t *irc, char *nick ) -{ - user_t *u, *t; - char *key; - gpointer okey, ovalue; - - if( !nick_ok( nick ) ) - return( 0 ); - - u = irc->users; - t = NULL; - while( u ) - { - if( nick_cmp( u->nick, nick ) == 0 ) - { - /* Get this key now already, since "nick" might be free()d - at the time we start playing with the hash... */ - key = g_strdup( nick ); - nick_lc( key ); - - if( t ) - t->next = u->next; - else - irc->users = u->next; - if( u->online ) - irc_kill( irc, u ); - g_free( u->nick ); - if( u->nick != u->user ) g_free( u->user ); - if( u->nick != u->host ) g_free( u->host ); - if( u->nick != u->realname ) g_free( u->realname ); - g_free( u->group ); - g_free( u->away ); - g_free( u->handle ); - g_free( u->sendbuf ); - if( u->sendbuf_timer ) b_event_remove( u->sendbuf_timer ); - g_free( u ); - - if( !g_hash_table_lookup_extended( irc->userhash, key, &okey, &ovalue ) || ovalue != u ) - { - g_free( key ); - return( 1 ); /* Although this is a severe error, the user is removed from the list... */ - } - g_hash_table_remove( irc->userhash, key ); - g_free( key ); - g_free( okey ); - - return( 1 ); - } - u = (t=u)->next; - } - - return( 0 ); -} - -user_t *user_find( irc_t *irc, char *nick ) -{ - char key[512] = ""; - - strncpy( key, nick, sizeof( key ) - 1 ); - if( nick_lc( key ) ) - return( g_hash_table_lookup( irc->userhash, key ) ); - else - return( NULL ); -} - -user_t *user_findhandle( struct im_connection *ic, const char *handle ) -{ - user_t *u; - char *nick; - - /* First, let's try a hash lookup. If it works, it's probably faster. */ - if( ( nick = g_hash_table_lookup( ic->acc->nicks, handle ) ) && - ( u = user_find( ic->irc, nick ) ) && - ( ic->acc->prpl->handle_cmp( handle, u->handle ) == 0 ) ) - return u; - - /* However, it doesn't always work, so in that case we'll have to dig - through the whole userlist. :-( */ - for( u = ic->irc->users; u; u = u->next ) - if( u->ic == ic && u->handle && ic->acc->prpl->handle_cmp( u->handle, handle ) == 0 ) - return u; - - return NULL; -} - -/* DO NOT PASS u->nick FOR oldnick !!! */ -void user_rename( irc_t *irc, char *oldnick, char *newnick ) -{ - user_t *u = user_find( irc, oldnick ); - gpointer okey, ovalue; - char *key; - - if( !u ) return; /* Should've been checked by the caller... */ - - g_free( u->nick ); - if( u->nick == u->user ) u->user = NULL; - if( u->nick == u->host ) u->host = NULL; - if( u->nick == u->realname ) u->realname = NULL; - u->nick = g_strdup( newnick ); - if( !u->user ) u->user = u->nick; - if( !u->host ) u->host = u->nick; - if( !u->realname ) u->realname = u->nick; - - /* Remove the old reference to this user from the hash and create a - new one with the new nick. This is indeed a bit messy. */ - key = g_strdup( oldnick ); - nick_lc( key ); - if( !g_hash_table_lookup_extended( irc->userhash, key, &okey, &ovalue ) || ovalue != u ) - { - g_free( key ); - return; /* This really shouldn't happen! */ - } - g_hash_table_remove( irc->userhash, key ); - g_free( key ); - g_free( okey ); - - key = g_strdup( newnick ); - nick_lc( key ); - g_hash_table_insert( irc->userhash, key, u ); - - /* Also, let's try to keep the linked list nicely sorted. Fear this - code. If my teacher would see this, she would cry. ;-) */ - { - user_t *u1, *lu1; - - /* Remove the user from the old position in the chain. */ - if( u == irc->users ) - { - irc->users = u->next; - } - else - { - u1 = u; - for( lu1 = irc->users; lu1->next != u1; lu1 = lu1->next ); - lu1->next = u1->next; - } - - /* Search for the new position. */ - for( lu1 = NULL, u1 = irc->users; u1; u1 = u1->next ) - { - if( nick_cmp( newnick, u1->nick ) < 0 ) - break; - - lu1 = u1; - } - - /* Insert it at this new position. */ - u->next = u1; - if( lu1 ) - lu1->next = u; - else - irc->users = u; - } -} diff --git a/user.h b/user.h deleted file mode 100644 index 8c4f9c44..00000000 --- a/user.h +++ /dev/null @@ -1,62 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Stuff to handle, save and search buddies */ - -/* - 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 -*/ -#ifndef __USER_H__ -#define __USER_H__ - -typedef struct __USER -{ - char *nick; - char *user; - char *host; - char *realname; - - char *away; - char *status_msg; /* Non-IRC extension, but nice on IM. */ - - char is_private; - char online; - - char *handle; - char *group; - struct im_connection *ic; - - char *sendbuf; - time_t last_typing_notice; - int sendbuf_len; - guint sendbuf_timer; - int sendbuf_flags; - - void (*send_handler) ( irc_t *irc, struct __USER *u, char *msg, int flags ); - - struct __USER *next; -} user_t; - -user_t *user_add( struct irc *irc, char *nick ); -int user_del( irc_t *irc, char *nick ); -G_MODULE_EXPORT user_t *user_find( irc_t *irc, char *nick ); -G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, const char *handle ); -void user_rename( irc_t *irc, char *oldnick, char *newnick ); - -#endif /* __USER_H__ */ -- cgit v1.2.3 From ebaebfe35c82460581fa6db518d8848996c9a0f4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 26 Mar 2010 21:57:00 -0400 Subject: PING and QUIT work now, and adding some files that weren't checked in so far. --- irc.c | 10 +++- irc.h | 1 + irc_commands.c | 7 +-- irc_send.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ irc_user.c | 113 +++++++++++++++++++++++++++++++++++++++++ protocols/bee.c | 47 +++++++++++++++++ protocols/bee.h | 18 +++++++ protocols/bee.o | Bin 0 -> 8692 bytes 8 files changed, 344 insertions(+), 5 deletions(-) create mode 100644 irc_send.c create mode 100644 irc_user.c create mode 100644 protocols/bee.c create mode 100644 protocols/bee.h create mode 100644 protocols/bee.o diff --git a/irc.c b/irc.c index 8e44fe9e..e6e853df 100644 --- a/irc.c +++ b/irc.c @@ -123,13 +123,16 @@ irc_t *irc_new( int fd ) irc->user = g_new0( irc_user_t, 1 ); irc->user->host = g_strdup( host ); - conf_loaddefaults( b ); + conf_loaddefaults( irc ); /* Evaluator sets the iconv/oconv structures. */ set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) ); irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" ); + g_free( myhost ); + g_free( host ); + return irc; } @@ -202,7 +205,10 @@ void irc_free( irc_t * irc ) */ while( irc->users ) - irc_user_free( irc, irc->users->data ); + { + irc_user_t *iu = irc->users->data; + irc_user_free( irc, iu->nick ); + } if( irc->ping_source_id > 0 ) b_event_remove( irc->ping_source_id ); diff --git a/irc.h b/irc.h index 482ef067..16a7046e 100644 --- a/irc.h +++ b/irc.h @@ -138,5 +138,6 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); int irc_user_free( irc_t *irc, const char *nick ); irc_user_t *irc_user_find( irc_t *irc, const char *nick ); int irc_user_rename( irc_t *irc, const char *old, const char *new ); +gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ); #endif diff --git a/irc_commands.c b/irc_commands.c index 81ddd588..7a4ba57e 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -93,7 +93,6 @@ static void irc_cmd_nick( irc_t *irc, char **cmd ) } } -#if 0 static void irc_cmd_quit( irc_t *irc, char **cmd ) { if( cmd[1] && *cmd[1] ) @@ -104,9 +103,11 @@ static void irc_cmd_quit( irc_t *irc, char **cmd ) static void irc_cmd_ping( irc_t *irc, char **cmd ) { - irc_write( irc, ":%s PONG %s :%s", irc->myhost, irc->myhost, cmd[1]?cmd[1]:irc->myhost ); + irc_write( irc, ":%s PONG %s :%s", irc->root->host, + irc->root->host, cmd[1]?cmd[1]:irc->root->host ); } +#if 0 static void irc_cmd_oper( irc_t *irc, char **cmd ) { if( global.conf->oper_pass && @@ -579,9 +580,9 @@ static const command_t irc_commands[] = { { "pass", 1, irc_cmd_pass, 0 }, { "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN }, { "nick", 1, irc_cmd_nick, 0 }, -#if 0 { "quit", 0, irc_cmd_quit, 0 }, { "ping", 0, irc_cmd_ping, 0 }, +#if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, { "names", 0, irc_cmd_names, IRC_CMD_LOGGED_IN }, diff --git a/irc_send.c b/irc_send.c new file mode 100644 index 00000000..7b4c567a --- /dev/null +++ b/irc_send.c @@ -0,0 +1,153 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* The IRC-based UI - Sending responses to commands/etc. */ + +/* + 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 +*/ + +#include "bitlbee.h" + +void irc_send_num( irc_t *irc, int code, char *format, ... ) +{ + char text[IRC_MAX_LINE]; + va_list params; + + va_start( params, format ); + g_vsnprintf( text, IRC_MAX_LINE, format, params ); + va_end( params ); + irc_write( irc, ":%s %03d %s %s", irc->root->host, code, irc->user->nick ? : "*", text ); + + return; +} + +void irc_send_login( irc_t *irc ) +{ + irc_user_t *iu = irc->user; + + irc->user = irc_user_new( irc, iu->nick ); + irc->user->user = iu->user; + irc->user->fullname = iu->fullname; + g_free( iu->nick ); + g_free( iu ); + + irc_send_num( irc, 1, ":Welcome to the BitlBee gateway, %s", irc->user->nick ); + irc_send_num( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->root->host ); + irc_send_num( irc, 3, ":%s", IRCD_INFO ); + irc_send_num( irc, 4, "%s %s %s %s", irc->root->host, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES ); + irc_send_num( irc, 5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee " + "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", + CTYPES, CMODES, MAX_NICK_LENGTH - 1 ); + irc_send_motd( irc ); + irc->umode[0] = '\0'; + /*irc_umode_set( irc, "+" UMODE, 1 );*/ + + irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n" + "If you've never used BitlBee before, please do read the help " + "information using the \x02help\x02 command. Lots of FAQs are " + "answered there.\n" + "If you already have an account on this server, just use the " + "\x02identify\x02 command to identify yourself." ); + + if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) + ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); + + irc->status |= USTATUS_LOGGED_IN; + + /* This is for bug #209 (use PASS to identify to NickServ). */ + if( irc->password != NULL ) + { + char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; + + /*irc_setpass( irc, NULL );*/ + /*root_command( irc, send_cmd );*/ + g_free( send_cmd[1] ); + } +} + +void irc_send_motd( irc_t *irc ) +{ + int fd; + + fd = open( global.conf->motdfile, O_RDONLY ); + if( fd == -1 ) + { + irc_send_num( irc, 422, ":We don't need MOTDs." ); + } + else + { + char linebuf[80]; /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */ + char *add, max; + int len; + + linebuf[79] = len = 0; + max = sizeof( linebuf ) - 1; + + irc_send_num( irc, 375, ":- %s Message Of The Day - ", irc->root->host ); + while( read( fd, linebuf + len, 1 ) == 1 ) + { + if( linebuf[len] == '\n' || len == max ) + { + linebuf[len] = 0; + irc_send_num( irc, 372, ":- %s", linebuf ); + len = 0; + } + else if( linebuf[len] == '%' ) + { + read( fd, linebuf + len, 1 ); + if( linebuf[len] == 'h' ) + add = irc->root->host; + else if( linebuf[len] == 'v' ) + add = BITLBEE_VERSION; + else if( linebuf[len] == 'n' ) + add = irc->user->nick; + else + add = "%"; + + strncpy( linebuf + len, add, max - len ); + while( linebuf[++len] ); + } + else if( len < max ) + { + len ++; + } + } + irc_send_num( irc, 376, ":End of MOTD" ); + close( fd ); + } +} + +/* FIXME/REPLACEME */ +int irc_usermsg( irc_t *irc, char *format, ... ) +{ + char text[1024]; + va_list params; + //irc_user_t *iu; + + va_start( params, format ); + g_vsnprintf( text, sizeof( text ), format, params ); + va_end( params ); + + fprintf( stderr, "%s\n", text ); + + return 1; + + /*return( irc_msgfrom( irc, u->nick, text ) );*/ +} diff --git a/irc_user.c b/irc_user.c new file mode 100644 index 00000000..d1f07bb6 --- /dev/null +++ b/irc_user.c @@ -0,0 +1,113 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search IRC buddies */ + +/* + 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 +*/ + +#include "bitlbee.h" + +irc_user_t *irc_user_new( irc_t *irc, const char *nick ) +{ + irc_user_t *iu = g_new0( irc_user_t, 1 ); + + iu->nick = g_strdup( nick ); + iu->user = iu->host = iu->fullname = iu->nick; + + iu->is_private = set_getbool( &irc->b->set, "private" ); + + iu->key = g_strdup( nick ); + nick_lc( iu->key ); + /* Using the hash table for speed and irc->users for easy iteration + through the list (since the GLib API doesn't have anything sane + for that.) */ + g_hash_table_insert( irc->nick_user_hash, iu->key, iu ); + irc->users = g_slist_insert_sorted( irc->users, iu, irc_user_cmp ); + + return iu; +} + +int irc_user_free( irc_t *irc, const char *nick ) +{ + irc_user_t *iu; + + if( !( iu = irc_user_find( irc, nick ) ) ) + return 0; + + irc->users = g_slist_remove( irc->users, iu ); + g_hash_table_remove( irc->nick_user_hash, iu->key ); + + g_free( iu->nick ); + if( iu->nick != iu->user ) g_free( iu->user ); + if( iu->nick != iu->host ) g_free( iu->host ); + if( iu->nick != iu->fullname ) g_free( iu->fullname ); + g_free( iu->sendbuf ); + if( iu->sendbuf_timer ) b_event_remove( iu->sendbuf_timer ); + g_free( iu->key ); + + return 1; +} + +irc_user_t *irc_user_find( irc_t *irc, const char *nick ) +{ + char key[strlen(nick)+1]; + + strcpy( key, nick ); + if( nick_lc( key ) ) + return g_hash_table_lookup( irc->nick_user_hash, key ); + else + return NULL; +} + +int irc_user_rename( irc_t *irc, const char *old, const char *new ) +{ + irc_user_t *iu = irc_user_find( irc, old ); + char key[strlen(new)+1]; + + strcpy( key, new ); + if( iu == NULL || !nick_lc( key ) || irc_user_find( irc, new ) ) + return 0; + + irc->users = g_slist_remove( irc->users, iu ); + g_hash_table_remove( irc->nick_user_hash, iu->key ); + + if( iu->nick == iu->user ) iu->user = NULL; + if( iu->nick == iu->host ) iu->host = NULL; + if( iu->nick == iu->fullname ) iu->fullname = NULL; + g_free( iu->nick ); + iu->nick = g_strdup( new ); + if( iu->user == NULL ) iu->user = g_strdup( iu->nick ); + if( iu->host == NULL ) iu->host = g_strdup( iu->nick ); + if( iu->fullname == NULL ) iu->fullname = g_strdup( iu->nick ); + + iu->key = g_strdup( key ); + g_hash_table_insert( irc->nick_user_hash, iu->key, iu ); + irc->users = g_slist_insert_sorted( irc->users, iu, irc_user_cmp ); + + return 1; +} + +gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ) +{ + const irc_user_t *a = a_, *b = b_; + + return strcmp( a->key, b->key ); +} diff --git a/protocols/bee.c b/protocols/bee.c new file mode 100644 index 00000000..c6f48901 --- /dev/null +++ b/protocols/bee.c @@ -0,0 +1,47 @@ +#include "bitlbee.h" + +bee_t *bee_new() +{ + bee_t *b = g_new0( bee_t, 1 ); + set_t *s; + + s = set_add( &b->set, "away", NULL, NULL/*set_eval_away_status*/, b ); + s->flags |= SET_NULL_OK; + s = set_add( &b->set, "auto_connect", "true", set_eval_bool, b ); + s = set_add( &b->set, "auto_reconnect", "true", set_eval_bool, b ); + s = set_add( &b->set, "auto_reconnect_delay", "5*3<900", NULL/*set_eval_account_reconnect_delay*/, b ); + s = set_add( &b->set, "debug", "false", set_eval_bool, b ); + s = set_add( &b->set, "password", NULL, NULL/*set_eval_password*/, b ); + s->flags |= SET_NULL_OK; + s = set_add( &b->set, "save_on_quit", "true", set_eval_bool, b ); + s = set_add( &b->set, "status", NULL, NULL/*set_eval_away_status*/, b ); + s->flags |= SET_NULL_OK; + s = set_add( &b->set, "strip_html", "true", NULL, b ); + + return b; +} + +void bee_free( bee_t *b ) +{ + while( b->accounts ) + { + account_t *acc = b->accounts->data; + + /* + if( acc->ic ) + imc_logout( acc->ic, FALSE ); + else if( acc->reconnect ) + cancel_auto_reconnect( acc ); + */ + + if( acc->ic == NULL ) + {} //account_del( b, acc ); + else + /* Nasty hack, but account_del() doesn't work in this + case and we don't want infinite loops, do we? ;-) */ + b->accounts = g_slist_remove( b->accounts, acc ); + } + + while( b->set ) + set_del( &b->set, b->set->key ); +} diff --git a/protocols/bee.h b/protocols/bee.h new file mode 100644 index 00000000..b25f0e08 --- /dev/null +++ b/protocols/bee.h @@ -0,0 +1,18 @@ +typedef struct bee_ui +{ + void *data; +} bee_ui_t; + +typedef struct bee +{ + struct set *set; + + GSList *users; + GSList *accounts; + + //const bee_ui_funcs_t *ui; + void *ui_data; +} bee_t; + +bee_t *bee_new(); +void bee_free( bee_t *b ); diff --git a/protocols/bee.o b/protocols/bee.o new file mode 100644 index 00000000..38c9044f Binary files /dev/null and b/protocols/bee.o differ -- cgit v1.2.3 From 4be823968d7f4cb1d11e4f6dda50ef606a0fd7b0 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 26 Mar 2010 22:39:08 -0400 Subject: Simple IRC channel interface, use it to represent the control channel. --- Makefile | 2 +- irc.c | 32 +++++++++++++++++++ irc.h | 31 ++++++++++++++++-- irc_channel.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ irc_send.c | 97 ++++++++++++++++++++++++++++++++++++++------------------ protocols/bee.o | Bin 8692 -> 0 bytes 6 files changed, 216 insertions(+), 34 deletions(-) create mode 100644 irc_channel.c delete mode 100644 protocols/bee.o diff --git a/Makefile b/Makefile index ecd133e2..9e300c12 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # Program variables #objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) -objects = bitlbee.o help.o ipc.o irc.o irc_commands.o irc_send.o irc_user.o nick.o set.o +objects = bitlbee.o help.o ipc.o irc.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o set.o headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols diff --git a/irc.c b/irc.c index e6e853df..7c7445e3 100644 --- a/irc.c +++ b/irc.c @@ -585,7 +585,39 @@ int irc_check_login( irc_t *irc ) } else { + irc_channel_t *ic; + irc_user_t *iu = irc->user; + + irc->user = irc_user_new( irc, iu->nick ); + irc->user->user = iu->user; + irc->user->fullname = iu->fullname; + g_free( iu->nick ); + g_free( iu ); + + irc->umode[0] = '\0'; + /*irc_umode_set( irc, "+" UMODE, 1 );*/ + + if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) + ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); + + irc->status |= USTATUS_LOGGED_IN; + + /* This is for bug #209 (use PASS to identify to NickServ). */ + if( irc->password != NULL ) + { + char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; + + /*irc_setpass( irc, NULL );*/ + /*root_command( irc, send_cmd );*/ + g_free( send_cmd[1] ); + } + irc_send_login( irc ); + + ic = irc_channel_new( irc, ROOT_CHAN ); + irc_channel_set_topic( ic, CONTROL_TOPIC ); + irc_channel_add_user( ic, irc->user ); + return 1; } } diff --git a/irc.h b/irc.h index 16a7046e..1ef73854 100644 --- a/irc.h +++ b/irc.h @@ -72,9 +72,8 @@ typedef struct irc struct query *queries; struct account *accounts; GSList *file_transfers; - struct chat *chatrooms; - GSList *users; + GSList *users, *channels; GHashTable *nick_user_hash; GHashTable *watches; @@ -105,6 +104,22 @@ typedef struct irc_user //struct user *b; } irc_user_t; +typedef enum +{ + IRC_CHANNEL_JOINED = 1, +} irc_channel_flags_t; + +typedef struct irc_channel +{ + irc_t *irc; + int flags; + char *name; + char *topic; + char mode[8]; + GSList *users; + struct set *set; +} irc_channel_t; + #include "user.h" /* irc.c */ @@ -124,6 +139,12 @@ void irc_vawrite( irc_t *irc, char *format, va_list params ); int irc_check_login( irc_t *irc ); +/* irc_channel.c */ +irc_channel_t *irc_channel_new( irc_t *irc, const char *name ); +int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); +int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); +int irc_channel_set_topic( irc_channel_t *ic, const char *topic ); + /* irc_commands.c */ void irc_exec( irc_t *irc, char **cmd ); @@ -131,7 +152,11 @@ void irc_exec( irc_t *irc, char **cmd ); void irc_send_num( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 ); void irc_send_login( irc_t *irc ); void irc_send_motd( irc_t *irc ); -int irc_usermsg( irc_t *irc, char *format, ... ); +void irc_usermsg( irc_t *irc, char *format, ... ); +void irc_send_join( irc_channel_t *ic, irc_user_t *iu ); +void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ); +void irc_send_names( irc_channel_t *ic ); +void irc_send_topic( irc_channel_t *ic ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); diff --git a/irc_channel.c b/irc_channel.c new file mode 100644 index 00000000..3db01ee5 --- /dev/null +++ b/irc_channel.c @@ -0,0 +1,88 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* The IRC-based UI - Representing (virtual) channels. */ + +/* + 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 +*/ + +#include "bitlbee.h" + +irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) +{ + irc_channel_t *ic; + + if( strchr( CTYPES, name[0] ) == NULL || !nick_ok( name + 1 ) ) + return NULL; + + ic = g_new0( irc_channel_t, 1 ); + ic->irc = irc; + ic->name = g_strdup( name ); + strcpy( ic->mode, CMODE ); + + irc_channel_add_user( ic, irc->root ); + + irc->channels = g_slist_prepend( irc->channels, ic ); + + return ic; +} + +int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) +{ + if( g_slist_find( ic->users, iu ) != NULL ) + return 0; + + ic->users = g_slist_insert_sorted( ic->users, iu, irc_user_cmp ); + + if( iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED ) + { + ic->flags |= IRC_CHANNEL_JOINED; + irc_send_join( ic, iu ); + } + + return 1; +} + +int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) +{ + if( g_slist_find( ic->users, iu ) == NULL ) + return 0; + + ic->users = g_slist_remove( ic->users, iu ); + + if( ic->flags & IRC_CHANNEL_JOINED ) + irc_send_part( ic, iu, "" ); + + if( iu == ic->irc->user ) + ic->flags &= ~IRC_CHANNEL_JOINED; + + return 1; +} + +int irc_channel_set_topic( irc_channel_t *ic, const char *topic ) +{ + g_free( ic->topic ); + ic->topic = g_strdup( topic ); + + if( ic->flags & IRC_CHANNEL_JOINED ) + irc_send_topic( ic ); + + return 1; +} diff --git a/irc_send.c b/irc_send.c index 7b4c567a..513f556d 100644 --- a/irc_send.c +++ b/irc_send.c @@ -33,21 +33,12 @@ void irc_send_num( irc_t *irc, int code, char *format, ... ) va_start( params, format ); g_vsnprintf( text, IRC_MAX_LINE, format, params ); va_end( params ); - irc_write( irc, ":%s %03d %s %s", irc->root->host, code, irc->user->nick ? : "*", text ); - return; + irc_write( irc, ":%s %03d %s %s", irc->root->host, code, irc->user->nick ? : "*", text ); } void irc_send_login( irc_t *irc ) { - irc_user_t *iu = irc->user; - - irc->user = irc_user_new( irc, iu->nick ); - irc->user->user = iu->user; - irc->user->fullname = iu->fullname; - g_free( iu->nick ); - g_free( iu ); - irc_send_num( irc, 1, ":Welcome to the BitlBee gateway, %s", irc->user->nick ); irc_send_num( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->root->host ); irc_send_num( irc, 3, ":%s", IRCD_INFO ); @@ -56,8 +47,6 @@ void irc_send_login( irc_t *irc ) "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", CTYPES, CMODES, MAX_NICK_LENGTH - 1 ); irc_send_motd( irc ); - irc->umode[0] = '\0'; - /*irc_umode_set( irc, "+" UMODE, 1 );*/ irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n" "If you've never used BitlBee before, please do read the help " @@ -65,21 +54,6 @@ void irc_send_login( irc_t *irc ) "answered there.\n" "If you already have an account on this server, just use the " "\x02identify\x02 command to identify yourself." ); - - if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) - ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); - - irc->status |= USTATUS_LOGGED_IN; - - /* This is for bug #209 (use PASS to identify to NickServ). */ - if( irc->password != NULL ) - { - char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; - - /*irc_setpass( irc, NULL );*/ - /*root_command( irc, send_cmd );*/ - g_free( send_cmd[1] ); - } } void irc_send_motd( irc_t *irc ) @@ -135,7 +109,7 @@ void irc_send_motd( irc_t *irc ) } /* FIXME/REPLACEME */ -int irc_usermsg( irc_t *irc, char *format, ... ) +void irc_usermsg( irc_t *irc, char *format, ... ) { char text[1024]; va_list params; @@ -147,7 +121,70 @@ int irc_usermsg( irc_t *irc, char *format, ... ) fprintf( stderr, "%s\n", text ); - return 1; - /*return( irc_msgfrom( irc, u->nick, text ) );*/ } + +void irc_send_join( irc_channel_t *ic, irc_user_t *iu ) +{ + irc_t *irc = ic->irc; + + irc_write( irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name ); + + if( iu == irc->user ) + { + irc_write( irc, ":%s MODE %s +%s", irc->root->host, ic->name, ic->mode ); + irc_send_names( ic ); + irc_send_topic( ic ); + } +} + +void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ) +{ + irc_write( ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason ); +} + +void irc_send_names( irc_channel_t *ic ) +{ + GSList *l; + irc_user_t *iu; + char namelist[385] = ""; + struct groupchat *c = NULL; + char *ops = set_getstr( &ic->irc->b->set, "ops" ); + + /* RFCs say there is no error reply allowed on NAMES, so when the + channel is invalid, just give an empty reply. */ + for( l = ic->users; l; l = l->next ) + { + irc_user_t *iu = l->data; + + if( strlen( namelist ) + strlen( iu->nick ) > sizeof( namelist ) - 4 ) + { + irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist ); + *namelist = 0; + } + + /* + if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) ) + strcat( namelist, "+" ); + else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) || + ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ) + strcat( namelist, "@" ); + */ + + strcat( namelist, iu->nick ); + strcat( namelist, " " ); + } + + if( *namelist ) + irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist ); + + irc_send_num( ic->irc, 366, "%s :End of /NAMES list", ic->name ); +} + +void irc_send_topic( irc_channel_t *ic ) +{ + if( ic->topic ) + irc_send_num( ic->irc, 332, "%s :%s", ic->name, ic->topic ); + else + irc_send_num( ic->irc, 331, "%s :No topic for this channel", ic->name ); +} diff --git a/protocols/bee.o b/protocols/bee.o deleted file mode 100644 index 38c9044f..00000000 Binary files a/protocols/bee.o and /dev/null differ -- cgit v1.2.3 From b9e020af6c6a88392caa9edd120fb576ec971430 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 26 Mar 2010 23:04:35 -0400 Subject: Added JOIN, NAMES and PART commands. --- irc.h | 1 + irc_channel.c | 15 ++++++++ irc_commands.c | 109 ++++++++++++++++++++++++++++----------------------------- 3 files changed, 69 insertions(+), 56 deletions(-) diff --git a/irc.h b/irc.h index 1ef73854..db07109d 100644 --- a/irc.h +++ b/irc.h @@ -141,6 +141,7 @@ int irc_check_login( irc_t *irc ); /* irc_channel.c */ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ); +irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic ); diff --git a/irc_channel.c b/irc_channel.c index 3db01ee5..17904d72 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -44,6 +44,21 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) return ic; } +irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ) +{ + GSList *l; + + for( l = irc->channels; l; l = l->next ) + { + irc_channel_t *ic = l->data; + + if( name[0] == ic->name[0] && nick_cmp( name + 1, ic->name + 1 ) == 0 ) + return ic; + } + + return NULL; +} + int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) { if( g_slist_find( ic->users, iu ) != NULL ) diff --git a/irc_commands.c b/irc_commands.c index 7a4ba57e..e5a97ad9 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -107,7 +107,57 @@ static void irc_cmd_ping( irc_t *irc, char **cmd ) irc->root->host, cmd[1]?cmd[1]:irc->root->host ); } +static void irc_cmd_join( irc_t *irc, char **cmd ) +{ + irc_channel_t *ic; + + if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL ) + ic = irc_channel_new( irc, cmd[1] ); + + if( ic == NULL ) + irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] ); + + if( ic->flags & IRC_CHANNEL_JOINED ) + return; /* Dude, you're already there... + RFC doesn't have any reply for that though? */ + + irc_channel_add_user( ic, irc->user ); +} + +static void irc_cmd_names( irc_t *irc, char **cmd ) +{ + irc_channel_t *ic; + + if( cmd[1] && ( ic = irc_channel_by_name( irc, cmd[1] ) ) ) + irc_send_names( ic ); + /* With no args, we should show /names of all chans. Make the code + below work well if necessary. + else + { + GSList *l; + + for( l = irc->channels; l; l = l->next ) + irc_send_names( l->data ); + } + */ +} + +static void irc_cmd_part( irc_t *irc, char **cmd ) +{ + irc_channel_t *ic; + + if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL ) + { + irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); + } + else if( !irc_channel_del_user( ic, irc->user ) ) + { + irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] ); + } +} + #if 0 +//#if 0 static void irc_cmd_oper( irc_t *irc, char **cmd ) { if( global.conf->oper_pass && @@ -152,59 +202,6 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) } } -static void irc_cmd_names( irc_t *irc, char **cmd ) -{ - irc_names( irc, cmd[1]?cmd[1]:irc->channel ); -} - -static void irc_cmd_part( irc_t *irc, char **cmd ) -{ - struct groupchat *c; - - if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) - { - user_t *u = user_find( irc, irc->nick ); - - /* Not allowed to leave control channel */ - irc_part( irc, u, irc->channel ); - irc_join( irc, u, irc->channel ); - } - else if( ( c = irc_chat_by_channel( irc, cmd[1] ) ) ) - { - user_t *u = user_find( irc, irc->nick ); - - irc_part( irc, u, c->channel ); - - if( c->ic ) - { - c->joined = 0; - c->ic->acc->prpl->chat_leave( c ); - } - } - else - { - irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); - } -} - -static void irc_cmd_join( irc_t *irc, char **cmd ) -{ - if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) - ; /* Dude, you're already there... - RFC doesn't have any reply for that though? */ - else if( cmd[1] ) - { - struct chat *c; - - if( strchr( CTYPES, cmd[1][0] ) == NULL || cmd[1][1] == 0 ) - irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] ); - else if( ( c = chat_bychannel( irc, cmd[1] ) ) && c->acc && c->acc->ic ) - chat_join( irc, c, cmd[2] ); - else - irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); - } -} - static void irc_cmd_invite( irc_t *irc, char **cmd ) { char *nick = cmd[1], *channel = cmd[2]; @@ -582,12 +579,12 @@ static const command_t irc_commands[] = { { "nick", 1, irc_cmd_nick, 0 }, { "quit", 0, irc_cmd_quit, 0 }, { "ping", 0, irc_cmd_ping, 0 }, + { "join", 1, irc_cmd_join, IRC_CMD_LOGGED_IN }, + { "names", 1, irc_cmd_names, IRC_CMD_LOGGED_IN }, + { "part", 1, irc_cmd_part, IRC_CMD_LOGGED_IN }, #if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, - { "names", 0, irc_cmd_names, IRC_CMD_LOGGED_IN }, - { "part", 1, irc_cmd_part, IRC_CMD_LOGGED_IN }, - { "join", 1, irc_cmd_join, IRC_CMD_LOGGED_IN }, { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, -- cgit v1.2.3 From 63a520b8ce1776442f2f79528ddd23fb0de51f94 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 26 Mar 2010 23:11:02 -0400 Subject: Collect that garbage. --- irc.c | 5 +++++ irc_channel.c | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/irc.c b/irc.c index 7c7445e3..f21dc974 100644 --- a/irc.c +++ b/irc.c @@ -210,6 +210,9 @@ void irc_free( irc_t * irc ) irc_user_free( irc, iu->nick ); } + while( irc->channels ) + irc_channel_free( irc->channels->data ); + if( irc->ping_source_id > 0 ) b_event_remove( irc->ping_source_id ); if( irc->r_watch_source_id > 0 ) @@ -591,7 +594,9 @@ int irc_check_login( irc_t *irc ) irc->user = irc_user_new( irc, iu->nick ); irc->user->user = iu->user; irc->user->fullname = iu->fullname; + g_free( iu->fullname ); g_free( iu->nick ); + g_free( iu->host ); g_free( iu ); irc->umode[0] = '\0'; diff --git a/irc_channel.c b/irc_channel.c index 17904d72..c58d6b1c 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -59,6 +59,23 @@ irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ) return NULL; } +int irc_channel_free( irc_channel_t *ic ) +{ + irc_t *irc = ic->irc; + + if( ic->flags & IRC_CHANNEL_JOINED ) + irc_channel_del_user( ic, irc->user ); + + irc->channels = g_slist_remove( irc->channels, ic ); + g_slist_free( ic->users ); + + g_free( ic->name ); + g_free( ic->topic ); + g_free( ic ); + + return 1; +} + int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) { if( g_slist_find( ic->users, iu ) != NULL ) -- cgit v1.2.3 From b95932eb5a897fd264f3762493285dd7037dccba Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 26 Mar 2010 23:39:23 -0400 Subject: Added WHOIS command. --- irc.c | 3 +-- irc.h | 3 +++ irc_commands.c | 68 +++++++++++++++++++++------------------------------------- irc_send.c | 26 ++++++++++++++++++++++ irc_user.c | 1 + 5 files changed, 55 insertions(+), 46 deletions(-) diff --git a/irc.c b/irc.c index f21dc974..6e4ba157 100644 --- a/irc.c +++ b/irc.c @@ -593,10 +593,9 @@ int irc_check_login( irc_t *irc ) irc->user = irc_user_new( irc, iu->nick ); irc->user->user = iu->user; + irc->user->host = iu->host; irc->user->fullname = iu->fullname; - g_free( iu->fullname ); g_free( iu->nick ); - g_free( iu->host ); g_free( iu ); irc->umode[0] = '\0'; diff --git a/irc.h b/irc.h index db07109d..646281ff 100644 --- a/irc.h +++ b/irc.h @@ -86,6 +86,8 @@ typedef struct irc typedef struct irc_user { + irc_t *irc; + char *nick; char *user; char *host; @@ -158,6 +160,7 @@ void irc_send_join( irc_channel_t *ic, irc_user_t *iu ); void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ); void irc_send_names( irc_channel_t *ic ); void irc_send_topic( irc_channel_t *ic ); +void irc_send_whois( irc_user_t *iu ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); diff --git a/irc_commands.c b/irc_commands.c index e5a97ad9..fbba928a 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -156,6 +156,28 @@ static void irc_cmd_part( irc_t *irc, char **cmd ) } } +static void irc_cmd_whois( irc_t *irc, char **cmd ) +{ + char *nick = cmd[1]; + irc_user_t *iu = irc_user_find( irc, nick ); + + if( iu ) + irc_send_whois( iu ); + else + irc_send_num( irc, 401, "%s :Nick does not exist", nick ); +} + +static void irc_cmd_whowas( irc_t *irc, char **cmd ) +{ + /* For some reason irssi tries a whowas when whois fails. We can + ignore this, but then the user never gets a "user not found" + message from irssi which is a bit annoying. So just respond + with not-found and irssi users will get better error messages */ + + irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] ); + irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] ); +} + #if 0 //#if 0 static void irc_cmd_oper( irc_t *irc, char **cmd ) @@ -474,48 +496,6 @@ static void irc_cmd_away( irc_t *irc, char **cmd ) set_setstr( &irc->set, "away", u->away ); } -static void irc_cmd_whois( irc_t *irc, char **cmd ) -{ - char *nick = cmd[1]; - user_t *u = user_find( irc, nick ); - - if( u ) - { - irc_send_num( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); - - if( u->ic ) - irc_send_num( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user, - u->ic->acc->server && *u->ic->acc->server ? u->ic->acc->server : "", - u->ic->acc->prpl->name ); - else - irc_send_num( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); - - if( !u->online ) - irc_send_num( irc, 301, "%s :%s", u->nick, "User is offline" ); - else if( u->away ) - irc_send_num( irc, 301, "%s :%s", u->nick, u->away ); - if( u->status_msg ) - irc_send_num( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); - - irc_send_num( irc, 318, "%s :End of /WHOIS list", nick ); - } - else - { - irc_send_num( irc, 401, "%s :Nick does not exist", nick ); - } -} - -static void irc_cmd_whowas( irc_t *irc, char **cmd ) -{ - /* For some reason irssi tries a whowas when whois fails. We can - ignore this, but then the user never gets a "user not found" - message from irssi which is a bit annoying. So just respond - with not-found and irssi users will get better error messages */ - - irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] ); - irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] ); -} - static void irc_cmd_nickserv( irc_t *irc, char **cmd ) { /* [SH] This aliases the NickServ command to PRIVMSG root */ @@ -582,6 +562,8 @@ static const command_t irc_commands[] = { { "join", 1, irc_cmd_join, IRC_CMD_LOGGED_IN }, { "names", 1, irc_cmd_names, IRC_CMD_LOGGED_IN }, { "part", 1, irc_cmd_part, IRC_CMD_LOGGED_IN }, + { "whois", 1, irc_cmd_whois, IRC_CMD_LOGGED_IN }, + { "whowas", 1, irc_cmd_whowas, IRC_CMD_LOGGED_IN }, #if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, @@ -594,8 +576,6 @@ static const command_t irc_commands[] = { { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, - { "whois", 1, irc_cmd_whois, IRC_CMD_LOGGED_IN }, - { "whowas", 1, irc_cmd_whowas, IRC_CMD_LOGGED_IN }, { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN }, diff --git a/irc_send.c b/irc_send.c index 513f556d..b19d6a0f 100644 --- a/irc_send.c +++ b/irc_send.c @@ -188,3 +188,29 @@ void irc_send_topic( irc_channel_t *ic ) else irc_send_num( ic->irc, 331, "%s :No topic for this channel", ic->name ); } + +void irc_send_whois( irc_user_t *iu ) +{ + irc_t *irc = iu->irc; + + irc_send_num( irc, 311, "%s %s %s * :%s", + iu->nick, iu->user, iu->host, iu->fullname ); + + /* + if( u->ic ) + irc_send_num( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user, + u->ic->acc->server && *u->ic->acc->server ? u->ic->acc->server : "", + u->ic->acc->prpl->name ); + else + irc_send_num( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); + */ + + /* + if( u->away ) + irc_send_num( irc, 301, "%s :%s", u->nick, u->away ); + if( u->status_msg ) + irc_send_num( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); + */ + + irc_send_num( irc, 318, "%s :End of /WHOIS list", iu->nick ); +} diff --git a/irc_user.c b/irc_user.c index d1f07bb6..1de0c4e3 100644 --- a/irc_user.c +++ b/irc_user.c @@ -29,6 +29,7 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ) { irc_user_t *iu = g_new0( irc_user_t, 1 ); + iu->irc = irc; iu->nick = g_strdup( nick ); iu->user = iu->host = iu->fullname = iu->nick; -- cgit v1.2.3 From 83e92bf7487623c10567502936ca727f3a4c104c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 08:30:00 -0400 Subject: Topic handling changes. --- irc.c | 2 +- irc.h | 7 +++++-- irc_channel.c | 12 ++++++++++-- irc_send.c | 16 +++++++++++++--- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/irc.c b/irc.c index 6e4ba157..bf84f66d 100644 --- a/irc.c +++ b/irc.c @@ -619,7 +619,7 @@ int irc_check_login( irc_t *irc ) irc_send_login( irc ); ic = irc_channel_new( irc, ROOT_CHAN ); - irc_channel_set_topic( ic, CONTROL_TOPIC ); + irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); irc_channel_add_user( ic, irc->user ); return 1; diff --git a/irc.h b/irc.h index 646281ff..48cba879 100644 --- a/irc.h +++ b/irc.h @@ -117,6 +117,8 @@ typedef struct irc_channel int flags; char *name; char *topic; + char *topic_who; + time_t topic_time; char mode[8]; GSList *users; struct set *set; @@ -144,9 +146,10 @@ int irc_check_login( irc_t *irc ); /* irc_channel.c */ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ); irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ); +int irc_channel_free( irc_channel_t *ic ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); -int irc_channel_set_topic( irc_channel_t *ic, const char *topic ); +int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); /* irc_commands.c */ void irc_exec( irc_t *irc, char **cmd ); @@ -159,7 +162,7 @@ void irc_usermsg( irc_t *irc, char *format, ... ); void irc_send_join( irc_channel_t *ic, irc_user_t *iu ); void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ); void irc_send_names( irc_channel_t *ic ); -void irc_send_topic( irc_channel_t *ic ); +void irc_send_topic( irc_channel_t *ic, gboolean topic_change ); void irc_send_whois( irc_user_t *iu ); /* irc_user.c */ diff --git a/irc_channel.c b/irc_channel.c index c58d6b1c..d15b73eb 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -108,13 +108,21 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) return 1; } -int irc_channel_set_topic( irc_channel_t *ic, const char *topic ) +int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *iu ) { g_free( ic->topic ); ic->topic = g_strdup( topic ); + g_free( ic->topic_who ); + if( iu ) + ic->topic_who = g_strdup_printf( "%s!%s@%s", iu->nick, iu->user, iu->host ); + else + ic->topic_who = NULL; + + ic->topic_time = time( NULL ); + if( ic->flags & IRC_CHANNEL_JOINED ) - irc_send_topic( ic ); + irc_send_topic( ic, TRUE ); return 1; } diff --git a/irc_send.c b/irc_send.c index b19d6a0f..f0c3958c 100644 --- a/irc_send.c +++ b/irc_send.c @@ -134,7 +134,7 @@ void irc_send_join( irc_channel_t *ic, irc_user_t *iu ) { irc_write( irc, ":%s MODE %s +%s", irc->root->host, ic->name, ic->mode ); irc_send_names( ic ); - irc_send_topic( ic ); + irc_send_topic( ic, FALSE ); } } @@ -181,10 +181,20 @@ void irc_send_names( irc_channel_t *ic ) irc_send_num( ic->irc, 366, "%s :End of /NAMES list", ic->name ); } -void irc_send_topic( irc_channel_t *ic ) +void irc_send_topic( irc_channel_t *ic, gboolean topic_change ) { - if( ic->topic ) + if( topic_change && ic->topic_who ) + { + irc_write( ic->irc, ":%s TOPIC %s :%s", ic->topic_who, + ic->name, ic->topic && *ic->topic ? ic->topic : "" ); + } + else if( ic->topic ) + { irc_send_num( ic->irc, 332, "%s :%s", ic->name, ic->topic ); + if( ic->topic_who ) + irc_send_num( ic->irc, 333, "%s %s %d", + ic->name, ic->topic_who, (int) ic->topic_time ); + } else irc_send_num( ic->irc, 331, "%s :No topic for this channel", ic->name ); } -- cgit v1.2.3 From 9b69eb789bdcb3667c4cc4ac74f3404ae3f60869 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 08:34:44 -0400 Subject: MOTD command added since, well, I had all the code for it already anyway. --- irc_commands.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index fbba928a..36807c54 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -178,6 +178,11 @@ static void irc_cmd_whowas( irc_t *irc, char **cmd ) irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] ); } +static void irc_cmd_motd( irc_t *irc, char **cmd ) +{ + irc_send_motd( irc ); +} + #if 0 //#if 0 static void irc_cmd_oper( irc_t *irc, char **cmd ) @@ -243,11 +248,11 @@ static void irc_cmd_invite( irc_t *irc, char **cmd ) static void irc_cmd_privmsg( irc_t *irc, char **cmd ) { - if ( !cmd[2] ) + if( !cmd[2] ) { irc_send_num( irc, 412, ":No text to send" ); } - else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) + else if( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) { irc_write( irc, ":%s!%s@%s %s %s :%s", irc->nick, irc->user, irc->host, cmd[0], cmd[1], cmd[2] ); } @@ -503,11 +508,6 @@ static void irc_cmd_nickserv( irc_t *irc, char **cmd ) root_command( irc, cmd + 1 ); } -static void irc_cmd_motd( irc_t *irc, char **cmd ) -{ - irc_motd( irc ); -} - static void irc_cmd_pong( irc_t *irc, char **cmd ) { /* We could check the value we get back from the user, but in @@ -564,6 +564,7 @@ static const command_t irc_commands[] = { { "part", 1, irc_cmd_part, IRC_CMD_LOGGED_IN }, { "whois", 1, irc_cmd_whois, IRC_CMD_LOGGED_IN }, { "whowas", 1, irc_cmd_whowas, IRC_CMD_LOGGED_IN }, + { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN }, #if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, @@ -578,7 +579,6 @@ static const command_t irc_commands[] = { { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, - { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN }, { "pong", 0, irc_cmd_pong, IRC_CMD_LOGGED_IN }, { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, -- cgit v1.2.3 From b91936398b8ada486e246f769f1f8b8836fa3f43 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 10:31:03 -0400 Subject: Mode stuff. Also disallow unsetting +R umode which was possible so far (and shouldn't be). --- irc.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- irc.h | 17 +++++++++++------ irc_channel.c | 5 +++++ irc_commands.c | 50 ++++++++++++++++++++++++++---------------------- 4 files changed, 98 insertions(+), 34 deletions(-) diff --git a/irc.c b/irc.c index bf84f66d..6dc88825 100644 --- a/irc.c +++ b/irc.c @@ -52,8 +52,6 @@ irc_t *irc_new( int fd ) irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal ); irc->watches = g_hash_table_new( g_str_hash, g_str_equal ); - strcpy( irc->umode, UMODE ); - irc->iconv = (GIConv) -1; irc->oconv = (GIConv) -1; @@ -598,9 +596,6 @@ int irc_check_login( irc_t *irc ) g_free( iu->nick ); g_free( iu ); - irc->umode[0] = '\0'; - /*irc_umode_set( irc, "+" UMODE, 1 );*/ - if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); @@ -618,6 +613,9 @@ int irc_check_login( irc_t *irc ) irc_send_login( irc ); + irc->umode[0] = '\0'; + irc_umode_set( irc, "+" UMODE, TRUE ); + ic = irc_channel_new( irc, ROOT_CHAN ); irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); irc_channel_add_user( ic, irc->user ); @@ -632,6 +630,58 @@ int irc_check_login( irc_t *irc ) } } +void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv ) +{ + /* allow_priv: Set to 0 if s contains user input, 1 if you want + to set a "privileged" mode (+o, +R, etc). */ + char m[128], st = 1; + const char *t; + int i; + char changes[512], *p, st2 = 2; + char badflag = 0; + + memset( m, 0, sizeof( m ) ); + + for( t = irc->umode; *t; t ++ ) + if( *t < sizeof( m ) ) + m[(int)*t] = 1; + + p = changes; + for( t = s; *t; t ++ ) + { + if( *t == '+' || *t == '-' ) + st = *t == '+'; + else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) || + ( st == 1 && strchr( UMODES, *t ) ) || + ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) ) + { + if( m[(int)*t] != st) + { + if( st != st2 ) + st2 = st, *p++ = st ? '+' : '-'; + *p++ = *t; + } + m[(int)*t] = st; + } + else + badflag = 1; + } + *p = '\0'; + + memset( irc->umode, 0, sizeof( irc->umode ) ); + + for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ ) + if( m[i] ) + irc->umode[strlen(irc->umode)] = i; + + if( badflag ) + irc_send_num( irc, 501, ":Unknown MODE flag" ); + if( *changes ) + irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick, + irc->user->user, irc->user->host, irc->user->nick, + changes ); +} + static char *set_eval_charset( set_t *set, char *value ) diff --git a/irc.h b/irc.h index 48cba879..db629a19 100644 --- a/irc.h +++ b/irc.h @@ -32,12 +32,14 @@ #define IRC_LOGIN_TIMEOUT 60 #define IRC_PING_STRING "PinglBee" -#define UMODES "abisw" -#define UMODES_PRIV "Ro" -#define CMODES "nt" -#define CMODE "t" -#define UMODE "s" -#define CTYPES "&#" +#define UMODES "abisw" /* Allowed umodes (although they mostly do nothing) */ +#define UMODES_PRIV "Ro" /* Allowed, but not by user directly */ +#define UMODES_KEEP "R" /* Don't allow unsetting using /MODE */ +#define CMODES "nt" /* Allowed modes */ +#define CMODE "t" /* Default mode */ +#define UMODE "s" /* Default mode */ + +#define CTYPES "&#" /* Valid channel name prefixes */ typedef enum { @@ -143,6 +145,8 @@ void irc_vawrite( irc_t *irc, char *format, va_list params ); int irc_check_login( irc_t *irc ); +void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv ); + /* irc_channel.c */ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ); irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ); @@ -150,6 +154,7 @@ int irc_channel_free( irc_channel_t *ic ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); +gboolean irc_channel_name_ok( const char *name ); /* irc_commands.c */ void irc_exec( irc_t *irc, char **cmd ); diff --git a/irc_channel.c b/irc_channel.c index d15b73eb..c2e2c685 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -126,3 +126,8 @@ int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_ return 1; } + +gboolean irc_channel_name_ok( const char *name ) +{ + return strchr( CTYPES, name[0] ) != NULL && nick_ok( name + 1 ); +} diff --git a/irc_commands.c b/irc_commands.c index 36807c54..5f9ae39f 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -183,29 +183,15 @@ static void irc_cmd_motd( irc_t *irc, char **cmd ) irc_send_motd( irc ); } -#if 0 -//#if 0 -static void irc_cmd_oper( irc_t *irc, char **cmd ) -{ - if( global.conf->oper_pass && - ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ? - md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 : - strcmp( cmd[2], global.conf->oper_pass ) == 0 ) ) - { - irc_umode_set( irc, "+o", 1 ); - irc_send_num( irc, 381, ":Password accepted" ); - } - else - { - irc_send_num( irc, 432, ":Incorrect password" ); - } -} - static void irc_cmd_mode( irc_t *irc, char **cmd ) { - if( strchr( CTYPES, *cmd[1] ) ) + if( irc_channel_name_ok( cmd[1] ) ) { - if( cmd[2] ) + irc_channel_t *ic; + + if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL ) + irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); + else if( cmd[2] ) { if( *cmd[2] == '+' || *cmd[2] == '-' ) irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] ); @@ -213,11 +199,11 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) irc_send_num( irc, 368, "%s :No bans possible", cmd[1] ); } else - irc_send_num( irc, 324, "%s +%s", cmd[1], CMODE ); + irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode ); } else { - if( nick_cmp( cmd[1], irc->nick ) == 0 ) + if( nick_cmp( cmd[1], irc->user->nick ) == 0 ) { if( cmd[2] ) irc_umode_set( irc, cmd[2], 0 ); @@ -229,6 +215,24 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) } } +#if 0 +//#if 0 +static void irc_cmd_oper( irc_t *irc, char **cmd ) +{ + if( global.conf->oper_pass && + ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ? + md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 : + strcmp( cmd[2], global.conf->oper_pass ) == 0 ) ) + { + irc_umode_set( irc, "+o", 1 ); + irc_send_num( irc, 381, ":Password accepted" ); + } + else + { + irc_send_num( irc, 432, ":Incorrect password" ); + } +} + static void irc_cmd_invite( irc_t *irc, char **cmd ) { char *nick = cmd[1], *channel = cmd[2]; @@ -565,9 +569,9 @@ static const command_t irc_commands[] = { { "whois", 1, irc_cmd_whois, IRC_CMD_LOGGED_IN }, { "whowas", 1, irc_cmd_whowas, IRC_CMD_LOGGED_IN }, { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN }, + { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, #if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, - { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, -- cgit v1.2.3 From 2f53ada73d7d43b538c157563ab5eb39b7592137 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 11:16:16 -0400 Subject: /WHO --- irc.h | 1 + irc_commands.c | 49 ++++++++++++++++--------------------------------- irc_send.c | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/irc.h b/irc.h index db629a19..33f25538 100644 --- a/irc.h +++ b/irc.h @@ -169,6 +169,7 @@ void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ); void irc_send_names( irc_channel_t *ic ); void irc_send_topic( irc_channel_t *ic, gboolean topic_change ); void irc_send_whois( irc_user_t *iu ); +void irc_send_who( irc_t *irc, GSList *l, const char *channel ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); diff --git a/irc_commands.c b/irc_commands.c index 5f9ae39f..b06e59bd 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -215,6 +215,21 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) } } +static void irc_cmd_who( irc_t *irc, char **cmd ) +{ + char *channel = cmd[1]; + irc_channel_t *ic; + struct groupchat *c; + GList *l; + + if( !channel || *channel == '0' || *channel == '*' || !*channel ) + irc_send_who( irc, irc->users, "**" ); + else if( ( ic = irc_channel_by_name( irc, channel ) ) ) + irc_send_who( irc, ic->users, channel ); + else + irc_send_num( irc, 403, "%s :No such channel", channel ); +} + #if 0 //#if 0 static void irc_cmd_oper( irc_t *irc, char **cmd ) @@ -301,38 +316,6 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) } } -static void irc_cmd_who( irc_t *irc, char **cmd ) -{ - char *channel = cmd[1]; - user_t *u = irc->users; - struct groupchat *c; - GList *l; - - if( !channel || *channel == '0' || *channel == '*' || !*channel ) - while( u ) - { - irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); - u = u->next; - } - else if( g_strcasecmp( channel, irc->channel ) == 0 ) - while( u ) - { - if( u->online ) - irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); - u = u->next; - } - else if( ( c = irc_chat_by_channel( irc, channel ) ) ) - for( l = c->in_room; l; l = l->next ) - { - if( ( u = user_findhandle( c->ic, l->data ) ) ) - irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); - } - else if( ( u = user_find( irc, channel ) ) ) - irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); - - irc_send_num( irc, 315, "%s :End of /WHO list", channel?channel:"**" ); -} - static void irc_cmd_userhost( irc_t *irc, char **cmd ) { user_t *u; @@ -570,12 +553,12 @@ static const command_t irc_commands[] = { { "whowas", 1, irc_cmd_whowas, IRC_CMD_LOGGED_IN }, { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN }, { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, + { "who", 0, irc_cmd_who, IRC_CMD_LOGGED_IN }, #if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, - { "who", 0, irc_cmd_who, IRC_CMD_LOGGED_IN }, { "userhost", 1, irc_cmd_userhost, IRC_CMD_LOGGED_IN }, { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, diff --git a/irc_send.c b/irc_send.c index f0c3958c..97fb3071 100644 --- a/irc_send.c +++ b/irc_send.c @@ -224,3 +224,18 @@ void irc_send_whois( irc_user_t *iu ) irc_send_num( irc, 318, "%s :End of /WHOIS list", iu->nick ); } + +void irc_send_who( irc_t *irc, GSList *l, const char *channel ) +{ + while( l ) + { + irc_user_t *iu = l->data; + /* TODO(wilmer): Restore away/channel information here */ + irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", + "*", iu->user, iu->host, irc->root->host, + iu->nick, 'H', iu->fullname ); + l = l->next; + } + + irc_send_num( irc, 315, "%s :End of /WHO list", channel ); +} -- cgit v1.2.3 From 280c56a7b24dc08b35a1ecd98c8f4b61435d1100 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 13:36:47 -0400 Subject: Added privmsg handlers to users/channels. root commands are coming back. --- Makefile | 2 +- irc.c | 3 ++ irc.h | 25 +++++++++++++-- irc_channel.c | 15 +++++++++ irc_commands.c | 99 ++++++++++++++++++++++++++++++++++----------------------- irc_send.c | 10 ++++-- irc_user.c | 32 ++++++++++++++++--- nick.c | 2 +- root_commands.c | 6 +++- 9 files changed, 142 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index 9e300c12..ed0316a4 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # Program variables #objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) -objects = bitlbee.o help.o ipc.o irc.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o set.o +objects = bitlbee.o help.o ipc.o irc.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols diff --git a/irc.c b/irc.c index 6dc88825..51bf8acd 100644 --- a/irc.c +++ b/irc.c @@ -113,10 +113,12 @@ irc_t *irc_new( int fd ) irc->root = iu = irc_user_new( irc, ROOT_NICK ); iu->host = g_strdup( myhost ); iu->fullname = g_strdup( ROOT_FN ); + iu->f = &irc_user_root_funcs; iu = irc_user_new( irc, NS_NICK ); iu->host = g_strdup( myhost ); iu->fullname = g_strdup( ROOT_FN ); + iu->f = &irc_user_root_funcs; irc->user = g_new0( irc_user_t, 1 ); irc->user->host = g_strdup( host ); @@ -593,6 +595,7 @@ int irc_check_login( irc_t *irc ) irc->user->user = iu->user; irc->user->host = iu->host; irc->user->fullname = iu->fullname; + irc->user->f = &irc_user_self_funcs; g_free( iu->nick ); g_free( iu ); diff --git a/irc.h b/irc.h index 33f25538..b72fdf76 100644 --- a/irc.h +++ b/irc.h @@ -106,8 +106,18 @@ typedef struct irc_user int sendbuf_flags; //struct user *b; + + const struct irc_user_funcs *f; } irc_user_t; +struct irc_user_funcs +{ + gboolean (*privmsg)( irc_user_t *iu, const char *msg ); +}; + +extern const struct irc_user_funcs irc_user_root_funcs; +extern const struct irc_user_funcs irc_user_self_funcs; + typedef enum { IRC_CHANNEL_JOINED = 1, @@ -116,16 +126,25 @@ typedef enum typedef struct irc_channel { irc_t *irc; - int flags; char *name; + char mode[8]; + int flags; + char *topic; char *topic_who; time_t topic_time; - char mode[8]; + GSList *users; struct set *set; + + const struct irc_channel_funcs *f; } irc_channel_t; +struct irc_channel_funcs +{ + gboolean (*privmsg)( irc_channel_t *iu, const char *msg ); +}; + #include "user.h" /* irc.c */ @@ -174,7 +193,7 @@ void irc_send_who( irc_t *irc, GSList *l, const char *channel ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); int irc_user_free( irc_t *irc, const char *nick ); -irc_user_t *irc_user_find( irc_t *irc, const char *nick ); +irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ); int irc_user_rename( irc_t *irc, const char *old, const char *new ); gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ); diff --git a/irc_channel.c b/irc_channel.c index c2e2c685..ec0433c1 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -25,6 +25,8 @@ #include "bitlbee.h" +static const struct irc_channel_funcs control_channel_funcs; + irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) { irc_channel_t *ic; @@ -33,6 +35,7 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) return NULL; ic = g_new0( irc_channel_t, 1 ); + ic->f = &control_channel_funcs; ic->irc = irc; ic->name = g_strdup( name ); strcpy( ic->mode, CMODE ); @@ -131,3 +134,15 @@ gboolean irc_channel_name_ok( const char *name ) { return strchr( CTYPES, name[0] ) != NULL && nick_ok( name + 1 ); } + +/* Channel-type dependent functions, for control channels: */ +static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) +{ + root_command_string( ic->irc, msg ); + + return TRUE; +} + +static const struct irc_channel_funcs control_channel_funcs = { + control_channel_privmsg, +}; diff --git a/irc_commands.c b/irc_commands.c index b06e59bd..e5c6a95f 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -76,7 +76,7 @@ static void irc_cmd_nick( irc_t *irc, char **cmd ) { irc_send_num( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" ); } - else if( irc_user_find( irc, cmd[1] ) ) + else if( irc_user_by_name( irc, cmd[1] ) ) { irc_send_num( irc, 433, ":This nick is already in use" ); } @@ -159,7 +159,7 @@ static void irc_cmd_part( irc_t *irc, char **cmd ) static void irc_cmd_whois( irc_t *irc, char **cmd ) { char *nick = cmd[1]; - irc_user_t *iu = irc_user_find( irc, nick ); + irc_user_t *iu = irc_user_by_name( irc, nick ); if( iu ) irc_send_whois( iu ); @@ -219,8 +219,6 @@ static void irc_cmd_who( irc_t *irc, char **cmd ) { char *channel = cmd[1]; irc_channel_t *ic; - struct groupchat *c; - GList *l; if( !channel || *channel == '0' || *channel == '*' || !*channel ) irc_send_who( irc, irc->users, "**" ); @@ -230,50 +228,35 @@ static void irc_cmd_who( irc_t *irc, char **cmd ) irc_send_num( irc, 403, "%s :No such channel", channel ); } -#if 0 -//#if 0 -static void irc_cmd_oper( irc_t *irc, char **cmd ) +static void irc_cmd_privmsg( irc_t *irc, char **cmd ) { - if( global.conf->oper_pass && - ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ? - md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 : - strcmp( cmd[2], global.conf->oper_pass ) == 0 ) ) + irc_channel_t *ic; + irc_user_t *iu; + + if( !cmd[2] ) { - irc_umode_set( irc, "+o", 1 ); - irc_send_num( irc, 381, ":Password accepted" ); + irc_send_num( irc, 412, ":No text to send" ); + } + else if( irc_channel_name_ok( cmd[1] ) && + ( ic = irc_channel_by_name( irc, cmd[1] ) ) ) + { + if( ic->f->privmsg ) + ic->f->privmsg( ic, cmd[2] ); + } + else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) ) + { + if( iu->f->privmsg ) + iu->f->privmsg( iu, cmd[2] ); } else { - irc_send_num( irc, 432, ":Incorrect password" ); + irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] ); } -} -static void irc_cmd_invite( irc_t *irc, char **cmd ) -{ - char *nick = cmd[1], *channel = cmd[2]; - struct groupchat *c = irc_chat_by_channel( irc, channel ); - user_t *u = user_find( irc, nick ); - - if( u && c && ( u->ic == c->ic ) ) - if( c->ic && c->ic->acc->prpl->chat_invite ) - { - c->ic->acc->prpl->chat_invite( c, u->handle, NULL ); - irc_send_num( irc, 341, "%s %s", nick, channel ); - return; - } - - irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); -} -static void irc_cmd_privmsg( irc_t *irc, char **cmd ) -{ - if( !cmd[2] ) - { - irc_send_num( irc, 412, ":No text to send" ); - } +#if 0 else if( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) { - irc_write( irc, ":%s!%s@%s %s %s :%s", irc->nick, irc->user, irc->host, cmd[0], cmd[1], cmd[2] ); } else { @@ -314,6 +297,44 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) } irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? OPT_AWAY : 0 ); } +#endif +} + + + +#if 0 +//#if 0 +static void irc_cmd_oper( irc_t *irc, char **cmd ) +{ + if( global.conf->oper_pass && + ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ? + md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 : + strcmp( cmd[2], global.conf->oper_pass ) == 0 ) ) + { + irc_umode_set( irc, "+o", 1 ); + irc_send_num( irc, 381, ":Password accepted" ); + } + else + { + irc_send_num( irc, 432, ":Incorrect password" ); + } +} + +static void irc_cmd_invite( irc_t *irc, char **cmd ) +{ + char *nick = cmd[1], *channel = cmd[2]; + struct groupchat *c = irc_chat_by_channel( irc, channel ); + user_t *u = user_find( irc, nick ); + + if( u && c && ( u->ic == c->ic ) ) + if( c->ic && c->ic->acc->prpl->chat_invite ) + { + c->ic->acc->prpl->chat_invite( c, u->handle, NULL ); + irc_send_num( irc, 341, "%s %s", nick, channel ); + return; + } + + irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); } static void irc_cmd_userhost( irc_t *irc, char **cmd ) @@ -554,10 +575,10 @@ static const command_t irc_commands[] = { { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN }, { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, { "who", 0, irc_cmd_who, IRC_CMD_LOGGED_IN }, + { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, #if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, - { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "userhost", 1, irc_cmd_userhost, IRC_CMD_LOGGED_IN }, { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, diff --git a/irc_send.c b/irc_send.c index 97fb3071..23298ef4 100644 --- a/irc_send.c +++ b/irc_send.c @@ -146,10 +146,8 @@ void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ) void irc_send_names( irc_channel_t *ic ) { GSList *l; - irc_user_t *iu; char namelist[385] = ""; - struct groupchat *c = NULL; - char *ops = set_getstr( &ic->irc->b->set, "ops" ); + //char *ops = set_getstr( &ic->irc->b->set, "ops" ); /* RFCs say there is no error reply allowed on NAMES, so when the channel is invalid, just give an empty reply. */ @@ -239,3 +237,9 @@ void irc_send_who( irc_t *irc, GSList *l, const char *channel ) irc_send_num( irc, 315, "%s :End of /WHO list", channel ); } + +void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg ) +{ + irc_write( iu->irc, ":%s!%s@%s %s %s :%s", + iu->nick, iu->user, iu->host, type, dst, msg ); +} diff --git a/irc_user.c b/irc_user.c index 1de0c4e3..a54fc09e 100644 --- a/irc_user.c +++ b/irc_user.c @@ -50,7 +50,7 @@ int irc_user_free( irc_t *irc, const char *nick ) { irc_user_t *iu; - if( !( iu = irc_user_find( irc, nick ) ) ) + if( !( iu = irc_user_by_name( irc, nick ) ) ) return 0; irc->users = g_slist_remove( irc->users, iu ); @@ -67,7 +67,7 @@ int irc_user_free( irc_t *irc, const char *nick ) return 1; } -irc_user_t *irc_user_find( irc_t *irc, const char *nick ) +irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ) { char key[strlen(nick)+1]; @@ -80,11 +80,11 @@ irc_user_t *irc_user_find( irc_t *irc, const char *nick ) int irc_user_rename( irc_t *irc, const char *old, const char *new ) { - irc_user_t *iu = irc_user_find( irc, old ); + irc_user_t *iu = irc_user_by_name( irc, old ); char key[strlen(new)+1]; strcpy( key, new ); - if( iu == NULL || !nick_lc( key ) || irc_user_find( irc, new ) ) + if( iu == NULL || !nick_lc( key ) || irc_user_by_name( irc, new ) ) return 0; irc->users = g_slist_remove( irc->users, iu ); @@ -112,3 +112,27 @@ gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ) return strcmp( a->key, b->key ); } + +/* User-type dependent functions, for root/NickServ: */ +static gboolean root_privmsg( irc_user_t *iu, const char *msg ) +{ + root_command_string( iu->irc, msg ); + + return TRUE; +} + +const struct irc_user_funcs irc_user_root_funcs = { + root_privmsg, +}; + +/* Echo to yourself: */ +static gboolean self_privmsg( irc_user_t *iu, const char *msg ) +{ + irc_send_msg( iu, "PRIVMSG", iu->nick, msg ); + + return TRUE; +} + +const struct irc_user_funcs irc_user_self_funcs = { + self_privmsg, +}; diff --git a/nick.c b/nick.c index 6d798bc6..002d2501 100644 --- a/nick.c +++ b/nick.c @@ -95,7 +95,7 @@ void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+ /* Now, find out if the nick is already in use at the moment, and make subtle changes to make it unique. */ - while( !nick_ok( nick ) || irc_user_find( acc->irc, nick ) ) + while( !nick_ok( nick ) || irc_user_by_name( acc->irc, nick ) ) { if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) { diff --git a/root_commands.c b/root_commands.c index d3b0c7d3..9b396379 100644 --- a/root_commands.c +++ b/root_commands.c @@ -31,7 +31,7 @@ #include -void root_command_string( irc_t *irc, user_t *u, char *command, int flags ) +void root_command_string( irc_t *irc, char *command ) { char *cmd[IRC_MAX_ARGS]; char *s; @@ -135,6 +135,7 @@ static void cmd_help( irc_t *irc, char **cmd ) } } +#if 0 static void cmd_account( irc_t *irc, char **cmd ); static void cmd_identify( irc_t *irc, char **cmd ) @@ -1216,9 +1217,11 @@ static void cmd_transfer( irc_t *irc, char **cmd ) } } } +#endif const command_t commands[] = { { "help", 0, cmd_help, 0 }, +#if 0 { "identify", 1, cmd_identify, 0 }, { "register", 1, cmd_register, 0 }, { "drop", 1, cmd_drop, 0 }, @@ -1239,5 +1242,6 @@ const command_t commands[] = { { "join_chat", 2, cmd_join_chat, 0 }, { "chat", 1, cmd_chat, 0 }, { "transfer", 0, cmd_transfer, 0 }, +#endif { NULL } }; -- cgit v1.2.3 From 74f1cdef999356e40e3fa3b6a2d89876b6c0c303 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 14:05:38 -0400 Subject: irc_usermsg() works a little bit again. Have to figure out how and where to restore multiline support though. --- irc.c | 2 ++ irc.h | 12 ++++++++++-- irc_channel.c | 3 +++ irc_send.c | 21 ++++++++++++++++++--- irc_user.c | 5 ++++- 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/irc.c b/irc.c index 51bf8acd..72933fef 100644 --- a/irc.c +++ b/irc.c @@ -623,6 +623,8 @@ int irc_check_login( irc_t *irc ) irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); irc_channel_add_user( ic, irc->user ); + irc->last_root_cmd = g_strdup( ROOT_CHAN ); + return 1; } } diff --git a/irc.h b/irc.h index b72fdf76..3c69c601 100644 --- a/irc.h +++ b/irc.h @@ -64,6 +64,8 @@ typedef struct irc struct irc_user *root; struct irc_user *user; + + char *last_root_cmd; char *password; /* HACK: Used to save the user's password, but before logging in, this may contain a password we should @@ -86,6 +88,11 @@ typedef struct irc struct bee *b; } irc_t; +typedef enum +{ + IRC_USER_PRIVATE = 1, +} irc_user_flags_t; + typedef struct irc_user { irc_t *irc; @@ -98,12 +105,12 @@ typedef struct irc_user /* Nickname in lowercase for case sensitive searches */ char *key; - char is_private; + irc_user_flags_t flags; char *sendbuf; int sendbuf_len; guint sendbuf_timer; - int sendbuf_flags; + //int sendbuf_flags; //struct user *b; @@ -189,6 +196,7 @@ void irc_send_names( irc_channel_t *ic ); void irc_send_topic( irc_channel_t *ic, gboolean topic_change ); void irc_send_whois( irc_user_t *iu ); void irc_send_who( irc_t *irc, GSList *l, const char *channel ); +void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); diff --git a/irc_channel.c b/irc_channel.c index ec0433c1..3afdddee 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -138,6 +138,9 @@ gboolean irc_channel_name_ok( const char *name ) /* Channel-type dependent functions, for control channels: */ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) { + g_free( ic->irc->last_root_cmd ); + ic->irc->last_root_cmd = g_strdup( ic->name ); + root_command_string( ic->irc, msg ); return TRUE; diff --git a/irc_send.c b/irc_send.c index 23298ef4..e93b2b6f 100644 --- a/irc_send.c +++ b/irc_send.c @@ -108,18 +108,33 @@ void irc_send_motd( irc_t *irc ) } } -/* FIXME/REPLACEME */ void irc_usermsg( irc_t *irc, char *format, ... ) { + irc_channel_t *ic; + irc_user_t *iu; char text[1024]; va_list params; - //irc_user_t *iu; va_start( params, format ); g_vsnprintf( text, sizeof( text ), format, params ); va_end( params ); - fprintf( stderr, "%s\n", text ); + if( irc->last_root_cmd && + irc_channel_name_ok( irc->last_root_cmd ) && + ( ic = irc_channel_by_name( irc, irc->last_root_cmd ) ) && + ic->flags & IRC_CHANNEL_JOINED ) + irc_send_msg( irc->root, "PRIVMSG", irc->last_root_cmd, text ); + else if( irc->last_root_cmd && + ( iu = irc_user_by_name( irc, irc->last_root_cmd ) ) && + iu->f == &irc_user_root_funcs ) + irc_send_msg( iu, "PRIVMSG", irc->user->nick, text ); + else + { + g_free( irc->last_root_cmd ); + irc->last_root_cmd = NULL; + + irc_send_msg( irc->root, "PRIVMSG", irc->user->nick, text ); + } /*return( irc_msgfrom( irc, u->nick, text ) );*/ } diff --git a/irc_user.c b/irc_user.c index a54fc09e..ef66f2b5 100644 --- a/irc_user.c +++ b/irc_user.c @@ -33,7 +33,7 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ) iu->nick = g_strdup( nick ); iu->user = iu->host = iu->fullname = iu->nick; - iu->is_private = set_getbool( &irc->b->set, "private" ); + iu->flags = set_getbool( &irc->b->set, "private" ) ? IRC_USER_PRIVATE : 0; iu->key = g_strdup( nick ); nick_lc( iu->key ); @@ -116,6 +116,9 @@ gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ) /* User-type dependent functions, for root/NickServ: */ static gboolean root_privmsg( irc_user_t *iu, const char *msg ) { + g_free( iu->irc->last_root_cmd ); + iu->irc->last_root_cmd = g_strdup( iu->nick ); + root_command_string( iu->irc, msg ); return TRUE; -- cgit v1.2.3 From 410bf6d47436e83a09378aac2a8de24709b83422 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 14:10:03 -0400 Subject: Fixed bug in /WHO $channel output. --- irc_send.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc_send.c b/irc_send.c index e93b2b6f..d9b7f8d2 100644 --- a/irc_send.c +++ b/irc_send.c @@ -245,7 +245,7 @@ void irc_send_who( irc_t *irc, GSList *l, const char *channel ) irc_user_t *iu = l->data; /* TODO(wilmer): Restore away/channel information here */ irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", - "*", iu->user, iu->host, irc->root->host, + channel ? : "*", iu->user, iu->host, irc->root->host, iu->nick, 'H', iu->fullname ); l = l->next; } -- cgit v1.2.3 From 6761a40af82d8134ee9fa2ac1d2b34475d297ad8 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 22:44:19 -0400 Subject: Restored multi-line message support. --- irc.h | 3 ++- irc_send.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- irc_user.c | 2 +- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/irc.h b/irc.h index 3c69c601..b1d8b6ef 100644 --- a/irc.h +++ b/irc.h @@ -196,7 +196,8 @@ void irc_send_names( irc_channel_t *ic ); void irc_send_topic( irc_channel_t *ic, gboolean topic_change ); void irc_send_whois( irc_user_t *iu ); void irc_send_who( irc_t *irc, GSList *l, const char *channel ); -void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg ); +void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix ); +void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); diff --git a/irc_send.c b/irc_send.c index d9b7f8d2..80d9b466 100644 --- a/irc_send.c +++ b/irc_send.c @@ -123,17 +123,17 @@ void irc_usermsg( irc_t *irc, char *format, ... ) irc_channel_name_ok( irc->last_root_cmd ) && ( ic = irc_channel_by_name( irc, irc->last_root_cmd ) ) && ic->flags & IRC_CHANNEL_JOINED ) - irc_send_msg( irc->root, "PRIVMSG", irc->last_root_cmd, text ); + irc_send_msg( irc->root, "PRIVMSG", irc->last_root_cmd, text, NULL ); else if( irc->last_root_cmd && ( iu = irc_user_by_name( irc, irc->last_root_cmd ) ) && iu->f == &irc_user_root_funcs ) - irc_send_msg( iu, "PRIVMSG", irc->user->nick, text ); + irc_send_msg( iu, "PRIVMSG", irc->user->nick, text, NULL ); else { g_free( irc->last_root_cmd ); irc->last_root_cmd = NULL; - irc_send_msg( irc->root, "PRIVMSG", irc->user->nick, text ); + irc_send_msg( irc->root, "PRIVMSG", irc->user->nick, text, NULL ); } /*return( irc_msgfrom( irc, u->nick, text ) );*/ @@ -253,7 +253,49 @@ void irc_send_who( irc_t *irc, GSList *l, const char *channel ) irc_send_num( irc, 315, "%s :End of /WHO list", channel ); } -void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg ) +void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix ) +{ + char last = 0; + const char *s = msg, *line = msg; + char raw_msg[strlen(msg)+1024]; + + while( !last ) + { + if( *s == '\r' && *(s+1) == '\n' ) + s++; + if( *s == '\n' ) + { + last = s[1] == 0; + } + else + { + last = s[0] == 0; + } + if( *s == 0 || *s == '\n' ) + { + if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && + g_strcasecmp( type, "PRIVMSG" ) == 0 ) + { + strcpy( raw_msg, "\001ACTION " ); + strncat( raw_msg, line + 4, s - line - 4 ); + strcat( raw_msg, "\001" ); + irc_send_msg_raw( iu, type, dst, raw_msg ); + } + else + { + *raw_msg = '\0'; + if( prefix && *prefix ) + strcpy( raw_msg, prefix ); + strncat( raw_msg, line, s - line ); + irc_send_msg_raw( iu, type, dst, raw_msg ); + } + line = s + 1; + } + s ++; + } +} + +void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg ) { irc_write( iu->irc, ":%s!%s@%s %s %s :%s", iu->nick, iu->user, iu->host, type, dst, msg ); diff --git a/irc_user.c b/irc_user.c index ef66f2b5..d9ea0d94 100644 --- a/irc_user.c +++ b/irc_user.c @@ -131,7 +131,7 @@ const struct irc_user_funcs irc_user_root_funcs = { /* Echo to yourself: */ static gboolean self_privmsg( irc_user_t *iu, const char *msg ) { - irc_send_msg( iu, "PRIVMSG", iu->nick, msg ); + irc_send_msg_raw( iu, "PRIVMSG", iu->nick, msg ); return TRUE; } -- cgit v1.2.3 From 3923003fb56f3b13eba1fa44c900175ae2574a1c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 22:49:19 -0400 Subject: Restored server-initiated PINGs. --- irc.c | 41 +++++++++++++++++++++++++++++++++++++++-- irc_commands.c | 18 +++++++++--------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/irc.c b/irc.c index 72933fef..4eaa55a8 100644 --- a/irc.c +++ b/irc.c @@ -27,6 +27,7 @@ GSList *irc_connection_list; +static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ); static char *set_eval_charset( set_t *set, char *value ); irc_t *irc_new( int fd ) @@ -86,8 +87,8 @@ irc_t *irc_new( int fd ) if( myhost == NULL ) myhost = g_strdup( "localhost.localdomain" ); - //if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) - // irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc ); + if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) + irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc ); irc_connection_list = g_slist_append( irc_connection_list, irc ); @@ -688,6 +689,42 @@ void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv ) } +/* Returns 0 if everything seems to be okay, a number >0 when there was a + timeout. The number returned is the number of seconds we received no + pongs from the user. When not connected yet, we don't ping but drop the + connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */ +static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ) +{ + irc_t *irc = _irc; + int rv = 0; + + if( !( irc->status & USTATUS_LOGGED_IN ) ) + { + if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) ) + rv = gettime() - irc->last_pong; + } + else + { + if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging ) + { + irc_write( irc, "PING :%s", IRC_PING_STRING ); + irc->pinging = 1; + } + else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) ) + { + rv = gettime() - irc->last_pong; + } + } + + if( rv > 0 ) + { + irc_abort( irc, 0, "Ping Timeout: %d seconds", rv ); + return FALSE; + } + + return TRUE; +} + static char *set_eval_charset( set_t *set, char *value ) { diff --git a/irc_commands.c b/irc_commands.c index e5c6a95f..858b5bdd 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -107,6 +107,14 @@ static void irc_cmd_ping( irc_t *irc, char **cmd ) irc->root->host, cmd[1]?cmd[1]:irc->root->host ); } +static void irc_cmd_pong( irc_t *irc, char **cmd ) +{ + /* We could check the value we get back from the user, but in + fact we don't care, we're just happy s/he's still alive. */ + irc->last_pong = gettime(); + irc->pinging = 0; +} + static void irc_cmd_join( irc_t *irc, char **cmd ) { irc_channel_t *ic; @@ -516,14 +524,6 @@ static void irc_cmd_nickserv( irc_t *irc, char **cmd ) root_command( irc, cmd + 1 ); } -static void irc_cmd_pong( irc_t *irc, char **cmd ) -{ - /* We could check the value we get back from the user, but in - fact we don't care, we're just happy he's still alive. */ - irc->last_pong = gettime(); - irc->pinging = 0; -} - static void irc_cmd_version( irc_t *irc, char **cmd ) { irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU ); @@ -567,6 +567,7 @@ static const command_t irc_commands[] = { { "nick", 1, irc_cmd_nick, 0 }, { "quit", 0, irc_cmd_quit, 0 }, { "ping", 0, irc_cmd_ping, 0 }, + { "pong", 0, irc_cmd_pong, IRC_CMD_LOGGED_IN }, { "join", 1, irc_cmd_join, IRC_CMD_LOGGED_IN }, { "names", 1, irc_cmd_names, IRC_CMD_LOGGED_IN }, { "part", 1, irc_cmd_part, IRC_CMD_LOGGED_IN }, @@ -587,7 +588,6 @@ static const command_t irc_commands[] = { { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, - { "pong", 0, irc_cmd_pong, IRC_CMD_LOGGED_IN }, { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, -- cgit v1.2.3 From 38ee0216a7058b0f227d1c32b288e041a397528a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 27 Mar 2010 23:03:57 -0400 Subject: Remove deleted user from channels too! --- irc_user.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/irc_user.c b/irc_user.c index d9ea0d94..8e20fd15 100644 --- a/irc_user.c +++ b/irc_user.c @@ -49,6 +49,7 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ) int irc_user_free( irc_t *irc, const char *nick ) { irc_user_t *iu; + GSList *l; if( !( iu = irc_user_by_name( irc, nick ) ) ) return 0; @@ -56,6 +57,9 @@ int irc_user_free( irc_t *irc, const char *nick ) irc->users = g_slist_remove( irc->users, iu ); g_hash_table_remove( irc->nick_user_hash, iu->key ); + for( l = irc->channels; l; l = l->next ) + irc_channel_del_user( (irc_channel_t*) l->data, iu ); + g_free( iu->nick ); if( iu->nick != iu->user ) g_free( iu->user ); if( iu->nick != iu->host ) g_free( iu->host ); -- cgit v1.2.3 From 10a96f44efbeb6af09e2728926ce15b6bda12131 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 29 Mar 2010 08:25:01 -0400 Subject: Start restoring IM-related bits, added bee_user.c with basic functions and UI callbacks. --- protocols/Makefile | 2 +- protocols/bee.h | 73 ++++++++++++++++++++++++++++++++--- protocols/bee_user.c | 82 +++++++++++++++++++++++++++++++++++++++ protocols/nogaim.c | 59 ++++++++++++++-------------- protocols/user.c | 106 --------------------------------------------------- protocols/user.h | 40 ------------------- 6 files changed, 180 insertions(+), 182 deletions(-) create mode 100644 protocols/bee_user.c delete mode 100644 protocols/user.c delete mode 100644 protocols/user.h diff --git a/protocols/Makefile b/protocols/Makefile index f1133cc9..1f89ee5a 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -10,7 +10,7 @@ # [SH] Program variables #objects = account.o nogaim.o user.o -objects = bee.o +objects = bee.o bee_user.o nogaim.o # [SH] The next two lines should contain the directory name (in $(subdirs)) diff --git a/protocols/bee.h b/protocols/bee.h index b25f0e08..e5c21120 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -1,7 +1,32 @@ -typedef struct bee_ui -{ - void *data; -} bee_ui_t; + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search buddies */ + +/* + 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 +*/ + +#ifndef __BEE_H__ +#define __BEE_H__ + +struct bee_ui_funcs; typedef struct bee { @@ -10,9 +35,47 @@ typedef struct bee GSList *users; GSList *accounts; - //const bee_ui_funcs_t *ui; + const struct bee_ui_funcs *ui; void *ui_data; } bee_t; bee_t *bee_new(); void bee_free( bee_t *b ); + +typedef enum +{ + BEE_USER_ONLINE = 1, + BEE_USER_AWAY = 2, +} bee_user_flags_t; + +typedef struct bee_user +{ + struct im_connection *ic; + char *handle; + char *fullname; + char *group; + + char *away; + char *status_msg; + + bee_t *bee; + void *ui_data; +} bee_user_t; + +typedef struct bee_ui_funcs +{ + gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); + gboolean (*user_free)( bee_t *bee, struct bee_user *bu ); +} bee_ui_funcs_t; + + +/* bee.c */ +bee_t *bee_new(); +void bee_free( bee_t *b ); + +/* bee_user.c */ +bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle ); +int bee_user_free( bee_t *bee, struct im_connection *ic, const char *handle ); +bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle ); + +#endif /* __BEE_H__ */ diff --git a/protocols/bee_user.c b/protocols/bee_user.c new file mode 100644 index 00000000..538aa509 --- /dev/null +++ b/protocols/bee_user.c @@ -0,0 +1,82 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search buddies */ + +/* + 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" + +bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle ) +{ + bee_user_t *bu; + + if( bee_user_by_handle( bee, ic, handle ) != NULL ) + return NULL; + + bu = g_new0( bee_user_t, 1 ); + bu->bee = bee; + bu->ic = ic; + bu->handle = g_strdup( handle ); + bee->users = g_slist_prepend( bee->users, bu ); + + if( bee->ui->user_new ) + bee->ui->user_new( bee, bu ); + + return bu; +} + +int bee_user_free( bee_t *bee, struct im_connection *ic, const char *handle ) +{ + bee_user_t *bu; + + if( ( bu = bee_user_by_handle( bee, ic, handle ) ) == NULL ) + return 0; + + if( bee->ui->user_free ) + bee->ui->user_free( bee, bu ); + + g_free( bu->handle ); + g_free( bu->fullname ); + g_free( bu->group ); + g_free( bu->away ); + g_free( bu->status_msg ); + + bee->users = g_slist_remove( bee->users, bu ); + + return 1; +} + +bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle ) +{ + GSList *l; + + for( l = bee->users; l; l = l->next ) + { + bee_user_t *bu = l->data; + + if( bu->ic == ic && ic->acc->prpl->handle_cmp( bu->handle, handle ) ) + return bu; + } + + return NULL; +} diff --git a/protocols/nogaim.c b/protocols/nogaim.c index c326e378..618616c7 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -198,8 +198,8 @@ static void serv_got_crap( struct im_connection *ic, char *format, ... ) text = g_strdup_vprintf( format, params ); va_end( params ); - if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &ic->irc->b->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->b->set, "strip_html" ) ) ) strip_html( text ); /* Try to find a different connection on the same protocol. */ @@ -264,7 +264,6 @@ void imcb_connected( struct im_connection *ic ) { irc_t *irc = ic->irc; struct chat *c; - user_t *u; /* MSN servers sometimes redirect you to a different server and do the whole login sequence again, so these "late" calls to this @@ -272,8 +271,6 @@ void imcb_connected( struct im_connection *ic ) if( ic->flags & OPT_LOGGED_IN ) return; - u = user_find( ic->irc, ic->irc->nick ); - imcb_log( ic, "Logged in" ); ic->keepalive = b_timeout_add( 60000, send_keepalive, ic ); @@ -286,6 +283,7 @@ void imcb_connected( struct im_connection *ic ) exponential backoff timer. */ ic->acc->auto_reconnect_delay = 0; + /* for( c = irc->chatrooms; c; c = c->next ) { if( c->acc != ic->acc ) @@ -294,6 +292,7 @@ void imcb_connected( struct im_connection *ic ) if( set_getbool( &c->set, "auto_join" ) ) chat_join( irc, c, NULL ); } + */ } gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ) @@ -359,7 +358,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) { /* Uhm... This is very sick. */ } - else if( allow_reconnect && set_getbool( &irc->set, "auto_reconnect" ) && + else if( allow_reconnect && set_getbool( &irc->b->set, "auto_reconnect" ) && set_getbool( &a->set, "auto_reconnect" ) && ( delay = account_reconnect_delay( a ) ) > 0 ) { @@ -390,7 +389,7 @@ void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *g if( user_findhandle( ic, handle ) ) { - if( set_getbool( &irc->set, "debug" ) ) + if( set_getbool( &irc->b->set, "debug" ) ) imcb_log( ic, "User already exists, ignoring add request: %s", handle ); return; @@ -469,7 +468,7 @@ void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char u->realname = g_strdup( realname ); - if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->set, "display_namechanges" ) ) + if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->b->set, "display_namechanges" ) ) imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname ); } @@ -517,7 +516,7 @@ void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const c /* Some processing to make sure this string is a valid IRC nickname. */ nick_strip( newnick ); - if( set_getbool( &ic->irc->set, "lcnicks" ) ) + if( set_getbool( &ic->irc->b->set, "lcnicks" ) ) nick_lc( newnick ); if( strcmp( u->nick, newnick ) != 0 ) @@ -625,14 +624,14 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, if( !u ) { - if( g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "add" ) == 0 ) + if( g_strcasecmp( set_getstr( &ic->irc->b->set, "handle_unknown" ), "add" ) == 0 ) { imcb_add_buddy( ic, (char*) handle, NULL ); u = user_findhandle( ic, (char*) handle ); } else { - if( set_getbool( &ic->irc->set, "debug" ) || g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "ignore" ) != 0 ) + if( set_getbool( &ic->irc->b->set, "debug" ) || g_strcasecmp( set_getstr( &ic->irc->b->set, "handle_unknown" ), "ignore" ) != 0 ) { imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle ); imcb_log( ic, "flags = %d, state = %s, message = %s", flags, @@ -692,14 +691,14 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, } /* LISPy... */ - if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ + if( ( set_getbool( &ic->irc->b->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ ( u->online ) && /* Don't touch offline people */ ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ { char *from; - if( set_getbool( &ic->irc->set, "simulate_netsplit" ) ) + if( set_getbool( &ic->irc->b->set, "simulate_netsplit" ) ) { from = g_strdup( ic->irc->myhost ); } @@ -724,18 +723,18 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui if( !u ) { - char *h = set_getstr( &irc->set, "handle_unknown" ); + char *h = set_getstr( &irc->b->set, "handle_unknown" ); if( g_strcasecmp( h, "ignore" ) == 0 ) { - if( set_getbool( &irc->set, "debug" ) ) + if( set_getbool( &irc->b->set, "debug" ) ) imcb_log( ic, "Ignoring message from unknown handle %s", handle ); return; } else if( g_strncasecmp( h, "add", 3 ) == 0 ) { - int private = set_getbool( &irc->set, "private" ); + int private = set_getbool( &irc->b->set, "private" ); if( h[3] ) { @@ -756,8 +755,8 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui } } - if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &ic->irc->b->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->b->set, "strip_html" ) ) ) strip_html( msg ); wrapped = word_wrap( msg, 425 ); @@ -769,7 +768,7 @@ void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) { user_t *u; - if( !set_getbool( &ic->irc->set, "typing_notice" ) ) + if( !set_getbool( &ic->irc->b->set, "typing_notice" ) ) return; if( ( u = user_findhandle( ic, handle ) ) ) @@ -800,7 +799,7 @@ struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ ); c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); - if( set_getbool( &ic->irc->set, "debug" ) ) + if( set_getbool( &ic->irc->b->set, "debug" ) ) imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle ); return c; @@ -812,7 +811,7 @@ void imcb_chat_free( struct groupchat *c ) struct groupchat *l; GList *ir; - if( set_getbool( &ic->irc->set, "debug" ) ) + if( set_getbool( &ic->irc->b->set, "debug" ) ) imcb_log( ic, "You were removed from conversation %p", c ); if( c ) @@ -859,8 +858,8 @@ void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t fl u = user_findhandle( ic, who ); - if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &ic->irc->b->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->b->set, "strip_html" ) ) ) strip_html( msg ); wrapped = word_wrap( msg, 425 ); @@ -905,8 +904,8 @@ void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at else u = user_findhandle( ic, who ); - if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &ic->irc->b->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->b->set, "strip_html" ) ) ) strip_html( topic ); g_free( c->topic ); @@ -924,7 +923,7 @@ void imcb_chat_add_buddy( struct groupchat *b, const char *handle ) user_t *u = user_findhandle( b->ic, handle ); int me = 0; - if( set_getbool( &b->ic->irc->set, "debug" ) ) + if( set_getbool( &b->ic->irc->b->set, "debug" ) ) imcb_log( b->ic, "User %s added to conversation %p", handle, b ); /* It might be yourself! */ @@ -959,7 +958,7 @@ void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char user_t *u; int me = 0; - if( set_getbool( &b->ic->irc->set, "debug" ) ) + if( set_getbool( &b->ic->irc->b->set, "debug" ) ) imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" ); /* It might be yourself! */ @@ -1017,7 +1016,7 @@ char *set_eval_away_devoice( set_t *set, char *value ) /* Horror.... */ - if( st != set_getbool( &irc->set, "away_devoice" ) ) + if( st != set_getbool( &irc->b->set, "away_devoice" ) ) { char list[80] = ""; user_t *u = irc->users; @@ -1106,7 +1105,7 @@ int imc_away_send_update( struct im_connection *ic ) char *away, *msg = NULL; away = set_getstr( &ic->acc->set, "away" ) ? - : set_getstr( &ic->irc->set, "away" ); + : set_getstr( &ic->irc->b->set, "away" ); if( away && *away ) { GList *m = ic->acc->prpl->away_states( ic ); @@ -1117,7 +1116,7 @@ int imc_away_send_update( struct im_connection *ic ) { away = NULL; msg = set_getstr( &ic->acc->set, "status" ) ? - : set_getstr( &ic->irc->set, "status" ); + : set_getstr( &ic->irc->b->set, "status" ); } ic->acc->prpl->set_away( ic, away, msg ); diff --git a/protocols/user.c b/protocols/user.c deleted file mode 100644 index f014586b..00000000 --- a/protocols/user.c +++ /dev/null @@ -1,106 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Stuff to handle, save and search buddies */ - -/* - 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" - -user_t *user_add( irc_t *irc, char *nick ) -{ - user_t *u, *lu = NULL; - char *key; - - if( !nick_ok( nick ) ) - return( NULL ); - - if( user_find( irc, nick ) != NULL ) - return( NULL ); - - return( u ); -} - -int user_del( irc_t *irc, char *nick ) -{ - user_t *u, *t; - char *key; - gpointer okey, ovalue; - - if( !nick_ok( nick ) ) - return( 0 ); - - u = irc->users; - t = NULL; - while( u ) - { - if( nick_cmp( u->nick, nick ) == 0 ) - { - /* Get this key now already, since "nick" might be free()d - at the time we start playing with the hash... */ - key = g_strdup( nick ); - nick_lc( key ); - - if( t ) - t->next = u->next; - else - irc->users = u->next; - if( u->online ) - irc_kill( irc, u ); - g_free( u->nick ); - if( u->nick != u->user ) g_free( u->user ); - if( u->nick != u->host ) g_free( u->host ); - if( u->nick != u->realname ) g_free( u->realname ); - g_free( u->group ); - g_free( u->away ); - g_free( u->handle ); - g_free( u->sendbuf ); - if( u->sendbuf_timer ) b_event_remove( u->sendbuf_timer ); - g_free( u ); - - return( 1 ); - } - u = (t=u)->next; - } - - return( 0 ); -} - -user_t *user_findhandle( struct im_connection *ic, const char *handle ) -{ - user_t *u; - char *nick; - - /* First, let's try a hash lookup. If it works, it's probably faster. */ - if( ( nick = g_hash_table_lookup( ic->acc->nicks, handle ) ) && - ( u = user_find( ic->irc, nick ) ) && - ( ic->acc->prpl->handle_cmp( handle, u->handle ) == 0 ) ) - return u; - - /* However, it doesn't always work, so in that case we'll have to dig - through the whole userlist. :-( */ - for( u = ic->irc->users; u; u = u->next ) - if( u->ic == ic && u->handle && ic->acc->prpl->handle_cmp( u->handle, handle ) == 0 ) - return u; - - return NULL; -} diff --git a/protocols/user.h b/protocols/user.h deleted file mode 100644 index 26697a3a..00000000 --- a/protocols/user.h +++ /dev/null @@ -1,40 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Stuff to handle, save and search buddies */ - -/* - 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 -*/ - -#ifndef __USER_H__ -#define __USER_H__ - -struct __USER -{ - struct im_connection *ic; - char *handle; - char *fullname; - char *group; - - char *away; - char *status_msg; -} user_t; - -#endif /* __USER_H__ */ -- cgit v1.2.3 From 81e04e162bdc4517b2f357fd16dfd76f68245464 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 31 Mar 2010 22:32:25 -0400 Subject: nogaim.c is close to doing something useful again without speaking any IRC itself. --- Makefile | 2 +- irc.h | 2 - irc_im.c | 75 +++++++++++++++ nick.c | 25 ++--- protocols/Makefile | 3 +- protocols/account.c | 36 +++---- protocols/account.h | 12 +-- protocols/bee.c | 14 +-- protocols/bee.h | 6 +- protocols/bee_user.c | 2 +- protocols/nogaim.c | 247 +++++++++++++++++++++--------------------------- protocols/nogaim.h | 2 +- protocols/oscar/oscar.c | 7 +- 13 files changed, 240 insertions(+), 193 deletions(-) create mode 100644 irc_im.c diff --git a/Makefile b/Makefile index ed0316a4..5e91b93b 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # Program variables #objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) -objects = bitlbee.o help.o ipc.o irc.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o +objects = bitlbee.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols diff --git a/irc.h b/irc.h index b1d8b6ef..d3714a4a 100644 --- a/irc.h +++ b/irc.h @@ -152,8 +152,6 @@ struct irc_channel_funcs gboolean (*privmsg)( irc_channel_t *iu, const char *msg ); }; -#include "user.h" - /* irc.c */ extern GSList *irc_connection_list; diff --git a/irc_im.c b/irc_im.c new file mode 100644 index 00000000..9f64981d --- /dev/null +++ b/irc_im.c @@ -0,0 +1,75 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Some glue to put the IRC and the IM stuff together. */ + +/* + 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 +*/ + +#include "bitlbee.h" + +static const struct irc_user_funcs irc_user_im_funcs; +static const struct bee_ui_funcs irc_ui_funcs; + +static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) +{ + irc_user_t *iu; + char nick[MAX_NICK_LENGTH+1], *s; + + memset( nick, 0, MAX_NICK_LENGTH + 1 ); + strcpy( nick, nick_get( bu->ic->acc, bu->handle ) ); + + iu = irc_user_new( (irc_t*) bee->ui_data, nick ); + + if( ( s = strchr( bu->handle, '@' ) ) ) + { + iu->host = g_strdup( s + 1 ); + iu->user = g_strndup( bu->handle, s - bu->handle ); + } + else if( bu->ic->acc->server ) + { + iu->host = g_strdup( bu->ic->acc->server ); + iu->user = g_strdup( bu->handle ); + + /* s/ /_/ ... important for AOL screennames */ + for( s = iu->user; *s; s ++ ) + if( *s == ' ' ) + *s = '_'; + } + else + { + iu->host = g_strdup( bu->ic->acc->prpl->name ); + iu->user = g_strdup( bu->handle ); + } + + iu->f = &irc_user_im_funcs; + //iu->last_typing_notice = 0; + + return TRUE; +} + + + +static const struct bee_ui_funcs irc_ui_funcs = { + bee_irc_user_new, +}; + +static const struct irc_user_funcs irc_user_im_funcs = { +}; diff --git a/nick.c b/nick.c index 002d2501..7188df14 100644 --- a/nick.c +++ b/nick.c @@ -77,7 +77,7 @@ char *nick_get( account_t *acc, const char *handle ) *(s++) = 0; nick_strip( nick ); - if( set_getbool( &acc->irc->b->set, "lcnicks" ) ) + if( set_getbool( &acc->bee->set, "lcnicks" ) ) nick_lc( nick ); } g_free( store_handle ); @@ -91,11 +91,12 @@ char *nick_get( account_t *acc, const char *handle ) void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+1] ) { + irc_t *irc = (irc_t*) acc->bee->ui_data; int inf_protection = 256; /* Now, find out if the nick is already in use at the moment, and make subtle changes to make it unique. */ - while( !nick_ok( nick ) || irc_user_by_name( acc->irc, nick ) ) + while( !nick_ok( nick ) || irc_user_by_name( irc, nick ) ) { if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) { @@ -111,19 +112,19 @@ void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+ { int i; - irc_usermsg( acc->irc, "Warning: Almost had an infinite loop in nick_get()! " - "This used to be a fatal BitlBee bug, but we tried to fix it. " - "This message should *never* appear anymore. " - "If it does, please *do* send us a bug report! " - "Please send all the following lines in your report:" ); + irc_usermsg( irc, "Warning: Almost had an infinite loop in nick_get()! " + "This used to be a fatal BitlBee bug, but we tried to fix it. " + "This message should *never* appear anymore. " + "If it does, please *do* send us a bug report! " + "Please send all the following lines in your report:" ); - irc_usermsg( acc->irc, "Trying to get a sane nick for handle %s", handle ); + irc_usermsg( irc, "Trying to get a sane nick for handle %s", handle ); for( i = 0; i < MAX_NICK_LENGTH; i ++ ) - irc_usermsg( acc->irc, "Char %d: %c/%d", i, nick[i], nick[i] ); + irc_usermsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] ); - irc_usermsg( acc->irc, "FAILED. Returning an insane nick now. Things might break. " - "Good luck, and please don't forget to paste the lines up here " - "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" ); + irc_usermsg( irc, "FAILED. Returning an insane nick now. Things might break. " + "Good luck, and please don't forget to paste the lines up here " + "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" ); g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() ); diff --git a/protocols/Makefile b/protocols/Makefile index 1f89ee5a..4d461088 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -9,8 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -#objects = account.o nogaim.o user.o -objects = bee.o bee_user.o nogaim.o +objects = account.o bee.o bee_user.o nogaim.o # [SH] The next two lines should contain the directory name (in $(subdirs)) diff --git a/protocols/account.c b/protocols/account.c index c549c866..a3c2e0d7 100644 --- a/protocols/account.c +++ b/protocols/account.c @@ -28,26 +28,26 @@ #include "account.h" #include "chat.h" -account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) +account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) { account_t *a; set_t *s; - if( irc->accounts ) + if( bee->accounts ) { - for( a = irc->accounts; a->next; a = a->next ); + for( a = bee->accounts; a->next; a = a->next ); a = a->next = g_new0( account_t, 1 ); } else { - irc->accounts = a = g_new0 ( account_t, 1 ); + bee->accounts = a = g_new0 ( account_t, 1 ); } a->prpl = prpl; a->user = g_strdup( user ); a->pass = g_strdup( pass ); a->auto_connect = 1; - a->irc = irc; + a->bee = bee; s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); s->flags |= ACC_SET_NOSAVE; @@ -152,7 +152,7 @@ char *set_eval_account( set_t *set, char *value ) return SET_INVALID; } -account_t *account_get( irc_t *irc, char *id ) +account_t *account_get( bee_t *bee, char *id ) { account_t *a, *ret = NULL; char *handle, *s; @@ -168,7 +168,7 @@ account_t *account_get( irc_t *irc, char *id ) if( ( proto = find_protocol( id ) ) ) { - for( a = irc->accounts; a; a = a->next ) + for( a = bee->accounts; a; a = a->next ) if( a->prpl == proto && a->prpl->handle_cmp( handle, a->user ) == 0 ) ret = a; @@ -185,14 +185,14 @@ account_t *account_get( irc_t *irc, char *id ) if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) { - for( a = irc->accounts; a; a = a->next ) + for( a = bee->accounts; a; a = a->next ) if( ( nr-- ) == 0 ) return( a ); return( NULL ); } - for( a = irc->accounts; a; a = a->next ) + for( a = bee->accounts; a; a = a->next ) { if( g_strcasecmp( id, a->prpl->name ) == 0 ) { @@ -213,7 +213,7 @@ account_t *account_get( irc_t *irc, char *id ) return( ret ); } -void account_del( irc_t *irc, account_t *acc ) +void account_del( bee_t *bee, account_t *acc ) { account_t *a, *l = NULL; struct chat *c, *nc; @@ -222,20 +222,22 @@ void account_del( irc_t *irc, account_t *acc ) /* Caller should have checked, accounts still in use can't be deleted. */ return; - for( a = irc->accounts; a; a = (l=a)->next ) + for( a = bee->accounts; a; a = (l=a)->next ) if( a == acc ) { if( l ) l->next = a->next; else - irc->accounts = a->next; + bee->accounts = a->next; - for( c = irc->chatrooms; c; c = nc ) + /** FIXME + for( c = bee->chatrooms; c; c = nc ) { nc = c->next; if( acc == c->acc ) - chat_del( irc, c ); + chat_del( bee, c ); } + */ while( a->set ) set_del( &a->set, a->set->key ); @@ -253,7 +255,7 @@ void account_del( irc_t *irc, account_t *acc ) } } -void account_on( irc_t *irc, account_t *a ) +void account_on( bee_t *bee, account_t *a ) { if( a->ic ) { @@ -267,7 +269,7 @@ void account_on( irc_t *irc, account_t *a ) a->prpl->login( a ); } -void account_off( irc_t *irc, account_t *a ) +void account_off( bee_t *bee, account_t *a ) { imc_logout( a->ic, FALSE ); a->ic = NULL; @@ -335,7 +337,7 @@ char *set_eval_account_reconnect_delay( set_t *set, char *value ) int account_reconnect_delay( account_t *a ) { - char *setting = set_getstr( &a->irc->b->set, "auto_reconnect_delay" ); + char *setting = set_getstr( &a->bee->set, "auto_reconnect_delay" ); struct account_reconnect_delay p; if( account_reconnect_delay_parse( setting, &p ) ) diff --git a/protocols/account.h b/protocols/account.h index 984dcfe6..be27542e 100644 --- a/protocols/account.h +++ b/protocols/account.h @@ -41,16 +41,16 @@ typedef struct account set_t *set; GHashTable *nicks; - struct irc *irc; + struct bee *bee; struct im_connection *ic; struct account *next; } account_t; -account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ); -account_t *account_get( irc_t *irc, char *id ); -void account_del( irc_t *irc, account_t *acc ); -void account_on( irc_t *irc, account_t *a ); -void account_off( irc_t *irc, account_t *a ); +account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ); +account_t *account_get( bee_t *bee, char *id ); +void account_del( bee_t *bee, account_t *acc ); +void account_on( bee_t *bee, account_t *a ); +void account_off( bee_t *bee, account_t *a ); char *set_eval_account( set_t *set, char *value ); char *set_eval_account_reconnect_delay( set_t *set, char *value ); diff --git a/protocols/bee.c b/protocols/bee.c index c6f48901..3f576b0b 100644 --- a/protocols/bee.c +++ b/protocols/bee.c @@ -23,25 +23,25 @@ bee_t *bee_new() void bee_free( bee_t *b ) { - while( b->accounts ) + account_t *acc = b->accounts; + + while( acc ) { - account_t *acc = b->accounts->data; - - /* if( acc->ic ) imc_logout( acc->ic, FALSE ); else if( acc->reconnect ) cancel_auto_reconnect( acc ); - */ if( acc->ic == NULL ) - {} //account_del( b, acc ); + account_del( b, acc ); else /* Nasty hack, but account_del() doesn't work in this case and we don't want infinite loops, do we? ;-) */ - b->accounts = g_slist_remove( b->accounts, acc ); + acc = acc->next; } while( b->set ) set_del( &b->set, b->set->key ); + + g_free( b ); } diff --git a/protocols/bee.h b/protocols/bee.h index e5c21120..e76e7988 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -33,7 +33,7 @@ typedef struct bee struct set *set; GSList *users; - GSList *accounts; + struct account *accounts; /* TODO(wilmer): Use GSList here too? */ const struct bee_ui_funcs *ui; void *ui_data; @@ -55,7 +55,8 @@ typedef struct bee_user char *fullname; char *group; - char *away; + bee_user_flags_t flags; + char *status; char *status_msg; bee_t *bee; @@ -66,6 +67,7 @@ typedef struct bee_ui_funcs { gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); gboolean (*user_free)( bee_t *bee, struct bee_user *bu ); + gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old ); } bee_ui_funcs_t; diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 538aa509..4356c141 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -58,7 +58,7 @@ int bee_user_free( bee_t *bee, struct im_connection *ic, const char *handle ) g_free( bu->handle ); g_free( bu->fullname ); g_free( bu->group ); - g_free( bu->away ); + g_free( bu->status ); g_free( bu->status_msg ); bee->users = g_slist_remove( bee->users, bu ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 618616c7..2edc8e75 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -91,8 +91,6 @@ void load_plugins(void) } #endif -/* nogaim.c */ - GList *protocols = NULL; void register_protocol (struct prpl *p) @@ -124,7 +122,6 @@ struct prpl *find_protocol(const char *name) return NULL; } -/* nogaim.c */ void nogaim_init() { extern void msn_initmodule(); @@ -155,15 +152,13 @@ void nogaim_init() GSList *get_connections() { return connections; } -/* multi.c */ - struct im_connection *imcb_new( account_t *acc ) { struct im_connection *ic; ic = g_new0( struct im_connection, 1 ); - ic->irc = acc->irc; + ic->bee = acc->bee; ic->acc = acc; acc->ic = ic; @@ -177,7 +172,7 @@ void imc_free( struct im_connection *ic ) account_t *a; /* Destroy the pointer to this connection from the account list */ - for( a = ic->irc->accounts; a; a = a->next ) + for( a = ic->bee->accounts; a; a = a->next ) if( a->ic == ic ) { a->ic = NULL; @@ -198,20 +193,21 @@ static void serv_got_crap( struct im_connection *ic, char *format, ... ) text = g_strdup_vprintf( format, params ); va_end( params ); - if( ( g_strcasecmp( set_getstr( &ic->irc->b->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->b->set, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) strip_html( text ); /* Try to find a different connection on the same protocol. */ - for( a = ic->irc->accounts; a; a = a->next ) + for( a = ic->bee->accounts; a; a = a->next ) if( a->prpl == ic->acc->prpl && a->ic != ic ) break; /* If we found one, include the screenname in the message. */ if( a ) - irc_usermsg( ic->irc, "%s(%s) - %s", ic->acc->prpl->name, ic->acc->user, text ); + /* FIXME(wilmer): ui_log callback or so */ + irc_usermsg( ic->bee->ui_data, "%s(%s) - %s", ic->acc->prpl->name, ic->acc->user, text ); else - irc_usermsg( ic->irc, "%s - %s", ic->acc->prpl->name, text ); + irc_usermsg( ic->bee->ui_data, "%s - %s", ic->acc->prpl->name, text ); g_free( text ); } @@ -262,9 +258,6 @@ static gboolean send_keepalive( gpointer d, gint fd, b_input_condition cond ) void imcb_connected( struct im_connection *ic ) { - irc_t *irc = ic->irc; - struct chat *c; - /* MSN servers sometimes redirect you to a different server and do the whole login sequence again, so these "late" calls to this function should be handled correctly. (IOW, ignored) */ @@ -300,7 +293,7 @@ gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ) account_t *a = data; a->reconnect = 0; - account_on( a->irc, a ); + account_on( a->bee, a ); return( FALSE ); /* Only have to run the timeout once */ } @@ -313,9 +306,9 @@ void cancel_auto_reconnect( account_t *a ) void imc_logout( struct im_connection *ic, int allow_reconnect ) { - irc_t *irc = ic->irc; - user_t *t, *u; + bee_t *bee = ic->bee; account_t *a; + GSList *l; int delay; /* Nested calls might happen sometimes, this is probably the best @@ -335,22 +328,17 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) g_free( ic->away ); ic->away = NULL; - u = irc->users; - while( u ) + for( l = bee->users; l; l = l->next ) { - if( u->ic == ic ) - { - t = u->next; - user_del( irc, u->nick ); - u = t; - } - else - u = u->next; + bee_user_t *bu = l->data; + + if( bu->ic == ic ) + bee_user_free( bee, ic, bu->handle ); } - query_del_by_conn( ic->irc, ic ); + //query_del_by_conn( ic->irc, ic ); - for( a = irc->accounts; a; a = a->next ) + for( a = bee->accounts; a; a = a->next ) if( a->ic == ic ) break; @@ -358,7 +346,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) { /* Uhm... This is very sick. */ } - else if( allow_reconnect && set_getbool( &irc->b->set, "auto_reconnect" ) && + else if( allow_reconnect && set_getbool( &bee->set, "auto_reconnect" ) && set_getbool( &a->set, "auto_reconnect" ) && ( delay = account_reconnect_delay( a ) ) > 0 ) { @@ -369,27 +357,21 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) imc_free( ic ); } - -/* dialogs.c */ - void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont ) { - query_add( ic->irc, ic, msg, doit, dont, data ); + //query_add( ic->irc, ic, msg, doit, dont, data ); } - -/* list.c */ - void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group ) { - user_t *u; - char nick[MAX_NICK_LENGTH+1], *s; - irc_t *irc = ic->irc; + bee_user_t *bu; + //char nick[MAX_NICK_LENGTH+1], *s; + bee_t *bee = ic->bee; - if( user_findhandle( ic, handle ) ) + if( bee_user_by_handle( bee, ic, handle ) ) { - if( set_getbool( &irc->b->set, "debug" ) ) + if( set_getbool( &bee->set, "debug" ) ) imcb_log( ic, "User already exists, ignoring add request: %s", handle ); return; @@ -400,63 +382,13 @@ void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *g even support groups so let's silently ignore this for now. */ } - memset( nick, 0, MAX_NICK_LENGTH + 1 ); - strcpy( nick, nick_get( ic->acc, handle ) ); - - u = user_add( ic->irc, nick ); - -// if( !realname || !*realname ) realname = nick; -// u->realname = g_strdup( realname ); - - if( ( s = strchr( handle, '@' ) ) ) - { - u->host = g_strdup( s + 1 ); - u->user = g_strndup( handle, s - handle ); - } - else if( ic->acc->server ) - { - u->host = g_strdup( ic->acc->server ); - u->user = g_strdup( handle ); - - /* s/ /_/ ... important for AOL screennames */ - for( s = u->user; *s; s ++ ) - if( *s == ' ' ) - *s = '_'; - } - else - { - u->host = g_strdup( ic->acc->prpl->name ); - u->user = g_strdup( handle ); - } - - u->ic = ic; - u->handle = g_strdup( handle ); - if( group ) u->group = g_strdup( group ); - u->send_handler = buddy_send_handler; - u->last_typing_notice = 0; -} - -struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle ) -{ - static struct buddy b[1]; - user_t *u; - - u = user_findhandle( ic, handle ); - - if( !u ) - return( NULL ); - - memset( b, 0, sizeof( b ) ); - strncpy( b->name, handle, 80 ); - strncpy( b->show, u->realname, BUDDY_ALIAS_MAXLEN ); - b->present = u->online; - b->ic = u->ic; - - return( b ); + bu = bee_user_new( bee, ic, handle ); + bu->group = g_strdup( group ); } void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname ) { +#if 0 user_t *u = user_findhandle( ic, handle ); char *set; @@ -468,7 +400,7 @@ void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char u->realname = g_strdup( realname ); - if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->b->set, "display_namechanges" ) ) + if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->bee->set, "display_namechanges" ) ) imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname ); } @@ -488,20 +420,19 @@ void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char g_free( name ); } +#endif } void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group ) { - user_t *u; - - if( ( u = user_findhandle( ic, handle ) ) ) - user_del( ic->irc, u->nick ); + bee_user_free( ic->bee, ic, handle ); } /* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM modules to suggest a nickname for a handle. */ void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick ) { +#if 0 user_t *u = user_findhandle( ic, handle ); char newnick[MAX_NICK_LENGTH+1], *orig_nick; @@ -516,7 +447,7 @@ void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const c /* Some processing to make sure this string is a valid IRC nickname. */ nick_strip( newnick ); - if( set_getbool( &ic->irc->b->set, "lcnicks" ) ) + if( set_getbool( &ic->bee->set, "lcnicks" ) ) nick_lc( newnick ); if( strcmp( u->nick, newnick ) != 0 ) @@ -533,6 +464,7 @@ void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const c g_free( orig_nick ); } } +#endif } @@ -564,6 +496,7 @@ static void imcb_ask_auth_cb_yes( void *data ) void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname ) { +#if 0 struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 ); char *s, *realname_ = NULL; @@ -578,6 +511,7 @@ void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *re data->ic = ic; data->handle = g_strdup( handle ); query_add( ic->irc, ic, s, imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data ); +#endif } @@ -598,6 +532,7 @@ static void imcb_ask_add_cb_yes( void *data ) void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname ) { +#if 0 struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 ); char *s; @@ -610,28 +545,23 @@ void imcb_ask_add( struct im_connection *ic, const char *handle, const char *rea data->ic = ic; data->handle = g_strdup( handle ); query_add( ic->irc, ic, s, imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data ); +#endif } - -/* server.c */ - void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ) { - user_t *u; - int oa, oo; - - u = user_findhandle( ic, (char*) handle ); + bee_t *bee = ic->bee; + bee_user_t *bu, *old; - if( !u ) + if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) { - if( g_strcasecmp( set_getstr( &ic->irc->b->set, "handle_unknown" ), "add" ) == 0 ) + if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "add" ) == 0 ) { - imcb_add_buddy( ic, (char*) handle, NULL ); - u = user_findhandle( ic, (char*) handle ); + bu = bee_user_new( bee, ic, handle ); } else { - if( set_getbool( &ic->irc->b->set, "debug" ) || g_strcasecmp( set_getstr( &ic->irc->b->set, "handle_unknown" ), "ignore" ) != 0 ) + if( set_getbool( &ic->bee->set, "debug" ) || g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "ignore" ) != 0 ) { imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle ); imcb_log( ic, "flags = %d, state = %s, message = %s", flags, @@ -642,6 +572,22 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, } } + /* May be nice to give the UI something to compare against. */ + old = g_memdup( bu, sizeof( bee_user_t ) ); + + /* TODO(wilmer): OPT_AWAY, or just state == NULL ? */ + bu->flags = ( flags & OPT_LOGGED_IN ? BEE_USER_ONLINE : 0 ) | + ( flags & OPT_AWAY ? BEE_USER_AWAY : 0 ); + bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state ); + bu->status_msg = g_strdup( message ); + + if( bee->ui->user_status ) + bee->ui->user_status( bee, bu, old ); + + g_free( old->status_msg ); + g_free( old->status ); + g_free( old ); +#if 0 oa = u->away != NULL; oo = u->online; @@ -691,14 +637,14 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, } /* LISPy... */ - if( ( set_getbool( &ic->irc->b->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ + if( ( set_getbool( &ic->bee->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ ( u->online ) && /* Don't touch offline people */ ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ { char *from; - if( set_getbool( &ic->irc->b->set, "simulate_netsplit" ) ) + if( set_getbool( &ic->bee->set, "simulate_netsplit" ) ) { from = g_strdup( ic->irc->myhost ); } @@ -711,11 +657,13 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, u->away?'-':'+', u->nick ); g_free( from ); } +#endif } void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) { - irc_t *irc = ic->irc; +#if 0 + bee_t *bee = ic->bee; char *wrapped; user_t *u; @@ -723,18 +671,18 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui if( !u ) { - char *h = set_getstr( &irc->b->set, "handle_unknown" ); + char *h = set_getstr( &bee->set, "handle_unknown" ); if( g_strcasecmp( h, "ignore" ) == 0 ) { - if( set_getbool( &irc->b->set, "debug" ) ) + if( set_getbool( &bee->set, "debug" ) ) imcb_log( ic, "Ignoring message from unknown handle %s", handle ); return; } else if( g_strncasecmp( h, "add", 3 ) == 0 ) { - int private = set_getbool( &irc->b->set, "private" ); + int private = set_getbool( &bee->set, "private" ); if( h[3] ) { @@ -755,20 +703,22 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui } } - if( ( g_strcasecmp( set_getstr( &ic->irc->b->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->b->set, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) strip_html( msg ); wrapped = word_wrap( msg, 425 ); irc_msgfrom( irc, u->nick, wrapped ); g_free( wrapped ); +#endif } void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) { +#if 0 user_t *u; - if( !set_getbool( &ic->irc->b->set, "typing_notice" ) ) + if( !set_getbool( &ic->bee->set, "typing_notice" ) ) return; if( ( u = user_findhandle( ic, handle ) ) ) @@ -778,10 +728,17 @@ void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) g_snprintf( buf, 256, "\1TYPING %d\1", ( flags >> 8 ) & 3 ); irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf ); } +#endif +} + +struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *handle ) +{ + return bee_user_by_handle( ic->bee, ic, handle ); } struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) { +#if 0 struct groupchat *c; /* This one just creates the conversation structure, user won't see anything yet */ @@ -799,19 +756,21 @@ struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ ); c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); - if( set_getbool( &ic->irc->b->set, "debug" ) ) + if( set_getbool( &ic->bee->set, "debug" ) ) imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle ); return c; +#endif } void imcb_chat_free( struct groupchat *c ) { +#if 0 struct im_connection *ic = c->ic; struct groupchat *l; GList *ir; - if( set_getbool( &ic->irc->b->set, "debug" ) ) + if( set_getbool( &ic->bee->set, "debug" ) ) imcb_log( ic, "You were removed from conversation %p", c ); if( c ) @@ -844,10 +803,12 @@ void imcb_chat_free( struct groupchat *c ) g_free( c->topic ); g_free( c ); } +#endif } void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at ) { +#if 0 struct im_connection *ic = c->ic; char *wrapped; user_t *u; @@ -858,8 +819,8 @@ void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t fl u = user_findhandle( ic, who ); - if( ( g_strcasecmp( set_getstr( &ic->irc->b->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->b->set, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) strip_html( msg ); wrapped = word_wrap( msg, 425 ); @@ -872,10 +833,12 @@ void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t fl imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped ); } g_free( wrapped ); +#endif } void imcb_chat_log( struct groupchat *c, char *format, ... ) { +#if 0 irc_t *irc = c->ic->irc; va_list params; char *text; @@ -890,10 +853,12 @@ void imcb_chat_log( struct groupchat *c, char *format, ... ) irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text ); g_free( text ); +#endif } void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ) { +#if 0 struct im_connection *ic = c->ic; user_t *u = NULL; @@ -904,8 +869,8 @@ void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at else u = user_findhandle( ic, who ); - if( ( g_strcasecmp( set_getstr( &ic->irc->b->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->b->set, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) strip_html( topic ); g_free( c->topic ); @@ -913,17 +878,16 @@ void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at if( c->joined && u ) irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic ); +#endif } - -/* buddy_chat.c */ - void imcb_chat_add_buddy( struct groupchat *b, const char *handle ) { +#if 0 user_t *u = user_findhandle( b->ic, handle ); int me = 0; - if( set_getbool( &b->ic->irc->b->set, "debug" ) ) + if( set_getbool( &b->ic->bee->set, "debug" ) ) imcb_log( b->ic, "User %s added to conversation %p", handle, b ); /* It might be yourself! */ @@ -950,15 +914,17 @@ void imcb_chat_add_buddy( struct groupchat *b, const char *handle ) irc_join( b->ic->irc, u, b->channel ); b->in_room = g_list_append( b->in_room, g_strdup( handle ) ); } +#endif } /* This function is one BIG hack... :-( EREWRITE */ void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ) { +#if 0 user_t *u; int me = 0; - if( set_getbool( &b->ic->irc->b->set, "debug" ) ) + if( set_getbool( &b->ic->bee->set, "debug" ) ) imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" ); /* It might be yourself! */ @@ -978,10 +944,12 @@ void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) ) irc_part( b->ic->irc, u, b->channel ); +#endif } static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) { +#if 0 GList *i; /* Find the handle in the room userlist and shoot it */ @@ -997,13 +965,14 @@ static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) i = i->next; } +#endif - return( 0 ); + return 0; } /* Misc. BitlBee stuff which shouldn't really be here */ - +#if 0 char *set_eval_away_devoice( set_t *set, char *value ) { irc_t *irc = set->data; @@ -1058,7 +1027,7 @@ char *set_eval_away_devoice( set_t *set, char *value ) return value; } - +#endif @@ -1105,7 +1074,7 @@ int imc_away_send_update( struct im_connection *ic ) char *away, *msg = NULL; away = set_getstr( &ic->acc->set, "away" ) ? - : set_getstr( &ic->irc->b->set, "away" ); + : set_getstr( &ic->bee->set, "away" ); if( away && *away ) { GList *m = ic->acc->prpl->away_states( ic ); @@ -1116,7 +1085,7 @@ int imc_away_send_update( struct im_connection *ic ) { away = NULL; msg = set_getstr( &ic->acc->set, "status" ) ? - : set_getstr( &ic->irc->b->set, "status" ); + : set_getstr( &ic->bee->set, "status" ); } ic->acc->prpl->set_away( ic, away, msg ); diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 324a2b46..4a334bf2 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -86,7 +86,7 @@ struct im_connection int evil; /* BitlBee */ - irc_t *irc; + bee_t *bee; struct groupchat *groupchats; }; diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index e0c32257..a5ca1ac8 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -1189,8 +1189,7 @@ static void gaim_icq_authgrant(void *data_) { message = 0; aim_ssi_auth_reply(od->sess, od->conn, uin, 1, ""); // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message); - if(imcb_find_buddy(data->ic, uin) == NULL) - imcb_ask_add(data->ic, uin, NULL); + imcb_ask_add(data->ic, uin, NULL); g_free(uin); g_free(data); @@ -1951,11 +1950,13 @@ static void oscar_get_info(struct im_connection *g, char *name) { static void oscar_get_away(struct im_connection *g, char *who) { struct oscar_data *odata = (struct oscar_data *)g->proto_data; if (odata->icq) { + /** FIXME(wilmer): Hmm, lost the ability to get away msgs here, do we care to get that back? struct buddy *budlight = imcb_find_buddy(g, who); if (budlight) if ((budlight->uc & 0xff80) >> 7) if (budlight->caps & AIM_CAPS_ICQSERVERRELAY) aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7); + */ } else aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE); } @@ -2093,7 +2094,7 @@ static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) { switch (curitem->type) { case 0x0000: /* Buddy */ - if ((curitem->name) && (!imcb_find_buddy(ic, nrm))) { + if ((curitem->name) && (!imcb_buddy_by_handle(ic, nrm))) { char *realname = NULL; if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1)) -- cgit v1.2.3 From d860a8ddf5039f7208bff4c179bc9fe1549da6da Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 31 Mar 2010 23:38:50 -0400 Subject: Restored "account" root command and restored enough stuff to be able to send messages. Also started moving stuff out from nogaim.* into bee_* files. --- irc.c | 2 + irc.h | 5 +- irc_commands.c | 18 ++--- irc_im.c | 31 ++++++++- protocols/bee.h | 12 ++++ protocols/bee_user.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++ protocols/nogaim.c | 182 -------------------------------------------------- protocols/nogaim.h | 13 +--- root_commands.c | 39 ++++++----- unix.c | 2 +- 10 files changed, 262 insertions(+), 226 deletions(-) diff --git a/irc.c b/irc.c index 4eaa55a8..de9b4a04 100644 --- a/irc.c +++ b/irc.c @@ -93,6 +93,8 @@ irc_t *irc_new( int fd ) irc_connection_list = g_slist_append( irc_connection_list, irc ); b = irc->b = bee_new(); + b->ui_data = irc; + b->ui = &irc_ui_funcs; s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc ); s = set_add( &b->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); diff --git a/irc.h b/irc.h index d3714a4a..b5aef53a 100644 --- a/irc.h +++ b/irc.h @@ -74,7 +74,6 @@ typedef struct irc char umode[8]; struct query *queries; - struct account *accounts; GSList *file_transfers; GSList *users, *channels; @@ -112,7 +111,7 @@ typedef struct irc_user guint sendbuf_timer; //int sendbuf_flags; - //struct user *b; + struct bee_user *bu; const struct irc_user_funcs *f; } irc_user_t; @@ -152,6 +151,8 @@ struct irc_channel_funcs gboolean (*privmsg)( irc_channel_t *iu, const char *msg ); }; +extern const struct bee_ui_funcs irc_ui_funcs; + /* irc.c */ extern GSList *irc_connection_list; diff --git a/irc_commands.c b/irc_commands.c index 858b5bdd..03d05417 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -308,6 +308,13 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) #endif } +static void irc_cmd_nickserv( irc_t *irc, char **cmd ) +{ + /* [SH] This aliases the NickServ command to PRIVMSG root */ + /* [TV] This aliases the NS command to PRIVMSG root as well */ + root_command( irc, cmd + 1 ); +} + #if 0 @@ -517,13 +524,6 @@ static void irc_cmd_away( irc_t *irc, char **cmd ) set_setstr( &irc->set, "away", u->away ); } -static void irc_cmd_nickserv( irc_t *irc, char **cmd ) -{ - /* [SH] This aliases the NickServ command to PRIVMSG root */ - /* [TV] This aliases the NS command to PRIVMSG root as well */ - root_command( irc, cmd + 1 ); -} - static void irc_cmd_version( irc_t *irc, char **cmd ) { irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU ); @@ -577,6 +577,8 @@ static const command_t irc_commands[] = { { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, { "who", 0, irc_cmd_who, IRC_CMD_LOGGED_IN }, { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, + { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, + { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, #if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, @@ -586,8 +588,6 @@ static const command_t irc_commands[] = { { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, - { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, - { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, diff --git a/irc_im.c b/irc_im.c index 9f64981d..07b478fb 100644 --- a/irc_im.c +++ b/irc_im.c @@ -25,8 +25,10 @@ #include "bitlbee.h" + +/* IM->IRC callbacks */ + static const struct irc_user_funcs irc_user_im_funcs; -static const struct bee_ui_funcs irc_ui_funcs; static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) { @@ -36,7 +38,8 @@ static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) memset( nick, 0, MAX_NICK_LENGTH + 1 ); strcpy( nick, nick_get( bu->ic->acc, bu->handle ) ); - iu = irc_user_new( (irc_t*) bee->ui_data, nick ); + bu->ui_data = iu = irc_user_new( (irc_t*) bee->ui_data, nick ); + iu->bu = bu; if( ( s = strchr( bu->handle, '@' ) ) ) { @@ -65,11 +68,33 @@ static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) return TRUE; } +static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu ) +{ + return irc_user_free( bee->ui_data, bu->ui_data ); +} +static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old ) +{ + return TRUE; +} -static const struct bee_ui_funcs irc_ui_funcs = { +const struct bee_ui_funcs irc_ui_funcs = { bee_irc_user_new, + bee_irc_user_free, + bee_irc_user_status, }; + +/* IRC->IM calls */ + +static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg ) +{ + if( iu->bu ) + return bee_user_msg( iu->irc->b, iu->bu, msg, 0 ); + else + return FALSE; +} + static const struct irc_user_funcs irc_user_im_funcs = { + bee_irc_user_privmsg, }; diff --git a/protocols/bee.h b/protocols/bee.h index e76e7988..c36a6b16 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -79,5 +79,17 @@ void bee_free( bee_t *b ); bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle ); int bee_user_free( bee_t *bee, struct im_connection *ic, const char *handle ); bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle ); +int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ); + +/* Callbacks from IM modules to core: */ +/* Buddy activity */ +/* To manipulate the status of a handle. + * - flags can be |='d with OPT_* constants. You will need at least: + * OPT_LOGGED_IN and OPT_AWAY. + * - 'state' and 'message' can be NULL */ +G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); +/* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); +/* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ +G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ); #endif /* __BEE_H__ */ diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 4356c141..66e25faf 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -80,3 +80,187 @@ bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char return NULL; } + +int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ) +{ + char *buf = NULL; + int st; + + if( ( bu->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "", 6 ) != 0 ) ) + { + buf = escape_html( msg ); + msg = buf; + } + + st = bu->ic->acc->prpl->buddy_msg( bu->ic, bu->handle, msg, flags ); + g_free( buf ); + + return st; +} + + +/* IM->UI callbacks */ +void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ) +{ + bee_t *bee = ic->bee; + bee_user_t *bu, *old; + + if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) + { + if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "add" ) == 0 ) + { + bu = bee_user_new( bee, ic, handle ); + } + else + { + if( set_getbool( &ic->bee->set, "debug" ) || g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "ignore" ) != 0 ) + { + imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle ); + imcb_log( ic, "flags = %d, state = %s, message = %s", flags, + state ? state : "NULL", message ? message : "NULL" ); + } + + return; + } + } + + /* May be nice to give the UI something to compare against. */ + old = g_memdup( bu, sizeof( bee_user_t ) ); + + /* TODO(wilmer): OPT_AWAY, or just state == NULL ? */ + bu->flags = ( flags & OPT_LOGGED_IN ? BEE_USER_ONLINE : 0 ) | + ( flags & OPT_AWAY ? BEE_USER_AWAY : 0 ); + bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state ); + bu->status_msg = g_strdup( message ); + + if( bee->ui->user_status ) + bee->ui->user_status( bee, bu, old ); + + g_free( old->status_msg ); + g_free( old->status ); + g_free( old ); +#if 0 + oa = u->away != NULL; + oo = u->online; + + g_free( u->away ); + g_free( u->status_msg ); + u->away = u->status_msg = NULL; + + if( ( flags & OPT_LOGGED_IN ) && !u->online ) + { + irc_spawn( ic->irc, u ); + u->online = 1; + } + else if( !( flags & OPT_LOGGED_IN ) && u->online ) + { + struct groupchat *c; + + irc_kill( ic->irc, u ); + u->online = 0; + + /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */ + for( c = ic->groupchats; c; c = c->next ) + remove_chat_buddy_silent( c, handle ); + } + + if( flags & OPT_AWAY ) + { + if( state && message ) + { + u->away = g_strdup_printf( "%s (%s)", state, message ); + } + else if( state ) + { + u->away = g_strdup( state ); + } + else if( message ) + { + u->away = g_strdup( message ); + } + else + { + u->away = g_strdup( "Away" ); + } + } + else + { + u->status_msg = g_strdup( message ); + } + + /* LISPy... */ + if( ( set_getbool( &ic->bee->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ + ( u->online ) && /* Don't touch offline people */ + ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ + ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ + { + char *from; + + if( set_getbool( &ic->bee->set, "simulate_netsplit" ) ) + { + from = g_strdup( ic->irc->myhost ); + } + else + { + from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, + ic->irc->myhost ); + } + irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, + u->away?'-':'+', u->nick ); + g_free( from ); + } +#endif +} + +void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) +{ +#if 0 + bee_t *bee = ic->bee; + char *wrapped; + user_t *u; + + u = user_findhandle( ic, handle ); + + if( !u ) + { + char *h = set_getstr( &bee->set, "handle_unknown" ); + + if( g_strcasecmp( h, "ignore" ) == 0 ) + { + if( set_getbool( &bee->set, "debug" ) ) + imcb_log( ic, "Ignoring message from unknown handle %s", handle ); + + return; + } + else if( g_strncasecmp( h, "add", 3 ) == 0 ) + { + int private = set_getbool( &bee->set, "private" ); + + if( h[3] ) + { + if( g_strcasecmp( h + 3, "_private" ) == 0 ) + private = 1; + else if( g_strcasecmp( h + 3, "_channel" ) == 0 ) + private = 0; + } + + imcb_add_buddy( ic, handle, NULL ); + u = user_findhandle( ic, handle ); + u->is_private = private; + } + else + { + imcb_log( ic, "Message from unknown handle %s:", handle ); + u = user_find( irc, irc->mynick ); + } + } + + if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) + strip_html( msg ); + + wrapped = word_wrap( msg, 425 ); + irc_msgfrom( irc, u->nick, wrapped ); + g_free( wrapped ); +#endif +} diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 2edc8e75..741bdb76 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -548,171 +548,6 @@ void imcb_ask_add( struct im_connection *ic, const char *handle, const char *rea #endif } -void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ) -{ - bee_t *bee = ic->bee; - bee_user_t *bu, *old; - - if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) - { - if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "add" ) == 0 ) - { - bu = bee_user_new( bee, ic, handle ); - } - else - { - if( set_getbool( &ic->bee->set, "debug" ) || g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "ignore" ) != 0 ) - { - imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle ); - imcb_log( ic, "flags = %d, state = %s, message = %s", flags, - state ? state : "NULL", message ? message : "NULL" ); - } - - return; - } - } - - /* May be nice to give the UI something to compare against. */ - old = g_memdup( bu, sizeof( bee_user_t ) ); - - /* TODO(wilmer): OPT_AWAY, or just state == NULL ? */ - bu->flags = ( flags & OPT_LOGGED_IN ? BEE_USER_ONLINE : 0 ) | - ( flags & OPT_AWAY ? BEE_USER_AWAY : 0 ); - bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state ); - bu->status_msg = g_strdup( message ); - - if( bee->ui->user_status ) - bee->ui->user_status( bee, bu, old ); - - g_free( old->status_msg ); - g_free( old->status ); - g_free( old ); -#if 0 - oa = u->away != NULL; - oo = u->online; - - g_free( u->away ); - g_free( u->status_msg ); - u->away = u->status_msg = NULL; - - if( ( flags & OPT_LOGGED_IN ) && !u->online ) - { - irc_spawn( ic->irc, u ); - u->online = 1; - } - else if( !( flags & OPT_LOGGED_IN ) && u->online ) - { - struct groupchat *c; - - irc_kill( ic->irc, u ); - u->online = 0; - - /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */ - for( c = ic->groupchats; c; c = c->next ) - remove_chat_buddy_silent( c, handle ); - } - - if( flags & OPT_AWAY ) - { - if( state && message ) - { - u->away = g_strdup_printf( "%s (%s)", state, message ); - } - else if( state ) - { - u->away = g_strdup( state ); - } - else if( message ) - { - u->away = g_strdup( message ); - } - else - { - u->away = g_strdup( "Away" ); - } - } - else - { - u->status_msg = g_strdup( message ); - } - - /* LISPy... */ - if( ( set_getbool( &ic->bee->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ - ( u->online ) && /* Don't touch offline people */ - ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ - ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ - { - char *from; - - if( set_getbool( &ic->bee->set, "simulate_netsplit" ) ) - { - from = g_strdup( ic->irc->myhost ); - } - else - { - from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, - ic->irc->myhost ); - } - irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, - u->away?'-':'+', u->nick ); - g_free( from ); - } -#endif -} - -void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) -{ -#if 0 - bee_t *bee = ic->bee; - char *wrapped; - user_t *u; - - u = user_findhandle( ic, handle ); - - if( !u ) - { - char *h = set_getstr( &bee->set, "handle_unknown" ); - - if( g_strcasecmp( h, "ignore" ) == 0 ) - { - if( set_getbool( &bee->set, "debug" ) ) - imcb_log( ic, "Ignoring message from unknown handle %s", handle ); - - return; - } - else if( g_strncasecmp( h, "add", 3 ) == 0 ) - { - int private = set_getbool( &bee->set, "private" ); - - if( h[3] ) - { - if( g_strcasecmp( h + 3, "_private" ) == 0 ) - private = 1; - else if( g_strcasecmp( h + 3, "_channel" ) == 0 ) - private = 0; - } - - imcb_add_buddy( ic, handle, NULL ); - u = user_findhandle( ic, handle ); - u->is_private = private; - } - else - { - imcb_log( ic, "Message from unknown handle %s:", handle ); - u = user_find( irc, irc->mynick ); - } - } - - if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) - strip_html( msg ); - - wrapped = word_wrap( msg, 425 ); - irc_msgfrom( irc, u->nick, wrapped ); - g_free( wrapped ); -#endif -} - void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) { #if 0 @@ -1034,23 +869,6 @@ char *set_eval_away_devoice( set_t *set, char *value ) /* The plan is to not allow straight calls to prpl functions anymore, but do them all from some wrappers. We'll start to define some down here: */ -int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags ) -{ - char *buf = NULL; - int st; - - if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "", 6 ) != 0 ) ) - { - buf = escape_html( msg ); - msg = buf; - } - - st = ic->acc->prpl->buddy_msg( ic, handle, msg, flags ); - g_free( buf ); - - return st; -} - int imc_chat_msg( struct groupchat *c, char *msg, int flags ) { char *buf = NULL; diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 4a334bf2..a93dc5d2 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* @@ -285,16 +285,8 @@ G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *h G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname ); G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick ); -/* Buddy activity */ -/* To manipulate the status of a handle. - * - flags can be |='d with OPT_* constants. You will need at least: - * OPT_LOGGED_IN and OPT_AWAY. - * - 'state' and 'message' can be NULL */ -G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); -/* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); -/* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ -G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ); G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ); +G_MODULE_EXPORT struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *handle ); G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle ); /* Groupchats */ @@ -319,7 +311,6 @@ G_MODULE_EXPORT void imcb_chat_free( struct groupchat *c ); /* Actions, or whatever. */ int imc_away_send_update( struct im_connection *ic ); -int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags ); int imc_chat_msg( struct groupchat *c, char *msg, int flags ); void imc_add_allow( struct im_connection *ic, char *handle ); diff --git a/root_commands.c b/root_commands.c index 9b396379..75f3af81 100644 --- a/root_commands.c +++ b/root_commands.c @@ -220,6 +220,7 @@ static void cmd_drop( irc_t *irc, char **cmd ) break; } } +#endif struct cmd_account_del_data { @@ -232,7 +233,7 @@ void cmd_account_del_yes( void *data ) struct cmd_account_del_data *cad = data; account_t *a; - for( a = cad->irc->accounts; a && a != cad->a; a = a->next ); + for( a = cad->irc->b->accounts; a && a != cad->a; a = a->next ); if( a == NULL ) { @@ -244,7 +245,7 @@ void cmd_account_del_yes( void *data ) } else { - account_del( cad->irc, a ); + account_del( cad->irc->b, a ); irc_usermsg( cad->irc, "Account deleted" ); } g_free( data ); @@ -285,7 +286,7 @@ static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead, cmd_ { set_name = set_full; - head = &irc->set; + head = &irc->b->set; } else { @@ -356,7 +357,7 @@ static set_t **cmd_account_set_findhead( irc_t *irc, char *id ) { account_t *a; - if( ( a = account_get( irc, id ) ) ) + if( ( a = account_get( irc->b, id ) ) ) return &a->set; else return NULL; @@ -404,7 +405,7 @@ static void cmd_account( irc_t *irc, char **cmd ) return; } - a = account_add( irc, prpl, cmd[3], cmd[4] ); + a = account_add( irc->b, prpl, cmd[3], cmd[4] ); if( cmd[5] ) { irc_usermsg( irc, "Warning: Passing a servername/other flags to `account add' " @@ -418,7 +419,7 @@ static void cmd_account( irc_t *irc, char **cmd ) { MIN_ARGS( 2 ); - if( !( a = account_get( irc, cmd[2] ) ) ) + if( !( a = account_get( irc->b, cmd[2] ) ) ) { irc_usermsg( irc, "Invalid account" ); } @@ -440,7 +441,7 @@ static void cmd_account( irc_t *irc, char **cmd ) "to change your username/password, use the `account " "set' command. Are you sure you want to delete this " "account?", a->prpl->name, a->user ); - query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad ); + //query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad ); g_free( msg ); } } @@ -451,7 +452,7 @@ static void cmd_account( irc_t *irc, char **cmd ) if( strchr( irc->umode, 'b' ) ) irc_usermsg( irc, "Account list:" ); - for( a = irc->accounts; a; a = a->next ) + for( a = irc->b->accounts; a; a = a->next ) { char *con; @@ -474,7 +475,7 @@ static void cmd_account( irc_t *irc, char **cmd ) { if( cmd[2] ) { - if( ( a = account_get( irc, cmd[2] ) ) ) + if( ( a = account_get( irc->b, cmd[2] ) ) ) { if( a->ic ) { @@ -483,7 +484,7 @@ static void cmd_account( irc_t *irc, char **cmd ) } else { - account_on( irc, a ); + account_on( irc->b, a ); } } else @@ -494,12 +495,13 @@ static void cmd_account( irc_t *irc, char **cmd ) } else { - if ( irc->accounts ) { + if ( irc->b->accounts ) + { irc_usermsg( irc, "Trying to get all accounts connected..." ); - for( a = irc->accounts; a; a = a->next ) + for( a = irc->b->accounts; a; a = a->next ) if( !a->ic && a->auto_connect ) - account_on( irc, a ); + account_on( irc->b, a ); } else { @@ -513,19 +515,19 @@ static void cmd_account( irc_t *irc, char **cmd ) { irc_usermsg( irc, "Deactivating all active (re)connections..." ); - for( a = irc->accounts; a; a = a->next ) + for( a = irc->b->accounts; a; a = a->next ) { if( a->ic ) - account_off( irc, a ); + account_off( irc->b, a ); else if( a->reconnect ) cancel_auto_reconnect( a ); } } - else if( ( a = account_get( irc, cmd[2] ) ) ) + else if( ( a = account_get( irc->b, cmd[2] ) ) ) { if( a->ic ) { - account_off( irc, a ); + account_off( irc->b, a ); } else if( a->reconnect ) { @@ -556,6 +558,7 @@ static void cmd_account( irc_t *irc, char **cmd ) } } +#if 0 static void cmd_add( irc_t *irc, char **cmd ) { account_t *a; @@ -1221,11 +1224,11 @@ static void cmd_transfer( irc_t *irc, char **cmd ) const command_t commands[] = { { "help", 0, cmd_help, 0 }, + { "account", 1, cmd_account, 0 }, #if 0 { "identify", 1, cmd_identify, 0 }, { "register", 1, cmd_register, 0 }, { "drop", 1, cmd_drop, 0 }, - { "account", 1, cmd_account, 0 }, { "add", 2, cmd_add, 0 }, { "info", 1, cmd_info, 0 }, { "rename", 2, cmd_rename, 0 }, diff --git a/unix.c b/unix.c index 647418fb..b916a4cc 100644 --- a/unix.c +++ b/unix.c @@ -61,7 +61,7 @@ int main( int argc, char *argv[] ) return( 1 ); b_main_init(); - //nogaim_init(); + nogaim_init(); srand( time( NULL ) ^ getpid() ); global.helpfile = g_strdup( HELP_FILE ); -- cgit v1.2.3 From e63507a356ac94085bcd00048b81d3ce2f27f287 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 1 Apr 2010 22:03:50 -0400 Subject: Synced the values of some old OPT_* flags with BEE_USER_*. --- protocols/bee.h | 4 ++-- protocols/bee_user.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/protocols/bee.h b/protocols/bee.h index c36a6b16..be14234b 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -44,8 +44,8 @@ void bee_free( bee_t *b ); typedef enum { - BEE_USER_ONLINE = 1, - BEE_USER_AWAY = 2, + BEE_USER_ONLINE = 1, /* Compatibility with old OPT_LOGGED_IN flag */ + BEE_USER_AWAY = 4, /* Compatibility with old OPT_AWAY flag */ } bee_user_flags_t; typedef struct bee_user diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 66e25faf..32022dd8 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -128,8 +128,7 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, old = g_memdup( bu, sizeof( bee_user_t ) ); /* TODO(wilmer): OPT_AWAY, or just state == NULL ? */ - bu->flags = ( flags & OPT_LOGGED_IN ? BEE_USER_ONLINE : 0 ) | - ( flags & OPT_AWAY ? BEE_USER_AWAY : 0 ); + bu->flags = flags; bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state ); bu->status_msg = g_strdup( message ); -- cgit v1.2.3 From fb117aee274bccfb6528288599ef81fe72191e12 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 1 Apr 2010 22:29:45 -0400 Subject: Cleaned lots of compiler warnings so I can get some signal again. --- bitlbee.h | 2 +- irc.c | 1 + irc_channel.c | 5 ++++- irc_commands.c | 2 +- irc_user.c | 5 ++++- protocols/account.c | 1 - protocols/bee.h | 2 +- protocols/bee_user.c | 4 +++- protocols/nogaim.c | 11 +++++++---- 9 files changed, 22 insertions(+), 11 deletions(-) diff --git a/bitlbee.h b/bitlbee.h index a40c48e6..24d09e2e 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -158,7 +158,7 @@ int bitlbee_inetd_init( void ); gboolean bitlbee_io_current_client_read( gpointer data, gint source, b_input_condition cond ); gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_condition cond ); -//void root_command_string( irc_t *irc, user_t *u, char *command, int flags ); +void root_command_string( irc_t *irc, char *command ); void root_command( irc_t *irc, char *command[] ); gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond ); diff --git a/irc.c b/irc.c index de9b4a04..5173dd9b 100644 --- a/irc.c +++ b/irc.c @@ -24,6 +24,7 @@ */ #include "bitlbee.h" +#include "ipc.h" GSList *irc_connection_list; diff --git a/irc_channel.c b/irc_channel.c index 3afdddee..db3ecd34 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -138,10 +138,13 @@ gboolean irc_channel_name_ok( const char *name ) /* Channel-type dependent functions, for control channels: */ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) { + char cmd[strlen(msg)+1]; + g_free( ic->irc->last_root_cmd ); ic->irc->last_root_cmd = g_strdup( ic->name ); - root_command_string( ic->irc, msg ); + strcpy( cmd, msg ); + root_command_string( ic->irc, cmd ); return TRUE; } diff --git a/irc_commands.c b/irc_commands.c index 03d05417..57268d07 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -38,7 +38,7 @@ static void irc_cmd_pass( irc_t *irc, char **cmd ) command last. (Possibly it won't send it at all if it turns out we don't require it, which will break this feature.) Try to identify using the given password. */ - /*return root_command( irc, send_cmd );*/ + return root_command( irc, send_cmd ); } /* Handling in pre-logged-in state, first see if this server is password-protected: */ diff --git a/irc_user.c b/irc_user.c index 8e20fd15..b3075c3b 100644 --- a/irc_user.c +++ b/irc_user.c @@ -120,10 +120,13 @@ gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ) /* User-type dependent functions, for root/NickServ: */ static gboolean root_privmsg( irc_user_t *iu, const char *msg ) { + char cmd[strlen(msg)+1]; + g_free( iu->irc->last_root_cmd ); iu->irc->last_root_cmd = g_strdup( iu->nick ); - root_command_string( iu->irc, msg ); + strcpy( cmd, msg ); + root_command_string( iu->irc, cmd ); return TRUE; } diff --git a/protocols/account.c b/protocols/account.c index a3c2e0d7..0bacea74 100644 --- a/protocols/account.c +++ b/protocols/account.c @@ -216,7 +216,6 @@ account_t *account_get( bee_t *bee, char *id ) void account_del( bee_t *bee, account_t *acc ) { account_t *a, *l = NULL; - struct chat *c, *nc; if( acc->ic ) /* Caller should have checked, accounts still in use can't be deleted. */ diff --git a/protocols/bee.h b/protocols/bee.h index be14234b..023c1644 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -90,6 +90,6 @@ int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ); G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); /* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ -G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ); +G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, guint32 flags, time_t sent_at ); #endif /* __BEE_H__ */ diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 32022dd8..2c11ca1d 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -91,8 +91,10 @@ int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ) buf = escape_html( msg ); msg = buf; } + else + buf = g_strdup( msg ); - st = bu->ic->acc->prpl->buddy_msg( bu->ic, bu->handle, msg, flags ); + st = bu->ic->acc->prpl->buddy_msg( bu->ic, bu->handle, buf, flags ); g_free( buf ); return st; diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 741bdb76..141ae9a3 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -37,8 +37,6 @@ #include "nogaim.h" #include "chat.h" -static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ); - GSList *connections; #ifdef WITH_PLUGINS @@ -474,6 +472,7 @@ struct imcb_ask_cb_data char *handle; }; +#if 0 static void imcb_ask_auth_cb_no( void *data ) { struct imcb_ask_cb_data *cbd = data; @@ -493,6 +492,7 @@ static void imcb_ask_auth_cb_yes( void *data ) g_free( cbd->handle ); g_free( cbd ); } +#endif void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname ) { @@ -515,6 +515,7 @@ void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *re } +#if 0 static void imcb_ask_add_cb_no( void *data ) { g_free( ((struct imcb_ask_cb_data*)data)->handle ); @@ -529,6 +530,7 @@ static void imcb_ask_add_cb_yes( void *data ) return imcb_ask_add_cb_no( data ); } +#endif void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname ) { @@ -596,6 +598,7 @@ struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) return c; #endif + return NULL; } void imcb_chat_free( struct groupchat *c ) @@ -782,9 +785,9 @@ void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char #endif } +#if 0 static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) { -#if 0 GList *i; /* Find the handle in the room userlist and shoot it */ @@ -800,10 +803,10 @@ static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) i = i->next; } -#endif return 0; } +#endif /* Misc. BitlBee stuff which shouldn't really be here */ -- cgit v1.2.3 From 231b08b07a2807881da1327408e33855370b7b95 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 1 Apr 2010 22:54:39 -0400 Subject: Show buddy online/offline status in the first channel. --- irc_im.c | 11 +++++++++++ protocols/bee_user.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/irc_im.c b/irc_im.c index 07b478fb..9a4ce176 100644 --- a/irc_im.c +++ b/irc_im.c @@ -75,6 +75,17 @@ static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu ) static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old ) { + irc_t *irc = bee->ui_data; + irc_channel_t *ic = irc->channels->data; /* For now, just pick the first channel. */ + + if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) ) + { + if( bu->flags & BEE_USER_ONLINE ) + irc_channel_add_user( ic, (irc_user_t*) bu->ui_data ); + else + irc_channel_del_user( ic, (irc_user_t*) bu->ui_data ); + } + return TRUE; } diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 2c11ca1d..cbcebe33 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -74,7 +74,7 @@ bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char { bee_user_t *bu = l->data; - if( bu->ic == ic && ic->acc->prpl->handle_cmp( bu->handle, handle ) ) + if( bu->ic == ic && ic->acc->prpl->handle_cmp( bu->handle, handle ) == 0 ) return bu; } -- cgit v1.2.3 From f012a9f0bb363cfcbdb6f2d563254ffba26b9fc8 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 1 Apr 2010 23:25:41 -0400 Subject: Can receive messages again. --- irc_im.c | 31 +++++++++++++++++ protocols/bee.h | 1 + protocols/bee_user.c | 95 +++++++--------------------------------------------- 3 files changed, 45 insertions(+), 82 deletions(-) diff --git a/irc_im.c b/irc_im.c index 9a4ce176..e13a9ab4 100644 --- a/irc_im.c +++ b/irc_im.c @@ -62,6 +62,9 @@ static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) iu->user = g_strdup( bu->handle ); } + if( set_getbool( &bee->set, "private" ) ) + iu->flags |= IRC_USER_PRIVATE; + iu->f = &irc_user_im_funcs; //iu->last_typing_notice = 0; @@ -89,10 +92,38 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old return TRUE; } +static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ) +{ + irc_t *irc = bee->ui_data; + irc_channel_t *ic = irc->channels->data; + irc_user_t *iu = (irc_user_t *) bu->ui_data; + char *dst, *prefix = NULL; + char *wrapped; + + if( iu->flags & IRC_USER_PRIVATE ) + { + dst = irc->user->nick; + } + else + { + dst = ic->name; + prefix = g_strdup_printf( "%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ) ); + } + + wrapped = word_wrap( msg, 425 ); + irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix ); + + g_free( wrapped ); + g_free( prefix ); + + return TRUE; +} + const struct bee_ui_funcs irc_ui_funcs = { bee_irc_user_new, bee_irc_user_free, bee_irc_user_status, + bee_irc_user_msg, }; diff --git a/protocols/bee.h b/protocols/bee.h index 023c1644..62c73e94 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -68,6 +68,7 @@ typedef struct bee_ui_funcs gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); gboolean (*user_free)( bee_t *bee, struct bee_user *bu ); gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old ); + gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ); } bee_ui_funcs_t; diff --git a/protocols/bee_user.c b/protocols/bee_user.c index cbcebe33..675d37c5 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -115,11 +115,11 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, } else { - if( set_getbool( &ic->bee->set, "debug" ) || g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "ignore" ) != 0 ) + if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "ignore" ) != 0 ) { - imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle ); - imcb_log( ic, "flags = %d, state = %s, message = %s", flags, - state ? state : "NULL", message ? message : "NULL" ); + imcb_log( ic, "imcb_buddy_status() for unknown handle %s:\n" + "flags = %d, state = %s, message = %s", handle, flags, + state ? state : "NULL", message ? message : "NULL" ); } return; @@ -141,54 +141,6 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, g_free( old->status ); g_free( old ); #if 0 - oa = u->away != NULL; - oo = u->online; - - g_free( u->away ); - g_free( u->status_msg ); - u->away = u->status_msg = NULL; - - if( ( flags & OPT_LOGGED_IN ) && !u->online ) - { - irc_spawn( ic->irc, u ); - u->online = 1; - } - else if( !( flags & OPT_LOGGED_IN ) && u->online ) - { - struct groupchat *c; - - irc_kill( ic->irc, u ); - u->online = 0; - - /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */ - for( c = ic->groupchats; c; c = c->next ) - remove_chat_buddy_silent( c, handle ); - } - - if( flags & OPT_AWAY ) - { - if( state && message ) - { - u->away = g_strdup_printf( "%s (%s)", state, message ); - } - else if( state ) - { - u->away = g_strdup( state ); - } - else if( message ) - { - u->away = g_strdup( message ); - } - else - { - u->away = g_strdup( "Away" ); - } - } - else - { - u->status_msg = g_strdup( message ); - } - /* LISPy... */ if( ( set_getbool( &ic->bee->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ ( u->online ) && /* Don't touch offline people */ @@ -215,53 +167,32 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) { -#if 0 bee_t *bee = ic->bee; char *wrapped; - user_t *u; + bee_user_t *bu; - u = user_findhandle( ic, handle ); + bu = bee_user_by_handle( bee, ic, handle ); - if( !u ) + if( !bu ) { char *h = set_getstr( &bee->set, "handle_unknown" ); if( g_strcasecmp( h, "ignore" ) == 0 ) { - if( set_getbool( &bee->set, "debug" ) ) - imcb_log( ic, "Ignoring message from unknown handle %s", handle ); - return; } else if( g_strncasecmp( h, "add", 3 ) == 0 ) { - int private = set_getbool( &bee->set, "private" ); - - if( h[3] ) - { - if( g_strcasecmp( h + 3, "_private" ) == 0 ) - private = 1; - else if( g_strcasecmp( h + 3, "_channel" ) == 0 ) - private = 0; - } - - imcb_add_buddy( ic, handle, NULL ); - u = user_findhandle( ic, handle ); - u->is_private = private; - } - else - { - imcb_log( ic, "Message from unknown handle %s:", handle ); - u = user_find( irc, irc->mynick ); + bu = bee_user_new( bee, ic, handle ); } } if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) strip_html( msg ); - - wrapped = word_wrap( msg, 425 ); - irc_msgfrom( irc, u->nick, wrapped ); - g_free( wrapped ); -#endif + + if( bee->ui->user_msg && bu ) + bee->ui->user_msg( bee, bu, msg, sent_at ); + else + imcb_log( ic, "Message from unknown handle %s:\n%s", handle, msg ); } -- cgit v1.2.3 From 1d3915951bfbcdfa1a7829a4082e90e154d4a486 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Apr 2010 20:18:24 -0400 Subject: Get full names properly. Handling of nick_source setting and imcb_nick_hint() is probably still broken. --- irc_im.c | 42 ++++++++++++++++++++++++++++++++++++++++++ irc_send.c | 38 ++++++++++++++++++++++++-------------- protocols/bee.h | 1 + protocols/bee_user.c | 1 - protocols/nogaim.c | 39 +++++++++------------------------------ 5 files changed, 76 insertions(+), 45 deletions(-) diff --git a/irc_im.c b/irc_im.c index e13a9ab4..812dcde1 100644 --- a/irc_im.c +++ b/irc_im.c @@ -119,9 +119,51 @@ static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, t return TRUE; } +static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) +{ + irc_user_t *iu = (irc_user_t *) bu->ui_data; + irc_t *irc = (irc_t *) bee->ui_data; + char *s; + + if( iu->fullname != iu->nick ) + g_free( iu->fullname ); + iu->fullname = g_strdup( bu->fullname ); + + /* Strip newlines (unlikely, but IRC-unfriendly so they must go) + TODO(wilmer): Do the same with away msgs again! */ + for( s = iu->fullname; *s; s ++ ) + if( isspace( *s ) ) *s = ' '; + + if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) ) + { + char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname ); + irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL ); + } + + s = set_getstr( &bu->ic->acc->set, "nick_source" ); + if( strcmp( s, "handle" ) != 0 ) + { + char *name = g_strdup( bu->fullname ); + + if( strcmp( s, "first_name" ) == 0 ) + { + int i; + for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {} + name[i] = '\0'; + } + + imcb_buddy_nick_hint( bu->ic, bu->handle, name ); + + g_free( name ); + } + + return TRUE; +} + const struct bee_ui_funcs irc_ui_funcs = { bee_irc_user_new, bee_irc_user_free, + bee_irc_user_fullname, bee_irc_user_status, bee_irc_user_msg, }; diff --git a/irc_send.c b/irc_send.c index 80d9b466..bbf84bd2 100644 --- a/irc_send.c +++ b/irc_send.c @@ -219,21 +219,31 @@ void irc_send_whois( irc_user_t *iu ) irc_send_num( irc, 311, "%s %s %s * :%s", iu->nick, iu->user, iu->host, iu->fullname ); - /* - if( u->ic ) - irc_send_num( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user, - u->ic->acc->server && *u->ic->acc->server ? u->ic->acc->server : "", - u->ic->acc->prpl->name ); + if( iu->bu ) + { + bee_user_t *bu = iu->bu; + + irc_send_num( irc, 312, "%s %s.%s :%s network", iu->nick, bu->ic->acc->user, + bu->ic->acc->server && *bu->ic->acc->server ? bu->ic->acc->server : "", + bu->ic->acc->prpl->name ); + + if( bu->status ) + { + if( bu->status_msg ) + irc_send_num( irc, 301, "%s :%s (%s)", iu->nick, bu->status, bu->status_msg ); + else + irc_send_num( irc, 301, "%s :%s", iu->nick, bu->status ); + } + + /* + if( u->status_msg ) + irc_send_num( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); + */ + } else - irc_send_num( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); - */ - - /* - if( u->away ) - irc_send_num( irc, 301, "%s :%s", u->nick, u->away ); - if( u->status_msg ) - irc_send_num( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); - */ + { + irc_send_num( irc, 312, "%s %s :%s", iu->nick, irc->root->host, IRCD_INFO " " BITLBEE_VERSION ); + } irc_send_num( irc, 318, "%s :End of /WHOIS list", iu->nick ); } diff --git a/protocols/bee.h b/protocols/bee.h index 62c73e94..61604265 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -67,6 +67,7 @@ typedef struct bee_ui_funcs { gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); gboolean (*user_free)( bee_t *bee, struct bee_user *bu ); + gboolean (*user_fullname)( bee_t *bee, bee_user_t *bu ); gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old ); gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ); } bee_ui_funcs_t; diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 675d37c5..20c760a9 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -168,7 +168,6 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) { bee_t *bee = ic->bee; - char *wrapped; bee_user_t *bu; bu = bee_user_by_handle( bee, ic, handle ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 141ae9a3..1e00d5ab 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -364,7 +364,6 @@ void imcb_ask( struct im_connection *ic, char *msg, void *data, void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group ) { bee_user_t *bu; - //char nick[MAX_NICK_LENGTH+1], *s; bee_t *bee = ic->bee; if( bee_user_by_handle( bee, ic, handle ) ) @@ -384,41 +383,21 @@ void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *g bu->group = g_strdup( group ); } -void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname ) +void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *fullname ) { -#if 0 - user_t *u = user_findhandle( ic, handle ); - char *set; + bee_t *bee = ic->bee; + bee_user_t *bu = bee_user_by_handle( bee, ic, handle ); - if( !u || !realname ) return; + if( !bu || !fullname ) return; - if( g_strcasecmp( u->realname, realname ) != 0 ) + if( strcmp( bu->fullname, fullname ) != 0 ) { - if( u->realname != u->nick ) g_free( u->realname ); - - u->realname = g_strdup( realname ); + g_free( bu->fullname ); + bu->fullname = g_strdup( fullname ); - if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->bee->set, "display_namechanges" ) ) - imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname ); + if( bee->ui->user_fullname ) + bee->ui->user_fullname( bee, bu ); } - - set = set_getstr( &ic->acc->set, "nick_source" ); - if( strcmp( set, "handle" ) != 0 ) - { - char *name = g_strdup( realname ); - - if( strcmp( set, "first_name" ) == 0 ) - { - int i; - for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {} - name[i] = '\0'; - } - - imcb_buddy_nick_hint( ic, handle, name ); - - g_free( name ); - } -#endif } void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group ) -- cgit v1.2.3 From 0b5cc72bc7b4192ff5b474b81038c299d03721f1 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Apr 2010 20:39:04 -0400 Subject: Send nickname change notifications when necessary. --- irc.h | 4 +++- irc_channel.c | 10 ++++++++-- irc_send.c | 6 ++++++ irc_user.c | 18 +++++++++++++++++- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/irc.h b/irc.h index b5aef53a..4a404b2a 100644 --- a/irc.h +++ b/irc.h @@ -178,6 +178,7 @@ irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ); int irc_channel_free( irc_channel_t *ic ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); +gboolean irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); gboolean irc_channel_name_ok( const char *name ); @@ -197,12 +198,13 @@ void irc_send_whois( irc_user_t *iu ); void irc_send_who( irc_t *irc, GSList *l, const char *channel ); void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix ); void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg ); +void irc_send_nick( irc_user_t *iu, const char *new ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); int irc_user_free( irc_t *irc, const char *nick ); irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ); -int irc_user_rename( irc_t *irc, const char *old, const char *new ); +int irc_user_set_nick( irc_t *irc, const char *old, const char *new ); gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ); #endif diff --git a/irc_channel.c b/irc_channel.c index db3ecd34..27216f3c 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -81,7 +81,7 @@ int irc_channel_free( irc_channel_t *ic ) int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) { - if( g_slist_find( ic->users, iu ) != NULL ) + if( !irc_channel_has_user( ic, iu ) ) return 0; ic->users = g_slist_insert_sorted( ic->users, iu, irc_user_cmp ); @@ -97,7 +97,7 @@ int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) { - if( g_slist_find( ic->users, iu ) == NULL ) + if( !irc_channel_has_user( ic, iu ) ) return 0; ic->users = g_slist_remove( ic->users, iu ); @@ -111,6 +111,12 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) return 1; } +/* Currently a fairly stupid one-liner but I fear it's going to get worse. :-) */ +gboolean irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ) +{ + return g_slist_find( ic->users, iu ) != NULL; +} + int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *iu ) { g_free( ic->topic ); diff --git a/irc_send.c b/irc_send.c index bbf84bd2..89b10020 100644 --- a/irc_send.c +++ b/irc_send.c @@ -310,3 +310,9 @@ void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const irc_write( iu->irc, ":%s!%s@%s %s %s :%s", iu->nick, iu->user, iu->host, type, dst, msg ); } + +void irc_send_nick( irc_user_t *iu, const char *new ) +{ + irc_write( iu->irc, ":%s!%s@%s NICK %s", + iu->nick, iu->user, iu->host, new ); +} diff --git a/irc_user.c b/irc_user.c index b3075c3b..7f356d8d 100644 --- a/irc_user.c +++ b/irc_user.c @@ -82,15 +82,31 @@ irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ) return NULL; } -int irc_user_rename( irc_t *irc, const char *old, const char *new ) +int irc_user_set_nick( irc_t *irc, const char *old, const char *new ) { irc_user_t *iu = irc_user_by_name( irc, old ); char key[strlen(new)+1]; + GSList *cl; strcpy( key, new ); if( iu == NULL || !nick_lc( key ) || irc_user_by_name( irc, new ) ) return 0; + for( cl = irc->channels; cl; cl = cl->next ) + { + irc_channel_t *ic = cl->data; + + /* Send a NICK update if we're renaming our user, or someone + who's in the same channel like our user. */ + if( iu == irc->user || + ( irc_channel_has_user( ic, irc->user ) && + irc_channel_has_user( ic, iu ) ) ) + { + irc_send_nick( iu, new ); + break; + } + } + irc->users = g_slist_remove( irc->users, iu ); g_hash_table_remove( irc->nick_user_hash, iu->key ); -- cgit v1.2.3 From 57c96f7be2511a0a50015512dc03a30ba0923862 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Apr 2010 21:00:02 -0400 Subject: Restored the rename command. --- irc.h | 2 +- irc_channel.c | 2 +- irc_user.c | 6 +++--- root_commands.c | 54 +++++++++++++++++++++++------------------------------- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/irc.h b/irc.h index 4a404b2a..d9cc422a 100644 --- a/irc.h +++ b/irc.h @@ -204,7 +204,7 @@ void irc_send_nick( irc_user_t *iu, const char *new ); irc_user_t *irc_user_new( irc_t *irc, const char *nick ); int irc_user_free( irc_t *irc, const char *nick ); irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ); -int irc_user_set_nick( irc_t *irc, const char *old, const char *new ); +int irc_user_set_nick( irc_user_t *iu, const char *new ); gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ); #endif diff --git a/irc_channel.c b/irc_channel.c index 27216f3c..27f33619 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -81,7 +81,7 @@ int irc_channel_free( irc_channel_t *ic ) int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) { - if( !irc_channel_has_user( ic, iu ) ) + if( irc_channel_has_user( ic, iu ) ) return 0; ic->users = g_slist_insert_sorted( ic->users, iu, irc_user_cmp ); diff --git a/irc_user.c b/irc_user.c index 7f356d8d..8ad20b54 100644 --- a/irc_user.c +++ b/irc_user.c @@ -82,9 +82,9 @@ irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ) return NULL; } -int irc_user_set_nick( irc_t *irc, const char *old, const char *new ) +int irc_user_set_nick( irc_user_t *iu, const char *new ) { - irc_user_t *iu = irc_user_by_name( irc, old ); + irc_t *irc = iu->irc; char key[strlen(new)+1]; GSList *cl; @@ -99,7 +99,7 @@ int irc_user_set_nick( irc_t *irc, const char *old, const char *new ) /* Send a NICK update if we're renaming our user, or someone who's in the same channel like our user. */ if( iu == irc->user || - ( irc_channel_has_user( ic, irc->user ) && + ( ( ic->flags & IRC_CHANNEL_JOINED ) && irc_channel_has_user( ic, iu ) ) ) { irc_send_nick( iu, new ); diff --git a/root_commands.c b/root_commands.c index 75f3af81..7e0b16d3 100644 --- a/root_commands.c +++ b/root_commands.c @@ -647,65 +647,55 @@ static void cmd_info( irc_t *irc, char **cmd ) ic->acc->prpl->get_info( ic, cmd[2] ); } } +#endif static void cmd_rename( irc_t *irc, char **cmd ) { - user_t *u; + irc_user_t *iu; - if( g_strcasecmp( cmd[1], irc->nick ) == 0 ) - { - irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] ); - } - else if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) + iu = irc_user_by_name( irc, cmd[1] ); + + if( iu == NULL ) { - if( strchr( CTYPES, cmd[2][0] ) && nick_ok( cmd[2] + 1 ) ) - { - u = user_find( irc, irc->nick ); - - irc_part( irc, u, irc->channel ); - g_free( irc->channel ); - irc->channel = g_strdup( cmd[2] ); - irc_join( irc, u, irc->channel ); - - if( strcmp( cmd[0], "set_rename" ) != 0 ) - set_setstr( &irc->set, "control_channel", cmd[2] ); - } + irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); } - else if( user_find( irc, cmd[2] ) && ( nick_cmp( cmd[1], cmd[2] ) != 0 ) ) + else if( iu == irc->user ) { - irc_usermsg( irc, "Nick `%s' already exists", cmd[2] ); + irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] ); } else if( !nick_ok( cmd[2] ) ) { irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] ); } - else if( !( u = user_find( irc, cmd[1] ) ) ) + else if( irc_user_by_name( irc, cmd[2] ) ) { - irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); + irc_usermsg( irc, "Nick `%s' already exists", cmd[2] ); } else { - user_rename( irc, cmd[1], cmd[2] ); - irc_write( irc, ":%s!%s@%s NICK %s", cmd[1], u->user, u->host, cmd[2] ); - if( g_strcasecmp( cmd[1], irc->mynick ) == 0 ) + if( !irc_user_set_nick( iu, cmd[2] ) ) + { + irc_usermsg( irc, "Error while changing nick" ); + return; + } + + if( iu == irc->root ) { - g_free( irc->mynick ); - irc->mynick = g_strdup( cmd[2] ); - /* If we're called internally (user did "set root_nick"), let's not go O(INF). :-) */ if( strcmp( cmd[0], "set_rename" ) != 0 ) - set_setstr( &irc->set, "root_nick", cmd[2] ); + set_setstr( &irc->b->set, "root_nick", cmd[2] ); } - else if( u->send_handler == buddy_send_handler ) + else if( iu->bu ) { - nick_set( u->ic->acc, u->handle, cmd[2] ); + nick_set( iu->bu->ic->acc, iu->bu->handle, cmd[2] ); } irc_usermsg( irc, "Nick successfully changed" ); } } +#if 0 char *set_eval_root_nick( set_t *set, char *new_nick ) { irc_t *irc = set->data; @@ -1231,7 +1221,9 @@ const command_t commands[] = { { "drop", 1, cmd_drop, 0 }, { "add", 2, cmd_add, 0 }, { "info", 1, cmd_info, 0 }, +#endif { "rename", 2, cmd_rename, 0 }, +#if 0 { "remove", 1, cmd_remove, 0 }, { "block", 1, cmd_block, 0 }, { "allow", 1, cmd_allow, 0 }, -- cgit v1.2.3 From 1f92a5851e0e3b1730e940980f2b0122c506c724 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Apr 2010 03:27:50 +0100 Subject: Restore the storage module. --- Makefile | 2 +- irc.c | 13 ++++++++++++ irc.h | 1 + root_commands.c | 66 +++++++++++++-------------------------------------------- storage_xml.c | 18 +++++++++------- unix.c | 2 -- 6 files changed, 40 insertions(+), 62 deletions(-) diff --git a/Makefile b/Makefile index 5e91b93b..1ea3deec 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # Program variables #objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) -objects = bitlbee.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o +objects = bitlbee.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o storage.o $(STORAGE_OBJS) headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols diff --git a/irc.c b/irc.c index 5173dd9b..4824d0ac 100644 --- a/irc.c +++ b/irc.c @@ -260,6 +260,19 @@ static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) return( TRUE ); } +/* USE WITH CAUTION! + Sets pass without checking */ +void irc_setpass (irc_t *irc, const char *pass) +{ + g_free (irc->password); + + if (pass) { + irc->password = g_strdup (pass); + } else { + irc->password = NULL; + } +} + static char **irc_splitlines( char *buffer ); void irc_process( irc_t *irc ) diff --git a/irc.h b/irc.h index d9cc422a..8a40541c 100644 --- a/irc.h +++ b/irc.h @@ -159,6 +159,7 @@ extern GSList *irc_connection_list; irc_t *irc_new( int fd ); void irc_abort( irc_t *irc, int immed, char *format, ... ) G_GNUC_PRINTF( 3, 4 ); void irc_free( irc_t *irc ); +void irc_setpass (irc_t *irc, const char *pass); void irc_process( irc_t *irc ); char **irc_parse_line( char *line ); diff --git a/root_commands.c b/root_commands.c index 7e0b16d3..73670d3a 100644 --- a/root_commands.c +++ b/root_commands.c @@ -135,7 +135,6 @@ static void cmd_help( irc_t *irc, char **cmd ) } } -#if 0 static void cmd_account( irc_t *irc, char **cmd ); static void cmd_identify( irc_t *irc, char **cmd ) @@ -161,7 +160,7 @@ static void cmd_identify( irc_t *irc, char **cmd ) irc_setpass( irc, cmd[1] ); irc->status |= USTATUS_IDENTIFIED; irc_umode_set( irc, "+R", 1 ); - if( set_getbool( &irc->set, "auto_connect" ) ) + if( set_getbool( &irc->b->set, "auto_connect" ) ) cmd_account( irc, account_on ); break; case STORAGE_OTHER_ERROR: @@ -201,7 +200,7 @@ static void cmd_drop( irc_t *irc, char **cmd ) { storage_status_t status; - status = storage_remove (irc->nick, cmd[1]); + status = storage_remove (irc->user->nick, cmd[1]); switch (status) { case STORAGE_NO_SUCH_USER: irc_usermsg( irc, "That account does not exist" ); @@ -213,14 +212,23 @@ static void cmd_drop( irc_t *irc, char **cmd ) irc_setpass( irc, NULL ); irc->status &= ~USTATUS_IDENTIFIED; irc_umode_set( irc, "-R", 1 ); - irc_usermsg( irc, "Account `%s' removed", irc->nick ); + irc_usermsg( irc, "Account `%s' removed", irc->user->nick ); break; default: irc_usermsg( irc, "Error: `%d'", status ); break; } } -#endif + +static void cmd_save( irc_t *irc, char **cmd ) +{ + if( ( irc->status & USTATUS_IDENTIFIED ) == 0 ) + irc_usermsg( irc, "Please create an account first" ); + else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK ) + irc_usermsg( irc, "Configuration saved" ); + else + irc_usermsg( irc, "Configuration could not be saved!" ); +} struct cmd_account_del_data { @@ -908,16 +916,6 @@ static void cmd_set( irc_t *irc, char **cmd ) cmd_set_real( irc, cmd, NULL, NULL ); } -static void cmd_save( irc_t *irc, char **cmd ) -{ - if( ( irc->status & USTATUS_IDENTIFIED ) == 0 ) - irc_usermsg( irc, "Please create an account first" ); - else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK ) - irc_usermsg( irc, "Configuration saved" ); - else - irc_usermsg( irc, "Configuration could not be saved!" ); -} - static void cmd_blist( irc_t *irc, char **cmd ) { int online = 0, away = 0, offline = 0; @@ -983,34 +981,6 @@ static void cmd_blist( irc_t *irc, char **cmd ) irc_usermsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline ); } -static void cmd_nick( irc_t *irc, char **cmd ) -{ - account_t *a; - - if( !cmd[1] || !( a = account_get( irc, cmd[1] ) ) ) - { - irc_usermsg( irc, "Invalid account"); - } - else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) ) - { - irc_usermsg( irc, "That account is not on-line" ); - } - else if ( !cmd[2] ) - { - irc_usermsg( irc, "Your name is `%s'" , a->ic->displayname ? a->ic->displayname : "NULL" ); - } - else if ( !a->prpl->set_my_name ) - { - irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); - } - else - { - irc_usermsg( irc, "Setting your name to `%s'", cmd[2] ); - - a->prpl->set_my_name( a->ic, cmd[2] ); - } -} - static void cmd_qlist( irc_t *irc, char **cmd ) { query_t *q = irc->queries; @@ -1031,12 +1001,6 @@ static void cmd_qlist( irc_t *irc, char **cmd ) irc_usermsg( irc, "%d, BitlBee: %s", num, q->question ); } -static void cmd_join_chat( irc_t *irc, char **cmd ) -{ - irc_usermsg( irc, "This command is now obsolete. " - "Please try the `chat' command instead." ); -} - static set_t **cmd_chat_set_findhead( irc_t *irc, char *id ) { struct chat *c; @@ -1215,10 +1179,11 @@ static void cmd_transfer( irc_t *irc, char **cmd ) const command_t commands[] = { { "help", 0, cmd_help, 0 }, { "account", 1, cmd_account, 0 }, -#if 0 { "identify", 1, cmd_identify, 0 }, { "register", 1, cmd_register, 0 }, { "drop", 1, cmd_drop, 0 }, + { "save", 0, cmd_save, 0 }, +#if 0 { "add", 2, cmd_add, 0 }, { "info", 1, cmd_info, 0 }, #endif @@ -1227,7 +1192,6 @@ const command_t commands[] = { { "remove", 1, cmd_remove, 0 }, { "block", 1, cmd_block, 0 }, { "allow", 1, cmd_allow, 0 }, - { "save", 0, cmd_save, 0 }, { "set", 0, cmd_set, 0 }, { "yes", 0, cmd_yesno, 0 }, { "no", 0, cmd_yesno, 0 }, diff --git a/storage_xml.c b/storage_xml.c index b6745c75..b81e1d0c 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -146,7 +146,7 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) && arc_decode( pass_cr, pass_len, &password, xd->given_pass ) ) { - xd->current_account = account_add( irc, prpl, handle, password ); + xd->current_account = account_add( irc->b, prpl, handle, password ); if( server ) set_setstr( &xd->current_account->set, "server", server ); if( autoconnect ) @@ -180,7 +180,7 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na else if( xd->current_account != NULL ) xd->current_set_head = &xd->current_account->set; else - xd->current_set_head = &xd->irc->set; + xd->current_set_head = &xd->irc->b->set; xd->current_setting = g_strdup( setting ); } @@ -214,7 +214,7 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na if( xd->current_account && handle && channel ) { - xd->current_chat = chat_add( xd->irc, xd->current_account, handle, channel ); + //xd->current_chat = chat_add( xd->irc, xd->current_account, handle, channel ); } else { @@ -352,7 +352,7 @@ static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const ch static storage_status_t xml_load( irc_t *irc, const char *password ) { - return xml_load_real( irc, irc->nick, password, XML_PASS_UNKNOWN ); + return xml_load_real( irc, irc->user->nick, password, XML_PASS_UNKNOWN ); } static storage_status_t xml_check_pass( const char *my_nick, const char *password ) @@ -395,7 +395,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) md5_byte_t pass_md5[21]; md5_state_t md5_state; - path2 = g_strdup( irc->nick ); + path2 = g_strdup( irc->user->nick ); nick_lc( path2 ); g_snprintf( path, sizeof( path ) - 2, "%s%s%s", global.conf->configdir, path2, ".xml" ); g_free( path2 ); @@ -421,17 +421,17 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) /* Save the hash in base64-encoded form. */ pass_buf = base64_encode( pass_md5, 21 ); - if( !xml_printf( fd, 0, "\n", irc->nick, pass_buf, XML_FORMAT_VERSION ) ) + if( !xml_printf( fd, 0, "\n", irc->user->nick, pass_buf, XML_FORMAT_VERSION ) ) goto write_error; g_free( pass_buf ); - for( set = irc->set; set; set = set->next ) + for( set = irc->b->set; set; set = set->next ) if( set->value ) if( !xml_printf( fd, 1, "%s\n", set->key, set->value ) ) goto write_error; - for( acc = irc->accounts; acc; acc = acc->next ) + for( acc = irc->b->accounts; acc; acc = acc->next ) { unsigned char *pass_cr; char *pass_b64; @@ -469,6 +469,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) if( g_hash_table_find( acc->nicks, xml_save_nick, & fd ) ) goto write_error; +#if 0 for( c = irc->chatrooms; c; c = c->next ) { if( c->acc != acc ) @@ -487,6 +488,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) if( !xml_printf( fd, 2, "\n" ) ) goto write_error; } +#endif if( !xml_printf( fd, 1, "\n" ) ) goto write_error; diff --git a/unix.c b/unix.c index b916a4cc..7088d0f8 100644 --- a/unix.c +++ b/unix.c @@ -114,14 +114,12 @@ int main( int argc, char *argv[] ) } } - /* global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); if( global.storage == NULL ) { log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); return( 1 ); } - */ /* Catch some signals to tell the user what's happening before quitting */ memset( &sig, 0, sizeof( sig ) ); -- cgit v1.2.3 From 17a6ee93f4fbefe8b4356d884fdd95f4e72ce8cc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Apr 2010 16:37:06 +0200 Subject: Including DCC stuff again, with a wonderful extra layer of abstraction. Some hooks are missing so sending files doesn't work yet. Receiving also still seems to have some issues. On the plus side, at least the MSN/Jabber modules work again. --- Makefile | 2 +- dcc.c | 69 ++++++++++------------------------------- dcc.h | 9 +++--- irc_im.c | 33 +++++++++++++++++++- protocols/Makefile | 2 +- protocols/bee.h | 5 +++ protocols/bee_ft.c | 66 +++++++++++++++++++++++++++++++++++++++ protocols/ft.h | 6 ++-- protocols/jabber/iq.c | 4 +-- protocols/jabber/jabber.c | 2 +- protocols/jabber/jabber_util.c | 8 ++--- protocols/jabber/s5bytestream.c | 14 ++++----- protocols/jabber/si.c | 10 +++--- protocols/msn/Makefile | 2 +- protocols/msn/msn.c | 4 ++- protocols/msn/msn_util.c | 3 +- protocols/msn/sb.c | 3 ++ protocols/nogaim.c | 2 +- 18 files changed, 157 insertions(+), 87 deletions(-) create mode 100644 protocols/bee_ft.c diff --git a/Makefile b/Makefile index 1ea3deec..8a3b3ef6 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # Program variables #objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) -objects = bitlbee.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o storage.o $(STORAGE_OBJS) +objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o storage.o $(STORAGE_OBJS) headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols diff --git a/dcc.c b/dcc.c index 558d923a..e97b822f 100644 --- a/dcc.c +++ b/dcc.c @@ -60,55 +60,16 @@ unsigned int local_transfer_id=1; */ unsigned int receivedchunks=0, receiveddata=0; -static void dcc_finish( file_transfer_t *file ); -static void dcc_close( file_transfer_t *file ); +void dcc_finish( file_transfer_t *file ); +void dcc_close( file_transfer_t *file ); gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); -int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); -gboolean dccs_recv_start( file_transfer_t *ft ); +int dccs_send_request( struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr ); gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); gboolean dccs_recv_write_request( file_transfer_t *ft ); gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ); gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ); -/* As defined in ft.h */ -file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) -{ - user_t *u = user_findhandle( ic, handle ); - /* one could handle this more intelligent like imcb_buddy_msg. - * can't call it directly though cause it does some wrapping. - * Maybe give imcb_buddy_msg a parameter NO_WRAPPING? */ - if (!u) return NULL; - - return dccs_send_start( ic, u->nick, file_name, file_size ); -}; - -/* As defined in ft.h */ -void imcb_file_canceled( file_transfer_t *file, char *reason ) -{ - if( file->canceled ) - file->canceled( file, reason ); - - dcc_close( file ); -} - -/* As defined in ft.h */ -gboolean imcb_file_recv_start( file_transfer_t *ft ) -{ - return dccs_recv_start( ft ); -} - -/* As defined in ft.h */ -void imcb_file_finished( file_transfer_t *file ) -{ - dcc_file_transfer_t *df = file->priv; - - if( file->bytes_transferred >= file->file_size ) - dcc_finish( file ); - else - df->proto_finished = TRUE; -} - -dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic ) +dcc_file_transfer_t *dcc_alloc_transfer( const char *file_name, size_t file_size, struct im_connection *ic ) { file_transfer_t *file = g_new0( file_transfer_t, 1 ); dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1 ); @@ -123,10 +84,11 @@ dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, stru } /* This is where the sending magic starts... */ -file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ) +file_transfer_t *dccs_send_start( struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size ) { file_transfer_t *file; dcc_file_transfer_t *df; + irc_t *irc = (irc_t *) ic->bee->ui_data; struct sockaddr_storage saddr; char *errmsg; char host[HOST_NAME_MAX]; @@ -149,20 +111,20 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha file->status = FT_STATUS_LISTENING; - if( !dccs_send_request( df, user_nick, &saddr ) ) + if( !dccs_send_request( df, iu, &saddr ) ) return NULL; /* watch */ df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); - df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file ); + irc->file_transfers = g_slist_prepend( irc->file_transfers, file ); df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); imcb_log( ic, "File transfer request from %s for %s (%zd kb).\n" "Accept the file transfer if you'd like the file. If you don't, " "issue the 'transfers reject' command.", - user_nick, file_name, file_size / 1024 ); + iu->nick, file_name, file_size / 1024 ); return file; } @@ -215,7 +177,7 @@ gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ) return dcc_abort( df , msg ": %s", strerror( errno ) ); /* Creates the "DCC SEND" line and sends it to the server */ -int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ) +int dccs_send_request( struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr ) { char ipaddr[INET6_ADDRSTRLEN]; const void *netaddr; @@ -249,8 +211,7 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc cmd = g_strdup_printf( "\001DCC SEND %s %s %u %zu\001", df->ft->file_name, ipaddr, port, df->ft->file_size ); - if ( !irc_msgfrom( df->ic->irc, user_nick, cmd ) ) - return dcc_abort( df, "Couldn't send `DCC SEND' message to %s.", user_nick ); + irc_send_msg_raw( iu, "PRIVMSG", iu->irc->user->nick, cmd ); g_free( cmd ); @@ -495,9 +456,10 @@ gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_l /* * Cleans up after a transfer. */ -static void dcc_close( file_transfer_t *file ) +void dcc_close( file_transfer_t *file ) { dcc_file_transfer_t *df = file->priv; + irc_t *irc = (irc_t *) df->ic->bee->ui_data; if( file->free ) file->free( file ); @@ -513,7 +475,7 @@ static void dcc_close( file_transfer_t *file ) if( df->progress_timeout ) b_event_remove( df->progress_timeout ); - df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file ); + irc->file_transfers = g_slist_remove( irc->file_transfers, file ); g_free( df ); g_free( file->file_name ); @@ -543,6 +505,7 @@ void dcc_finish( file_transfer_t *file ) */ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) { + irc_t *irc = (irc_t *) ic->bee->ui_data; char *pattern = "SEND" " (([^\"][^ ]*)|\"(([^\"]|\\\")*)\")" " (([0-9]*)|([^ ]*))" @@ -626,7 +589,7 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) freeaddrinfo( rp ); g_free( input ); - df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, ft ); + irc->file_transfers = g_slist_prepend( irc->file_transfers, ft ); return ft; } diff --git a/dcc.h b/dcc.h index cc784478..c0cf6ccc 100644 --- a/dcc.h +++ b/dcc.h @@ -94,11 +94,12 @@ typedef struct dcc_file_transfer { int proto_finished; } dcc_file_transfer_t; -file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ); - +file_transfer_t *dccs_send_start( struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size ); void dcc_canceled( file_transfer_t *file, char *reason ); - gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_size ); - file_transfer_t *dcc_request( struct im_connection *ic, char *line ); +void dcc_finish( file_transfer_t *file ); +void dcc_close( file_transfer_t *file ); +gboolean dccs_recv_start( file_transfer_t *ft ); + #endif diff --git a/irc_im.c b/irc_im.c index 812dcde1..26d826bd 100644 --- a/irc_im.c +++ b/irc_im.c @@ -24,7 +24,7 @@ */ #include "bitlbee.h" - +#include "dcc.h" /* IM->IRC callbacks */ @@ -160,12 +160,43 @@ static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) return TRUE; } +/* File transfers */ +static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ) +{ + return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size ); +} + +gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft ) +{ + return dccs_recv_start( ft ); +} + +void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft ) +{ + return dcc_close( ft ); +} + +void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file ) +{ + dcc_file_transfer_t *df = file->priv; + + if( file->bytes_transferred >= file->file_size ) + dcc_finish( file ); + else + df->proto_finished = TRUE; +} + const struct bee_ui_funcs irc_ui_funcs = { bee_irc_user_new, bee_irc_user_free, bee_irc_user_fullname, bee_irc_user_status, bee_irc_user_msg, + + bee_irc_ft_in_start, + bee_irc_ft_out_start, + bee_irc_ft_close, + bee_irc_ft_finished, }; diff --git a/protocols/Makefile b/protocols/Makefile index 4d461088..46c73559 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -9,7 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -objects = account.o bee.o bee_user.o nogaim.o +objects = account.o bee.o bee_ft.o bee_user.o nogaim.o # [SH] The next two lines should contain the directory name (in $(subdirs)) diff --git a/protocols/bee.h b/protocols/bee.h index 61604265..6f896c51 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -70,6 +70,11 @@ typedef struct bee_ui_funcs gboolean (*user_fullname)( bee_t *bee, bee_user_t *bu ); gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old ); gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ); + + struct file_transfer* (*ft_in_start)( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ); + gboolean (*ft_out_start)( struct im_connection *ic, struct file_transfer *ft ); + void (*ft_close)( struct im_connection *ic, struct file_transfer *ft ); + void (*ft_finished)( struct im_connection *ic, struct file_transfer *ft ); } bee_ui_funcs_t; diff --git a/protocols/bee_ft.c b/protocols/bee_ft.c new file mode 100644 index 00000000..1026eab3 --- /dev/null +++ b/protocols/bee_ft.c @@ -0,0 +1,66 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway * +* * +* Copyright 2010 Wilmer van der Gaast * +\********************************************************************/ + +/* + 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 "ft.h" + +file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) +{ + bee_t *bee = ic->bee; + bee_user_t *bu = bee_user_by_handle( bee, ic, handle ); + + if( bee->ui->ft_in_start ) + return bee->ui->ft_in_start( bee, bu, file_name, file_size ); + else + return NULL; +} + +gboolean imcb_file_recv_start( struct im_connection *ic, file_transfer_t *ft ) +{ + bee_t *bee = ic->bee; + + if( bee->ui->ft_out_start ) + return bee->ui->ft_out_start( ic, ft ); + else + return FALSE; +} + +void imcb_file_canceled( struct im_connection *ic, file_transfer_t *file, char *reason ) +{ + bee_t *bee = ic->bee; + + if( file->canceled ) + file->canceled( file, reason ); + + if( bee->ui->ft_close ) + bee->ui->ft_close( ic, file ); +} + +void imcb_file_finished( struct im_connection *ic, file_transfer_t *file ) +{ + bee_t *bee = ic->bee; + + if( bee->ui->ft_finished ) + bee->ui->ft_finished( ic, file ); +} diff --git a/protocols/ft.h b/protocols/ft.h index 1155f06f..c1ee2b49 100644 --- a/protocols/ft.h +++ b/protocols/ft.h @@ -167,9 +167,9 @@ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *user_nick * This should be called by a protocol when the transfer is canceled. Note that * the canceled() and free() callbacks given in file will be called by this function. */ -void imcb_file_canceled( file_transfer_t *file, char *reason ); +void imcb_file_canceled( struct im_connection *ic, file_transfer_t *file, char *reason ); -gboolean imcb_file_recv_start( file_transfer_t *ft ); +gboolean imcb_file_recv_start( struct im_connection *ic, file_transfer_t *ft ); -void imcb_file_finished( file_transfer_t *file ); +void imcb_file_finished( struct im_connection *ic, file_transfer_t *file ); #endif diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index f5fbdc13..bdedeb08 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -391,7 +391,7 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node * { if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) ) { - if( initial || imcb_find_buddy( ic, jid ) == NULL ) + if( initial || bee_user_by_handle( ic->bee, ic, jid ) == NULL ) imcb_add_buddy( ic, jid, ( group && group->text_len ) ? group->text : NULL ); @@ -589,7 +589,7 @@ static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct ( s = xt_find_attr( node, "type" ) ) && strcmp( s, "result" ) == 0 ) { - if( imcb_find_buddy( ic, jid ) == NULL ) + if( bee_user_by_handle( ic->bee, ic, jid ) == NULL ) imcb_add_buddy( ic, jid, NULL ); } else diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 956769b7..acad525e 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -266,7 +266,7 @@ static void jabber_logout( struct im_connection *ic ) struct jabber_data *jd = ic->proto_data; while( jd->filetransfers ) - imcb_file_canceled( ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" ); + imcb_file_canceled( ic, ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" ); while( jd->streamhosts ) { diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index bd2fbe8c..6f58f124 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -278,8 +278,7 @@ static void jabber_buddy_ask_yes( void *data ) presence_send_request( bla->ic, bla->handle, "subscribed" ); - if( imcb_find_buddy( bla->ic, bla->handle ) == NULL ) - imcb_ask_add( bla->ic, bla->handle, NULL ); + imcb_ask_add( bla->ic, bla->handle, NULL ); g_free( bla->handle ); g_free( bla ); @@ -461,7 +460,7 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_, } if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && - ( bare_exists || imcb_find_buddy( ic, jid ) ) ) + ( bare_exists || bee_user_by_handle( ic->bee, ic, jid ) ) ) { *s = '/'; bud = jabber_buddy_add( ic, jid ); @@ -482,7 +481,8 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_, if( bud == NULL ) /* No match. Create it now? */ - return ( ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid_ ) ) ? + return ( ( flags & GET_BUDDY_CREAT ) && + bee_user_by_handle( ic->bee, ic, jid_ ) ) ? jabber_buddy_add( ic, jid_ ) : NULL; else if( bud->resource && ( flags & GET_BUDDY_EXACT ) ) /* We want an exact match, so in thise case there shouldn't be a /resource. */ diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 58a6c2e4..4896e380 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -565,7 +565,7 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); xt_free_node( reply ); - imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); + imcb_file_canceled( tf->ic, tf->ft, "couldn't connect to any streamhosts" ); bt->tf->watch_in = 0; /* MUST always return FALSE! */ @@ -602,7 +602,7 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) xt_add_attr( reply, "id", tf->iq_id ); if( !jabber_write_packet( tf->ic, reply ) ) - imcb_file_canceled( tf->ft, "Error transmitting bytestream response" ); + imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream response" ); xt_free_node( reply ); } @@ -642,7 +642,7 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) tf->bytesread += ret; if( tf->bytesread >= tf->ft->file_size ) - imcb_file_finished( tf->ft ); + imcb_file_finished( tf->ic, tf->ft ); tf->ft->write( tf->ft, tf->ft->buffer, ret ); @@ -658,7 +658,7 @@ gboolean jabber_bs_recv_write_request( file_transfer_t *ft ) if( tf->watch_in ) { - imcb_file_canceled( ft, "BUG in jabber file transfer: write_request called when already watching for input" ); + imcb_file_canceled( tf->ic, ft, "BUG in jabber file transfer: write_request called when already watching for input" ); return FALSE; } @@ -704,7 +704,7 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l return jabber_bs_abort( bt, "send() sent %d instead of %d (send buffer too big!)", ret, len ); if( tf->byteswritten >= ft->file_size ) - imcb_file_finished( ft ); + imcb_file_finished( tf->ic, ft ); else bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); @@ -1004,7 +1004,7 @@ gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts jabber_cache_add( tf->ic, iq, jabber_bs_send_handle_reply ); if( !jabber_write_packet( tf->ic, iq ) ) - imcb_file_canceled( tf->ft, "Error transmitting bytestream request" ); + imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream request" ); return TRUE; } @@ -1019,7 +1019,7 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) error ); if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */ - imcb_file_canceled( tf->ft, error ); + imcb_file_canceled( tf->ic, tf->ft, error ); /* MUST always return FALSE! */ return FALSE; diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index bfb64f11..58c0e17f 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -90,11 +90,11 @@ int jabber_si_check_features( struct jabber_transfer *tf, GSList *features ) { } if( !foundft ) - imcb_file_canceled( tf->ft, "Buddy's client doesn't feature file transfers" ); + imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature file transfers" ); else if( !foundbt ) - imcb_file_canceled( tf->ft, "Buddy's client doesn't feature byte streams (required)" ); + imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature byte streams (required)" ); else if( !foundsi ) - imcb_file_canceled( tf->ft, "Buddy's client doesn't feature stream initiation (required)" ); + imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature stream initiation (required)" ); return foundft && foundbt && foundsi; } @@ -108,7 +108,7 @@ void jabber_si_transfer_start( struct jabber_transfer *tf ) { jabber_si_send_request( tf->ic, tf->bud->full_jid, tf ); /* and start the receive logic */ - imcb_file_recv_start( tf->ft ); + imcb_file_recv_start( tf->ic, tf->ft ); } @@ -155,7 +155,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, if( bud == NULL ) { - imcb_file_canceled( ft, "Couldn't find buddy (BUG?)" ); + imcb_file_canceled( ic, ft, "Couldn't find buddy (BUG?)" ); return; } diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 5d199b9e..6a588613 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = invitation.o msn.o msn_util.o ns.o passport.o sb.o tables.o +objects = msn.o msn_util.o ns.o passport.o sb.o tables.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 3a8b8f7b..8b73f103 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -80,9 +80,11 @@ static void msn_logout( struct im_connection *ic ) if( md ) { + /** Disabling MSN ft support for now. while( md->filetransfers ) { imcb_file_canceled( md->filetransfers->data, "Closing connection" ); } + */ if( md->fd >= 0 ) closesocket( md->fd ); @@ -343,7 +345,7 @@ void msn_initmodule() ret->rem_deny = msn_rem_deny; ret->send_typing = msn_send_typing; ret->handle_cmp = g_strcasecmp; - ret->transfer_request = msn_ftp_transfer_request; + //ret->transfer_request = msn_ftp_transfer_request; register_protocol(ret); } diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 668a8b8a..f85981e5 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -95,8 +95,7 @@ static void msn_buddy_ask_yes( void *data ) msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname ); - if( imcb_find_buddy( bla->ic, bla->handle ) == NULL ) - imcb_ask_add( bla->ic, bla->handle, NULL ); + imcb_ask_add( bla->ic, bla->handle, NULL ); g_free( bla->handle ); g_free( bla->realname ); diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index c3302e57..a935ce97 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -690,6 +690,8 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int /* PANIC! */ } } +#if 0 + // Disable MSN ft support for now. else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 ) { char *command = msn_findheader( body, "Invitation-Command:", blen ); @@ -722,6 +724,7 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int g_free( command ); } +#endif else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0 ) { imcb_error( sb->ic, "Cannot receive file from %s: BitlBee does not " diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 1e00d5ab..4521eb32 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -390,7 +390,7 @@ void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char if( !bu || !fullname ) return; - if( strcmp( bu->fullname, fullname ) != 0 ) + if( !bu->fullname || strcmp( bu->fullname, fullname ) != 0 ) { g_free( bu->fullname ); bu->fullname = g_strdup( fullname ); -- cgit v1.2.3 From 3ab1d317831a6c1830bb648a1a8d63a41c92f651 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Apr 2010 20:12:25 +0200 Subject: Fixing a bug in s5bytestream code to deal with incomplete SOCKS5 messages and my rewrite of the bs_peek function. --- protocols/jabber/s5bytestream.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 58a6c2e4..36a2e438 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -481,10 +481,11 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con return TRUE; else if( ret < sizeof( struct socks5_message ) ) { - /* Either a buggy proxy or just one that doesnt regard the SHOULD in XEP-0065 - * saying the reply SHOULD contain the address */ - - ASSERTSOCKOP( ret = recv( fd, &socks5_reply, ret, 0 ), "Dequeuing after MSG_PEEK" ); + /* Either a buggy proxy or just one that doesnt regard + * the SHOULD in XEP-0065 saying the reply SHOULD + * contain the address. We'll take it, so make sure the + * next jabber_bs_peek starts with an empty buffer. */ + bt->peek_buf_len = 0; } if( !( socks5_reply.ver == 5 ) || -- cgit v1.2.3 From e00da6322b3a78509d83e8f2c01cfec90465eb9f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Apr 2010 21:10:12 +0200 Subject: Restored query/ask stuff. --- Makefile | 4 ++-- protocols/nogaim.c | 18 ++++++------------ query.c | 4 ++-- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 8a3b3ef6..948e9b58 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,8 @@ -include Makefile.settings # Program variables -#objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) -objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o storage.o $(STORAGE_OBJS) +#objects = chat.o +objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 4521eb32..cbfbe00a 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -358,7 +358,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont ) { - //query_add( ic->irc, ic, msg, doit, dont, data ); + query_add( (irc_t *) ic->bee->ui_data, ic, msg, doit, dont, data ); } void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group ) @@ -451,7 +451,6 @@ struct imcb_ask_cb_data char *handle; }; -#if 0 static void imcb_ask_auth_cb_no( void *data ) { struct imcb_ask_cb_data *cbd = data; @@ -471,11 +470,9 @@ static void imcb_ask_auth_cb_yes( void *data ) g_free( cbd->handle ); g_free( cbd ); } -#endif void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname ) { -#if 0 struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 ); char *s, *realname_ = NULL; @@ -489,12 +486,11 @@ void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *re data->ic = ic; data->handle = g_strdup( handle ); - query_add( ic->irc, ic, s, imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data ); -#endif + query_add( (irc_t *) ic->bee->ui_data, ic, s, + imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data ); } -#if 0 static void imcb_ask_add_cb_no( void *data ) { g_free( ((struct imcb_ask_cb_data*)data)->handle ); @@ -509,24 +505,22 @@ static void imcb_ask_add_cb_yes( void *data ) return imcb_ask_add_cb_no( data ); } -#endif void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname ) { -#if 0 struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 ); char *s; /* TODO: Make a setting for this! */ - if( user_findhandle( ic, handle ) != NULL ) + if( bee_user_by_handle( ic->bee, ic, handle ) != NULL ) return; s = g_strdup_printf( "The user %s is not in your buddy list yet. Do you want to add him/her now?", handle ); data->ic = ic; data->handle = g_strdup( handle ); - query_add( ic->irc, ic, s, imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data ); -#endif + query_add( (irc_t *) ic->bee->ui_data, ic, s, + imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data ); } void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) diff --git a/query.c b/query.c index e8f69572..b60669f7 100644 --- a/query.c +++ b/query.c @@ -63,7 +63,7 @@ query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, irc->queries = q; } - if( g_strcasecmp( set_getstr( &irc->set, "query_order" ), "lifo" ) == 0 || irc->queries == q ) + if( g_strcasecmp( set_getstr( &irc->b->set, "query_order" ), "lifo" ) == 0 || irc->queries == q ) query_display( irc, q ); return( q ); @@ -178,7 +178,7 @@ static query_t *query_default( irc_t *irc ) { query_t *q; - if( g_strcasecmp( set_getstr( &irc->set, "query_order" ), "fifo" ) == 0 ) + if( g_strcasecmp( set_getstr( &irc->b->set, "query_order" ), "fifo" ) == 0 ) q = irc->queries; else for( q = irc->queries; q && q->next; q = q->next ); -- cgit v1.2.3 From dbb0ce32e607dbcd6c5ad4d5ab309e969afd6fd4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Apr 2010 21:38:45 +0200 Subject: Restored add/remove command and, oh, yes/no commands. Nice to be able to answer the questions.. --- root_commands.c | 66 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/root_commands.c b/root_commands.c index 73670d3a..7f3a6599 100644 --- a/root_commands.c +++ b/root_commands.c @@ -566,7 +566,6 @@ static void cmd_account( irc_t *irc, char **cmd ) } } -#if 0 static void cmd_add( irc_t *irc, char **cmd ) { account_t *a; @@ -579,7 +578,7 @@ static void cmd_add( irc_t *irc, char **cmd ) cmd ++; } - if( !( a = account_get( irc, cmd[1] ) ) ) + if( !( a = account_get( irc->b, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); return; @@ -597,7 +596,7 @@ static void cmd_add( irc_t *irc, char **cmd ) irc_usermsg( irc, "The requested nick `%s' is invalid", cmd[3] ); return; } - else if( user_find( irc, cmd[3] ) ) + else if( irc_user_by_name( irc, cmd[3] ) ) { irc_usermsg( irc, "The requested nick `%s' already exists", cmd[3] ); return; @@ -611,14 +610,37 @@ static void cmd_add( irc_t *irc, char **cmd ) if( add_on_server ) a->ic->acc->prpl->add_buddy( a->ic, cmd[2], NULL ); else - /* Yeah, officially this is a call-*back*... So if we just - called add_buddy, we'll wait for the IM server to respond - before we do this. */ - imcb_add_buddy( a->ic, cmd[2], NULL ); + /* Only for add -tmp. For regular adds, this callback will + be called once the IM server confirms. */ + bee_user_new( irc->b, a->ic, cmd[2] ); irc_usermsg( irc, "Adding `%s' to your contact list", cmd[2] ); } +static void cmd_remove( irc_t *irc, char **cmd ) +{ + irc_user_t *iu; + bee_user_t *bu; + char *s; + + if( !( iu = irc_user_by_name( irc, cmd[1] ) ) || !( bu = iu->bu ) ) + { + irc_usermsg( irc, "Buddy `%s' not found", cmd[1] ); + return; + } + s = g_strdup( bu->handle ); + + bu->ic->acc->prpl->remove_buddy( bu->ic, bu->handle, NULL ); + nick_del( bu->ic->acc, bu->handle ); + irc_user_free( irc, cmd[1] ); + + irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] ); + g_free( s ); + + return; +} + +#if 0 static void cmd_info( irc_t *irc, char **cmd ) { struct im_connection *ic; @@ -732,28 +754,6 @@ char *set_eval_control_channel( set_t *set, char *new_name ) return strcmp( irc->channel, new_name ) == 0 ? new_name : SET_INVALID; } -static void cmd_remove( irc_t *irc, char **cmd ) -{ - user_t *u; - char *s; - - if( !( u = user_find( irc, cmd[1] ) ) || !u->ic ) - { - irc_usermsg( irc, "Buddy `%s' not found", cmd[1] ); - return; - } - s = g_strdup( u->handle ); - - u->ic->acc->prpl->remove_buddy( u->ic, u->handle, NULL ); - nick_del( u->ic->acc, u->handle ); - user_del( irc, cmd[1] ); - - irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] ); - g_free( s ); - - return; -} - static void cmd_block( irc_t *irc, char **cmd ) { struct im_connection *ic; @@ -872,6 +872,7 @@ static void cmd_allow( irc_t *irc, char **cmd ) irc_usermsg( irc, "Buddy `%s' moved from your block- to your allow-list", cmd[2] ); } } +#endif static void cmd_yesno( irc_t *irc, char **cmd ) { @@ -916,6 +917,7 @@ static void cmd_set( irc_t *irc, char **cmd ) cmd_set_real( irc, cmd, NULL, NULL ); } +#if 0 static void cmd_blist( irc_t *irc, char **cmd ) { int online = 0, away = 0, offline = 0; @@ -1183,18 +1185,20 @@ const command_t commands[] = { { "register", 1, cmd_register, 0 }, { "drop", 1, cmd_drop, 0 }, { "save", 0, cmd_save, 0 }, -#if 0 { "add", 2, cmd_add, 0 }, + { "remove", 1, cmd_remove, 0 }, +#if 0 { "info", 1, cmd_info, 0 }, #endif { "rename", 2, cmd_rename, 0 }, #if 0 - { "remove", 1, cmd_remove, 0 }, { "block", 1, cmd_block, 0 }, { "allow", 1, cmd_allow, 0 }, +#endif { "set", 0, cmd_set, 0 }, { "yes", 0, cmd_yesno, 0 }, { "no", 0, cmd_yesno, 0 }, +#if 0 { "blist", 0, cmd_blist, 0 }, { "nick", 1, cmd_nick, 0 }, { "qlist", 0, cmd_qlist, 0 }, -- cgit v1.2.3 From ffa1173a0ea836029bae1f41ec13c64b39bfb6a4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Apr 2010 21:53:27 +0200 Subject: Allow the user to change his/her nick. My old concern was that it gets hairy when dealing with the storage/NickServ part. But meh, just reset all that state when the nick changes and require the user to re-register if s/he wants to save stuff. The only problem's when s/he identifies and may end up getting every account added twice. --- irc_commands.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index 57268d07..b1b764b9 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -72,11 +72,7 @@ static void irc_cmd_user( irc_t *irc, char **cmd ) static void irc_cmd_nick( irc_t *irc, char **cmd ) { - if( irc->user->nick ) - { - irc_send_num( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" ); - } - else if( irc_user_by_name( irc, cmd[1] ) ) + if( irc_user_by_name( irc, cmd[1] ) ) { irc_send_num( irc, 433, ":This nick is already in use" ); } @@ -85,6 +81,17 @@ static void irc_cmd_nick( irc_t *irc, char **cmd ) /* [SH] Invalid characters. */ irc_send_num( irc, 432, ":This nick contains invalid characters" ); } + else if( irc->user->nick ) + { + if( irc->status & USTATUS_IDENTIFIED ) + { + irc_setpass( irc, NULL ); + irc->status &= ~USTATUS_IDENTIFIED; + irc_umode_set( irc, "-R", 1 ); + } + + irc_user_set_nick( irc->user, cmd[1] ); + } else { irc->user->nick = g_strdup( cmd[1] ); -- cgit v1.2.3 From 824084011a9f3740d074957f7f769ab6f3547f79 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 00:22:17 +0200 Subject: Don't send the topic at join time if there is none. This seems to be the standard. --- irc_send.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/irc_send.c b/irc_send.c index 89b10020..b78b90bd 100644 --- a/irc_send.c +++ b/irc_send.c @@ -149,7 +149,8 @@ void irc_send_join( irc_channel_t *ic, irc_user_t *iu ) { irc_write( irc, ":%s MODE %s +%s", irc->root->host, ic->name, ic->mode ); irc_send_names( ic ); - irc_send_topic( ic, FALSE ); + if( ic->topic && *ic->topic ) + irc_send_topic( ic, FALSE ); } } -- cgit v1.2.3 From 57119e85387ba80192ccf36756c71b4dbb7947cb Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 00:26:45 +0200 Subject: Actually bomb out if channel creation fails at /join time. --- irc_commands.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/irc_commands.c b/irc_commands.c index b1b764b9..bf55e2b8 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -130,7 +130,10 @@ static void irc_cmd_join( irc_t *irc, char **cmd ) ic = irc_channel_new( irc, cmd[1] ); if( ic == NULL ) + { irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] ); + return; + } if( ic->flags & IRC_CHANNEL_JOINED ) return; /* Dude, you're already there... -- cgit v1.2.3 From d986463c9e026b2a5f003f44db3105aa5449fc27 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 00:31:15 +0200 Subject: Show status msg in /WHOIS, using a hopefully more proper 320 numeric reply. --- irc_send.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/irc_send.c b/irc_send.c index b78b90bd..3eecf5f4 100644 --- a/irc_send.c +++ b/irc_send.c @@ -228,18 +228,15 @@ void irc_send_whois( irc_user_t *iu ) bu->ic->acc->server && *bu->ic->acc->server ? bu->ic->acc->server : "", bu->ic->acc->prpl->name ); - if( bu->status ) + if( bu->status || bu->status_msg ) { - if( bu->status_msg ) - irc_send_num( irc, 301, "%s :%s (%s)", iu->nick, bu->status, bu->status_msg ); + int num = bu->flags & BEE_USER_AWAY ? 301 : 320; + + if( bu->status && bu->status_msg ) + irc_send_num( irc, num, "%s :%s (%s)", iu->nick, bu->status, bu->status_msg ); else - irc_send_num( irc, 301, "%s :%s", iu->nick, bu->status ); + irc_send_num( irc, num, "%s :%s", iu->nick, bu->status ? : bu->status_msg ); } - - /* - if( u->status_msg ) - irc_send_num( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); - */ } else { -- cgit v1.2.3 From eabc9d2c1b1d29aeb47162da64ce2b607c3d43ff Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 00:49:32 +0200 Subject: Fixed cleanup issues when turning off an account. Also fixed syntax of *_user_free(). --- irc.c | 5 +---- irc.h | 2 +- irc_im.c | 2 +- irc_user.c | 5 ++--- protocols/bee.h | 2 +- protocols/bee_user.c | 6 ++---- protocols/nogaim.c | 9 ++++++--- root_commands.c | 2 +- 8 files changed, 15 insertions(+), 18 deletions(-) diff --git a/irc.c b/irc.c index 4824d0ac..b6a1b9f9 100644 --- a/irc.c +++ b/irc.c @@ -209,10 +209,7 @@ void irc_free( irc_t * irc ) */ while( irc->users ) - { - irc_user_t *iu = irc->users->data; - irc_user_free( irc, iu->nick ); - } + irc_user_free( irc, (irc_user_t *) irc->users->data ); while( irc->channels ) irc_channel_free( irc->channels->data ); diff --git a/irc.h b/irc.h index 8a40541c..e981b918 100644 --- a/irc.h +++ b/irc.h @@ -203,7 +203,7 @@ void irc_send_nick( irc_user_t *iu, const char *new ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); -int irc_user_free( irc_t *irc, const char *nick ); +int irc_user_free( irc_t *irc, irc_user_t *iu ); irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ); int irc_user_set_nick( irc_user_t *iu, const char *new ); gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ); diff --git a/irc_im.c b/irc_im.c index 26d826bd..ccf0d55c 100644 --- a/irc_im.c +++ b/irc_im.c @@ -73,7 +73,7 @@ static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu ) { - return irc_user_free( bee->ui_data, bu->ui_data ); + return irc_user_free( bee->ui_data, (irc_user_t *) bu->ui_data ); } static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old ) diff --git a/irc_user.c b/irc_user.c index 8ad20b54..b7629490 100644 --- a/irc_user.c +++ b/irc_user.c @@ -46,12 +46,11 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ) return iu; } -int irc_user_free( irc_t *irc, const char *nick ) +int irc_user_free( irc_t *irc, irc_user_t *iu ) { - irc_user_t *iu; GSList *l; - if( !( iu = irc_user_by_name( irc, nick ) ) ) + if( !iu ) return 0; irc->users = g_slist_remove( irc->users, iu ); diff --git a/protocols/bee.h b/protocols/bee.h index 6f896c51..e87bb762 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -84,7 +84,7 @@ void bee_free( bee_t *b ); /* bee_user.c */ bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle ); -int bee_user_free( bee_t *bee, struct im_connection *ic, const char *handle ); +int bee_user_free( bee_t *bee, bee_user_t *bu ); bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle ); int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ); diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 20c760a9..f856e7a2 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -45,11 +45,9 @@ bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *hand return bu; } -int bee_user_free( bee_t *bee, struct im_connection *ic, const char *handle ) +int bee_user_free( bee_t *bee, bee_user_t *bu ) { - bee_user_t *bu; - - if( ( bu = bee_user_by_handle( bee, ic, handle ) ) == NULL ) + if( !bu ) return 0; if( bee->ui->user_free ) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index cbfbe00a..4dd60ea6 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -326,12 +326,15 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) g_free( ic->away ); ic->away = NULL; - for( l = bee->users; l; l = l->next ) + for( l = bee->users; l; ) { bee_user_t *bu = l->data; + GSList *next = l->next; if( bu->ic == ic ) - bee_user_free( bee, ic, bu->handle ); + bee_user_free( bee, bu ); + + l = next; } //query_del_by_conn( ic->irc, ic ); @@ -402,7 +405,7 @@ void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group ) { - bee_user_free( ic->bee, ic, handle ); + bee_user_free( ic->bee, bee_user_by_handle( ic->bee, ic, handle ) ); } /* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM diff --git a/root_commands.c b/root_commands.c index 7f3a6599..e5a5b41d 100644 --- a/root_commands.c +++ b/root_commands.c @@ -632,7 +632,7 @@ static void cmd_remove( irc_t *irc, char **cmd ) bu->ic->acc->prpl->remove_buddy( bu->ic, bu->handle, NULL ); nick_del( bu->ic->acc, bu->handle ); - irc_user_free( irc, cmd[1] ); + //TODO(wilmer): bee_user_free() and/or let the IM mod do it? irc_user_free( irc, cmd[1] ); irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] ); g_free( s ); -- cgit v1.2.3 From 6c56f426fb2823ea6b41d1c9028448b7f8d5db09 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 01:04:24 +0200 Subject: Allow short versions of root commands (just give enough to match exactly one command), for the lazy users. --- root_commands.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/root_commands.c b/root_commands.c index e5a5b41d..60aa3bf6 100644 --- a/root_commands.c +++ b/root_commands.c @@ -91,14 +91,20 @@ void root_command_string( irc_t *irc, char *command ) void root_command( irc_t *irc, char *cmd[] ) { - int i; + int i, len; if( !cmd[0] ) return; + len = strlen( cmd[0] ); for( i = 0; commands[i].command; i++ ) - if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) + if( g_strncasecmp( commands[i].command, cmd[0], len ) == 0 ) { + if( commands[i+1].command && + g_strncasecmp( commands[i+1].command, cmd[0], len ) == 0 ) + /* Only match on the first letters if the match is unique. */ + break; + MIN_ARGS( commands[i].required_parameters ); commands[i].execute( irc, cmd ); @@ -1178,32 +1184,30 @@ static void cmd_transfer( irc_t *irc, char **cmd ) } #endif +/* IMPORTANT: Keep this list sorted! The short command logic needs that. */ const command_t commands[] = { - { "help", 0, cmd_help, 0 }, { "account", 1, cmd_account, 0 }, + { "add", 2, cmd_add, 0 }, + { "drop", 1, cmd_drop, 0 }, + { "help", 0, cmd_help, 0 }, { "identify", 1, cmd_identify, 0 }, + { "no", 0, cmd_yesno, 0 }, { "register", 1, cmd_register, 0 }, - { "drop", 1, cmd_drop, 0 }, - { "save", 0, cmd_save, 0 }, - { "add", 2, cmd_add, 0 }, { "remove", 1, cmd_remove, 0 }, -#if 0 - { "info", 1, cmd_info, 0 }, -#endif { "rename", 2, cmd_rename, 0 }, -#if 0 - { "block", 1, cmd_block, 0 }, - { "allow", 1, cmd_allow, 0 }, -#endif + { "save", 0, cmd_save, 0 }, { "set", 0, cmd_set, 0 }, { "yes", 0, cmd_yesno, 0 }, - { "no", 0, cmd_yesno, 0 }, #if 0 + { "allow", 1, cmd_allow, 0 }, { "blist", 0, cmd_blist, 0 }, + { "block", 1, cmd_block, 0 }, + { "chat", 1, cmd_chat, 0 }, + { "ft", 0, cmd_transfer, 0 }, + { "info", 1, cmd_info, 0 }, + { "join_chat", 2, cmd_join_chat, 0 }, { "nick", 1, cmd_nick, 0 }, { "qlist", 0, cmd_qlist, 0 }, - { "join_chat", 2, cmd_join_chat, 0 }, - { "chat", 1, cmd_chat, 0 }, { "transfer", 0, cmd_transfer, 0 }, #endif { NULL } -- cgit v1.2.3 From aa44bddd276f047edd39fdb3eadc9b1e87f0014f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 01:18:03 +0200 Subject: Restored info command. --- root_commands.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/root_commands.c b/root_commands.c index 60aa3bf6..3853a73e 100644 --- a/root_commands.c +++ b/root_commands.c @@ -646,7 +646,6 @@ static void cmd_remove( irc_t *irc, char **cmd ) return; } -#if 0 static void cmd_info( irc_t *irc, char **cmd ) { struct im_connection *ic; @@ -654,16 +653,16 @@ static void cmd_info( irc_t *irc, char **cmd ) if( !cmd[2] ) { - user_t *u = user_find( irc, cmd[1] ); - if( !u || !u->ic ) + irc_user_t *iu = irc_user_by_name( irc, cmd[1] ); + if( !iu || !iu->bu ) { irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); return; } - ic = u->ic; - cmd[2] = u->handle; + ic = iu->bu->ic; + cmd[2] = iu->bu->handle; } - else if( !( a = account_get( irc, cmd[1] ) ) ) + else if( !( a = account_get( irc->b, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); return; @@ -683,7 +682,6 @@ static void cmd_info( irc_t *irc, char **cmd ) ic->acc->prpl->get_info( ic, cmd[2] ); } } -#endif static void cmd_rename( irc_t *irc, char **cmd ) { @@ -1191,6 +1189,7 @@ const command_t commands[] = { { "drop", 1, cmd_drop, 0 }, { "help", 0, cmd_help, 0 }, { "identify", 1, cmd_identify, 0 }, + { "info", 1, cmd_info, 0 }, { "no", 0, cmd_yesno, 0 }, { "register", 1, cmd_register, 0 }, { "remove", 1, cmd_remove, 0 }, @@ -1204,9 +1203,7 @@ const command_t commands[] = { { "block", 1, cmd_block, 0 }, { "chat", 1, cmd_chat, 0 }, { "ft", 0, cmd_transfer, 0 }, - { "info", 1, cmd_info, 0 }, { "join_chat", 2, cmd_join_chat, 0 }, - { "nick", 1, cmd_nick, 0 }, { "qlist", 0, cmd_qlist, 0 }, { "transfer", 0, cmd_transfer, 0 }, #endif -- cgit v1.2.3 From e21c0f8b276cc3ca177bcf6217eba9c634d410f7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 01:31:43 +0200 Subject: Always show root's greeting inside the control channel. --- irc.c | 8 ++++++++ irc_send.c | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/irc.c b/irc.c index b6a1b9f9..8f4024dd 100644 --- a/irc.c +++ b/irc.c @@ -639,6 +639,14 @@ int irc_check_login( irc_t *irc ) irc->last_root_cmd = g_strdup( ROOT_CHAN ); + irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN, + "Welcome to the BitlBee gateway!\n\n" + "If you've never used BitlBee before, please do read the help " + "information using the \x02help\x02 command. Lots of FAQs are " + "answered there.\n" + "If you already have an account on this server, just use the " + "\x02identify\x02 command to identify yourself.", NULL ); + return 1; } } diff --git a/irc_send.c b/irc_send.c index 3eecf5f4..ce4c86fe 100644 --- a/irc_send.c +++ b/irc_send.c @@ -47,13 +47,6 @@ void irc_send_login( irc_t *irc ) "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", CTYPES, CMODES, MAX_NICK_LENGTH - 1 ); irc_send_motd( irc ); - - irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n" - "If you've never used BitlBee before, please do read the help " - "information using the \x02help\x02 command. Lots of FAQs are " - "answered there.\n" - "If you already have an account on this server, just use the " - "\x02identify\x02 command to identify yourself." ); } void irc_send_motd( irc_t *irc ) -- cgit v1.2.3 From 24b8bbb2616d685006a279e46a4bd2e8e7cf6694 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 02:06:49 +0200 Subject: Start handling CTCPs, in a saner way than before. --- irc.h | 1 + irc_commands.c | 29 ++++++++++++++++++++++++++--- irc_user.c | 10 ++++++++++ lib/misc.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ lib/misc.h | 2 ++ root_commands.c | 43 +------------------------------------------ 6 files changed, 85 insertions(+), 45 deletions(-) diff --git a/irc.h b/irc.h index e981b918..450d3259 100644 --- a/irc.h +++ b/irc.h @@ -119,6 +119,7 @@ typedef struct irc_user struct irc_user_funcs { gboolean (*privmsg)( irc_user_t *iu, const char *msg ); + gboolean (*ctcp)( irc_user_t *iu, char * const* ctcp ); }; extern const struct irc_user_funcs irc_user_root_funcs; diff --git a/irc_commands.c b/irc_commands.c index bf55e2b8..5387fb4d 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -254,16 +254,39 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) if( !cmd[2] ) { irc_send_num( irc, 412, ":No text to send" ); + return; } - else if( irc_channel_name_ok( cmd[1] ) && - ( ic = irc_channel_by_name( irc, cmd[1] ) ) ) + + /* Don't treat CTCP actions as real CTCPs, just convert them right now. */ + if( g_strncasecmp( cmd[2], "\001ACTION", 7 ) == 0 ) + { + cmd[2] += 4; + strcpy( cmd[2], "/me" ); + if( cmd[2][strlen(cmd[2])-1] == '\001' ) + cmd[2][strlen(cmd[2])-1] = '\0'; + } + + if( irc_channel_name_ok( cmd[1] ) && + ( ic = irc_channel_by_name( irc, cmd[1] ) ) ) { if( ic->f->privmsg ) ic->f->privmsg( ic, cmd[2] ); } else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) ) { - if( iu->f->privmsg ) + if( cmd[2][0] == '\001' ) + { + char **ctcp; + + if( iu->f->ctcp == NULL ) + return; + if( cmd[2][strlen(cmd[2])-1] == '\001' ) + cmd[2][strlen(cmd[2])-1] = '\0'; + + ctcp = split_command_parts( cmd[2] + 1 ); + iu->f->ctcp( iu, ctcp ); + } + else if( iu->f->privmsg ) iu->f->privmsg( iu, cmd[2] ); } else diff --git a/irc_user.c b/irc_user.c index b7629490..cf1d1e3e 100644 --- a/irc_user.c +++ b/irc_user.c @@ -146,8 +146,18 @@ static gboolean root_privmsg( irc_user_t *iu, const char *msg ) return TRUE; } +static gboolean root_ctcp( irc_user_t *iu, char * const *ctcp ) +{ + if( g_strcasecmp( ctcp[0], "VERSION" ) == 0 ) + { + } + + return TRUE; +} + const struct irc_user_funcs irc_user_root_funcs = { root_privmsg, + root_ctcp, }; /* Echo to yourself: */ diff --git a/lib/misc.c b/lib/misc.c index fe2ff17c..2901704a 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -613,3 +613,48 @@ int md5_verify_password( char *password, char *hash ) return ret; } + +char **split_command_parts( char *command ) +{ + static char *cmd[IRC_MAX_ARGS+1]; + char *s, q = 0; + int k; + + memset( cmd, 0, sizeof( cmd ) ); + cmd[0] = command; + k = 1; + for( s = command; *s && k < IRC_MAX_ARGS; s ++ ) + if( *s == ' ' && !q ) + { + *s = 0; + while( *++s == ' ' ); + if( *s == '"' || *s == '\'' ) + { + q = *s; + s ++; + } + if( *s ) + { + cmd[k++] = s; + s --; + } + else + { + break; + } + } + else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) ) + { + char *cpy; + + for( cpy = s; *cpy; cpy ++ ) + cpy[0] = cpy[1]; + } + else if( *s == q ) + { + q = *s = 0; + } + cmd[k] = NULL; + + return cmd; +} diff --git a/lib/misc.h b/lib/misc.h index ce36caf5..e0e08bf1 100644 --- a/lib/misc.h +++ b/lib/misc.h @@ -67,4 +67,6 @@ G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl ); G_MODULE_EXPORT int md5_verify_password( char *password, char *hash ); +G_MODULE_EXPORT char **split_command_parts( char *command ); + #endif diff --git a/root_commands.c b/root_commands.c index 3853a73e..7a4c021e 100644 --- a/root_commands.c +++ b/root_commands.c @@ -33,48 +33,7 @@ void root_command_string( irc_t *irc, char *command ) { - char *cmd[IRC_MAX_ARGS]; - char *s; - int k; - char q = 0; - - memset( cmd, 0, sizeof( cmd ) ); - cmd[0] = command; - k = 1; - for( s = command; *s && k < ( IRC_MAX_ARGS - 1 ); s ++ ) - if( *s == ' ' && !q ) - { - *s = 0; - while( *++s == ' ' ); - if( *s == '"' || *s == '\'' ) - { - q = *s; - s ++; - } - if( *s ) - { - cmd[k++] = s; - s --; - } - else - { - break; - } - } - else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) ) - { - char *cpy; - - for( cpy = s; *cpy; cpy ++ ) - cpy[0] = cpy[1]; - } - else if( *s == q ) - { - q = *s = 0; - } - cmd[k] = NULL; - - root_command( irc, cmd ); + root_command( irc, split_command_parts( command ) ); } #define MIN_ARGS( x, y... ) \ -- cgit v1.2.3 From 89c11e735164b7212d27bb64ff1a00ac50b9c746 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Apr 2010 01:54:55 +0200 Subject: Restored CTCP/DCC hooks for outgoing file transfers. --- dcc.c | 72 +++++++++++++++++--------------------------------------------- dcc.h | 2 +- irc_im.c | 19 +++++++++++++++++ lib/misc.c | 5 ++++- 4 files changed, 43 insertions(+), 55 deletions(-) diff --git a/dcc.c b/dcc.c index e97b822f..f17f52aa 100644 --- a/dcc.c +++ b/dcc.c @@ -503,69 +503,37 @@ void dcc_finish( file_transfer_t *file ) * IP can be an unsigned int (IPV4) or something else (IPV6) * */ -file_transfer_t *dcc_request( struct im_connection *ic, char *line ) +file_transfer_t *dcc_request( struct im_connection *ic, char* const* ctcp ) { irc_t *irc = (irc_t *) ic->bee->ui_data; - char *pattern = "SEND" - " (([^\"][^ ]*)|\"(([^\"]|\\\")*)\")" - " (([0-9]*)|([^ ]*))" - " ([0-9]*)" - " ([0-9]*)\001"; - regmatch_t pmatch[10]; - regex_t re; file_transfer_t *ft; dcc_file_transfer_t *df; - char errbuf[256]; - int regerrcode, gret; - - if( ( regerrcode = regcomp( &re, pattern, REG_EXTENDED ) ) || - ( regerrcode = regexec( &re, line, 10, pmatch, 0 ) ) ) { - regerror( regerrcode,&re,errbuf,sizeof( errbuf ) ); - imcb_log( ic, - "DCC: error parsing 'DCC SEND': %s, line: %s", - errbuf, line ); - return NULL; - } - - if( ( pmatch[1].rm_so > 0 ) && - ( pmatch[5].rm_so > 0 ) && - ( pmatch[8].rm_so > 0 ) && - ( pmatch[9].rm_so > 0 ) ) + int gret; + size_t filesize; + + if( ctcp[5] != NULL && + sscanf( ctcp[4], "%zd", &filesize ) == 1 && /* Just int. validation. */ + sscanf( ctcp[5], "%zd", &filesize ) == 1 ) { - char *input = g_strdup( line ); char *filename, *host, *port; - size_t filesize; struct addrinfo hints, *rp; - - /* "filename" or filename */ - if ( pmatch[2].rm_so > 0 ) - { - input[pmatch[2].rm_eo] = '\0'; - filename = input + pmatch[2].rm_so; - } else - { - input[pmatch[3].rm_eo] = '\0'; - filename = input + pmatch[3].rm_so; - } - - input[pmatch[5].rm_eo] = '\0'; - - /* number means ipv4, something else means ipv6 */ - if ( pmatch[6].rm_so > 0 ) + + filename = ctcp[2]; + + host = ctcp[3]; + while( *host && isdigit( *host ) ) host++; /* Just digits? */ + if( *host == '\0' ) { - struct in_addr ipaddr = { .s_addr = htonl( strtoul( input + pmatch[5].rm_so, NULL, 10 ) ) }; + struct in_addr ipaddr = { .s_addr = htonl( atoll( ctcp[3] ) ) }; host = inet_ntoa( ipaddr ); } else { /* Contains non-numbers, hopefully an IPV6 address */ - host = input + pmatch[7].rm_so; + host = ctcp[3]; } - input[pmatch[8].rm_eo] = '\0'; - input[pmatch[9].rm_eo] = '\0'; - - port = input + pmatch[8].rm_so; - filesize = atoll( input + pmatch[9].rm_so ); + port = ctcp[4]; + filesize = atoll( ctcp[5] ); memset( &hints, 0, sizeof ( struct addrinfo ) ); hints.ai_socktype = SOCK_STREAM; @@ -573,7 +541,6 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) ) { - g_free( input ); imcb_log( ic, "DCC: getaddrinfo() failed with %s " "when parsing incoming 'DCC SEND': " "host %s, port %s", @@ -587,14 +554,13 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) memcpy( &df->saddr, rp->ai_addr, rp->ai_addrlen ); freeaddrinfo( rp ); - g_free( input ); irc->file_transfers = g_slist_prepend( irc->file_transfers, ft ); return ft; } - - imcb_log( ic, "DCC: couldnt parse 'DCC SEND' line: %s", line ); + else + imcb_log( ic, "DCC: couldnt parse `DCC SEND' line" ); return NULL; } diff --git a/dcc.h b/dcc.h index c0cf6ccc..f3e7aa98 100644 --- a/dcc.h +++ b/dcc.h @@ -97,7 +97,7 @@ typedef struct dcc_file_transfer { file_transfer_t *dccs_send_start( struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size ); void dcc_canceled( file_transfer_t *file, char *reason ); gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_size ); -file_transfer_t *dcc_request( struct im_connection *ic, char *line ); +file_transfer_t *dcc_request( struct im_connection *ic, char* const* ctcp ); void dcc_finish( file_transfer_t *file ); void dcc_close( file_transfer_t *file ); gboolean dccs_recv_start( file_transfer_t *ft ); diff --git a/irc_im.c b/irc_im.c index ccf0d55c..85828eb1 100644 --- a/irc_im.c +++ b/irc_im.c @@ -210,6 +210,25 @@ static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg ) return FALSE; } +static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp ) +{ + if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0 + && g_strcasecmp( ctcp[1], "SEND" ) == 0 ) + { + if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request ) + { + file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp ); + if ( ft ) + iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle ); + + return TRUE; + } + } + + return FALSE; +} + static const struct irc_user_funcs irc_user_im_funcs = { bee_irc_user_privmsg, + bee_irc_user_ctcp, }; diff --git a/lib/misc.c b/lib/misc.c index 2901704a..fa5bb9a4 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -654,7 +654,10 @@ char **split_command_parts( char *command ) { q = *s = 0; } - cmd[k] = NULL; + + /* Full zero-padding for easier argc checking. */ + while( k <= IRC_MAX_ARGS ) + cmd[k++] = NULL; return cmd; } -- cgit v1.2.3 From 7b59872d86160099968d0f8cdafc2853b074d39b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Apr 2010 02:04:55 +0200 Subject: Support for simple VERSION/PING CTCPs to root. --- irc.h | 1 + irc_send.c | 13 +++++++++++++ irc_user.c | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/irc.h b/irc.h index 450d3259..8caf17fa 100644 --- a/irc.h +++ b/irc.h @@ -200,6 +200,7 @@ void irc_send_whois( irc_user_t *iu ); void irc_send_who( irc_t *irc, GSList *l, const char *channel ); void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix ); void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg ); +void irc_send_msg_f( irc_user_t *iu, const char *type, const char *dst, const char *format, ... ) G_GNUC_PRINTF( 4, 5 ); void irc_send_nick( irc_user_t *iu, const char *new ); /* irc_user.c */ diff --git a/irc_send.c b/irc_send.c index ce4c86fe..57d15ade 100644 --- a/irc_send.c +++ b/irc_send.c @@ -302,6 +302,19 @@ void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const iu->nick, iu->user, iu->host, type, dst, msg ); } +void irc_send_msg_f( irc_user_t *iu, const char *type, const char *dst, const char *format, ... ) +{ + char text[IRC_MAX_LINE]; + va_list params; + + va_start( params, format ); + g_vsnprintf( text, IRC_MAX_LINE, format, params ); + va_end( params ); + + irc_write( iu->irc, ":%s!%s@%s %s %s :%s", + iu->nick, iu->user, iu->host, type, dst, text ); +} + void irc_send_nick( irc_user_t *iu, const char *new ) { irc_write( iu->irc, ":%s!%s@%s NICK %s", diff --git a/irc_user.c b/irc_user.c index cf1d1e3e..7b2d4be1 100644 --- a/irc_user.c +++ b/irc_user.c @@ -150,6 +150,13 @@ static gboolean root_ctcp( irc_user_t *iu, char * const *ctcp ) { if( g_strcasecmp( ctcp[0], "VERSION" ) == 0 ) { + irc_send_msg_f( iu, "NOTICE", iu->irc->user->nick, "\001%s %s\001", + ctcp[0], "BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ); + } + else if( g_strcasecmp( ctcp[0], "PING" ) == 0 ) + { + irc_send_msg_f( iu, "NOTICE", iu->irc->user->nick, "\001%s %s\001", + ctcp[0], ctcp[1] ? : "" ); } return TRUE; -- cgit v1.2.3 From 3a9b1236ab5e089b7413486d7d6bf61274a61d2a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Apr 2010 10:11:08 +0200 Subject: Allow sending typing notifications (CTCP TYPING) again. --- irc_im.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/irc_im.c b/irc_im.c index 85828eb1..50f38366 100644 --- a/irc_im.c +++ b/irc_im.c @@ -224,6 +224,20 @@ static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp ) return TRUE; } } + else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 ) + { + if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] ) + { + int st = ctcp[1][0]; + if( st >= '0' && st <= '2' ) + { + st <<= 8; + iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st ); + } + + return TRUE; + } + } return FALSE; } -- cgit v1.2.3 From 573dab069d2c35910b3cdede3374a5749cb20a89 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Apr 2010 12:20:04 +0200 Subject: Incoming typing notifications. --- irc_im.c | 14 ++++++++++++++ protocols/bee.h | 1 + protocols/bee_user.c | 11 +++++++++++ protocols/nogaim.c | 18 ------------------ 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/irc_im.c b/irc_im.c index 50f38366..db6836fa 100644 --- a/irc_im.c +++ b/irc_im.c @@ -119,6 +119,19 @@ static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, t return TRUE; } +static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags ) +{ + irc_t *irc = (irc_t *) bee->ui_data; + + if( set_getbool( &bee->set, "typing_notice" ) ) + irc_send_msg_f( (irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick, + "\001TYPING %d\001", ( flags >> 8 ) & 3 ); + else + return FALSE; + + return TRUE; +} + static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) { irc_user_t *iu = (irc_user_t *) bu->ui_data; @@ -192,6 +205,7 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_user_fullname, bee_irc_user_status, bee_irc_user_msg, + bee_irc_user_typing, bee_irc_ft_in_start, bee_irc_ft_out_start, diff --git a/protocols/bee.h b/protocols/bee.h index e87bb762..62f60477 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -70,6 +70,7 @@ typedef struct bee_ui_funcs gboolean (*user_fullname)( bee_t *bee, bee_user_t *bu ); gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old ); gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ); + gboolean (*user_typing)( bee_t *bee, bee_user_t *bu, guint32 flags ); struct file_transfer* (*ft_in_start)( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ); gboolean (*ft_out_start)( struct im_connection *ic, struct file_transfer *ft ); diff --git a/protocols/bee_user.c b/protocols/bee_user.c index f856e7a2..7a38882b 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -193,3 +193,14 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui else imcb_log( ic, "Message from unknown handle %s:\n%s", handle, msg ); } + +void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) +{ + bee_user_t *bu; + + if( ic->bee->ui->user_typing && + ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ) + { + ic->bee->ui->user_typing( ic->bee, bu, flags ); + } +} diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 4dd60ea6..f7db5c37 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -526,24 +526,6 @@ void imcb_ask_add( struct im_connection *ic, const char *handle, const char *rea imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data ); } -void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) -{ -#if 0 - user_t *u; - - if( !set_getbool( &ic->bee->set, "typing_notice" ) ) - return; - - if( ( u = user_findhandle( ic, handle ) ) ) - { - char buf[256]; - - g_snprintf( buf, 256, "\1TYPING %d\1", ( flags >> 8 ) & 3 ); - irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf ); - } -#endif -} - struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *handle ) { return bee_user_by_handle( ic->bee, ic, handle ); -- cgit v1.2.3 From 81186cab101fa8c2f82137014d0b3c060b658cb0 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Apr 2010 13:38:41 +0200 Subject: /away and set away/status stuff back. --- irc_commands.c | 30 +++++++++++++----------------- protocols/bee.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index 5387fb4d..54c0067c 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -351,7 +351,6 @@ static void irc_cmd_nickserv( irc_t *irc, char **cmd ) #if 0 -//#if 0 static void irc_cmd_oper( irc_t *irc, char **cmd ) { if( global.conf->oper_pass && @@ -523,40 +522,37 @@ static void irc_cmd_topic( irc_t *irc, char **cmd ) irc_topic( irc, channel ); } } +#endif static void irc_cmd_away( irc_t *irc, char **cmd ) { - user_t *u = user_find( irc, irc->nick ); - char *away = cmd[1]; - - if( !u ) return; + char *set; - if( away && *away ) + if( cmd[1] && *cmd[1] ) { + char away[strlen(cmd[1])+1]; int i, j; /* Copy away string, but skip control chars. Mainly because Jabber really doesn't like them. */ - u->away = g_malloc( strlen( away ) + 1 ); - for( i = j = 0; away[i]; i ++ ) - if( ( u->away[j] = away[i] ) >= ' ' ) + for( i = j = 0; cmd[1][i]; i ++ ) + if( ( away[j] = cmd[1][i] ) >= ' ' ) j ++; - u->away[j] = 0; + away[j] = '\0'; - irc_send_num( irc, 306, ":You're now away: %s", u->away ); - /* irc_umode_set( irc, irc->myhost, "+a" ); */ + irc_send_num( irc, 306, ":You're now away: %s", away ); + set = away; } else { - if( u->away ) g_free( u->away ); - u->away = NULL; - /* irc_umode_set( irc, irc->myhost, "-a" ); */ irc_send_num( irc, 305, ":Welcome back" ); + set = NULL; } - set_setstr( &irc->set, "away", u->away ); + set_setstr( &irc->b->set, "away", set ); } +#if 0 static void irc_cmd_version( irc_t *irc, char **cmd ) { irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU ); @@ -612,6 +608,7 @@ static const command_t irc_commands[] = { { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, + { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, #if 0 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, @@ -620,7 +617,6 @@ static const command_t irc_commands[] = { { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, - { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, diff --git a/protocols/bee.c b/protocols/bee.c index 3f576b0b..1aaa90f0 100644 --- a/protocols/bee.c +++ b/protocols/bee.c @@ -1,11 +1,39 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Some IM-core stuff */ + +/* + 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" +static char *set_eval_away_status( set_t *set, char *value ); + bee_t *bee_new() { bee_t *b = g_new0( bee_t, 1 ); set_t *s; - s = set_add( &b->set, "away", NULL, NULL/*set_eval_away_status*/, b ); + s = set_add( &b->set, "away", NULL, set_eval_away_status, b ); s->flags |= SET_NULL_OK; s = set_add( &b->set, "auto_connect", "true", set_eval_bool, b ); s = set_add( &b->set, "auto_reconnect", "true", set_eval_bool, b ); @@ -14,7 +42,7 @@ bee_t *bee_new() s = set_add( &b->set, "password", NULL, NULL/*set_eval_password*/, b ); s->flags |= SET_NULL_OK; s = set_add( &b->set, "save_on_quit", "true", set_eval_bool, b ); - s = set_add( &b->set, "status", NULL, NULL/*set_eval_away_status*/, b ); + s = set_add( &b->set, "status", NULL, set_eval_away_status, b ); s->flags |= SET_NULL_OK; s = set_add( &b->set, "strip_html", "true", NULL, b ); @@ -45,3 +73,22 @@ void bee_free( bee_t *b ) g_free( b ); } + +static char *set_eval_away_status( set_t *set, char *value ) +{ + bee_t *bee = set->data; + account_t *a; + + g_free( set->value ); + set->value = g_strdup( value ); + + for( a = bee->accounts; a; a = a->next ) + { + struct im_connection *ic = a->ic; + + if( ic && ic->flags & OPT_LOGGED_IN ) + imc_away_send_update( ic ); + } + + return value; +} -- cgit v1.2.3 From d33679e4ffd9f36f14f677553b56f9b8ad72dd0d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 14:17:12 +0200 Subject: Call bee_free() from irc_free() or daemon mode gets pretty sad. --- irc.c | 12 ++++++------ protocols/bee.c | 18 ++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/irc.c b/irc.c index a195adb4..93b41071 100644 --- a/irc.c +++ b/irc.c @@ -197,18 +197,19 @@ void irc_free( irc_t * irc ) { log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); - /* if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) ) if( storage_save( irc, NULL, TRUE ) != STORAGE_OK ) - irc_usermsg( irc, "Error while saving settings!" ); - */ + log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick ); irc_connection_list = g_slist_remove( irc_connection_list, irc ); - /* while( irc->queries != NULL ) query_del( irc, irc->queries ); - */ + + /* This is a little bit messy: bee_free() frees all b->users which + calls us back to free the corresponding irc->users. So do this + before we clear the remaining ones ourselves. */ + bee_free( irc->b ); while( irc->users ) irc_user_free( irc, (irc_user_t *) irc->users->data ); @@ -239,7 +240,6 @@ void irc_free( irc_t * irc ) g_free( irc->sendbuffer ); g_free( irc->readbuffer ); - g_free( irc->password ); g_free( irc ); diff --git a/protocols/bee.c b/protocols/bee.c index 1aaa90f0..de9550c2 100644 --- a/protocols/bee.c +++ b/protocols/bee.c @@ -51,21 +51,19 @@ bee_t *bee_new() void bee_free( bee_t *b ) { - account_t *acc = b->accounts; - - while( acc ) + while( b->accounts ) { - if( acc->ic ) - imc_logout( acc->ic, FALSE ); - else if( acc->reconnect ) - cancel_auto_reconnect( acc ); + if( b->accounts->ic ) + imc_logout( b->accounts->ic, FALSE ); + else if( b->accounts->reconnect ) + cancel_auto_reconnect( b->accounts ); - if( acc->ic == NULL ) - account_del( b, acc ); + if( b->accounts->ic == NULL ) + account_del( b, b->accounts ); else /* Nasty hack, but account_del() doesn't work in this case and we don't want infinite loops, do we? ;-) */ - acc = acc->next; + b->accounts = b->accounts->next; } while( b->set ) -- cgit v1.2.3 From d7d677dac118a1240ada9ddcc8617c49293e5a5e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 14:34:39 +0200 Subject: Restored a few more trivial commands including all the oper stuff. --- irc_commands.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index 54c0067c..f4c6ce48 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -350,7 +350,6 @@ static void irc_cmd_nickserv( irc_t *irc, char **cmd ) -#if 0 static void irc_cmd_oper( irc_t *irc, char **cmd ) { if( global.conf->oper_pass && @@ -367,6 +366,7 @@ static void irc_cmd_oper( irc_t *irc, char **cmd ) } } +#if 0 static void irc_cmd_invite( irc_t *irc, char **cmd ) { char *nick = cmd[1], *channel = cmd[2]; @@ -552,31 +552,30 @@ static void irc_cmd_away( irc_t *irc, char **cmd ) set_setstr( &irc->b->set, "away", set ); } -#if 0 static void irc_cmd_version( irc_t *irc, char **cmd ) { - irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU ); + irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", + BITLBEE_VERSION, irc->root->host, ARCH, CPU ); } static void irc_cmd_completions( irc_t *irc, char **cmd ) { - user_t *u = user_find( irc, irc->mynick ); help_t *h; set_t *s; int i; - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "OK" ); + irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK" ); for( i = 0; commands[i].command; i ++ ) - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", commands[i].command ); + irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", commands[i].command ); for( h = global.help; h; h = h->next ) - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->title ); + irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title ); - for( s = irc->set; s; s = s->next ) - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key ); + for( s = irc->b->set; s; s = s->next ) + irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key ); - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "END" ); + irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END" ); } static void irc_cmd_rehash( irc_t *irc, char **cmd ) @@ -588,7 +587,6 @@ static void irc_cmd_rehash( irc_t *irc, char **cmd ) irc_send_num( irc, 382, "%s :Rehashing", global.conf_file ); } -#endif static const command_t irc_commands[] = { { "pass", 1, irc_cmd_pass, 0 }, @@ -609,16 +607,17 @@ static const command_t irc_commands[] = { { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, -#if 0 + { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, + { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, +#if 0 { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "userhost", 1, irc_cmd_userhost, IRC_CMD_LOGGED_IN }, { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, - { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, - { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, +#endif { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "deaf", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "wallops", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, @@ -626,7 +625,6 @@ static const command_t irc_commands[] = { { "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY }, { "restart", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "kill", 2, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, -#endif { NULL } }; -- cgit v1.2.3 From 003a12bd2361cd1ce4d83eeaa1b81d95101ea778 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 15:35:41 +0200 Subject: Restored all remaining IRC commands that make some sense to have at this point. --- irc.h | 1 + irc_commands.c | 48 +++++++++++++++++++++++++++--------------------- irc_im.c | 17 +++++++++++++++-- irc_user.c | 19 +++++++++++++++++++ 4 files changed, 62 insertions(+), 23 deletions(-) diff --git a/irc.h b/irc.h index 76ea650a..3a1a3520 100644 --- a/irc.h +++ b/irc.h @@ -209,6 +209,7 @@ int irc_user_free( irc_t *irc, irc_user_t *iu ); irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ); int irc_user_set_nick( irc_user_t *iu, const char *new ); gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ); +const char *irc_user_get_away( irc_user_t *iu ); /* irc_util.c */ char *set_eval_timezone( struct set *set, char *value ); diff --git a/irc_commands.c b/irc_commands.c index f4c6ce48..35169607 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -383,10 +383,10 @@ static void irc_cmd_invite( irc_t *irc, char **cmd ) irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); } +#endif static void irc_cmd_userhost( irc_t *irc, char **cmd ) { - user_t *u; int i; /* [TV] Usable USERHOST-implementation according to @@ -396,18 +396,18 @@ static void irc_cmd_userhost( irc_t *irc, char **cmd ) */ for( i = 1; cmd[i]; i ++ ) - if( ( u = user_find( irc, cmd[i] ) ) ) - { - if( u->online && u->away ) - irc_send_num( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host ); - else - irc_send_num( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host ); - } + { + irc_user_t *iu = irc_user_by_name( irc, cmd[i] ); + + if( iu ) + irc_send_num( irc, 302, ":%s=%c%s@%s", iu->nick, + irc_user_get_away( iu ) ? '-' : '+', + iu->user, iu->host ); + } } static void irc_cmd_ison( irc_t *irc, char **cmd ) { - user_t *u; char buff[IRC_MAX_LINE]; int lenleft, i; @@ -423,17 +423,20 @@ static void irc_cmd_ison( irc_t *irc, char **cmd ) this = cmd[i]; while( *this ) { + irc_user_t *iu; + if( ( next = strchr( this, ' ' ) ) ) *next = 0; - if( ( u = user_find( irc, this ) ) && u->online ) + if( ( iu = irc_user_by_name( irc, this ) ) && + iu->bu && iu->bu->flags & BEE_USER_ONLINE ) { - lenleft -= strlen( u->nick ) + 1; + lenleft -= strlen( iu->nick ) + 1; if( lenleft < 0 ) break; - strcat( buff, u->nick ); + strcat( buff, iu->nick ); strcat( buff, " " ); } @@ -469,7 +472,7 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) for( i = 1; cmd[i]; i ++ ) { char *nick; - user_t *u; + irc_user_t *iu; if( !cmd[i][0] || !cmd[i][1] ) break; @@ -477,17 +480,19 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) nick = g_strdup( cmd[i] + 1 ); nick_lc( nick ); - u = user_find( irc, nick ); + iu = irc_user_by_name( irc, nick ); if( cmd[i][0] == '+' ) { if( !g_hash_table_lookup( irc->watches, nick ) ) g_hash_table_insert( irc->watches, nick, nick ); - if( u && u->online ) - irc_send_num( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" ); + if( iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE ) + irc_send_num( irc, 604, "%s %s %s %d :%s", iu->nick, iu->user, + iu->host, (int) time( NULL ), "is online" ); else - irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" ); + irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", + (int) time( NULL ), "is offline" ); } else if( cmd[i][0] == '-' ) { @@ -504,6 +509,7 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) } } +#if 0 static void irc_cmd_topic( irc_t *irc, char **cmd ) { char *channel = cmd[1]; @@ -609,15 +615,15 @@ static const command_t irc_commands[] = { { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, - { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, -#if 0 - { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, - { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "userhost", 1, irc_cmd_userhost, IRC_CMD_LOGGED_IN }, { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, +#if 0 + { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, + { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, #endif + { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "deaf", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "wallops", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, diff --git a/irc_im.c b/irc_im.c index dd015ba9..d6f66d74 100644 --- a/irc_im.c +++ b/irc_im.c @@ -79,14 +79,27 @@ static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu ) static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old ) { irc_t *irc = bee->ui_data; + irc_user_t *iu = bu->ui_data; irc_channel_t *ic = irc->channels->data; /* For now, just pick the first channel. */ if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) ) { if( bu->flags & BEE_USER_ONLINE ) - irc_channel_add_user( ic, (irc_user_t*) bu->ui_data ); + { + if( g_hash_table_lookup( irc->watches, iu->key ) ) + irc_send_num( irc, 600, "%s %s %s %d :%s", iu->nick, iu->user, + iu->host, (int) time( NULL ), "logged online" ); + + irc_channel_add_user( ic, iu ); + } else - irc_channel_del_user( ic, (irc_user_t*) bu->ui_data ); + { + if( g_hash_table_lookup( irc->watches, iu->key ) ) + irc_send_num( irc, 601, "%s %s %s %d :%s", iu->nick, iu->user, + iu->host, (int) time( NULL ), "logged offline" ); + + irc_channel_del_user( ic, iu ); + } } return TRUE; diff --git a/irc_user.c b/irc_user.c index 7b2d4be1..13c6d5bd 100644 --- a/irc_user.c +++ b/irc_user.c @@ -132,6 +132,25 @@ gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ) return strcmp( a->key, b->key ); } +const char *irc_user_get_away( irc_user_t *iu ) +{ + irc_t *irc = iu->irc; + bee_user_t *bu = iu->bu; + + if( iu == irc->user ) + return set_getstr( &irc->b->set, "away" ); + else if( bu ) + { + if( !bu->flags & BEE_USER_ONLINE ) + return "Offline"; + else if( bu->flags & BEE_USER_AWAY ) + /* TODO: status msgs, etc. */ + return bu->status; + } + + return NULL; +} + /* User-type dependent functions, for root/NickServ: */ static gboolean root_privmsg( irc_user_t *iu, const char *msg ) { -- cgit v1.2.3 From 4c3519a8e9f8733577b0ca060a80606955f92cce Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 15 Apr 2010 00:45:09 +0200 Subject: Restored blist command. --- irc_user.c | 13 +++++++++++-- root_commands.c | 49 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/irc_user.c b/irc_user.c index 13c6d5bd..1884e66e 100644 --- a/irc_user.c +++ b/irc_user.c @@ -144,8 +144,17 @@ const char *irc_user_get_away( irc_user_t *iu ) if( !bu->flags & BEE_USER_ONLINE ) return "Offline"; else if( bu->flags & BEE_USER_AWAY ) - /* TODO: status msgs, etc. */ - return bu->status; + { + if( bu->status_msg ) + { + static char ret[MAX_STRING]; + g_snprintf( ret, MAX_STRING - 1, "%s (%s)", + bu->status ? : "Away", bu->status_msg ); + return ret; + } + else + return bu->status ? : "Away"; + } } return NULL; diff --git a/root_commands.c b/root_commands.c index 7a4c021e..3dbf8bcd 100644 --- a/root_commands.c +++ b/root_commands.c @@ -880,11 +880,10 @@ static void cmd_set( irc_t *irc, char **cmd ) cmd_set_real( irc, cmd, NULL, NULL ); } -#if 0 static void cmd_blist( irc_t *irc, char **cmd ) { int online = 0, away = 0, offline = 0; - user_t *u; + GSList *l; char s[256]; char *format; int n_online = 0, n_away = 0, n_offline = 0; @@ -905,40 +904,58 @@ static void cmd_blist( irc_t *irc, char **cmd ) else format = "%-16.16s %-40.40s %s"; - irc_usermsg( irc, format, "Nick", "User/Host/Network", "Status" ); + irc_usermsg( irc, format, "Nick", "Handle/Account", "Status" ); - for( u = irc->users; u; u = u->next ) if( u->ic && u->online && !u->away ) + for( l = irc->users; l; l = l->next ) { + irc_user_t *iu = l->data; + bee_user_t *bu = iu->bu; + + if( !bu || ( bu->flags & ( BEE_USER_ONLINE | BEE_USER_AWAY ) ) != BEE_USER_ONLINE ) + continue; + if( online == 1 ) { char st[256] = "Online"; - if( u->status_msg ) - g_snprintf( st, sizeof( st ) - 1, "Online (%s)", u->status_msg ); + if( bu->status_msg ) + g_snprintf( st, sizeof( st ) - 1, "Online (%s)", bu->status_msg ); - g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user ); - irc_usermsg( irc, format, u->nick, s, st ); + g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user ); + irc_usermsg( irc, format, iu->nick, s, st ); } n_online ++; } - for( u = irc->users; u; u = u->next ) if( u->ic && u->online && u->away ) + for( l = irc->users; l; l = l->next ) { + irc_user_t *iu = l->data; + bee_user_t *bu = iu->bu; + + if( !bu || !( bu->flags & BEE_USER_ONLINE ) || !( bu->flags & BEE_USER_AWAY ) ) + continue; + if( away == 1 ) { - g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user ); - irc_usermsg( irc, format, u->nick, s, u->away ); + g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user ); + irc_usermsg( irc, format, iu->nick, s, irc_user_get_away( iu ) ); } n_away ++; } - for( u = irc->users; u; u = u->next ) if( u->ic && !u->online ) + for( l = irc->users; l; l = l->next ) { + irc_user_t *iu = l->data; + bee_user_t *bu = iu->bu; + + if( !bu || bu->flags & BEE_USER_ONLINE ) + continue; + if( offline == 1 ) { - g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user ); - irc_usermsg( irc, format, u->nick, s, "Offline" ); + g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user ); + irc_usermsg( irc, format, iu->nick, s, "Offline" ); } n_offline ++; } @@ -946,6 +963,7 @@ static void cmd_blist( irc_t *irc, char **cmd ) irc_usermsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline ); } +#if 0 static void cmd_qlist( irc_t *irc, char **cmd ) { query_t *q = irc->queries; @@ -1145,6 +1163,7 @@ static void cmd_transfer( irc_t *irc, char **cmd ) const command_t commands[] = { { "account", 1, cmd_account, 0 }, { "add", 2, cmd_add, 0 }, + { "blist", 0, cmd_blist, 0 }, { "drop", 1, cmd_drop, 0 }, { "help", 0, cmd_help, 0 }, { "identify", 1, cmd_identify, 0 }, @@ -1158,11 +1177,9 @@ const command_t commands[] = { { "yes", 0, cmd_yesno, 0 }, #if 0 { "allow", 1, cmd_allow, 0 }, - { "blist", 0, cmd_blist, 0 }, { "block", 1, cmd_block, 0 }, { "chat", 1, cmd_chat, 0 }, { "ft", 0, cmd_transfer, 0 }, - { "join_chat", 2, cmd_join_chat, 0 }, { "qlist", 0, cmd_qlist, 0 }, { "transfer", 0, cmd_transfer, 0 }, #endif -- cgit v1.2.3 From e7edbb75b23e200a8b6a8e766ede7af319a75dc9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 15 Apr 2010 00:56:19 +0200 Subject: Don't show an empty status message line in /whois if status_msg is "". And don't show version info in the response, WTF was that there anyway? --- irc_send.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/irc_send.c b/irc_send.c index 57d15ade..0d9aba48 100644 --- a/irc_send.c +++ b/irc_send.c @@ -221,7 +221,8 @@ void irc_send_whois( irc_user_t *iu ) bu->ic->acc->server && *bu->ic->acc->server ? bu->ic->acc->server : "", bu->ic->acc->prpl->name ); - if( bu->status || bu->status_msg ) + if( ( bu->status && *bu->status ) || + ( bu->status_msg && *bu->status_msg ) ) { int num = bu->flags & BEE_USER_AWAY ? 301 : 320; @@ -233,7 +234,7 @@ void irc_send_whois( irc_user_t *iu ) } else { - irc_send_num( irc, 312, "%s %s :%s", iu->nick, irc->root->host, IRCD_INFO " " BITLBEE_VERSION ); + irc_send_num( irc, 312, "%s %s :%s", iu->nick, irc->root->host, IRCD_INFO ); } irc_send_num( irc, 318, "%s :End of /WHOIS list", iu->nick ); -- cgit v1.2.3 From a897467549fc75eee8fdd7c255ee5f55c3714ac6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 18 Apr 2010 00:43:55 +0200 Subject: I should stop doing commits with the debugging stuff still enabled. --- protocols/purple/purple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 997b09f7..44a21fae 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -871,7 +871,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - purple_debug_set_ui_ops( &bee_debug_uiops ); + //purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From 9d4352c51300548b4a053dab85517f0dd0cb386c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 21 Apr 2010 01:03:01 +0200 Subject: Restored a few more root commands. --- dcc.c | 2 +- protocols/ft.h | 1 + root_commands.c | 14 +++++++------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/dcc.c b/dcc.c index f17f52aa..470acccc 100644 --- a/dcc.c +++ b/dcc.c @@ -77,7 +77,7 @@ dcc_file_transfer_t *dcc_alloc_transfer( const char *file_name, size_t file_size file->file_size = file_size; file->file_name = g_strdup( file_name ); file->local_id = local_transfer_id++; - df->ic = ic; + file->ic = df->ic = ic; df->ft = file; return df; diff --git a/protocols/ft.h b/protocols/ft.h index c1ee2b49..159f16f2 100644 --- a/protocols/ft.h +++ b/protocols/ft.h @@ -106,6 +106,7 @@ typedef struct file_transfer { * IM-protocol specific data associated with this file transfer. */ gpointer data; + struct im_connection *ic; /* * Private data. diff --git a/root_commands.c b/root_commands.c index 3dbf8bcd..7f835cd8 100644 --- a/root_commands.c +++ b/root_commands.c @@ -963,7 +963,6 @@ static void cmd_blist( irc_t *irc, char **cmd ) irc_usermsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline ); } -#if 0 static void cmd_qlist( irc_t *irc, char **cmd ) { query_t *q = irc->queries; @@ -984,6 +983,7 @@ static void cmd_qlist( irc_t *irc, char **cmd ) irc_usermsg( irc, "%d, BitlBee: %s", num, q->question ); } +#if 0 static set_t **cmd_chat_set_findhead( irc_t *irc, char *id ) { struct chat *c; @@ -1095,6 +1095,7 @@ static void cmd_chat( irc_t *irc, char **cmd ) irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] ); } } +#endif static void cmd_transfer( irc_t *irc, char **cmd ) { @@ -1144,20 +1145,19 @@ static void cmd_transfer( irc_t *irc, char **cmd ) if( file->status == FT_STATUS_LISTENING ) { irc_usermsg( irc, "Rejecting file transfer for %s", file->file_name ); - imcb_file_canceled( file, "Denied by user" ); + imcb_file_canceled( file->ic, file, "Denied by user" ); } break; case CANCEL: if( file->local_id == fid ) { irc_usermsg( irc, "Canceling file transfer for %s", file->file_name ); - imcb_file_canceled( file, "Canceled by user" ); + imcb_file_canceled( file->ic, file, "Canceled by user" ); } break; } } } -#endif /* IMPORTANT: Keep this list sorted! The short command logic needs that. */ const command_t commands[] = { @@ -1165,23 +1165,23 @@ const command_t commands[] = { { "add", 2, cmd_add, 0 }, { "blist", 0, cmd_blist, 0 }, { "drop", 1, cmd_drop, 0 }, + { "ft", 0, cmd_transfer, 0 }, { "help", 0, cmd_help, 0 }, { "identify", 1, cmd_identify, 0 }, { "info", 1, cmd_info, 0 }, { "no", 0, cmd_yesno, 0 }, + { "qlist", 0, cmd_qlist, 0 }, { "register", 1, cmd_register, 0 }, { "remove", 1, cmd_remove, 0 }, { "rename", 2, cmd_rename, 0 }, { "save", 0, cmd_save, 0 }, { "set", 0, cmd_set, 0 }, + { "transfer", 0, cmd_transfer, 0 }, { "yes", 0, cmd_yesno, 0 }, #if 0 { "allow", 1, cmd_allow, 0 }, { "block", 1, cmd_block, 0 }, { "chat", 1, cmd_chat, 0 }, - { "ft", 0, cmd_transfer, 0 }, - { "qlist", 0, cmd_qlist, 0 }, - { "transfer", 0, cmd_transfer, 0 }, #endif { NULL } }; -- cgit v1.2.3 From 2272cb30851366324e844681b97c383c655ccc33 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 21 Apr 2010 01:11:32 +0200 Subject: Restored block/allow commands. Only "chat" is left now, which will need some more rethinking anyway (and groupchat support is not back yet anyway). --- root_commands.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/root_commands.c b/root_commands.c index 7f835cd8..d5da0691 100644 --- a/root_commands.c +++ b/root_commands.c @@ -716,13 +716,14 @@ char *set_eval_control_channel( set_t *set, char *new_name ) return strcmp( irc->channel, new_name ) == 0 ? new_name : SET_INVALID; } +#endif static void cmd_block( irc_t *irc, char **cmd ) { struct im_connection *ic; account_t *a; - if( !cmd[2] && ( a = account_get( irc, cmd[1] ) ) && a->ic ) + if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic ) { char *format; GSList *l; @@ -735,8 +736,9 @@ static void cmd_block( irc_t *irc, char **cmd ) irc_usermsg( irc, format, "Handle", "Nickname" ); for( l = a->ic->deny; l; l = l->next ) { - user_t *u = user_findhandle( a->ic, l->data ); - irc_usermsg( irc, format, l->data, u ? u->nick : "(none)" ); + bee_user_t *bu = bee_user_by_handle( irc->b, a->ic, l->data ); + irc_user_t *iu = bu ? bu->ui_data : NULL; + irc_usermsg( irc, format, l->data, iu ? iu->nick : "(none)" ); } irc_usermsg( irc, "End of list." ); @@ -744,16 +746,16 @@ static void cmd_block( irc_t *irc, char **cmd ) } else if( !cmd[2] ) { - user_t *u = user_find( irc, cmd[1] ); - if( !u || !u->ic ) + irc_user_t *iu = irc_user_by_name( irc, cmd[1] ); + if( !iu || !iu->bu ) { irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); return; } - ic = u->ic; - cmd[2] = u->handle; + ic = iu->bu->ic; + cmd[2] = iu->bu->handle; } - else if( !( a = account_get( irc, cmd[1] ) ) ) + else if( !( a = account_get( irc->b, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); return; @@ -781,7 +783,7 @@ static void cmd_allow( irc_t *irc, char **cmd ) struct im_connection *ic; account_t *a; - if( !cmd[2] && ( a = account_get( irc, cmd[1] ) ) && a->ic ) + if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic ) { char *format; GSList *l; @@ -794,8 +796,9 @@ static void cmd_allow( irc_t *irc, char **cmd ) irc_usermsg( irc, format, "Handle", "Nickname" ); for( l = a->ic->permit; l; l = l->next ) { - user_t *u = user_findhandle( a->ic, l->data ); - irc_usermsg( irc, format, l->data, u ? u->nick : "(none)" ); + bee_user_t *bu = bee_user_by_handle( irc->b, a->ic, l->data ); + irc_user_t *iu = bu ? bu->ui_data : NULL; + irc_usermsg( irc, format, l->data, iu ? iu->nick : "(none)" ); } irc_usermsg( irc, "End of list." ); @@ -803,16 +806,16 @@ static void cmd_allow( irc_t *irc, char **cmd ) } else if( !cmd[2] ) { - user_t *u = user_find( irc, cmd[1] ); - if( !u || !u->ic ) + irc_user_t *iu = irc_user_by_name( irc, cmd[1] ); + if( !iu || !iu->bu ) { irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); return; } - ic = u->ic; - cmd[2] = u->handle; + ic = iu->bu->ic; + cmd[2] = iu->bu->handle; } - else if( !( a = account_get( irc, cmd[1] ) ) ) + else if( !( a = account_get( irc->b, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); return; @@ -835,7 +838,6 @@ static void cmd_allow( irc_t *irc, char **cmd ) irc_usermsg( irc, "Buddy `%s' moved from your block- to your allow-list", cmd[2] ); } } -#endif static void cmd_yesno( irc_t *irc, char **cmd ) { @@ -1163,7 +1165,9 @@ static void cmd_transfer( irc_t *irc, char **cmd ) const command_t commands[] = { { "account", 1, cmd_account, 0 }, { "add", 2, cmd_add, 0 }, + { "allow", 1, cmd_allow, 0 }, { "blist", 0, cmd_blist, 0 }, + { "block", 1, cmd_block, 0 }, { "drop", 1, cmd_drop, 0 }, { "ft", 0, cmd_transfer, 0 }, { "help", 0, cmd_help, 0 }, @@ -1179,8 +1183,6 @@ const command_t commands[] = { { "transfer", 0, cmd_transfer, 0 }, { "yes", 0, cmd_yesno, 0 }, #if 0 - { "allow", 1, cmd_allow, 0 }, - { "block", 1, cmd_block, 0 }, { "chat", 1, cmd_chat, 0 }, #endif { NULL } -- cgit v1.2.3 From b5b40ffd38e315223c6e38e4a291cbd58e471062 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Apr 2010 17:57:34 +0100 Subject: Added BITLBEE_CONFIGURE_FLAGS variable so configure flags can be overridden when generating debs. --- debian/rules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 788e5006..56a02fbb 100755 --- a/debian/rules +++ b/debian/rules @@ -1,5 +1,6 @@ #!/usr/bin/make -f +BITLBEE_CONFIGURE_FLAGS ?= DEBUG ?= 0 ifdef BITLBEE_VERSION @@ -13,7 +14,7 @@ endif build-arch: build-arch-stamp build-arch-stamp: [ -d debian ] - ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent + ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS) $(MAKE) # $(MAKE) -C doc/ all touch build-arch-stamp -- cgit v1.2.3 From c5213622819e578c577158be6b0610ef9486fe45 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Apr 2010 18:04:28 +0100 Subject: libpurple-dev build dependency. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 86488c8a..8f861aeb 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Wilmer van der Gaast Uploaders: Jelmer Vernooij Standards-Version: 3.8.0 -Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf +Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf, libpurple-dev Homepage: http://www.bitlbee.org/ Vcs-Bzr: http://code.bitlbee.org/bitlbee/ DM-Upload-Allowed: yes -- cgit v1.2.3 From a4cdf43ba4247914985765ef26a1d77239a15a10 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 1 May 2010 15:19:09 +0100 Subject: Same fix like 566 in mainline: tm_mon + 1 to get the right month in timestamps. --- irc_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc_util.c b/irc_util.c index e73b86ae..f664a835 100644 --- a/irc_util.c +++ b/irc_util.c @@ -110,6 +110,6 @@ char *irc_format_timestamp( irc_t *irc, time_t msg_ts ) else return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d " "%02d:%02d:%02d\x02]\x02 ", - msg.tm_year + 1900, msg.tm_mon, msg.tm_mday, + msg.tm_year + 1900, msg.tm_mon + 1, msg.tm_mday, msg.tm_hour, msg.tm_min, msg.tm_sec ); } -- cgit v1.2.3 From f4850088ea8527d8cfd46dedea678bb0d5d93471 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 00:55:48 +0100 Subject: Support at least incoming groupchats. Not sure yet how starting them is going to work. --- protocols/purple/purple.c | 78 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 44a21fae..b81415ea 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -364,6 +364,13 @@ static int purple_send_typing( struct im_connection *ic, char *who, int flags ) } } +static void purple_chat_msg( struct groupchat *gc, char *message, int flags ) +{ + PurpleConversation *pc = gc->data; + + purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message ); +} + void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); static void purple_ui_init(); @@ -505,6 +512,64 @@ static PurpleBlistUiOps bee_blist_uiops = prplcb_blist_remove, }; +void prplcb_conv_new( PurpleConversation *conv ) +{ + if( conv->type == PURPLE_CONV_TYPE_CHAT ) + { + struct im_connection *ic = purple_ic_by_pa( conv->account ); + struct groupchat *gc; + + gc = imcb_chat_new( ic, conv->name ); + conv->ui_data = gc; + gc->data = conv; + } +} + +void prplcb_conv_free( PurpleConversation *conv ) +{ + struct groupchat *gc = conv->ui_data; + + imcb_chat_free( gc ); +} + +void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals ) +{ + struct groupchat *gc = conv->ui_data; + GList *b; + + for( b = cbuddies; b; b = b->next ) + { + PurpleConvChatBuddy *pcb = b->data; + + imcb_chat_add_buddy( gc, pcb->name ); + } +} + +void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies ) +{ + struct groupchat *gc = conv->ui_data; + GList *b; + + for( b = cbuddies; b; b = b->next ) + imcb_chat_remove_buddy( gc, b->data, "" ); +} + +void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) +{ + struct groupchat *gc = conv->ui_data; + PurpleBuddy *buddy; + + /* ..._SEND means it's an outgoing message, no need to echo those. */ + if( flags & PURPLE_MESSAGE_SEND ) + return; + + buddy = purple_find_buddy( conv->account, who ); + if( buddy != NULL ) + who = purple_buddy_get_name( buddy ); + + imcb_chat_msg( gc, who, (char*) message, 0, mtime ); +} + static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) { struct im_connection *ic = purple_ic_by_pa( conv->account ); @@ -523,14 +588,14 @@ static void prplcb_conv_im( PurpleConversation *conv, const char *who, const cha static PurpleConversationUiOps bee_conv_uiops = { - NULL, /* create_conversation */ - NULL, /* destroy_conversation */ - NULL, /* write_chat */ + prplcb_conv_new, /* create_conversation */ + prplcb_conv_free, /* destroy_conversation */ + prplcb_conv_chat_msg, /* write_chat */ prplcb_conv_im, /* write_im */ NULL, /* write_conv */ - NULL, /* chat_add_users */ + prplcb_conv_add_users, /* chat_add_users */ NULL, /* chat_rename_user */ - NULL, /* chat_remove_users */ + prplcb_conv_del_users, /* chat_remove_users */ NULL, /* chat_update_user */ NULL, /* present */ NULL, /* has_focus */ @@ -917,7 +982,8 @@ void purple_initmodule() funcs.keepalive = purple_keepalive; funcs.send_typing = purple_send_typing; funcs.handle_cmp = g_strcasecmp; - /* TODO(wilmer): Set this one only for protocols that support it? */ + /* TODO(wilmer): Set these only for protocols that support them? */ + funcs.chat_msg = purple_chat_msg; funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From a348d00feb203641a3c2e8d2e3596f86d4bdbe0b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 16:49:01 +0100 Subject: Although probably the Twitter module won't interfere with libpurple, for now I'll let BitlBee-libpurple be just that. --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index 3799e2da..6b950464 100755 --- a/configure +++ b/configure @@ -532,6 +532,7 @@ EOF jabber=0 oscar=0 yahoo=0 + twitter=0 fi if [ "$msn" = 0 ]; then -- cgit v1.2.3 From 8ad5c34dfc1eff0112f73414fd4b566fae0c9ce3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 16:53:18 +0100 Subject: Added support for creating groupchats. This can only be done in a horribly broken way which is surely going to break somehow someday. --- protocols/purple/purple.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b81415ea..0f60f630 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -52,6 +52,22 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) return purple_ic_by_pa( purple_connection_get_account( gc ) ); } +static gboolean purple_menu_cmp( const char *a, const char *b ) +{ + while( *a && *b ) + { + while( *a == '_' ) a ++; + while( *b == '_' ) b ++; + if( tolower( *a ) != tolower( *b ) ) + return FALSE; + + a ++; + b ++; + } + + return ( *a == '\0' && *b == '\0' ); +} + static void purple_init( account_t *acc ) { PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data ); @@ -371,6 +387,67 @@ static void purple_chat_msg( struct groupchat *gc, char *message, int flags ) purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message ); } +struct groupchat *purple_chat_with( struct im_connection *ic, char *who ) +{ + /* No, "of course" this won't work this way. Or in fact, it almost + does, but it only lets you send msgs to it, you won't receive + any. Instead, we have to click the virtual menu item. + PurpleAccount *pa = ic->proto_data; + PurpleConversation *pc; + PurpleConvChat *pcc; + struct groupchat *gc; + + gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" ); + gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" ); + pc->ui_data = gc; + + pcc = PURPLE_CONV_CHAT( pc ); + purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE ); + purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE ); + //purple_conv_chat_add_user( pcc, who, "", 0, TRUE ); + */ + + /* There went my nice afternoon. :-( */ + + PurpleAccount *pa = ic->proto_data; + PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); + PurplePluginProtocolInfo *pi = prpl->info->extra_info; + PurpleBuddy *pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who ); + PurpleMenuAction *mi; + GList *menu; + void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */ + + if( !pb || !pi || !pi->blist_node_menu ) + return NULL; + + menu = pi->blist_node_menu( &pb->node ); + while( menu ) + { + mi = menu->data; + if( purple_menu_cmp( mi->label, "initiate chat" ) || + purple_menu_cmp( mi->label, "initiate conference" ) ) + break; + menu = menu->next; + } + + if( menu == NULL ) + return NULL; + + /* Call the fucker. */ + callback = (void*) mi->callback; + callback( &pb->node, menu->data ); + + return NULL; +} + +void purple_chat_invite( struct groupchat *gc, char *who, char *message ) +{ + PurpleConversation *pc = gc->data; + PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc ); + + purple_conv_chat_invite_user( pcc, who, message && *message ? message : "Please join my chat", FALSE ); +} + void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); static void purple_ui_init(); @@ -537,6 +614,14 @@ void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean struct groupchat *gc = conv->ui_data; GList *b; + if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 ) + { + /* Work around the broken MSN module which fucks up the user's + handle completely when informing him/her that he just + successfully joined the room s/he just created (v2.6.6). */ + imcb_chat_add_buddy( gc, gc->ic->acc->user ); + } + for( b = cbuddies; b; b = b->next ) { PurpleConvChatBuddy *pcb = b->data; @@ -984,6 +1069,8 @@ void purple_initmodule() funcs.handle_cmp = g_strcasecmp; /* TODO(wilmer): Set these only for protocols that support them? */ funcs.chat_msg = purple_chat_msg; + funcs.chat_with = purple_chat_with; + funcs.chat_invite = purple_chat_invite; funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From 15794dcafac99b2be1c400bc54a510fe61c4ebac Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 17:03:41 +0100 Subject: Groupchat support "finished". Named chatrooms are not supported yet. This only adds support for the "chat with" command and for getting pulled into other people's chats. --- protocols/purple/purple.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 0f60f630..90312d0d 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -448,6 +448,13 @@ void purple_chat_invite( struct groupchat *gc, char *who, char *message ) purple_conv_chat_invite_user( pcc, who, message && *message ? message : "Please join my chat", FALSE ); } +void purple_chat_leave( struct groupchat *gc, char *who ) +{ + PurpleConversation *pc = gc->data; + + purple_conversation_destroy( pc ); +} + void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); static void purple_ui_init(); @@ -1071,6 +1078,7 @@ void purple_initmodule() funcs.chat_msg = purple_chat_msg; funcs.chat_with = purple_chat_with; funcs.chat_invite = purple_chat_invite; + funcs.chat_leave = purple_chat_leave; funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From bda2975f43df2fd2409243082f6d98dc8c9e6fde Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 17:14:14 +0100 Subject: Warning on the libpurple+libevent combination. --- configure | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure b/configure index 6b950464..69d59d0d 100755 --- a/configure +++ b/configure @@ -533,6 +533,13 @@ EOF oscar=0 yahoo=0 twitter=0 + + if [ "$events" = "libevent" ]; then + echo + echo 'Warning: Some libpurple modules (including msn-pecan) do their event handling' + echo 'outside libpurple, talking to GLib directly. At least for now the combination' + echo 'libpurple + libevent is *not* recommended!' + fi fi if [ "$msn" = 0 ]; then -- cgit v1.2.3 From bce78c8e6b9363175943a1b10df76fdbd87ba0c8 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 19:48:26 +0100 Subject: Restored classical control channel behaviour (the ability to talk to contacts in there). --- irc_channel.c | 35 ++++++++++++++++++++++++++++++----- irc_commands.c | 3 +++ irc_im.c | 2 +- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/irc_channel.c b/irc_channel.c index 27f33619..60c2e422 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -144,13 +144,38 @@ gboolean irc_channel_name_ok( const char *name ) /* Channel-type dependent functions, for control channels: */ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) { - char cmd[strlen(msg)+1]; + irc_t *irc = ic->irc; + const char *s; - g_free( ic->irc->last_root_cmd ); - ic->irc->last_root_cmd = g_strdup( ic->name ); + /* Scan for non-whitespace chars followed by a colon: */ + for( s = msg; *s && !isspace( *s ) && *s != ':'; s ++ ) {} - strcpy( cmd, msg ); - root_command_string( ic->irc, cmd ); + if( *s == ':' ) + { + char to[s-msg+1]; + irc_user_t *iu; + + strncpy( to, msg, s - msg ); + while( *(++s) && isspace( *s ) ) {} + + iu = irc_user_by_name( irc, to ); + if( iu && iu->f->privmsg ) + { + iu->flags &= ~IRC_USER_PRIVATE; + iu->f->privmsg( iu, s ); + } + } + else + { + /* TODO: Maybe just use root->privmsg here now? */ + char cmd[strlen(msg)+1]; + + g_free( ic->irc->last_root_cmd ); + ic->irc->last_root_cmd = g_strdup( ic->name ); + + strcpy( cmd, msg ); + root_command_string( ic->irc, cmd ); + } return TRUE; } diff --git a/irc_commands.c b/irc_commands.c index 35169607..dd7cc730 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -287,7 +287,10 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) iu->f->ctcp( iu, ctcp ); } else if( iu->f->privmsg ) + { + iu->flags |= IRC_USER_PRIVATE; iu->f->privmsg( iu, cmd[2] ); + } } else { diff --git a/irc_im.c b/irc_im.c index d6f66d74..1363df4a 100644 --- a/irc_im.c +++ b/irc_im.c @@ -125,7 +125,7 @@ static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, t else { dst = ic->name; - prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ); + prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" ); } wrapped = word_wrap( msg, 425 ); -- cgit v1.2.3 From e54112f152c375df81a21181f755ced5f57165bc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 3 May 2010 00:44:33 +0100 Subject: Put a channel userlist in irc_channel_user elements so we can save flags (i.e. modes). --- irc.h | 15 ++++++++++++++- irc_channel.c | 43 ++++++++++++++++++++++++++++++++++++------- irc_send.c | 7 ++++++- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/irc.h b/irc.h index 3a1a3520..0d233844 100644 --- a/irc.h +++ b/irc.h @@ -152,6 +152,19 @@ struct irc_channel_funcs gboolean (*privmsg)( irc_channel_t *iu, const char *msg ); }; +typedef enum +{ + IRC_CHANNEL_USER_OP = 1, + IRC_CHANNEL_USER_HALFOP = 2, + IRC_CHANNEL_USER_VOICE = 4, +} irc_channel_user_flags_t; + +typedef struct irc_channel_user +{ + irc_user_t *iu; + int flags; +} irc_channel_user_t; + extern const struct bee_ui_funcs irc_ui_funcs; /* irc.c */ @@ -180,7 +193,7 @@ irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ); int irc_channel_free( irc_channel_t *ic ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); -gboolean irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); +irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); gboolean irc_channel_name_ok( const char *name ); diff --git a/irc_channel.c b/irc_channel.c index 60c2e422..17ea64d3 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -25,6 +25,7 @@ #include "bitlbee.h" +static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ ); static const struct irc_channel_funcs control_channel_funcs; irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) @@ -70,7 +71,11 @@ int irc_channel_free( irc_channel_t *ic ) irc_channel_del_user( ic, irc->user ); irc->channels = g_slist_remove( irc->channels, ic ); - g_slist_free( ic->users ); + while( ic->users ) + { + g_free( ic->users->data ); + ic->users = g_slist_remove( ic->users, ic->users->data ); + } g_free( ic->name ); g_free( ic->topic ); @@ -81,10 +86,15 @@ int irc_channel_free( irc_channel_t *ic ) int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) { + irc_channel_user_t *icu; + if( irc_channel_has_user( ic, iu ) ) return 0; - ic->users = g_slist_insert_sorted( ic->users, iu, irc_user_cmp ); + icu = g_new0( irc_channel_user_t, 1 ); + icu->iu = iu; + + ic->users = g_slist_insert_sorted( ic->users, icu, irc_channel_user_cmp ); if( iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED ) { @@ -97,10 +107,13 @@ int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) { - if( !irc_channel_has_user( ic, iu ) ) + irc_channel_user_t *icu; + + if( !( icu = irc_channel_has_user( ic, iu ) ) ) return 0; - ic->users = g_slist_remove( ic->users, iu ); + ic->users = g_slist_remove( ic->users, icu ); + g_free( icu ); if( ic->flags & IRC_CHANNEL_JOINED ) irc_send_part( ic, iu, "" ); @@ -111,10 +124,19 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) return 1; } -/* Currently a fairly stupid one-liner but I fear it's going to get worse. :-) */ -gboolean irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ) +irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ) { - return g_slist_find( ic->users, iu ) != NULL; + GSList *l; + + for( l = ic->users; l; l = l->next ) + { + irc_channel_user_t *icu = l->data; + + if( icu->iu == iu ) + return icu; + } + + return NULL; } int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *iu ) @@ -141,6 +163,13 @@ gboolean irc_channel_name_ok( const char *name ) return strchr( CTYPES, name[0] ) != NULL && nick_ok( name + 1 ); } +static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ ) +{ + const irc_channel_user_t *a = a_, *b = b_; + + return irc_user_cmp( a->iu, b->iu ); +} + /* Channel-type dependent functions, for control channels: */ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) { diff --git a/irc_send.c b/irc_send.c index 0d9aba48..a1df68b2 100644 --- a/irc_send.c +++ b/irc_send.c @@ -162,7 +162,8 @@ void irc_send_names( irc_channel_t *ic ) channel is invalid, just give an empty reply. */ for( l = ic->users; l; l = l->next ) { - irc_user_t *iu = l->data; + irc_channel_user_t *icu = l->data; + irc_user_t *iu = icu->iu; if( strlen( namelist ) + strlen( iu->nick ) > sizeof( namelist ) - 4 ) { @@ -242,9 +243,13 @@ void irc_send_whois( irc_user_t *iu ) void irc_send_who( irc_t *irc, GSList *l, const char *channel ) { + gboolean is_channel = strcmp( channel, "**" ) != 0; + while( l ) { irc_user_t *iu = l->data; + if( is_channel ) + iu = ((irc_channel_user_t*)iu)->iu; /* TODO(wilmer): Restore away/channel information here */ irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", channel ? : "*", iu->user, iu->host, irc->root->host, -- cgit v1.2.3 From 6a9d068e73b6d08056302fdc85dd706a2dd647a5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 3 May 2010 01:39:39 +0100 Subject: Restore away_devoice. --- irc.c | 4 ++++ irc.h | 3 +++ irc_channel.c | 16 ++++++++++++++++ irc_im.c | 4 ++++ irc_send.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 81 insertions(+), 7 deletions(-) diff --git a/irc.c b/irc.c index 93b41071..4a7ba332 100644 --- a/irc.c +++ b/irc.c @@ -639,6 +639,10 @@ int irc_check_login( irc_t *irc ) irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); irc_channel_add_user( ic, irc->user ); + if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 || + strcmp( set_getstr( &irc->b->set, "ops" ), "user" ) == 0 ) + irc_channel_user_set_mode( ic, irc->user, IRC_CHANNEL_USER_OP ); + irc->last_root_cmd = g_strdup( ROOT_CHAN ); irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN, diff --git a/irc.h b/irc.h index 0d233844..2d1987eb 100644 --- a/irc.h +++ b/irc.h @@ -195,6 +195,7 @@ int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); +void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ); gboolean irc_channel_name_ok( const char *name ); /* irc_commands.c */ @@ -215,6 +216,8 @@ void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg ); void irc_send_msg_f( irc_user_t *iu, const char *type, const char *dst, const char *format, ... ) G_GNUC_PRINTF( 4, 5 ); void irc_send_nick( irc_user_t *iu, const char *new ); +void irc_send_channel_user_mode_diff( irc_channel_t *ic, irc_user_t *iu, + irc_channel_user_flags_t old, irc_channel_user_flags_t new ); /* irc_user.c */ irc_user_t *irc_user_new( irc_t *irc, const char *nick ); diff --git a/irc_channel.c b/irc_channel.c index 17ea64d3..fd79ba71 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -42,6 +42,9 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) strcpy( ic->mode, CMODE ); irc_channel_add_user( ic, irc->root ); + if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 || + strcmp( set_getstr( &irc->b->set, "ops" ), "root" ) == 0 ) + irc_channel_user_set_mode( ic, irc->root, IRC_CHANNEL_USER_OP ); irc->channels = g_slist_prepend( irc->channels, ic ); @@ -158,6 +161,19 @@ int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_ return 1; } +void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ) +{ + irc_channel_user_t *icu = irc_channel_has_user( ic, iu ); + + if( icu->flags == flags ) + return; + + if( ic->flags & IRC_CHANNEL_JOINED ) + irc_send_channel_user_mode_diff( ic, iu, icu->flags, flags ); + + icu->flags = flags; +} + gboolean irc_channel_name_ok( const char *name ) { return strchr( CTYPES, name[0] ) != NULL && nick_ok( name + 1 ); diff --git a/irc_im.c b/irc_im.c index 1363df4a..00ea3cd7 100644 --- a/irc_im.c +++ b/irc_im.c @@ -91,6 +91,10 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old iu->host, (int) time( NULL ), "logged online" ); irc_channel_add_user( ic, iu ); + + if( set_getbool( &bee->set, "away_devoice" ) ) + irc_channel_user_set_mode( ic, iu, ( bu->flags & BEE_USER_AWAY ) ? + 0 : IRC_CHANNEL_USER_VOICE ); } else { diff --git a/irc_send.c b/irc_send.c index a1df68b2..d3485221 100644 --- a/irc_send.c +++ b/irc_send.c @@ -156,7 +156,6 @@ void irc_send_names( irc_channel_t *ic ) { GSList *l; char namelist[385] = ""; - //char *ops = set_getstr( &ic->irc->b->set, "ops" ); /* RFCs say there is no error reply allowed on NAMES, so when the channel is invalid, just give an empty reply. */ @@ -171,13 +170,12 @@ void irc_send_names( irc_channel_t *ic ) *namelist = 0; } - /* - if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) ) - strcat( namelist, "+" ); - else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) || - ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ) + if( icu->flags & IRC_CHANNEL_USER_OP ) strcat( namelist, "@" ); - */ + else if( icu->flags & IRC_CHANNEL_USER_HALFOP ) + strcat( namelist, "%" ); + else if( icu->flags & IRC_CHANNEL_USER_VOICE ) + strcat( namelist, "+" ); strcat( namelist, iu->nick ); strcat( namelist, " " ); @@ -326,3 +324,52 @@ void irc_send_nick( irc_user_t *iu, const char *new ) irc_write( iu->irc, ":%s!%s@%s NICK %s", iu->nick, iu->user, iu->host, new ); } + +/* Send an update of a user's mode inside a channel, compared to what it was. */ +void irc_send_channel_user_mode_diff( irc_channel_t *ic, irc_user_t *iu, + irc_channel_user_flags_t old, irc_channel_user_flags_t new ) +{ + char changes[3*(5+strlen(iu->nick))]; + char from[strlen(ic->irc->root->nick)+strlen(ic->irc->root->user)+strlen(ic->irc->root->host)+3]; + int n; + + *changes = '\0'; n = 0; + if( ( old & IRC_CHANNEL_USER_OP ) != ( new & IRC_CHANNEL_USER_OP ) ) + { + n ++; + if( new & IRC_CHANNEL_USER_OP ) + strcat( changes, "+o" ); + else + strcat( changes, "-o" ); + } + if( ( old & IRC_CHANNEL_USER_HALFOP ) != ( new & IRC_CHANNEL_USER_HALFOP ) ) + { + n ++; + if( new & IRC_CHANNEL_USER_HALFOP ) + strcat( changes, "+h" ); + else + strcat( changes, "-h" ); + } + if( ( old & IRC_CHANNEL_USER_VOICE ) != ( new & IRC_CHANNEL_USER_VOICE ) ) + { + n ++; + if( new & IRC_CHANNEL_USER_VOICE ) + strcat( changes, "+v" ); + else + strcat( changes, "-v" ); + } + while( n ) + { + strcat( changes, " " ); + strcat( changes, iu->nick ); + n --; + } + + if( set_getbool( &ic->irc->b->set, "simulate_netsplit" ) ) + g_snprintf( from, sizeof( from ), "%s", ic->irc->root->host ); + else + g_snprintf( from, sizeof( from ), "%s!%s@%s", ic->irc->root->nick, + ic->irc->root->user, ic->irc->root->host ); + + irc_write( ic->irc, ":%s MODE %s %s", from, ic->name, changes ); +} -- cgit v1.2.3 From 1a3ba05edfd8228908999d7c589f039513558539 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 3 May 2010 12:55:06 +0100 Subject: Fixed strncpy() usage, and show error when trying to msg non-existent people via the channel. --- irc_channel.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/irc_channel.c b/irc_channel.c index fd79ba71..d318c397 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -200,6 +200,7 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) char to[s-msg+1]; irc_user_t *iu; + memset( to, 0, sizeof( to ) ); strncpy( to, msg, s - msg ); while( *(++s) && isspace( *s ) ) {} @@ -209,6 +210,11 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) iu->flags &= ~IRC_USER_PRIVATE; iu->f->privmsg( iu, s ); } + else + { + irc_send_msg_f( irc->root, "PRIVMSG", ic->name, + "User does not exist: %s", to ); + } } else { -- cgit v1.2.3 From 0d4a068823e4a205c465f10a05ab699f0cef8e06 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 3 May 2010 23:58:15 +0100 Subject: Removed some disabled code related to away_devoice (now implemented anyway). --- protocols/bee_user.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 7a38882b..8db2fa28 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -138,29 +138,6 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, g_free( old->status_msg ); g_free( old->status ); g_free( old ); -#if 0 - /* LISPy... */ - if( ( set_getbool( &ic->bee->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ - ( u->online ) && /* Don't touch offline people */ - ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ - ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ - { - char *from; - - if( set_getbool( &ic->bee->set, "simulate_netsplit" ) ) - { - from = g_strdup( ic->irc->myhost ); - } - else - { - from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, - ic->irc->myhost ); - } - irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, - u->away?'-':'+', u->nick ); - g_free( from ); - } -#endif } void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) -- cgit v1.2.3 From 9893da32ab881e135748295fc5c48aada552098b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 4 May 2010 00:55:33 +0100 Subject: Add irc_channel_printf() for slightly saner root messages from a random channel. --- irc.h | 1 + irc_channel.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/irc.h b/irc.h index 2d1987eb..75342d05 100644 --- a/irc.h +++ b/irc.h @@ -196,6 +196,7 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ); +void irc_channel_printf( irc_channel_t *ic, char *format, ... ); gboolean irc_channel_name_ok( const char *name ); /* irc_commands.c */ diff --git a/irc_channel.c b/irc_channel.c index d318c397..2988837e 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -174,6 +174,19 @@ void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_u icu->flags = flags; } +void irc_channel_printf( irc_channel_t *ic, char *format, ... ) +{ + va_list params; + char *text; + + va_start( params, format ); + text = g_strdup_vprintf( format, params ); + va_end( params ); + + irc_send_msg( ic->irc->root, "PRIVMSG", ic->name, text, NULL ); + g_free( text ); +} + gboolean irc_channel_name_ok( const char *name ) { return strchr( CTYPES, name[0] ) != NULL && nick_ok( name + 1 ); @@ -212,8 +225,7 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) } else { - irc_send_msg_f( irc->root, "PRIVMSG", ic->name, - "User does not exist: %s", to ); + irc_channel_printf( ic, "User does not exist: %s", to ); } } else -- cgit v1.2.3 From eb504957c2ffa1a3130951d5381672fb3ef9dfd9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 4 May 2010 09:45:10 +0100 Subject: Show offline/away status better in /WHO and /WHOIS. --- irc.h | 1 + irc_im.c | 6 ++++++ irc_send.c | 7 ++++++- protocols/bee_user.c | 3 +++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/irc.h b/irc.h index 75342d05..86a731c5 100644 --- a/irc.h +++ b/irc.h @@ -90,6 +90,7 @@ typedef struct irc typedef enum { IRC_USER_PRIVATE = 1, + IRC_USER_AWAY = 2, } irc_user_flags_t; typedef struct irc_user diff --git a/irc_im.c b/irc_im.c index 00ea3cd7..fe7287b5 100644 --- a/irc_im.c +++ b/irc_im.c @@ -82,6 +82,12 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old irc_user_t *iu = bu->ui_data; irc_channel_t *ic = irc->channels->data; /* For now, just pick the first channel. */ + /* Do this outside the if below since away state can change without + the online state changing. */ + iu->flags &= ~IRC_USER_AWAY; + if( bu->flags & BEE_USER_AWAY || !( bu->flags & BEE_USER_ONLINE ) ) + iu->flags |= IRC_USER_AWAY; + if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) ) { if( bu->flags & BEE_USER_ONLINE ) diff --git a/irc_send.c b/irc_send.c index d3485221..b3283152 100644 --- a/irc_send.c +++ b/irc_send.c @@ -230,6 +230,10 @@ void irc_send_whois( irc_user_t *iu ) else irc_send_num( irc, num, "%s :%s", iu->nick, bu->status ? : bu->status_msg ); } + else if( !( bu->flags & BEE_USER_ONLINE ) ) + { + irc_send_num( irc, 301, "%s :%s", iu->nick, "User is offline" ); + } } else { @@ -251,7 +255,8 @@ void irc_send_who( irc_t *irc, GSList *l, const char *channel ) /* TODO(wilmer): Restore away/channel information here */ irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s", channel ? : "*", iu->user, iu->host, irc->root->host, - iu->nick, 'H', iu->fullname ); + iu->nick, iu->flags & IRC_USER_AWAY ? 'G' : 'H', + iu->fullname ); l = l->next; } diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 8db2fa28..0dd40cab 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -42,6 +42,9 @@ bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *hand if( bee->ui->user_new ) bee->ui->user_new( bee, bu ); + /* Offline by default. This will set the right flags. */ + imcb_buddy_status( ic, handle, 0, NULL, NULL ); + return bu; } -- cgit v1.2.3 From 94383231eddf56112cf74f2ae65d691821d70803 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 5 May 2010 00:38:18 +0100 Subject: Use irc_channel_name_ok() and check if a channel with that name already exists before creating it. --- irc_channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc_channel.c b/irc_channel.c index 2988837e..63c46d95 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -32,7 +32,7 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) { irc_channel_t *ic; - if( strchr( CTYPES, name[0] ) == NULL || !nick_ok( name + 1 ) ) + if( !irc_channel_name_ok( name ) || irc_channel_by_name( irc, name ) ) return NULL; ic = g_new0( irc_channel_t, 1 ); -- cgit v1.2.3 From aea8b68bd0e057441d671c008200e71dd046a211 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 6 May 2010 01:28:56 +0100 Subject: Starting to restore chatroom stuff. Only enough to create and be joined into a room. More will follow soon. --- irc.h | 1 + irc_im.c | 69 +++++++++++ protocols/Makefile | 2 +- protocols/bee.c | 3 + protocols/bee.h | 26 ++++ protocols/jabber/conference.c | 4 +- protocols/jabber/jabber.c | 2 +- protocols/jabber/presence.c | 4 +- protocols/nogaim.c | 270 ------------------------------------------ protocols/nogaim.h | 8 +- 10 files changed, 111 insertions(+), 278 deletions(-) diff --git a/irc.h b/irc.h index 86a731c5..cb34423b 100644 --- a/irc.h +++ b/irc.h @@ -146,6 +146,7 @@ typedef struct irc_channel struct set *set; const struct irc_channel_funcs *f; + void *data; } irc_channel_t; struct irc_channel_funcs diff --git a/irc_im.c b/irc_im.c index fe7287b5..513b75b9 100644 --- a/irc_im.c +++ b/irc_im.c @@ -202,6 +202,68 @@ static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) return TRUE; } + +/* Groupchats */ +gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) +{ + irc_t *irc = bee->ui_data; + irc_channel_t *ic; + char *topic; + int i; + + for( i = 0; i <= 999; i ++ ) + { + char name[16]; + sprintf( name, "&chat_%03d", i ); + if( ( ic = irc_channel_new( irc, name ) ) ) + break; + } + + if( ic == NULL ) + return FALSE; + + c->ui_data = ic; + ic->data = c; + + topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); + irc_channel_set_topic( ic, topic, irc->root ); + g_free( topic ); + + return TRUE; +} + +gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) +{ + irc_channel_t *ic = c->ui_data; + + if( ic->flags & IRC_CHANNEL_JOINED ) + irc_channel_printf( ic, "Cleaning up channel, bye!" ); + + irc_channel_free( ic ); + + return TRUE; +} + +gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *format, ... ) +{ +} + +gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, const char *who, const char *msg, time_t sent_at ) +{ +} + +gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) +{ + irc_t *irc = bee->ui_data; + + irc_channel_add_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data ); +} + +gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) +{ +} + + /* File transfers */ static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ) { @@ -236,6 +298,13 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_user_msg, bee_irc_user_typing, + bee_irc_chat_new, + bee_irc_chat_free, + NULL, + NULL, + bee_irc_chat_add_user, + NULL, + bee_irc_ft_in_start, bee_irc_ft_out_start, bee_irc_ft_close, diff --git a/protocols/Makefile b/protocols/Makefile index 46c73559..bf2533cb 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -9,7 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -objects = account.o bee.o bee_ft.o bee_user.o nogaim.o +objects = account.o bee.o bee_chat.o bee_ft.o bee_user.o nogaim.o # [SH] The next two lines should contain the directory name (in $(subdirs)) diff --git a/protocols/bee.c b/protocols/bee.c index de9550c2..8c38d550 100644 --- a/protocols/bee.c +++ b/protocols/bee.c @@ -46,6 +46,8 @@ bee_t *bee_new() s->flags |= SET_NULL_OK; s = set_add( &b->set, "strip_html", "true", NULL, b ); + b->user = g_malloc( 1 ); + return b; } @@ -69,6 +71,7 @@ void bee_free( bee_t *b ) while( b->set ) set_del( &b->set, b->set->key ); + g_free( b->user ); g_free( b ); } diff --git a/protocols/bee.h b/protocols/bee.h index 62f60477..f69d29d4 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -27,6 +27,7 @@ #define __BEE_H__ struct bee_ui_funcs; +struct groupchat; typedef struct bee { @@ -35,6 +36,11 @@ typedef struct bee GSList *users; struct account *accounts; /* TODO(wilmer): Use GSList here too? */ + /* Symbolic, to refer to the local user (who has no real bee_user + object). Not to be used by anything except so far imcb_chat_add/ + remove_buddy(). This seems slightly cleaner than abusing NULL. */ + struct bee_user *user; + const struct bee_ui_funcs *ui; void *ui_data; } bee_t; @@ -72,6 +78,13 @@ typedef struct bee_ui_funcs gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ); gboolean (*user_typing)( bee_t *bee, bee_user_t *bu, guint32 flags ); + gboolean (*chat_new)( bee_t *bee, struct groupchat *c ); + gboolean (*chat_free)( bee_t *bee, struct groupchat *c ); + gboolean (*chat_log)( bee_t *bee, struct groupchat *c, const char *format, ... ); + gboolean (*chat_msg)( bee_t *bee, struct groupchat *c, const char *who, const char *msg, time_t sent_at ); + gboolean (*chat_add_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); + gboolean (*chat_remove_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); + struct file_transfer* (*ft_in_start)( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ); gboolean (*ft_out_start)( struct im_connection *ic, struct file_transfer *ft ); void (*ft_close)( struct im_connection *ic, struct file_transfer *ft ); @@ -100,4 +113,17 @@ G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *ha /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, guint32 flags, time_t sent_at ); +/* bee_chat.c */ +#if 0 +struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ); +void imcb_chat_name_hint( struct groupchat *c, const char *name ); +void imcb_chat_free( struct groupchat *c ); +void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at ); +void imcb_chat_log( struct groupchat *c, char *format, ... ); +void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ); +void imcb_chat_add_buddy( struct groupchat *b, const char *handle ); +void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ); +static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ); +#endif + #endif /* __BEE_H__ */ diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index f434c58a..17108428 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -91,11 +91,13 @@ static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_no struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name ) { char *normalized = jabber_normalize( name ); + GSList *l; struct groupchat *ret; struct jabber_chat *jc; - for( ret = ic->groupchats; ret; ret = ret->next ) + for( l = ic->groupchats; l; l = l->next ) { + ret = l->data; jc = ret->data; if( strcmp( normalized, jc->name ) == 0 ) break; diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index acad525e..75bc44d3 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -281,7 +281,7 @@ static void jabber_logout( struct im_connection *ic ) jabber_end_stream( ic ); while( ic->groupchats ) - jabber_chat_free( ic->groupchats ); + jabber_chat_free( ic->groupchats->data ); if( jd->r_inpa >= 0 ) b_event_remove( jd->r_inpa ); diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 006eeead..dadccfb9 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -205,6 +205,7 @@ int presence_send_update( struct im_connection *ic ) struct jabber_data *jd = ic->proto_data; struct xt_node *node, *cap; struct groupchat *c; + GSList *l; int st; node = jabber_make_packet( "presence", NULL, NULL, NULL ); @@ -228,8 +229,9 @@ int presence_send_update( struct im_connection *ic ) /* Have to send this update to all groupchats too, the server won't do this automatically. */ - for( c = ic->groupchats; c && st; c = c->next ) + for( l = ic->groupchats; l && st; l = l->next ) { + struct groupchat *c = l->data; struct jabber_chat *jc = c->data; xt_add_attr( node, "to", jc->my_full_jid ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 74ec0642..149e64f1 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -536,276 +536,6 @@ struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *han return bee_user_by_handle( ic->bee, ic, handle ); } -struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) -{ -#if 0 - struct groupchat *c; - - /* This one just creates the conversation structure, user won't see anything yet */ - - if( ic->groupchats ) - { - for( c = ic->groupchats; c->next; c = c->next ); - c = c->next = g_new0( struct groupchat, 1 ); - } - else - ic->groupchats = c = g_new0( struct groupchat, 1 ); - - c->ic = ic; - c->title = g_strdup( handle ); - c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ ); - c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); - - if( set_getbool( &ic->bee->set, "debug" ) ) - imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle ); - - return c; -#endif - return NULL; -} - -void imcb_chat_name_hint( struct groupchat *c, const char *name ) -{ -#if 0 - if( !c->joined ) - { - struct im_connection *ic = c->ic; - char stripped[MAX_NICK_LENGTH+1], *full_name; - - strncpy( stripped, name, MAX_NICK_LENGTH ); - stripped[MAX_NICK_LENGTH] = '\0'; - nick_strip( stripped ); - if( set_getbool( &ic->irc->set, "lcnicks" ) ) - nick_lc( stripped ); - - full_name = g_strdup_printf( "&%s", stripped ); - - if( stripped[0] && - nick_cmp( stripped, ic->irc->channel + 1 ) != 0 && - irc_chat_by_channel( ic->irc, full_name ) == NULL ) - { - g_free( c->channel ); - c->channel = full_name; - } - else - { - g_free( full_name ); - } - } -#endif -} - -void imcb_chat_free( struct groupchat *c ) -{ -#if 0 - struct im_connection *ic = c->ic; - struct groupchat *l; - GList *ir; - - if( set_getbool( &ic->bee->set, "debug" ) ) - imcb_log( ic, "You were removed from conversation %p", c ); - - if( c ) - { - if( c->joined ) - { - user_t *u, *r; - - r = user_find( ic->irc, ic->irc->mynick ); - irc_privmsg( ic->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" ); - - u = user_find( ic->irc, ic->irc->nick ); - irc_kick( ic->irc, u, c->channel, r ); - /* irc_part( ic->irc, u, c->channel ); */ - } - - /* Find the previous chat in the linked list. */ - for( l = ic->groupchats; l && l->next != c; l = l->next ); - - if( l ) - l->next = c->next; - else - ic->groupchats = c->next; - - for( ir = c->in_room; ir; ir = ir->next ) - g_free( ir->data ); - g_list_free( c->in_room ); - g_free( c->channel ); - g_free( c->title ); - g_free( c->topic ); - g_free( c ); - } -#endif -} - -void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at ) -{ -#if 0 - struct im_connection *ic = c->ic; - char *wrapped; - user_t *u; - - /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ - if( g_strcasecmp( who, ic->acc->user ) == 0 ) - return; - - u = user_findhandle( ic, who ); - - if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) - strip_html( msg ); - - wrapped = word_wrap( msg, 425 ); - if( c && u ) - { - char *ts = NULL; - if( set_getbool( &ic->irc->set, "display_timestamps" ) ) - ts = format_timestamp( ic->irc, sent_at ); - irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped ); - g_free( ts ); - } - else - { - imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped ); - } - g_free( wrapped ); -#endif -} - -void imcb_chat_log( struct groupchat *c, char *format, ... ) -{ -#if 0 - irc_t *irc = c->ic->irc; - va_list params; - char *text; - user_t *u; - - va_start( params, format ); - text = g_strdup_vprintf( format, params ); - va_end( params ); - - u = user_find( irc, irc->mynick ); - - irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text ); - - g_free( text ); -#endif -} - -void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ) -{ -#if 0 - struct im_connection *ic = c->ic; - user_t *u = NULL; - - if( who == NULL) - u = user_find( ic->irc, ic->irc->mynick ); - else if( g_strcasecmp( who, ic->acc->user ) == 0 ) - u = user_find( ic->irc, ic->irc->nick ); - else - u = user_findhandle( ic, who ); - - if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) - strip_html( topic ); - - g_free( c->topic ); - c->topic = g_strdup( topic ); - - if( c->joined && u ) - irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic ); -#endif -} - -void imcb_chat_add_buddy( struct groupchat *b, const char *handle ) -{ -#if 0 - user_t *u = user_findhandle( b->ic, handle ); - int me = 0; - - if( set_getbool( &b->ic->bee->set, "debug" ) ) - imcb_log( b->ic, "User %s added to conversation %p", handle, b ); - - /* It might be yourself! */ - if( b->ic->acc->prpl->handle_cmp( handle, b->ic->acc->user ) == 0 ) - { - u = user_find( b->ic->irc, b->ic->irc->nick ); - if( !b->joined ) - irc_join( b->ic->irc, u, b->channel ); - b->joined = me = 1; - } - - /* Most protocols allow people to join, even when they're not in - your contact list. Try to handle that here */ - if( !u ) - { - imcb_add_buddy( b->ic, handle, NULL ); - u = user_findhandle( b->ic, handle ); - } - - /* Add the handle to the room userlist, if it's not 'me' */ - if( !me ) - { - if( b->joined ) - irc_join( b->ic->irc, u, b->channel ); - b->in_room = g_list_append( b->in_room, g_strdup( handle ) ); - } -#endif -} - -/* This function is one BIG hack... :-( EREWRITE */ -void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ) -{ -#if 0 - user_t *u; - int me = 0; - - if( set_getbool( &b->ic->bee->set, "debug" ) ) - imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" ); - - /* It might be yourself! */ - if( g_strcasecmp( handle, b->ic->acc->user ) == 0 ) - { - if( b->joined == 0 ) - return; - - u = user_find( b->ic->irc, b->ic->irc->nick ); - b->joined = 0; - me = 1; - } - else - { - u = user_findhandle( b->ic, handle ); - } - - if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) ) - irc_part( b->ic->irc, u, b->channel ); -#endif -} - -#if 0 -static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) -{ - GList *i; - - /* Find the handle in the room userlist and shoot it */ - i = b->in_room; - while( i ) - { - if( g_strcasecmp( handle, i->data ) == 0 ) - { - g_free( i->data ); - b->in_room = g_list_remove( b->in_room, i->data ); - return( 1 ); - } - - i = i->next; - } - - return 0; -} -#endif - /* Misc. BitlBee stuff which shouldn't really be here */ #if 0 diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 6632827c..580b4001 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -88,7 +88,7 @@ struct im_connection /* BitlBee */ bee_t *bee; - struct groupchat *groupchats; + GSList *groupchats; }; struct groupchat { @@ -99,10 +99,9 @@ struct groupchat { * "nick list". This is how you can check who is in the group chat * already, for example to avoid adding somebody two times. */ GList *in_room; - GList *ignored; + //GList *ignored; - struct groupchat *next; - char *channel; + //struct groupchat *next; /* The title variable contains the ID you gave when you created the * chat using imcb_chat_new(). */ char *title; @@ -113,6 +112,7 @@ struct groupchat { /* This is for you, you can add your own structure here to extend this * structure for your protocol's needs. */ void *data; + void *ui_data; }; struct buddy { -- cgit v1.2.3 From f1a089067fcc4fcae05cdbfa0f51a8a3cc8f6783 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 00:41:49 +0100 Subject: Would be nice to include bee_chat.c in the repo... --- protocols/bee_chat.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 protocols/bee_chat.c diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c new file mode 100644 index 00000000..501bb6aa --- /dev/null +++ b/protocols/bee_chat.c @@ -0,0 +1,263 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle rooms */ + +/* + 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" + +struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) +{ + struct groupchat *c = g_new0( struct groupchat, 1 ); + bee_t *bee = ic->bee; + + /* This one just creates the conversation structure, user won't see + anything yet until s/he is joined to the conversation. (This + allows you to add other already present participants first.) */ + + ic->groupchats = g_slist_prepend( ic->groupchats, c ); + c->ic = ic; + c->title = g_strdup( handle ); + c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); + + if( set_getbool( &ic->bee->set, "debug" ) ) + imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle ); + + if( bee->ui->chat_new ) + bee->ui->chat_new( bee, c ); + + return c; +} + +void imcb_chat_name_hint( struct groupchat *c, const char *name ) +{ +#if 0 + if( !c->joined ) + { + struct im_connection *ic = c->ic; + char stripped[MAX_NICK_LENGTH+1], *full_name; + + strncpy( stripped, name, MAX_NICK_LENGTH ); + stripped[MAX_NICK_LENGTH] = '\0'; + nick_strip( stripped ); + if( set_getbool( &ic->irc->set, "lcnicks" ) ) + nick_lc( stripped ); + + full_name = g_strdup_printf( "&%s", stripped ); + + if( stripped[0] && + nick_cmp( stripped, ic->irc->channel + 1 ) != 0 && + irc_chat_by_channel( ic->irc, full_name ) == NULL ) + { + g_free( c->channel ); + c->channel = full_name; + } + else + { + g_free( full_name ); + } + } +#endif +} + +void imcb_chat_free( struct groupchat *c ) +{ + struct im_connection *ic = c->ic; + bee_t *bee = ic->bee; + GList *ir; + + if( bee->ui->chat_free ) + bee->ui->chat_free( bee, c ); + + if( set_getbool( &ic->bee->set, "debug" ) ) + imcb_log( ic, "You were removed from conversation %p", c ); + + ic->groupchats = g_slist_remove( ic->groupchats, c ); + + for( ir = c->in_room; ir; ir = ir->next ) + g_free( ir->data ); + g_list_free( c->in_room ); + g_free( c->title ); + g_free( c->topic ); + g_free( c ); +} + +void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at ) +{ +#if 0 + struct im_connection *ic = c->ic; + char *wrapped; + user_t *u; + + /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ + if( g_strcasecmp( who, ic->acc->user ) == 0 ) + return; + + u = user_findhandle( ic, who ); + + if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) + strip_html( msg ); + + wrapped = word_wrap( msg, 425 ); + if( c && u ) + { + char *ts = NULL; + if( set_getbool( &ic->irc->set, "display_timestamps" ) ) + ts = format_timestamp( ic->irc, sent_at ); + irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped ); + g_free( ts ); + } + else + { + imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped ); + } + g_free( wrapped ); +#endif +} + +void imcb_chat_log( struct groupchat *c, char *format, ... ) +{ +#if 0 + irc_t *irc = c->ic->irc; + va_list params; + char *text; + user_t *u; + + va_start( params, format ); + text = g_strdup_vprintf( format, params ); + va_end( params ); + + u = user_find( irc, irc->mynick ); + + irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text ); + + g_free( text ); +#endif +} + +void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ) +{ +#if 0 + struct im_connection *ic = c->ic; + user_t *u = NULL; + + if( who == NULL) + u = user_find( ic->irc, ic->irc->mynick ); + else if( g_strcasecmp( who, ic->acc->user ) == 0 ) + u = user_find( ic->irc, ic->irc->nick ); + else + u = user_findhandle( ic, who ); + + if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) + strip_html( topic ); + + g_free( c->topic ); + c->topic = g_strdup( topic ); + + if( c->joined && u ) + irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic ); +#endif +} + +void imcb_chat_add_buddy( struct groupchat *c, const char *handle ) +{ + struct im_connection *ic = c->ic; + bee_t *bee = ic->bee; + bee_user_t *bu = bee_user_by_handle( bee, ic, handle ); + gboolean me; + + if( set_getbool( &c->ic->bee->set, "debug" ) ) + imcb_log( c->ic, "User %s added to conversation %p", handle, c ); + + me = ic->acc->prpl->handle_cmp( handle, ic->acc->user ) == 0; + + /* Most protocols allow people to join, even when they're not in + your contact list. Try to handle that here */ + if( !me && !bu ) + bu = bee_user_new( bee, ic, handle ); + + /* Add the handle to the room userlist */ + /* TODO: Use bu instead of a string */ + c->in_room = g_list_append( c->in_room, g_strdup( handle ) ); + + if( bee->ui->chat_add_user ) + bee->ui->chat_add_user( bee, c, me ? bee->user : bu ); + + if( me ) + c->joined = 1; +} + +/* This function is one BIG hack... :-( EREWRITE */ +void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ) +{ +#if 0 + user_t *u; + int me = 0; + + if( set_getbool( &b->ic->bee->set, "debug" ) ) + imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" ); + + /* It might be yourself! */ + if( g_strcasecmp( handle, b->ic->acc->user ) == 0 ) + { + if( b->joined == 0 ) + return; + + u = user_find( b->ic->irc, b->ic->irc->nick ); + b->joined = 0; + me = 1; + } + else + { + u = user_findhandle( b->ic, handle ); + } + + if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) ) + irc_part( b->ic->irc, u, b->channel ); +#endif +} + +#if 0 +static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) +{ + GList *i; + + /* Find the handle in the room userlist and shoot it */ + i = b->in_room; + while( i ) + { + if( g_strcasecmp( handle, i->data ) == 0 ) + { + g_free( i->data ); + b->in_room = g_list_remove( b->in_room, i->data ); + return( 1 ); + } + + i = i->next; + } + + return 0; +} +#endif -- cgit v1.2.3 From 27e2c66f28bc196d766ac179aa5eae0d190565d5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 01:25:15 +0100 Subject: Support for receiving messages in chatrooms. --- irc_im.c | 23 +++++++++++++++++++---- protocols/bee.h | 4 ++-- protocols/bee_chat.c | 44 ++++++++++++++++---------------------------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/irc_im.c b/irc_im.c index 513b75b9..27822349 100644 --- a/irc_im.c +++ b/irc_im.c @@ -244,12 +244,27 @@ gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) return TRUE; } -gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *format, ... ) +gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text ) { + irc_channel_t *ic = c->ui_data; + + irc_channel_printf( ic, "%s", text ); } -gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, const char *who, const char *msg, time_t sent_at ) +gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at ) { + irc_t *irc = bee->ui_data; + irc_user_t *iu = bu->ui_data; + irc_channel_t *ic = c->ui_data; + char *ts = NULL; + + if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) ) + ts = irc_format_timestamp( irc, sent_at ); + + irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts ); + g_free( ts ); + + return TRUE; } gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) @@ -300,8 +315,8 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_chat_new, bee_irc_chat_free, - NULL, - NULL, + bee_irc_chat_log, + bee_irc_chat_msg, bee_irc_chat_add_user, NULL, diff --git a/protocols/bee.h b/protocols/bee.h index f69d29d4..27e31d05 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -80,8 +80,8 @@ typedef struct bee_ui_funcs gboolean (*chat_new)( bee_t *bee, struct groupchat *c ); gboolean (*chat_free)( bee_t *bee, struct groupchat *c ); - gboolean (*chat_log)( bee_t *bee, struct groupchat *c, const char *format, ... ); - gboolean (*chat_msg)( bee_t *bee, struct groupchat *c, const char *who, const char *msg, time_t sent_at ); + gboolean (*chat_log)( bee_t *bee, struct groupchat *c, const char *text ); + gboolean (*chat_msg)( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at ); gboolean (*chat_add_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); gboolean (*chat_remove_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index 501bb6aa..0c7bebd9 100644 --- a/protocols/bee_chat.c +++ b/protocols/bee_chat.c @@ -104,56 +104,44 @@ void imcb_chat_free( struct groupchat *c ) void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at ) { -#if 0 struct im_connection *ic = c->ic; - char *wrapped; - user_t *u; + bee_t *bee = ic->bee; + bee_user_t *bu; + char *s; /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ if( g_strcasecmp( who, ic->acc->user ) == 0 ) return; - u = user_findhandle( ic, who ); + bu = bee_user_by_handle( bee, ic, who ); - if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || - ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) + s = set_getstr( &ic->bee->set, "strip_html" ); + if( ( g_strcasecmp( s, "always" ) == 0 ) || + ( ( ic->flags & OPT_DOES_HTML ) && s ) ) strip_html( msg ); - wrapped = word_wrap( msg, 425 ); - if( c && u ) - { - char *ts = NULL; - if( set_getbool( &ic->irc->set, "display_timestamps" ) ) - ts = format_timestamp( ic->irc, sent_at ); - irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped ); - g_free( ts ); - } + if( bu && bee->ui->chat_msg ) + bee->ui->chat_msg( bee, c, bu, msg, sent_at ); else - { - imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped ); - } - g_free( wrapped ); -#endif + imcb_chat_log( c, "Message from unknown participant %s: %s", who, msg ); } void imcb_chat_log( struct groupchat *c, char *format, ... ) { -#if 0 - irc_t *irc = c->ic->irc; + struct im_connection *ic = c->ic; + bee_t *bee = ic->bee; va_list params; char *text; - user_t *u; + + if( !bee->ui->chat_log ) + return; va_start( params, format ); text = g_strdup_vprintf( format, params ); va_end( params ); - u = user_find( irc, irc->mynick ); - - irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text ); - + bee->ui->chat_log( bee, c, text ); g_free( text ); -#endif } void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ) -- cgit v1.2.3 From b17ce85d2c4e69637531a7989b30c7011832ccb9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 01:45:10 +0100 Subject: Users leaving really show up again. --- irc_im.c | 11 ++++++++++- protocols/bee_chat.c | 29 +++++++++++++---------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/irc_im.c b/irc_im.c index 27822349..c71f30be 100644 --- a/irc_im.c +++ b/irc_im.c @@ -249,6 +249,8 @@ gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text ) irc_channel_t *ic = c->ui_data; irc_channel_printf( ic, "%s", text ); + + return TRUE; } gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at ) @@ -272,10 +274,17 @@ gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu irc_t *irc = bee->ui_data; irc_channel_add_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data ); + + return TRUE; } gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) { + irc_t *irc = bee->ui_data; + + irc_channel_del_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data ); + + return TRUE; } @@ -318,7 +327,7 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_chat_log, bee_irc_chat_msg, bee_irc_chat_add_user, - NULL, + bee_irc_chat_remove_user, bee_irc_ft_in_start, bee_irc_ft_out_start, diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index 0c7bebd9..f1d1a2c1 100644 --- a/protocols/bee_chat.c +++ b/protocols/bee_chat.c @@ -197,34 +197,31 @@ void imcb_chat_add_buddy( struct groupchat *c, const char *handle ) c->joined = 1; } -/* This function is one BIG hack... :-( EREWRITE */ -void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ) +void imcb_chat_remove_buddy( struct groupchat *c, const char *handle, const char *reason ) { -#if 0 - user_t *u; - int me = 0; + struct im_connection *ic = c->ic; + bee_t *bee = ic->bee; + bee_user_t *bu = NULL; - if( set_getbool( &b->ic->bee->set, "debug" ) ) - imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" ); + if( set_getbool( &bee->set, "debug" ) ) + imcb_log( ic, "User %s removed from conversation %p (%s)", handle, c, reason ? reason : "" ); /* It might be yourself! */ - if( g_strcasecmp( handle, b->ic->acc->user ) == 0 ) + if( g_strcasecmp( handle, ic->acc->user ) == 0 ) { - if( b->joined == 0 ) + if( c->joined == 0 ) return; - u = user_find( b->ic->irc, b->ic->irc->nick ); - b->joined = 0; - me = 1; + bu = bee->user; + c->joined = 0; } else { - u = user_findhandle( b->ic, handle ); + bu = bee_user_by_handle( bee, ic, handle ); } - if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) ) - irc_part( b->ic->irc, u, b->channel ); -#endif + if( bee->ui->chat_remove_user ) + bee->ui->chat_remove_user( bee, c, bu ); } #if 0 -- cgit v1.2.3 From e4816eab28eff86f2303261f8ae292acd84212dd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 01:48:48 +0100 Subject: Reshuffled irc_im.c into a saner order. --- irc_im.c | 101 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/irc_im.c b/irc_im.c index c71f30be..d8224801 100644 --- a/irc_im.c +++ b/irc_im.c @@ -26,7 +26,7 @@ #include "bitlbee.h" #include "dcc.h" -/* IM->IRC callbacks */ +/* IM->IRC callbacks: Simple IM/buddy-related stuff. */ static const struct irc_user_funcs irc_user_im_funcs; @@ -202,8 +202,55 @@ static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) return TRUE; } +/* IRC->IM calls */ + +static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg ) +{ + if( iu->bu ) + return bee_user_msg( iu->irc->b, iu->bu, msg, 0 ); + else + return FALSE; +} + +static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp ) +{ + if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0 + && g_strcasecmp( ctcp[1], "SEND" ) == 0 ) + { + if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request ) + { + file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp ); + if ( ft ) + iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle ); + + return TRUE; + } + } + else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 ) + { + if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] ) + { + int st = ctcp[1][0]; + if( st >= '0' && st <= '2' ) + { + st <<= 8; + iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st ); + } + + return TRUE; + } + } + + return FALSE; +} + +static const struct irc_user_funcs irc_user_im_funcs = { + bee_irc_user_privmsg, + bee_irc_user_ctcp, +}; + -/* Groupchats */ +/* IM->IRC: Groupchats */ gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) { irc_t *irc = bee->ui_data; @@ -288,7 +335,7 @@ gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t * } -/* File transfers */ +/* IM->IRC: File transfers */ static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ) { return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size ); @@ -334,51 +381,3 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_ft_close, bee_irc_ft_finished, }; - - -/* IRC->IM calls */ - -static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg ) -{ - if( iu->bu ) - return bee_user_msg( iu->irc->b, iu->bu, msg, 0 ); - else - return FALSE; -} - -static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp ) -{ - if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0 - && g_strcasecmp( ctcp[1], "SEND" ) == 0 ) - { - if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request ) - { - file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp ); - if ( ft ) - iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle ); - - return TRUE; - } - } - else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 ) - { - if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] ) - { - int st = ctcp[1][0]; - if( st >= '0' && st <= '2' ) - { - st <<= 8; - iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st ); - } - - return TRUE; - } - } - - return FALSE; -} - -static const struct irc_user_funcs irc_user_im_funcs = { - bee_irc_user_privmsg, - bee_irc_user_ctcp, -}; -- cgit v1.2.3 From a87754b68bb1eb07397d71a93ffcb0f3fc089266 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 02:02:12 +0100 Subject: Restored support for outgoing messages. This code is all so much saner now.. --- irc.h | 2 +- irc_im.c | 37 ++++++++++++++++++++++++++++--------- protocols/bee.h | 1 + protocols/bee_chat.c | 20 ++++++++++++++++++++ 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/irc.h b/irc.h index cb34423b..34533728 100644 --- a/irc.h +++ b/irc.h @@ -151,7 +151,7 @@ typedef struct irc_channel struct irc_channel_funcs { - gboolean (*privmsg)( irc_channel_t *iu, const char *msg ); + gboolean (*privmsg)( irc_channel_t *ic, const char *msg ); }; typedef enum diff --git a/irc_im.c b/irc_im.c index d8224801..a6aa6052 100644 --- a/irc_im.c +++ b/irc_im.c @@ -251,7 +251,9 @@ static const struct irc_user_funcs irc_user_im_funcs = { /* IM->IRC: Groupchats */ -gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) +static const struct irc_channel_funcs irc_channel_im_chat_funcs; + +static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) { irc_t *irc = bee->ui_data; irc_channel_t *ic; @@ -271,6 +273,7 @@ gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) c->ui_data = ic; ic->data = c; + ic->f = &irc_channel_im_chat_funcs; topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); irc_channel_set_topic( ic, topic, irc->root ); @@ -279,7 +282,7 @@ gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) return TRUE; } -gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) +static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) { irc_channel_t *ic = c->ui_data; @@ -291,7 +294,7 @@ gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) return TRUE; } -gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text ) +static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text ) { irc_channel_t *ic = c->ui_data; @@ -300,7 +303,7 @@ gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text ) return TRUE; } -gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at ) +static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at ) { irc_t *irc = bee->ui_data; irc_user_t *iu = bu->ui_data; @@ -316,7 +319,7 @@ gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, cons return TRUE; } -gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) +static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) { irc_t *irc = bee->ui_data; @@ -325,7 +328,7 @@ gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu return TRUE; } -gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) +static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) { irc_t *irc = bee->ui_data; @@ -334,6 +337,22 @@ gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t * return TRUE; } +/* IRC->IM */ + +static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg ) +{ + struct groupchat *c = ic->data; + + bee_chat_msg( ic->irc->b, c, msg, 0 ); + + return TRUE; + +} + +static const struct irc_channel_funcs irc_channel_im_chat_funcs = { + bee_irc_channel_chat_privmsg, +}; + /* IM->IRC: File transfers */ static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ) @@ -341,17 +360,17 @@ static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const c return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size ); } -gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft ) +static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft ) { return dccs_recv_start( ft ); } -void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft ) +static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft ) { return dcc_close( ft ); } -void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file ) +static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file ) { dcc_file_transfer_t *df = file->priv; diff --git a/protocols/bee.h b/protocols/bee.h index 27e31d05..982bb914 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -125,5 +125,6 @@ void imcb_chat_add_buddy( struct groupchat *b, const char *handle ); void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ); static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ); #endif +int bee_chat_msg( bee_t *bee, struct groupchat *c, const char *msg, int flags ); #endif /* __BEE_H__ */ diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index f1d1a2c1..b523e544 100644 --- a/protocols/bee_chat.c +++ b/protocols/bee_chat.c @@ -246,3 +246,23 @@ static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) return 0; } #endif + +int bee_chat_msg( bee_t *bee, struct groupchat *c, const char *msg, int flags ) +{ + struct im_connection *ic = c->ic; + char *buf = NULL; + int st; + + if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "", 6 ) != 0 ) ) + { + buf = escape_html( msg ); + msg = buf; + } + else + buf = g_strdup( msg ); + + ic->acc->prpl->chat_msg( c, buf, flags ); + g_free( buf ); + + return 1; +} -- cgit v1.2.3 From bfb99eebd101fff1e15783c8fe4f00398c8052b3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 13:13:23 +0100 Subject: Allow leaving groupchat channels. --- irc.h | 3 +++ irc_channel.c | 4 ++++ irc_im.c | 14 +++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/irc.h b/irc.h index 34533728..e28182ce 100644 --- a/irc.h +++ b/irc.h @@ -152,6 +152,9 @@ typedef struct irc_channel struct irc_channel_funcs { gboolean (*privmsg)( irc_channel_t *ic, const char *msg ); + gboolean (*join)( irc_channel_t *ic ); + gboolean (*part)( irc_channel_t *ic, const char *msg ); + gboolean (*topic)( irc_channel_t *ic, const char *new ); }; typedef enum diff --git a/irc_channel.c b/irc_channel.c index 63c46d95..c8ee7a92 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -122,7 +122,11 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) irc_send_part( ic, iu, "" ); if( iu == ic->irc->user ) + { ic->flags &= ~IRC_CHANNEL_JOINED; + if( ic->f->part ) + ic->f->part( ic, NULL ); + } return 1; } diff --git a/irc_im.c b/irc_im.c index a6aa6052..fad02659 100644 --- a/irc_im.c +++ b/irc_im.c @@ -338,7 +338,6 @@ static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_u } /* IRC->IM */ - static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg ) { struct groupchat *c = ic->data; @@ -349,8 +348,21 @@ static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg } +static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg ) +{ + struct groupchat *c = ic->data; + + if( c->ic->acc->prpl->chat_leave ) + c->ic->acc->prpl->chat_leave( c ); + + return TRUE; + +} + static const struct irc_channel_funcs irc_channel_im_chat_funcs = { bee_irc_channel_chat_privmsg, + NULL, + bee_irc_channel_chat_part, }; -- cgit v1.2.3 From d343eaaa2bf278a530de20a0841967e6e8759e96 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 13:37:49 +0100 Subject: Restored imcb_chat_name_hint(). --- irc_im.c | 35 +++++++++++++++++++++++++++++++++- protocols/bee.h | 1 + protocols/bee_chat.c | 54 ++++------------------------------------------------ 3 files changed, 39 insertions(+), 51 deletions(-) diff --git a/irc_im.c b/irc_im.c index fad02659..1f36059d 100644 --- a/irc_im.c +++ b/irc_im.c @@ -337,6 +337,37 @@ static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_u return TRUE; } +static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name ) +{ + irc_t *irc = bee->ui_data; + irc_channel_t *ic = c->ui_data; + char stripped[MAX_NICK_LENGTH+1], *full_name; + + /* Don't rename a channel if the user's in it already. */ + if( ic->flags & IRC_CHANNEL_JOINED ) + return FALSE; + + strncpy( stripped, name, MAX_NICK_LENGTH ); + stripped[MAX_NICK_LENGTH] = '\0'; + nick_strip( stripped ); + if( set_getbool( &bee->set, "lcnicks" ) ) + nick_lc( stripped ); + + full_name = g_strdup_printf( "&%s", stripped ); + + if( stripped[0] && irc_channel_by_name( irc, full_name ) == NULL ) + { + g_free( ic->name ); + ic->name = full_name; + } + else + { + g_free( full_name ); + } + + return TRUE; +} + /* IRC->IM */ static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg ) { @@ -361,8 +392,9 @@ static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg ) static const struct irc_channel_funcs irc_channel_im_chat_funcs = { bee_irc_channel_chat_privmsg, - NULL, + NULL, /* join */ bee_irc_channel_chat_part, + NULL, /* topic */ }; @@ -406,6 +438,7 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_chat_msg, bee_irc_chat_add_user, bee_irc_chat_remove_user, + bee_irc_chat_name_hint, bee_irc_ft_in_start, bee_irc_ft_out_start, diff --git a/protocols/bee.h b/protocols/bee.h index 982bb914..c1b95881 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -84,6 +84,7 @@ typedef struct bee_ui_funcs gboolean (*chat_msg)( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at ); gboolean (*chat_add_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); gboolean (*chat_remove_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); + gboolean (*chat_name_hint)( bee_t *bee, struct groupchat *c, const char *name ); struct file_transfer* (*ft_in_start)( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ); gboolean (*ft_out_start)( struct im_connection *ic, struct file_transfer *ft ); diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index b523e544..36e4c453 100644 --- a/protocols/bee_chat.c +++ b/protocols/bee_chat.c @@ -51,33 +51,10 @@ struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) void imcb_chat_name_hint( struct groupchat *c, const char *name ) { -#if 0 - if( !c->joined ) - { - struct im_connection *ic = c->ic; - char stripped[MAX_NICK_LENGTH+1], *full_name; - - strncpy( stripped, name, MAX_NICK_LENGTH ); - stripped[MAX_NICK_LENGTH] = '\0'; - nick_strip( stripped ); - if( set_getbool( &ic->irc->set, "lcnicks" ) ) - nick_lc( stripped ); - - full_name = g_strdup_printf( "&%s", stripped ); - - if( stripped[0] && - nick_cmp( stripped, ic->irc->channel + 1 ) != 0 && - irc_chat_by_channel( ic->irc, full_name ) == NULL ) - { - g_free( c->channel ); - c->channel = full_name; - } - else - { - g_free( full_name ); - } - } -#endif + bee_t *bee = c->ic->bee; + + if( bee->ui->chat_name_hint ) + bee->ui->chat_name_hint( bee, c, name ); } void imcb_chat_free( struct groupchat *c ) @@ -224,29 +201,6 @@ void imcb_chat_remove_buddy( struct groupchat *c, const char *handle, const char bee->ui->chat_remove_user( bee, c, bu ); } -#if 0 -static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) -{ - GList *i; - - /* Find the handle in the room userlist and shoot it */ - i = b->in_room; - while( i ) - { - if( g_strcasecmp( handle, i->data ) == 0 ) - { - g_free( i->data ); - b->in_room = g_list_remove( b->in_room, i->data ); - return( 1 ); - } - - i = i->next; - } - - return 0; -} -#endif - int bee_chat_msg( bee_t *bee, struct groupchat *c, const char *msg, int flags ) { struct im_connection *ic = c->ic; -- cgit v1.2.3 From 9e27f1841b1160d3506a3c48701a661e86f2173b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 14:11:09 +0100 Subject: Support for receiving chatroom topics. Since I didn't restore named chatroom support I could only test this using gdb. --- irc_im.c | 24 +++++++++++++++++++++++- protocols/bee.h | 1 + protocols/bee_chat.c | 21 +++++++++------------ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/irc_im.c b/irc_im.c index 1f36059d..b7c90f2c 100644 --- a/irc_im.c +++ b/irc_im.c @@ -337,6 +337,23 @@ static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_u return TRUE; } +static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu ) +{ + irc_t *irc = bee->ui_data; + irc_user_t *iu; + + if( bu == NULL ) + iu = irc->root; + else if( bu == bee->user ) + iu = irc->user; + else + iu = bu->ui_data; + + irc_channel_set_topic( c->ui_data, new, iu ); + + return TRUE; +} + static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name ) { irc_t *irc = bee->ui_data; @@ -390,11 +407,15 @@ static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg ) } +static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new ) +{ +} + static const struct irc_channel_funcs irc_channel_im_chat_funcs = { bee_irc_channel_chat_privmsg, NULL, /* join */ bee_irc_channel_chat_part, - NULL, /* topic */ + bee_irc_channel_chat_topic, }; @@ -438,6 +459,7 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_chat_msg, bee_irc_chat_add_user, bee_irc_chat_remove_user, + bee_irc_chat_topic, bee_irc_chat_name_hint, bee_irc_ft_in_start, diff --git a/protocols/bee.h b/protocols/bee.h index c1b95881..e0ab0030 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -84,6 +84,7 @@ typedef struct bee_ui_funcs gboolean (*chat_msg)( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at ); gboolean (*chat_add_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); gboolean (*chat_remove_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); + gboolean (*chat_topic)( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu ); gboolean (*chat_name_hint)( bee_t *bee, struct groupchat *c, const char *name ); struct file_transfer* (*ft_in_start)( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ); diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index 36e4c453..e565b616 100644 --- a/protocols/bee_chat.c +++ b/protocols/bee_chat.c @@ -123,27 +123,25 @@ void imcb_chat_log( struct groupchat *c, char *format, ... ) void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ) { -#if 0 struct im_connection *ic = c->ic; - user_t *u = NULL; + bee_t *bee = ic->bee; + bee_user_t *bu; + + if( !bee->ui->chat_topic ) + return; if( who == NULL) - u = user_find( ic->irc, ic->irc->mynick ); + bu = NULL; else if( g_strcasecmp( who, ic->acc->user ) == 0 ) - u = user_find( ic->irc, ic->irc->nick ); + bu = bee->user; else - u = user_findhandle( ic, who ); + bu = bee_user_by_handle( bee, ic, who ); if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) strip_html( topic ); - g_free( c->topic ); - c->topic = g_strdup( topic ); - - if( c->joined && u ) - irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic ); -#endif + bee->ui->chat_topic( bee, c, topic, bu ); } void imcb_chat_add_buddy( struct groupchat *c, const char *handle ) @@ -205,7 +203,6 @@ int bee_chat_msg( bee_t *bee, struct groupchat *c, const char *msg, int flags ) { struct im_connection *ic = c->ic; char *buf = NULL; - int st; if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "", 6 ) != 0 ) ) { -- cgit v1.2.3 From b0364dc3b881e4b5e9afd1ff62d5b3cc792a87ca Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 14:21:36 +0100 Subject: Also allow addressing people inside the control channel using a comma. --- irc_channel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/irc_channel.c b/irc_channel.c index c8ee7a92..2ff00068 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -210,9 +210,9 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) const char *s; /* Scan for non-whitespace chars followed by a colon: */ - for( s = msg; *s && !isspace( *s ) && *s != ':'; s ++ ) {} + for( s = msg; *s && !isspace( *s ) && *s != ':' && *s != ','; s ++ ) {} - if( *s == ':' ) + if( *s == ':' || *s == ',' ) { char to[s-msg+1]; irc_user_t *iu; -- cgit v1.2.3 From fd45e1d1d63761a0bf18c7ad885d72acd6367746 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 15:12:32 +0100 Subject: Always show normal contacts in &bitlbee, not just the first channel in the list. --- irc.c | 2 +- irc.h | 2 ++ irc_im.c | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/irc.c b/irc.c index 4a7ba332..39943877 100644 --- a/irc.c +++ b/irc.c @@ -635,7 +635,7 @@ int irc_check_login( irc_t *irc ) irc->umode[0] = '\0'; irc_umode_set( irc, "+" UMODE, TRUE ); - ic = irc_channel_new( irc, ROOT_CHAN ); + ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN ); irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); irc_channel_add_user( ic, irc->user ); diff --git a/irc.h b/irc.h index e28182ce..79e23f09 100644 --- a/irc.h +++ b/irc.h @@ -77,6 +77,7 @@ typedef struct irc GSList *file_transfers; GSList *users, *channels; + struct irc_channel *default_channel; GHashTable *nick_user_hash; GHashTable *watches; @@ -129,6 +130,7 @@ extern const struct irc_user_funcs irc_user_self_funcs; typedef enum { IRC_CHANNEL_JOINED = 1, + IRC_CHANNEL_CONTACTS = 256, } irc_channel_flags_t; typedef struct irc_channel diff --git a/irc_im.c b/irc_im.c index b7c90f2c..51114bfd 100644 --- a/irc_im.c +++ b/irc_im.c @@ -80,7 +80,7 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old { irc_t *irc = bee->ui_data; irc_user_t *iu = bu->ui_data; - irc_channel_t *ic = irc->channels->data; /* For now, just pick the first channel. */ + irc_channel_t *ic = irc->default_channel; /* Do this outside the if below since away state can change without the online state changing. */ @@ -118,7 +118,7 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ) { irc_t *irc = bee->ui_data; - irc_channel_t *ic = irc->channels->data; + irc_channel_t *ic = irc->default_channel; irc_user_t *iu = (irc_user_t *) bu->ui_data; char *dst, *prefix = NULL; char *wrapped, *ts = NULL; @@ -409,6 +409,7 @@ static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg ) static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new ) { + return TRUE; } static const struct irc_channel_funcs irc_channel_im_chat_funcs = { -- cgit v1.2.3 From 4a9fd5f7a980831ee2c96a728f4f83137cfc73fe Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 15:19:12 +0100 Subject: Fixed *facepalm* memory management/scoping bug in /AWAY command. --- irc_commands.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index dd7cc730..b41c1a42 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -550,15 +550,13 @@ static void irc_cmd_away( irc_t *irc, char **cmd ) away[j] = '\0'; irc_send_num( irc, 306, ":You're now away: %s", away ); - set = away; + set_setstr( &irc->b->set, "away", away ); } else { irc_send_num( irc, 305, ":Welcome back" ); - set = NULL; + set_setstr( &irc->b->set, "away", NULL ); } - - set_setstr( &irc->b->set, "away", set ); } static void irc_cmd_version( irc_t *irc, char **cmd ) -- cgit v1.2.3 From eaaa9862451175392d6df48c4795b188518bed4b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 15:48:38 +0100 Subject: Misc. cleanup. Also updated the Yahoo! module to deal with struct groupchat in a GSList so that a default config fully compiles again. --- irc.c | 1 + irc_commands.c | 2 -- protocols/bee.h | 1 + protocols/bee_chat.c | 15 +++++++++++++++ protocols/jabber/presence.c | 1 - protocols/yahoo/yahoo.c | 20 +++++++++----------- 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/irc.c b/irc.c index 39943877..13a77a72 100644 --- a/irc.c +++ b/irc.c @@ -241,6 +241,7 @@ void irc_free( irc_t * irc ) g_free( irc->sendbuffer ); g_free( irc->readbuffer ); g_free( irc->password ); + g_free( irc->last_root_cmd ); g_free( irc ); diff --git a/irc_commands.c b/irc_commands.c index b41c1a42..f43ef583 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -535,8 +535,6 @@ static void irc_cmd_topic( irc_t *irc, char **cmd ) static void irc_cmd_away( irc_t *irc, char **cmd ) { - char *set; - if( cmd[1] && *cmd[1] ) { char away[strlen(cmd[1])+1]; diff --git a/protocols/bee.h b/protocols/bee.h index e0ab0030..b8fee2ae 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -128,5 +128,6 @@ void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ); #endif int bee_chat_msg( bee_t *bee, struct groupchat *c, const char *msg, int flags ); +struct groupchat *bee_chat_by_title( bee_t *bee, struct im_connection *ic, const char *title ); #endif /* __BEE_H__ */ diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index e565b616..3e17a42f 100644 --- a/protocols/bee_chat.c +++ b/protocols/bee_chat.c @@ -217,3 +217,18 @@ int bee_chat_msg( bee_t *bee, struct groupchat *c, const char *msg, int flags ) return 1; } + +struct groupchat *bee_chat_by_title( bee_t *bee, struct im_connection *ic, const char *title ) +{ + struct groupchat *c; + GSList *l; + + for( l = ic->groupchats; l; l = l->next ) + { + c = l->data; + if( strcmp( c->title, title ) == 0 ) + return c; + } + + return NULL; +} diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index dadccfb9..2875d23e 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -204,7 +204,6 @@ int presence_send_update( struct im_connection *ic ) { struct jabber_data *jd = ic->proto_data; struct xt_node *node, *cap; - struct groupchat *c; GSList *l; int st; diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index b61f6ff9..4fd7bee5 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -152,7 +152,7 @@ static void byahoo_logout( struct im_connection *ic ) GSList *l; while( ic->groupchats ) - imcb_chat_free( ic->groupchats ); + imcb_chat_free( ic->groupchats->data ); for( l = yd->buddygroups; l; l = l->next ) { @@ -790,10 +790,14 @@ static void byahoo_accept_conf( void *data ) { struct byahoo_conf_invitation *inv = data; struct groupchat *b; + GSList *l; - for( b = inv->ic->groupchats; b; b = b->next ) + for( l = inv->ic->groupchats; l; l = l->next ) + { + b = l->data; if( b == inv->c ) break; + } if( b != NULL ) { @@ -855,9 +859,7 @@ void ext_yahoo_conf_userdecline( int id, const char *ignored, const char *who, c void ext_yahoo_conf_userjoin( int id, const char *ignored, const char *who, const char *room ) { struct im_connection *ic = byahoo_get_ic_by_id( id ); - struct groupchat *c; - - for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next ); + struct groupchat *c = bee_chat_by_title( ic->bee, ic, room ); if( c ) imcb_chat_add_buddy( c, (char*) who ); @@ -867,9 +869,7 @@ void ext_yahoo_conf_userleave( int id, const char *ignored, const char *who, con { struct im_connection *ic = byahoo_get_ic_by_id( id ); - struct groupchat *c; - - for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next ); + struct groupchat *c = bee_chat_by_title( ic->bee, ic, room ); if( c ) imcb_chat_remove_buddy( c, (char*) who, "" ); @@ -879,9 +879,7 @@ void ext_yahoo_conf_message( int id, const char *ignored, const char *who, const { struct im_connection *ic = byahoo_get_ic_by_id( id ); char *m = byahoo_strip( msg ); - struct groupchat *c; - - for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next ); + struct groupchat *c = bee_chat_by_title( ic->bee, ic, room ); if( c ) imcb_chat_msg( c, (char*) who, (char*) m, 0, 0 ); -- cgit v1.2.3 From e68565706f0c2ea710e7ea83cd5a69e538eb061c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 15:58:32 +0100 Subject: Fixed buggy jabber_chat_by_jid() after GSList change. --- protocols/jabber/conference.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 17108428..0d0e3318 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -104,7 +104,7 @@ struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name } g_free( normalized ); - return ret; + return l && ret; } void jabber_chat_free( struct groupchat *c ) -- cgit v1.2.3 From 66b9e36aa9dfd123c66194d645a3c60cc3dc49bc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 22:52:25 +0100 Subject: Restored /invite for groupchats. --- irc.h | 1 + irc_commands.c | 31 +++++++++++++++++-------------- irc_im.c | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/irc.h b/irc.h index 79e23f09..61859871 100644 --- a/irc.h +++ b/irc.h @@ -157,6 +157,7 @@ struct irc_channel_funcs gboolean (*join)( irc_channel_t *ic ); gboolean (*part)( irc_channel_t *ic, const char *msg ); gboolean (*topic)( irc_channel_t *ic, const char *new ); + gboolean (*invite)( irc_channel_t *ic, irc_user_t *iu ); }; typedef enum diff --git a/irc_commands.c b/irc_commands.c index f43ef583..f56f45a0 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -369,24 +369,27 @@ static void irc_cmd_oper( irc_t *irc, char **cmd ) } } -#if 0 static void irc_cmd_invite( irc_t *irc, char **cmd ) { - char *nick = cmd[1], *channel = cmd[2]; - struct groupchat *c = irc_chat_by_channel( irc, channel ); - user_t *u = user_find( irc, nick ); + irc_channel_t *ic; + irc_user_t *iu; - if( u && c && ( u->ic == c->ic ) ) - if( c->ic && c->ic->acc->prpl->chat_invite ) - { - c->ic->acc->prpl->chat_invite( c, u->handle, NULL ); - irc_send_num( irc, 341, "%s %s", nick, channel ); - return; - } + if( ( iu = irc_user_by_name( irc, cmd[1] ) ) == NULL ) + { + irc_send_num( irc, 401, "%s :No such nick", cmd[1] ); + return; + } + else if( ( ic = irc_channel_by_name( irc, cmd[2] ) ) == NULL ) + { + irc_send_num( irc, 403, "%s :No such channel", cmd[2] ); + return; + } - irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); + if( ic->f->invite ) + ic->f->invite( ic, iu ); + else + irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] ); } -#endif static void irc_cmd_userhost( irc_t *irc, char **cmd ) { @@ -617,8 +620,8 @@ static const command_t irc_commands[] = { { "userhost", 1, irc_cmd_userhost, IRC_CMD_LOGGED_IN }, { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, -#if 0 { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, +#if 0 { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, #endif diff --git a/irc_im.c b/irc_im.c index 51114bfd..c5384fed 100644 --- a/irc_im.c +++ b/irc_im.c @@ -412,11 +412,26 @@ static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new ) return TRUE; } +static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu ) +{ + struct groupchat *c = ic->data; + + if( iu->bu->ic != c->ic ) + irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name ); + else if( c->ic->acc->prpl->chat_invite ) + c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL ); + else + irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name ); + + return TRUE; +} + static const struct irc_channel_funcs irc_channel_im_chat_funcs = { bee_irc_channel_chat_privmsg, NULL, /* join */ bee_irc_channel_chat_part, bee_irc_channel_chat_topic, + bee_irc_channel_chat_invite, }; -- cgit v1.2.3 From eb37735451207895e7e1b5b3dcc0f9cbe178ad38 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 00:54:37 +0100 Subject: This is how you now start groupchats: /join #channel, /invite people. --- irc.h | 5 ++++- irc_channel.c | 34 +++++++++++++++++++++++++++++++++- irc_im.c | 13 ++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/irc.h b/irc.h index 61859871..9f4578bb 100644 --- a/irc.h +++ b/irc.h @@ -130,7 +130,10 @@ extern const struct irc_user_funcs irc_user_self_funcs; typedef enum { IRC_CHANNEL_JOINED = 1, - IRC_CHANNEL_CONTACTS = 256, + + /* Hack: Set this flag right before jumping into IM when we expect + a call to imcb_chat_new(). */ + IRC_CHANNEL_CHAT_PICKME = 0x10000, } irc_channel_flags_t; typedef struct irc_channel diff --git a/irc_channel.c b/irc_channel.c index 2ff00068..2c6601d2 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -27,6 +27,7 @@ static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ ); static const struct irc_channel_funcs control_channel_funcs; +static const struct irc_channel_funcs groupchat_stub_funcs; irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) { @@ -36,7 +37,6 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) return NULL; ic = g_new0( irc_channel_t, 1 ); - ic->f = &control_channel_funcs; ic->irc = irc; ic->name = g_strdup( name ); strcpy( ic->mode, CMODE ); @@ -48,6 +48,11 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) irc->channels = g_slist_prepend( irc->channels, ic ); + if( name[0] == '&' ) + ic->f = &control_channel_funcs; + else /* if( name[0] == '#' ) */ + ic->f = &groupchat_stub_funcs; + return ic; } @@ -250,3 +255,30 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) static const struct irc_channel_funcs control_channel_funcs = { control_channel_privmsg, }; + +/* Groupchat stub: Only handles /INVITE at least for now. */ +static gboolean groupchat_stub_invite( irc_channel_t *ic, irc_user_t *iu ) +{ + bee_user_t *bu = iu->bu; + + if( iu->bu->ic->acc->prpl->chat_with ) + { + ic->flags |= IRC_CHANNEL_CHAT_PICKME; + iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle ); + ic->flags &= ~IRC_CHANNEL_CHAT_PICKME; + return TRUE; + } + else + { + irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name ); + return FALSE; + } +} + +static const struct irc_channel_funcs groupchat_stub_funcs = { + NULL, + NULL, + NULL, + NULL, + groupchat_stub_invite, +}; diff --git a/irc_im.c b/irc_im.c index c5384fed..42186bc9 100644 --- a/irc_im.c +++ b/irc_im.c @@ -258,9 +258,20 @@ static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) irc_t *irc = bee->ui_data; irc_channel_t *ic; char *topic; + GSList *l; int i; - for( i = 0; i <= 999; i ++ ) + /* Try to find a channel that expects to receive a groupchat. + This flag is set by groupchat_stub_invite(). */ + for( l = irc->channels; l; l = l->next ) + { + ic = l->data; + if( ic->flags & IRC_CHANNEL_CHAT_PICKME ) + break; + } + + /* If we found none, just generate some stupid name. */ + if( l == NULL ) for( i = 0; i <= 999; i ++ ) { char name[16]; sprintf( name, "&chat_%03d", i ); -- cgit v1.2.3 From 36577aa5efb2ef3daafd17f9ad179fedef28278e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 01:28:38 +0100 Subject: Create the struct groupchat early on in msn_chat_with() so the new chat setup method works properly. --- protocols/msn/msn.c | 5 ++--- protocols/msn/sb.c | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 85dd22ec..d6a4b158 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -222,6 +222,7 @@ static void msn_chat_leave( struct groupchat *c ) static struct groupchat *msn_chat_with( struct im_connection *ic, char *who ) { struct msn_switchboard *sb; + struct groupchat *c = imcb_chat_new( ic, who ); if( ( sb = msn_sb_by_handle( ic, who ) ) ) { @@ -239,10 +240,8 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who ) msn_sb_write_msg( ic, m ); - return NULL; + return c; } - - return NULL; } static void msn_keepalive( struct im_connection *ic ) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 641af5e7..626cc83e 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -236,7 +236,10 @@ struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb ) /* Create the groupchat structure. */ g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session ); - sb->chat = imcb_chat_new( ic, buf ); + if( sb->who ) + sb->chat = bee_chat_by_title( ic->bee, ic, sb->who ); + if( sb->chat == NULL ) + sb->chat = imcb_chat_new( ic, buf ); /* Populate the channel. */ if( sb->who ) imcb_chat_add_buddy( sb->chat, sb->who ); -- cgit v1.2.3 From 75610c3b53a68451d9eaf40fdc8a5e6419a13339 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 01:40:54 +0100 Subject: Fixed up OSCAR to work with the new way of setting up groupchats. --- protocols/oscar/oscar.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index a5ca1ac8..de594eee 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -798,7 +798,9 @@ static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) { chatcon = find_oscar_chat_by_conn(ic, fr->conn); chatcon->id = id; - chatcon->cnv = imcb_chat_new(ic, chatcon->show); + chatcon->cnv = bee_chat_by_title(ic->bee, ic, chatcon->show); + if (chatcon->cnv == NULL) + chatcon->cnv = imcb_chat_new(ic, chatcon->show); chatcon->cnv->data = chatcon; return 1; @@ -2650,9 +2652,13 @@ struct groupchat *oscar_chat_with(struct im_connection * ic, char *who) struct groupchat *ret; static int chat_id = 0; char * chatname; + struct groupchat *c; - chatname = g_strdup_printf("%s%d", ic->acc->user, chat_id++); - + chatname = g_strdup_printf("%s%s_%d", isdigit(*ic->acc->user) ? "icq_" : "", + ic->acc->user, chat_id++); + + c = imcb_chat_new(ic, chatname); + ret = oscar_chat_join(ic, chatname, NULL, NULL); aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0); -- cgit v1.2.3 From e5abfd413a797b268db0b107d0748eb7e40da431 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 12:26:57 +0100 Subject: Safety check for yesterday's fixes: Double-check that a groupchat struct isn't claimed already. --- protocols/msn/sb.c | 7 +++++-- protocols/oscar/oscar.c | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 626cc83e..bfdbfe64 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -232,13 +232,16 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb ) { struct im_connection *ic = sb->ic; + struct groupchat *c = NULL; char buf[1024]; /* Create the groupchat structure. */ g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session ); if( sb->who ) - sb->chat = bee_chat_by_title( ic->bee, ic, sb->who ); - if( sb->chat == NULL ) + c = bee_chat_by_title( ic->bee, ic, sb->who ); + if( c && !msn_sb_by_chat( c ) ) + sb->chat = c; + else sb->chat = imcb_chat_new( ic, buf ); /* Populate the channel. */ diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 2d07f912..94dd876e 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -786,6 +786,7 @@ static int gaim_parse_logout(aim_session_t *sess, aim_frame_t *fr, ...) { static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) { struct im_connection *ic = sess->aux_data; struct chat_connection *chatcon; + struct groupchat *c = NULL; static int id = 1; aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0); @@ -798,8 +799,11 @@ static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) { chatcon = find_oscar_chat_by_conn(ic, fr->conn); chatcon->id = id; - chatcon->cnv = bee_chat_by_title(ic->bee, ic, chatcon->show); - if (chatcon->cnv == NULL) + + c = bee_chat_by_title(ic->bee, ic, chatcon->show); + if (c && !c->data) + chatcon->cnv = c; + else chatcon->cnv = imcb_chat_new(ic, chatcon->show); chatcon->cnv->data = chatcon; -- cgit v1.2.3 From 47fae0ffdda6ea4509ba00b56d15fb25ffe19eea Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 13:05:50 +0100 Subject: No. Run the part handler from the IRC /PART command, not from irc_channel.c. This was causing troubles with Twitter at disconnect time. --- irc_channel.c | 4 ---- irc_commands.c | 7 ++++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/irc_channel.c b/irc_channel.c index 2c6601d2..528d0442 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -127,11 +127,7 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) irc_send_part( ic, iu, "" ); if( iu == ic->irc->user ) - { ic->flags &= ~IRC_CHANNEL_JOINED; - if( ic->f->part ) - ic->f->part( ic, NULL ); - } return 1; } diff --git a/irc_commands.c b/irc_commands.c index f56f45a0..e6954071 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -168,7 +168,12 @@ static void irc_cmd_part( irc_t *irc, char **cmd ) { irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); } - else if( !irc_channel_del_user( ic, irc->user ) ) + else if( irc_channel_del_user( ic, irc->user ) ) + { + if( ic->f->part ) + ic->f->part( ic, NULL ); + } + else { irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] ); } -- cgit v1.2.3 From 5a673f32c7bdf10cec2e0ccabce605ec9c12859e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 14:04:45 +0100 Subject: Pick up buddy group information from OSCAR server-side contact list. --- protocols/oscar/oscar.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 94dd876e..fa710ece 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -254,8 +254,6 @@ static char *normalize(const char *s) g_return_val_if_fail((s != NULL), NULL); u = t = g_strdup(s); - - strcpy(t, s); g_strdown(t); while (*t && (x < BUF_LEN - 1)) { @@ -2089,7 +2087,7 @@ static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) { static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) { struct im_connection *ic = sess->aux_data; - struct aim_ssi_item *curitem; + struct aim_ssi_item *curitem, *curgroup; int tmp; char *nrm; @@ -2105,8 +2103,8 @@ static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) { if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1)) realname = aim_gettlv_str(curitem->data, 0x0131, 1); - - imcb_add_buddy(ic, nrm, NULL); + + imcb_add_buddy(ic, nrm, curgroup->gid == curitem->gid ? curgroup->name : NULL); if (realname) { imcb_buddy_nick_hint(ic, nrm, realname); @@ -2116,6 +2114,10 @@ static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) { } break; + case 0x0001: /* Group */ + curgroup = curitem; + break; + case 0x0002: /* Permit buddy */ if (curitem->name) { GSList *list; -- cgit v1.2.3 From dcd16c5f8b8788d476bf4193701fc61656dfbf14 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 14:21:24 +0100 Subject: Read group information from Jabber contact lists. The code was already there, but with a simple typo. --- protocols/jabber/iq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index bdedeb08..a5495196 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -382,7 +382,7 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node * c = query->children; while( ( c = xt_find_node( c, "item" ) ) ) { - struct xt_node *group = xt_find_node( node->children, "group" ); + struct xt_node *group = xt_find_node( c->children, "group" ); char *jid = xt_find_attr( c, "jid" ); char *name = xt_find_attr( c, "name" ); char *sub = xt_find_attr( c, "subscription" ); -- cgit v1.2.3 From 9ac3ed11de72046c318398481603c6680af37cf2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 18:20:51 +0100 Subject: First bits for different channel types. --- irc.h | 17 +++++++++++++++++ irc_channel.c | 23 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/irc.h b/irc.h index 9f4578bb..c6e78b41 100644 --- a/irc.h +++ b/irc.h @@ -161,6 +161,9 @@ struct irc_channel_funcs gboolean (*part)( irc_channel_t *ic, const char *msg ); gboolean (*topic)( irc_channel_t *ic, const char *new ); gboolean (*invite)( irc_channel_t *ic, irc_user_t *iu ); + + gboolean (*_init)( irc_channel_t *ic ); + gboolean (*_free)( irc_channel_t *ic ); }; typedef enum @@ -176,6 +179,20 @@ typedef struct irc_channel_user int flags; } irc_channel_user_t; +typedef enum +{ + IRC_CC_TYPE_DEFAULT, + IRC_CC_TYPE_REST, + IRC_CC_TYPE_GROUP, + IRC_CC_TYPE_ACCOUNT, +} irc_control_channel_type_t; + +struct irc_control_channel +{ + irc_control_channel_type_t type; + struct bee_group *group; +}; + extern const struct bee_ui_funcs irc_ui_funcs; /* irc.c */ diff --git a/irc_channel.c b/irc_channel.c index 528d0442..e6570f4c 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -53,6 +53,13 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) else /* if( name[0] == '#' ) */ ic->f = &groupchat_stub_funcs; + if( ic->f->_init ) + if( !ic->f->_init( ic ) ) + { + irc_channel_free( ic ); + return NULL; + } + return ic; } @@ -248,8 +255,24 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) return TRUE; } +static gboolean control_channel_init( irc_channel_t *ic ) +{ + struct irc_control_channel *icc; + + ic->data = icc = g_new0( struct irc_control_channel, 1 ); + icc->type = IRC_CC_TYPE_DEFAULT; + + return TRUE; +} + static const struct irc_channel_funcs control_channel_funcs = { control_channel_privmsg, + NULL, + NULL, + NULL, + NULL, + + control_channel_init, }; /* Groupchat stub: Only handles /INVITE at least for now. */ -- cgit v1.2.3 From 7aadd714313ba3e966720e7565f72118b97bd551 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 19:05:55 +0100 Subject: Keep track of contact groups in a slightly more efficient way. --- protocols/bee.c | 2 ++ protocols/bee.h | 11 ++++++++++- protocols/bee_user.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ protocols/nogaim.c | 2 +- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/protocols/bee.c b/protocols/bee.c index 8c38d550..471ce02a 100644 --- a/protocols/bee.c +++ b/protocols/bee.c @@ -71,6 +71,8 @@ void bee_free( bee_t *b ) while( b->set ) set_del( &b->set, b->set->key ); + bee_group_free( b ); + g_free( b->user ); g_free( b ); } diff --git a/protocols/bee.h b/protocols/bee.h index b8fee2ae..100593f9 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -34,6 +34,7 @@ typedef struct bee struct set *set; GSList *users; + GSList *groups; struct account *accounts; /* TODO(wilmer): Use GSList here too? */ /* Symbolic, to refer to the local user (who has no real bee_user @@ -59,7 +60,7 @@ typedef struct bee_user struct im_connection *ic; char *handle; char *fullname; - char *group; + struct bee_group *group; bee_user_flags_t flags; char *status; @@ -69,6 +70,12 @@ typedef struct bee_user void *ui_data; } bee_user_t; +typedef struct bee_group +{ + char *key; + char *name; +} bee_group_t; + typedef struct bee_ui_funcs { gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); @@ -103,6 +110,8 @@ bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *hand int bee_user_free( bee_t *bee, bee_user_t *bu ); bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle ); int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ); +bee_group_t *bee_group_by_name( bee_t *bee, const char *name, gboolean creat ); +void bee_group_free( bee_t *bee ); /* Callbacks from IM modules to core: */ /* Buddy activity */ diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 0dd40cab..fdf84934 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -102,6 +102,54 @@ int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ) } +/* Groups */ +static bee_group_t *bee_group_new( bee_t *bee, const char *name ) +{ + bee_group_t *bg = g_new0( bee_group_t, 1 ); + + bg->name = g_strdup( name ); + bg->key = g_utf8_casefold( name, -1 ); + bee->groups = g_slist_prepend( bee->groups, bg ); + + return bg; +} + +bee_group_t *bee_group_by_name( bee_t *bee, const char *name, gboolean creat ) +{ + GSList *l; + char *key; + + if( name == NULL ) + return NULL; + + key = g_utf8_casefold( name, -1 ); + for( l = bee->groups; l; l = l->next ) + { + bee_group_t *bg = l->data; + if( strcmp( bg->key, key ) == 0 ) + break; + } + g_free( key ); + + if( !l ) + return creat ? bee_group_new( bee, name ) : NULL; + else + return l->data; +} + +void bee_group_free( bee_t *bee ) +{ + while( bee->groups ) + { + bee_group_t *bg = bee->groups->data; + g_free( bg->name ); + g_free( bg->key ); + g_free( bg ); + bee->groups = g_slist_remove( bee->groups, bee->groups->data ); + } +} + + /* IM->UI callbacks */ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ) { diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 149e64f1..5696a01e 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -388,7 +388,7 @@ void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *g } bu = bee_user_new( bee, ic, handle ); - bu->group = g_strdup( group ); + bu->group = bee_group_by_name( bee, group, TRUE ); } void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *fullname ) -- cgit v1.2.3 From 3130e7074e567070fcc7be627a3836fa3f213142 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 22:39:31 +0100 Subject: Do not free bu->group anymore, it's no longer a string! --- protocols/bee_user.c | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/bee_user.c b/protocols/bee_user.c index fdf84934..b1dcffc8 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -58,7 +58,6 @@ int bee_user_free( bee_t *bee, bee_user_t *bu ) g_free( bu->handle ); g_free( bu->fullname ); - g_free( bu->group ); g_free( bu->status ); g_free( bu->status_msg ); -- cgit v1.2.3 From 13c1a9f1bbf57bebbab621de609581c4fad54fb2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 22:52:17 +0100 Subject: /join &groupname and all people in that group will be in that channel. --- irc.h | 3 +++ irc_channel.c | 5 +++++ irc_im.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/irc.h b/irc.h index c6e78b41..ed9a7c3a 100644 --- a/irc.h +++ b/irc.h @@ -260,4 +260,7 @@ const char *irc_user_get_away( irc_user_t *iu ); char *set_eval_timezone( struct set *set, char *value ); char *irc_format_timestamp( irc_t *irc, time_t msg_ts ); +/* irc_im.c */ +void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ); + #endif diff --git a/irc_channel.c b/irc_channel.c index e6570f4c..159c16dc 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -262,6 +262,11 @@ static gboolean control_channel_init( irc_channel_t *ic ) ic->data = icc = g_new0( struct irc_control_channel, 1 ); icc->type = IRC_CC_TYPE_DEFAULT; + if( ( icc->group = bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) ) ) + icc->type = IRC_CC_TYPE_GROUP; + + bee_irc_channel_update( ic->irc, ic, NULL ); + return TRUE; } diff --git a/irc_im.c b/irc_im.c index 42186bc9..8ed9b143 100644 --- a/irc_im.c +++ b/irc_im.c @@ -80,7 +80,6 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old { irc_t *irc = bee->ui_data; irc_user_t *iu = bu->ui_data; - irc_channel_t *ic = irc->default_channel; /* Do this outside the if below since away state can change without the online state changing. */ @@ -95,26 +94,71 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old if( g_hash_table_lookup( irc->watches, iu->key ) ) irc_send_num( irc, 600, "%s %s %s %d :%s", iu->nick, iu->user, iu->host, (int) time( NULL ), "logged online" ); - - irc_channel_add_user( ic, iu ); - - if( set_getbool( &bee->set, "away_devoice" ) ) - irc_channel_user_set_mode( ic, iu, ( bu->flags & BEE_USER_AWAY ) ? - 0 : IRC_CHANNEL_USER_VOICE ); } else { if( g_hash_table_lookup( irc->watches, iu->key ) ) irc_send_num( irc, 601, "%s %s %s %d :%s", iu->nick, iu->user, iu->host, (int) time( NULL ), "logged offline" ); - - irc_channel_del_user( ic, iu ); } } + bee_irc_channel_update( irc, NULL, iu ); + return TRUE; } +void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) +{ + struct irc_control_channel *icc; + GSList *l; + gboolean show; + + if( ic == NULL ) + { + for( l = irc->channels; l; l = l->next ) + { + ic = l->data; + /* TODO: Just add a type flag or so.. */ + if( ic->f == irc->default_channel->f ) + bee_irc_channel_update( irc, ic, iu ); + } + return; + } + if( iu == NULL ) + { + for( l = irc->users; l; l = l->next ) + { + iu = l->data; + if( iu->bu ) + bee_irc_channel_update( irc, ic, l->data ); + } + return; + } + + icc = ic->data; + + if( !( iu->bu->flags & BEE_USER_ONLINE ) ) + show = FALSE; + else if( icc->type == IRC_CC_TYPE_DEFAULT ) + show = TRUE; + else if( icc->type == IRC_CC_TYPE_GROUP ) + show = iu->bu->group == icc->group; + + if( !show ) + { + irc_channel_del_user( ic, iu ); + } + else + { + irc_channel_add_user( ic, iu ); + + if( set_getbool( &irc->b->set, "away_devoice" ) ) + irc_channel_user_set_mode( ic, iu, ( iu->bu->flags & BEE_USER_AWAY ) ? + 0 : IRC_CHANNEL_USER_VOICE ); + } +} + static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ) { irc_t *irc = bee->ui_data; -- cgit v1.2.3 From 4e608d6fee8ee39b871338524b6da00aa5a6e86b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 22:56:39 +0100 Subject: Pick up group changes coming in during a session. Reflecting them in the session will be a bit more complicated. --- protocols/nogaim.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 5696a01e..51679f88 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -374,20 +374,9 @@ void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *g bee_user_t *bu; bee_t *bee = ic->bee; - if( bee_user_by_handle( bee, ic, handle ) ) - { - if( set_getbool( &bee->set, "debug" ) ) - imcb_log( ic, "User already exists, ignoring add request: %s", handle ); - - return; - - /* Buddy seems to exist already. Let's ignore this request then... - Eventually subsequent calls to this function *should* be possible - when a buddy is in multiple groups. But for now BitlBee doesn't - even support groups so let's silently ignore this for now. */ - } + if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) + bu = bee_user_new( bee, ic, handle ); - bu = bee_user_new( bee, ic, handle ); bu->group = bee_group_by_name( bee, group, TRUE ); } -- cgit v1.2.3 From a067771fa1799e689653931f6743b3502182b850 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 23:08:30 +0100 Subject: Per-account channels also exist now. --- irc.h | 1 + irc_channel.c | 2 ++ irc_im.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/irc.h b/irc.h index ed9a7c3a..eeb9b1b2 100644 --- a/irc.h +++ b/irc.h @@ -191,6 +191,7 @@ struct irc_control_channel { irc_control_channel_type_t type; struct bee_group *group; + struct account *account; }; extern const struct bee_ui_funcs irc_ui_funcs; diff --git a/irc_channel.c b/irc_channel.c index 159c16dc..f000a997 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -264,6 +264,8 @@ static gboolean control_channel_init( irc_channel_t *ic ) if( ( icc->group = bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) ) ) icc->type = IRC_CC_TYPE_GROUP; + else if( ( icc->account = account_get( ic->irc->b, ic->name + 1 ) ) ) + icc->type = IRC_CC_TYPE_ACCOUNT; bee_irc_channel_update( ic->irc, ic, NULL ); diff --git a/irc_im.c b/irc_im.c index 8ed9b143..72dc2cc9 100644 --- a/irc_im.c +++ b/irc_im.c @@ -144,6 +144,8 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) show = TRUE; else if( icc->type == IRC_CC_TYPE_GROUP ) show = iu->bu->group == icc->group; + else if( icc->type == IRC_CC_TYPE_ACCOUNT ) + show = iu->bu->ic->acc == icc->account; if( !show ) { -- cgit v1.2.3 From d8acfd3ba84e018554d8564f08e9a50cde56b4a4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 10 May 2010 00:23:34 +0100 Subject: Purple lists mix up key and value; key == what the user sees, *value* is what the module understands. This should hopefully resolve QQ issues. --- protocols/purple/purple.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 90312d0d..edd10219 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -140,8 +140,11 @@ static void purple_init( account_t *acc ) for( io = purple_account_option_get_list( o ); io; io = io->next ) { PurpleKeyValuePair *kv = io->data; - opts = g_slist_append( opts, kv->key ); - g_string_append_printf( help, "%s, ", kv->key ); + opts = g_slist_append( opts, kv->value ); + if( strcmp( kv->value, kv->key ) != 0 ) + g_string_append_printf( help, "%s (%s), ", kv->value, kv->key ); + else + g_string_append_printf( help, "%s, ", kv->value ); } g_string_truncate( help, help->len - 2 ); eval = set_eval_list; -- cgit v1.2.3 From 4c17d19ddb4a039e3ff9e33e87d5f538a0b0c19b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 10 May 2010 10:05:26 +0100 Subject: Fixed irc_channel_name_ok(): One-character channel names are okay, also the first character after the prefix *can* be a number. --- irc_channel.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/irc_channel.c b/irc_channel.c index f000a997..28cd7d43 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -201,7 +201,18 @@ void irc_channel_printf( irc_channel_t *ic, char *format, ... ) gboolean irc_channel_name_ok( const char *name ) { - return strchr( CTYPES, name[0] ) != NULL && nick_ok( name + 1 ); + char name_[strlen(name)+1]; + + /* Check if the first character is in CTYPES (#&) */ + if( strchr( CTYPES, name[0] ) == NULL ) + return FALSE; + + /* Check the rest of the name. Just checking name + 1 doesn't work + since it will fail if the first character is a number, or if + it's a one-char channel name - both of which are legal. */ + name_[0] = '_'; + strcpy( name_ + 1, name + 1 ); + return nick_ok( name_ ); } static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ ) -- cgit v1.2.3 From 3663bb3ee9bc20d83642103f03a53831caee454d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 13 May 2010 01:19:33 +0100 Subject: Restore query cleanup on IM disconnects. --- protocols/nogaim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 51679f88..241c9833 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -342,7 +342,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) l = next; } - //query_del_by_conn( ic->irc, ic ); + query_del_by_conn( (irc_t*) ic->bee->ui_data, ic ); for( a = bee->accounts; a; a = a->next ) if( a->ic == ic ) -- cgit v1.2.3 From 6fd4d46eb854902391549de95774260a94ae7072 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 13 May 2010 01:22:28 +0100 Subject: Restore "account del". --- root_commands.c | 50 ++------------------------------------------------ 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/root_commands.c b/root_commands.c index d5da0691..bbf888c1 100644 --- a/root_commands.c +++ b/root_commands.c @@ -195,40 +195,6 @@ static void cmd_save( irc_t *irc, char **cmd ) irc_usermsg( irc, "Configuration could not be saved!" ); } -struct cmd_account_del_data -{ - account_t *a; - irc_t *irc; -}; - -void cmd_account_del_yes( void *data ) -{ - struct cmd_account_del_data *cad = data; - account_t *a; - - for( a = cad->irc->b->accounts; a && a != cad->a; a = a->next ); - - if( a == NULL ) - { - irc_usermsg( cad->irc, "Account already deleted" ); - } - else if( a->ic ) - { - irc_usermsg( cad->irc, "Account is still logged in, can't delete" ); - } - else - { - account_del( cad->irc->b, a ); - irc_usermsg( cad->irc, "Account deleted" ); - } - g_free( data ); -} - -void cmd_account_del_no( void *data ) -{ - g_free( data ); -} - static void cmd_showset( irc_t *irc, set_t **head, char *key ) { char *val; @@ -402,20 +368,8 @@ static void cmd_account( irc_t *irc, char **cmd ) } else { - struct cmd_account_del_data *cad; - char *msg; - - cad = g_malloc( sizeof( struct cmd_account_del_data ) ); - cad->a = a; - cad->irc = irc; - - msg = g_strdup_printf( "If you remove this account (%s(%s)), BitlBee will " - "also forget all your saved nicknames. If you want " - "to change your username/password, use the `account " - "set' command. Are you sure you want to delete this " - "account?", a->prpl->name, a->user ); - //query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad ); - g_free( msg ); + account_del( irc->b, a ); + irc_usermsg( irc, "Account deleted" ); } } else if( g_strcasecmp( cmd[1], "list" ) == 0 ) -- cgit v1.2.3 From 58f5ef70c3824b881ad6b35f854a8c8ac59a5d32 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 13 May 2010 01:30:36 +0100 Subject: Use ?, not &&. --- protocols/jabber/conference.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 0d0e3318..bb3fbcf3 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -104,7 +104,7 @@ struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name } g_free( normalized ); - return l && ret; + return l ? ret : NULL; } void jabber_chat_free( struct groupchat *c ) -- cgit v1.2.3 From ca0981ad1e427644a33fc31fe78d63ea834f0fa0 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 14 May 2010 00:05:07 +0100 Subject: As long as this is a separate branch, enabling purple by default's not a bad idea. --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 69d59d0d..12e79633 100755 --- a/configure +++ b/configure @@ -26,7 +26,7 @@ jabber=1 oscar=1 yahoo=1 twitter=1 -purple=0 +purple=1 debug=0 strip=1 -- cgit v1.2.3 From ad404ab26aa3cfdfc3c76f6926e556e333d02753 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 3 Jun 2010 01:20:53 +0100 Subject: Restore add_* handle_unknown settings. --- irc_im.c | 10 ++++++++++ protocols/bee.h | 3 ++- protocols/bee_chat.c | 2 +- protocols/bee_user.c | 7 ++++--- protocols/nogaim.c | 2 +- root_commands.c | 2 +- 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/irc_im.c b/irc_im.c index 72dc2cc9..6e257b03 100644 --- a/irc_im.c +++ b/irc_im.c @@ -65,6 +65,16 @@ static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) if( set_getbool( &bee->set, "private" ) ) iu->flags |= IRC_USER_PRIVATE; + if( bu->flags & BEE_USER_LOCAL ) + { + char *s = set_getstr( &bee->set, "handle_unknown" ); + + if( strcmp( s, "add_private" ) == 0 ) + iu->flags |= IRC_USER_PRIVATE; + else if( strcmp( s, "add_channel" ) == 0 ) + iu->flags &= ~IRC_USER_PRIVATE; + } + iu->f = &irc_user_im_funcs; //iu->last_typing_notice = 0; diff --git a/protocols/bee.h b/protocols/bee.h index 100593f9..e421db57 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -53,6 +53,7 @@ typedef enum { BEE_USER_ONLINE = 1, /* Compatibility with old OPT_LOGGED_IN flag */ BEE_USER_AWAY = 4, /* Compatibility with old OPT_AWAY flag */ + BEE_USER_LOCAL = 256, /* Locally-added contacts (not in real contact list) */ } bee_user_flags_t; typedef struct bee_user @@ -106,7 +107,7 @@ bee_t *bee_new(); void bee_free( bee_t *b ); /* bee_user.c */ -bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle ); +bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags ); int bee_user_free( bee_t *bee, bee_user_t *bu ); bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle ); int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ); diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index 3e17a42f..3be6f189 100644 --- a/protocols/bee_chat.c +++ b/protocols/bee_chat.c @@ -159,7 +159,7 @@ void imcb_chat_add_buddy( struct groupchat *c, const char *handle ) /* Most protocols allow people to join, even when they're not in your contact list. Try to handle that here */ if( !me && !bu ) - bu = bee_user_new( bee, ic, handle ); + bu = bee_user_new( bee, ic, handle, BEE_USER_LOCAL ); /* Add the handle to the room userlist */ /* TODO: Use bu instead of a string */ diff --git a/protocols/bee_user.c b/protocols/bee_user.c index b1dcffc8..fd2e8635 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -26,7 +26,7 @@ #define BITLBEE_CORE #include "bitlbee.h" -bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle ) +bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags ) { bee_user_t *bu; @@ -36,6 +36,7 @@ bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *hand bu = g_new0( bee_user_t, 1 ); bu->bee = bee; bu->ic = ic; + bu->flags = flags; bu->handle = g_strdup( handle ); bee->users = g_slist_prepend( bee->users, bu ); @@ -159,7 +160,7 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, { if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "add" ) == 0 ) { - bu = bee_user_new( bee, ic, handle ); + bu = bee_user_new( bee, ic, handle, BEE_USER_LOCAL ); } else { @@ -207,7 +208,7 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui } else if( g_strncasecmp( h, "add", 3 ) == 0 ) { - bu = bee_user_new( bee, ic, handle ); + bu = bee_user_new( bee, ic, handle, BEE_USER_LOCAL ); } } diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 241c9833..00fe0ebf 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -375,7 +375,7 @@ void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *g bee_t *bee = ic->bee; if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) - bu = bee_user_new( bee, ic, handle ); + bu = bee_user_new( bee, ic, handle, 0 ); bu->group = bee_group_by_name( bee, group, TRUE ); } diff --git a/root_commands.c b/root_commands.c index bbf888c1..aeb4f62a 100644 --- a/root_commands.c +++ b/root_commands.c @@ -531,7 +531,7 @@ static void cmd_add( irc_t *irc, char **cmd ) else /* Only for add -tmp. For regular adds, this callback will be called once the IM server confirms. */ - bee_user_new( irc->b, a->ic, cmd[2] ); + bee_user_new( irc->b, a->ic, cmd[2], BEE_USER_LOCAL ); irc_usermsg( irc, "Adding `%s' to your contact list", cmd[2] ); } -- cgit v1.2.3 From 7b71feb6f88b7e14199b8f7e9930f76d5324e356 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 14 May 2010 00:34:38 +0100 Subject: Just enough code to join named chatrooms again. This UI is *not* final, the "chat" command will probably mostly stick around for bw compatibility. Still thinking about how this should work eventually. --- irc.h | 6 ++++++ irc_channel.c | 20 +++++++++++++++++- irc_commands.c | 8 ++++++++ root_commands.c | 64 +++++++++------------------------------------------------ 4 files changed, 43 insertions(+), 55 deletions(-) diff --git a/irc.h b/irc.h index eeb9b1b2..03600f1e 100644 --- a/irc.h +++ b/irc.h @@ -194,6 +194,12 @@ struct irc_control_channel struct account *account; }; +struct irc_groupchat_stub +{ + struct account *acc; + char *room; +}; + extern const struct bee_ui_funcs irc_ui_funcs; /* irc.c */ diff --git a/irc_channel.c b/irc_channel.c index 28cd7d43..a3a7e1ee 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -312,9 +312,27 @@ static gboolean groupchat_stub_invite( irc_channel_t *ic, irc_user_t *iu ) } } +static gboolean groupchat_stub_join( irc_channel_t *ic ) +{ + struct irc_groupchat_stub *igs = ic->data; + + if( igs && igs->acc->ic && igs->acc->prpl->chat_join ) + { + ic->flags |= IRC_CHANNEL_CHAT_PICKME; + igs->acc->prpl->chat_join( igs->acc->ic, igs->room, ic->irc->user->nick, NULL ); + ic->flags &= ~IRC_CHANNEL_CHAT_PICKME; + return FALSE; + } + else + { + irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name ); + return FALSE; + } +} + static const struct irc_channel_funcs groupchat_stub_funcs = { NULL, - NULL, + groupchat_stub_join, NULL, NULL, groupchat_stub_invite, diff --git a/irc_commands.c b/irc_commands.c index e6954071..530e849b 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -139,6 +139,14 @@ static void irc_cmd_join( irc_t *irc, char **cmd ) return; /* Dude, you're already there... RFC doesn't have any reply for that though? */ + if( ic->f->join && !ic->f->join( ic ) ) + /* The story is: FALSE either means the handler showed an error + message, or is doing some work before the join should be + confirmed. (In the latter case, the caller should take care + of that confirmation.) + TRUE means all's good, let the user join the channel right away. */ + return; + irc_channel_add_user( ic, irc->user ); } diff --git a/root_commands.c b/root_commands.c index aeb4f62a..65f3de9a 100644 --- a/root_commands.c +++ b/root_commands.c @@ -939,17 +939,6 @@ static void cmd_qlist( irc_t *irc, char **cmd ) irc_usermsg( irc, "%d, BitlBee: %s", num, q->question ); } -#if 0 -static set_t **cmd_chat_set_findhead( irc_t *irc, char *id ) -{ - struct chat *c; - - if( ( c = chat_get( irc, id ) ) ) - return &c->set; - else - return NULL; -} - static void cmd_chat( irc_t *irc, char **cmd ) { account_t *acc; @@ -958,10 +947,11 @@ static void cmd_chat( irc_t *irc, char **cmd ) if( g_strcasecmp( cmd[1], "add" ) == 0 ) { char *channel, *s; + struct irc_channel *ic; MIN_ARGS( 3 ); - if( !( acc = account_get( irc, cmd[2] ) ) ) + if( !( acc = account_get( irc->b, cmd[2] ) ) ) { irc_usermsg( irc, "Invalid account" ); return; @@ -980,53 +970,21 @@ static void cmd_chat( irc_t *irc, char **cmd ) if( strchr( CTYPES, channel[0] ) == NULL ) { - s = g_strdup_printf( "%c%s", CTYPES[0], channel ); + s = g_strdup_printf( "#%s", channel ); g_free( channel ); channel = s; } - if( ( c = chat_add( irc, acc, cmd[3], channel ) ) ) - irc_usermsg( irc, "Chatroom added successfully." ); - else - irc_usermsg( irc, "Could not add chatroom." ); - - g_free( channel ); - } - else if( g_strcasecmp( cmd[1], "list" ) == 0 ) - { - int i = 0; - - if( strchr( irc->umode, 'b' ) ) - irc_usermsg( irc, "Chatroom list:" ); - - for( c = irc->chatrooms; c; c = c->next ) + if( ( ic = irc_channel_new( irc, channel ) ) ) { - irc_usermsg( irc, "%2d. %s(%s) %s, %s", i, c->acc->prpl->name, - c->acc->user, c->handle, c->channel ); + struct irc_groupchat_stub *igs; - i ++; - } - irc_usermsg( irc, "End of chatroom list" ); - } - else if( g_strcasecmp( cmd[1], "set" ) == 0 ) - { - MIN_ARGS( 2 ); - - cmd_set_real( irc, cmd + 1, cmd_chat_set_findhead, NULL ); - } - else if( g_strcasecmp( cmd[1], "del" ) == 0 ) - { - MIN_ARGS( 2 ); - - if( ( c = chat_get( irc, cmd[2] ) ) ) - { - chat_del( irc, c ); - } - else - { - irc_usermsg( irc, "Could not remove chat." ); + ic->data = igs = g_new0( struct irc_groupchat_stub, 1 ); + igs->acc = acc; + igs->room = g_strdup( cmd[3] ); } } + /* else if( g_strcasecmp( cmd[1], "with" ) == 0 ) { user_t *u; @@ -1046,12 +1004,12 @@ static void cmd_chat( irc_t *irc, char **cmd ) irc_usermsg( irc, "Can't open a groupchat with %s.", cmd[2] ); } } + */ else { irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] ); } } -#endif static void cmd_transfer( irc_t *irc, char **cmd ) { @@ -1136,8 +1094,6 @@ const command_t commands[] = { { "set", 0, cmd_set, 0 }, { "transfer", 0, cmd_transfer, 0 }, { "yes", 0, cmd_yesno, 0 }, -#if 0 { "chat", 1, cmd_chat, 0 }, -#endif { NULL } }; -- cgit v1.2.3 From 2309152972f1d52af6adbb0e7e5d9c5ad3ae80f9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 17 May 2010 01:14:14 +0100 Subject: Split off the file transfer stuff into a separate file. What a mess. --- protocols/purple/Makefile | 2 +- protocols/purple/ft.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++ protocols/purple/purple.c | 199 +-------------------------------------- 3 files changed, 237 insertions(+), 198 deletions(-) create mode 100644 protocols/purple/ft.c diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile index 15460529..403db799 100644 --- a/protocols/purple/Makefile +++ b/protocols/purple/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = purple.o +objects = ft.o purple.o CFLAGS += -Wall $(PURPLE_CFLAGS) LFLAGS += -r diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c new file mode 100644 index 00000000..62a1092a --- /dev/null +++ b/protocols/purple/ft.c @@ -0,0 +1,234 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* libpurple module - File transfer stuff * +* * +* Copyright 2009-2010 Wilmer van der Gaast * +* * +* 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 along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "bitlbee.h" + +#include + +#include +#include + +struct prpl_xfer_data +{ + PurpleXfer *xfer; + file_transfer_t *ft; + gint ready_timer; + char *buf; + int buf_len; +}; + +static file_transfer_t *next_ft; + +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); + +/* Glorious hack: We seem to have to remind at least some libpurple plugins + that we're ready because this info may get lost if we give it too early. + So just do it ten times a second. :-/ */ +static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + purple_xfer_ui_ready( px->xfer ); + + return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; +} + +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); + return TRUE; +} + +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ + struct prpl_xfer_data *px = ft->data; + + px->buf = g_memdup( buffer, len ); + px->buf_len = len; + + //purple_xfer_ui_ready( px->xfer ); + px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); + + return TRUE; +} + +static void prpl_xfer_accept( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_accepted( px->xfer, NULL ); + prpl_xfer_write_request( ft ); +} + +static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) +{ + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_denied( px->xfer ); +} + +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + PurpleXfer *xfer = data; + struct im_connection *ic = purple_ic_by_pa( xfer->account ); + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + PurpleBuddy *buddy; + const char *who; + + buddy = purple_find_buddy( xfer->account, xfer->who ); + who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; + + /* TODO(wilmer): After spreading some more const goodness in BitlBee, + remove the evil cast below. */ + px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); + px->ft->data = px; + px->xfer = data; + px->xfer->ui_data = px; + + px->ft->accept = prpl_xfer_accept; + px->ft->canceled = prpl_xfer_canceled; + px->ft->write_request = prpl_xfer_write_request; + + return FALSE; +} + +static void prplcb_xfer_new( PurpleXfer *xfer ) +{ + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) + { + /* This should suppress the stupid file dialog. */ + purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + + /* Sadly the xfer struct is still empty ATM so come back after + the caller is done. */ + b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); + } + else + { + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + px->ft = next_ft; + px->ft->data = px; + px->xfer = xfer; + px->xfer->ui_data = px; + + purple_xfer_set_filename( xfer, px->ft->file_name ); + purple_xfer_set_size( xfer, px->ft->file_size ); + + next_ft = NULL; + } +} + +static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); +} + +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); +} + +static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + gboolean st; + + fprintf( stderr, "xfer_write %d %d\n", size, px->buf_len ); + + b_event_remove( px->ready_timer ); + px->ready_timer = 0; + + st = px->ft->write( px->ft, (char*) buffer, size ); + + if( st && xfer->bytes_remaining == size ) + imcb_file_finished( px->ft ); + + return st ? size : 0; +} + +gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + + fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + + if( px->buf ) + { + *buffer = px->buf; + px->buf = NULL; + + px->ft->write_request( px->ft ); + + return px->buf_len; + } + + return 0; +} + +PurpleXferUiOps bee_xfer_uiops = +{ + prplcb_xfer_new, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_progress, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_write, + prplcb_xfer_read, + prplcb_xfer_dbg, +}; + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +{ + PurpleAccount *pa = ic->proto_data; + struct prpl_xfer_data *px; + + /* xfer_new() will pick up this variable. It's a hack but we're not + multi-threaded anyway. */ + next_ft = ft; + serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); + + ft->write = prpl_xfer_write; + + px = ft->data; + imcb_file_recv_start( ft ); + + px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); +} + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + if( px->ft->status & FT_STATUS_TRANSFERRING ) + { + fprintf( stderr, "The ft, it is ready...\n" ); + px->ft->write_request( px->ft ); + + return FALSE; + } + + return TRUE; +} diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index edd10219..b01fb991 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -36,7 +36,7 @@ GSList *purple_connections; libpurple in daemon mode anyway. */ static irc_t *local_irc; -static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { GSList *i; @@ -826,202 +826,7 @@ static PurpleNotifyUiOps bee_notify_uiops = prplcb_notify_email, }; - -struct prpl_xfer_data -{ - PurpleXfer *xfer; - file_transfer_t *ft; - gint ready_timer; - char *buf; - int buf_len; -}; - -static file_transfer_t *next_ft; - -/* Glorious hack: We seem to have to remind at least some libpurple plugins - that we're ready because this info may get lost if we give it too early. - So just do it ten times a second. :-/ */ -static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) -{ - struct prpl_xfer_data *px = data; - - purple_xfer_ui_ready( px->xfer ); - - return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; -} - -static gboolean prpl_xfer_write_request( struct file_transfer *ft ) -{ - struct prpl_xfer_data *px = ft->data; - px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); - return TRUE; -} - -static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) -{ - struct prpl_xfer_data *px = xfer->ui_data; - gboolean st; - - b_event_remove( px->ready_timer ); - px->ready_timer = 0; - - st = px->ft->write( px->ft, (char*) buffer, size ); - - if( st && xfer->bytes_remaining == size ) - imcb_file_finished( px->ft ); - - return st ? size : 0; -} - -static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) -{ - struct prpl_xfer_data *px = ft->data; - - px->buf = g_memdup( buffer, len ); - px->buf_len = len; - - //purple_xfer_ui_ready( px->xfer ); - px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); - - return TRUE; -} - -static void prpl_xfer_accept( struct file_transfer *ft ) -{ - struct prpl_xfer_data *px = ft->data; - purple_xfer_request_accepted( px->xfer, NULL ); - prpl_xfer_write_request( ft ); -} - -static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) -{ - struct prpl_xfer_data *px = ft->data; - purple_xfer_request_denied( px->xfer ); -} - -static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) -{ - PurpleXfer *xfer = data; - struct im_connection *ic = purple_ic_by_pa( xfer->account ); - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); - PurpleBuddy *buddy; - const char *who; - - buddy = purple_find_buddy( xfer->account, xfer->who ); - who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; - - /* TODO(wilmer): After spreading some more const goodness in BitlBee, - remove the evil cast below. */ - px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); - px->ft->data = px; - px->xfer = data; - px->xfer->ui_data = px; - - px->ft->accept = prpl_xfer_accept; - px->ft->canceled = prpl_xfer_canceled; - px->ft->write_request = prpl_xfer_write_request; - - return FALSE; -} - -static void prplcb_xfer_new( PurpleXfer *xfer ) -{ - if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) - { - /* This should suppress the stupid file dialog. */ - purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); - - /* Sadly the xfer struct is still empty ATM so come back after - the caller is done. */ - b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); - } - else - { - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); - - px->ft = next_ft; - px->ft->data = px; - px->xfer = xfer; - px->xfer->ui_data = px; - - purple_xfer_set_filename( xfer, px->ft->file_name ); - purple_xfer_set_size( xfer, px->ft->file_size ); - - next_ft = NULL; - } -} - -static void prplcb_xfer_dbg( PurpleXfer *xfer ) -{ - fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); -} - -gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) -{ - struct prpl_xfer_data *px = xfer->ui_data; - - fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); - - if( px->buf ) - { - *buffer = px->buf; - px->buf = NULL; - - px->ft->write_request( px->ft ); - - return px->buf_len; - } - - return 0; -} - -static PurpleXferUiOps bee_xfer_uiops = -{ - prplcb_xfer_new, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_write, - prplcb_xfer_read, - prplcb_xfer_dbg, -}; - -static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); - -void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) -{ - PurpleAccount *pa = ic->proto_data; - struct prpl_xfer_data *px; - - /* xfer_new() will pick up this variable. It's a hack but we're not - multi-threaded anyway. */ - next_ft = ft; - serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); - - ft->write = prpl_xfer_write; - - px = ft->data; - imcb_file_recv_start( ft ); - - px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); -} - -static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) -{ - struct prpl_xfer_data *px = data; - - if( px->ft->status & FT_STATUS_TRANSFERRING ) - { - fprintf( stderr, "The ft, it is ready...\n" ); - px->ft->write_request( px->ft ); - - return FALSE; - } - - return TRUE; -} +extern PurpleXferUiOps bee_xfer_uiops; static void purple_ui_init() { -- cgit v1.2.3 From 553767cacf43e1a4a1038530c47dc0af5fdd0369 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 17 May 2010 21:30:45 +0100 Subject: Move direct ft stuff to an unused file: This gets too hairy and too fragile. I don't have time to work out all the details, I doubt if this is supposed to work reliably yet at all. Let's go for the simple via-disk approach for now. --- protocols/purple/ft-direct.c | 239 +++++++++++++++++++++++++++++++++++++++++++ protocols/purple/ft.c | 137 +++++++------------------ 2 files changed, 275 insertions(+), 101 deletions(-) create mode 100644 protocols/purple/ft-direct.c diff --git a/protocols/purple/ft-direct.c b/protocols/purple/ft-direct.c new file mode 100644 index 00000000..98a16d75 --- /dev/null +++ b/protocols/purple/ft-direct.c @@ -0,0 +1,239 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* libpurple module - File transfer stuff * +* * +* Copyright 2009-2010 Wilmer van der Gaast * +* * +* 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 along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +/* This code tries to do direct file transfers, i.e. without caching the file + locally on disk first. Since libpurple can only do this since version 2.6.0 + and even then very unreliably (and not with all IM modules), I'm canning + this code for now. */ + +#include "bitlbee.h" + +#include + +#include +#include + +struct prpl_xfer_data +{ + PurpleXfer *xfer; + file_transfer_t *ft; + gint ready_timer; + char *buf; + int buf_len; +}; + +static file_transfer_t *next_ft; + +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); + +/* Glorious hack: We seem to have to remind at least some libpurple plugins + that we're ready because this info may get lost if we give it too early. + So just do it ten times a second. :-/ */ +static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + purple_xfer_ui_ready( px->xfer ); + + return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; +} + +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); + return TRUE; +} + +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ + struct prpl_xfer_data *px = ft->data; + + px->buf = g_memdup( buffer, len ); + px->buf_len = len; + + //purple_xfer_ui_ready( px->xfer ); + px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); + + return TRUE; +} + +static void prpl_xfer_accept( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_accepted( px->xfer, NULL ); + prpl_xfer_write_request( ft ); +} + +static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) +{ + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_denied( px->xfer ); +} + +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + PurpleXfer *xfer = data; + struct im_connection *ic = purple_ic_by_pa( xfer->account ); + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + PurpleBuddy *buddy; + const char *who; + + buddy = purple_find_buddy( xfer->account, xfer->who ); + who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; + + /* TODO(wilmer): After spreading some more const goodness in BitlBee, + remove the evil cast below. */ + px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); + px->ft->data = px; + px->xfer = data; + px->xfer->ui_data = px; + + px->ft->accept = prpl_xfer_accept; + px->ft->canceled = prpl_xfer_canceled; + px->ft->write_request = prpl_xfer_write_request; + + return FALSE; +} + +static void prplcb_xfer_new( PurpleXfer *xfer ) +{ + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) + { + /* This should suppress the stupid file dialog. */ + purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + + /* Sadly the xfer struct is still empty ATM so come back after + the caller is done. */ + b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); + } + else + { + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + px->ft = next_ft; + px->ft->data = px; + px->xfer = xfer; + px->xfer->ui_data = px; + + purple_xfer_set_filename( xfer, px->ft->file_name ); + purple_xfer_set_size( xfer, px->ft->file_size ); + + next_ft = NULL; + } +} + +static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); +} + +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); +} + +static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + gboolean st; + + fprintf( stderr, "xfer_write %d %d\n", size, px->buf_len ); + + b_event_remove( px->ready_timer ); + px->ready_timer = 0; + + st = px->ft->write( px->ft, (char*) buffer, size ); + + if( st && xfer->bytes_remaining == size ) + imcb_file_finished( px->ft ); + + return st ? size : 0; +} + +gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + + fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + + if( px->buf ) + { + *buffer = px->buf; + px->buf = NULL; + + px->ft->write_request( px->ft ); + + return px->buf_len; + } + + return 0; +} + +PurpleXferUiOps bee_xfer_uiops = +{ + prplcb_xfer_new, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_progress, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_write, + prplcb_xfer_read, + prplcb_xfer_dbg, +}; + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +{ + PurpleAccount *pa = ic->proto_data; + struct prpl_xfer_data *px; + + /* xfer_new() will pick up this variable. It's a hack but we're not + multi-threaded anyway. */ + next_ft = ft; + serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); + + ft->write = prpl_xfer_write; + + px = ft->data; + imcb_file_recv_start( ft ); + + px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); +} + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + if( px->ft->status & FT_STATUS_TRANSFERRING ) + { + fprintf( stderr, "The ft, it is ready...\n" ); + px->ft->write_request( px->ft ); + + return FALSE; + } + + return TRUE; +} diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index 62a1092a..4b5a3f49 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -21,6 +21,11 @@ * * \***************************************************************************/ +/* Do file transfers via disk for now, since libpurple was really designed + for straight-to/from disk fts and is only just learning how to pass the + file contents the the UI instead (2.6.0 and higher it seems, and with + varying levels of success). */ + #include "bitlbee.h" #include @@ -41,36 +46,16 @@ static file_transfer_t *next_ft; struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); -/* Glorious hack: We seem to have to remind at least some libpurple plugins - that we're ready because this info may get lost if we give it too early. - So just do it ten times a second. :-/ */ -static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) -{ - struct prpl_xfer_data *px = data; - - purple_xfer_ui_ready( px->xfer ); - - return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; -} - static gboolean prpl_xfer_write_request( struct file_transfer *ft ) { - struct prpl_xfer_data *px = ft->data; - px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); - return TRUE; + return FALSE; } static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) { struct prpl_xfer_data *px = ft->data; - px->buf = g_memdup( buffer, len ); - px->buf_len = len; - - //purple_xfer_ui_ready( px->xfer ); - px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); - - return TRUE; + return FALSE; } static void prpl_xfer_accept( struct file_transfer *ft ) @@ -86,30 +71,7 @@ static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) purple_xfer_request_denied( px->xfer ); } -static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) -{ - PurpleXfer *xfer = data; - struct im_connection *ic = purple_ic_by_pa( xfer->account ); - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); - PurpleBuddy *buddy; - const char *who; - - buddy = purple_find_buddy( xfer->account, xfer->who ); - who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; - - /* TODO(wilmer): After spreading some more const goodness in BitlBee, - remove the evil cast below. */ - px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); - px->ft->data = px; - px->xfer = data; - px->xfer->ui_data = px; - - px->ft->accept = prpl_xfer_accept; - px->ft->canceled = prpl_xfer_canceled; - px->ft->write_request = prpl_xfer_write_request; - - return FALSE; -} +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ); static void prplcb_xfer_new( PurpleXfer *xfer ) { @@ -124,6 +86,7 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) } else { + /* struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); px->ft = next_ft; @@ -135,54 +98,43 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) purple_xfer_set_size( xfer, px->ft->file_size ); next_ft = NULL; + */ } } -static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) -{ - fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); -} - -static void prplcb_xfer_dbg( PurpleXfer *xfer ) -{ - fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); -} - -static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) { - struct prpl_xfer_data *px = xfer->ui_data; - gboolean st; - - fprintf( stderr, "xfer_write %d %d\n", size, px->buf_len ); + PurpleXfer *xfer = data; + struct im_connection *ic = purple_ic_by_pa( xfer->account ); + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + PurpleBuddy *buddy; + const char *who; - b_event_remove( px->ready_timer ); - px->ready_timer = 0; + buddy = purple_find_buddy( xfer->account, xfer->who ); + who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; - st = px->ft->write( px->ft, (char*) buffer, size ); + /* TODO(wilmer): After spreading some more const goodness in BitlBee, + remove the evil cast below. */ + px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); + px->ft->data = px; + px->xfer = data; + px->xfer->ui_data = px; - if( st && xfer->bytes_remaining == size ) - imcb_file_finished( px->ft ); + px->ft->accept = prpl_xfer_accept; + px->ft->canceled = prpl_xfer_canceled; + px->ft->write_request = prpl_xfer_write_request; - return st ? size : 0; + return FALSE; } -gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { - struct prpl_xfer_data *px = xfer->ui_data; - - fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); +} - if( px->buf ) - { - *buffer = px->buf; - px->buf = NULL; - - px->ft->write_request( px->ft ); - - return px->buf_len; - } - - return 0; +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); } PurpleXferUiOps bee_xfer_uiops = @@ -193,8 +145,8 @@ PurpleXferUiOps bee_xfer_uiops = prplcb_xfer_progress, prplcb_xfer_dbg, prplcb_xfer_dbg, - prplcb_xfer_write, - prplcb_xfer_read, + NULL, + NULL, prplcb_xfer_dbg, }; @@ -214,21 +166,4 @@ void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, cha px = ft->data; imcb_file_recv_start( ft ); - - px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); -} - -static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) -{ - struct prpl_xfer_data *px = data; - - if( px->ft->status & FT_STATUS_TRANSFERRING ) - { - fprintf( stderr, "The ft, it is ready...\n" ); - px->ft->write_request( px->ft ); - - return FALSE; - } - - return TRUE; } -- cgit v1.2.3 From 8822d23385bf7c35d67eaff1d0ce81470ba73f4f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 18 May 2010 00:23:20 +0100 Subject: This receives files but is very fragile if anything unusual happens (like a cancellation/timeout/etc). --- protocols/purple/ft.c | 117 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 15 deletions(-) diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index 4b5a3f49..57d45beb 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -37,24 +37,74 @@ struct prpl_xfer_data { PurpleXfer *xfer; file_transfer_t *ft; - gint ready_timer; - char *buf; - int buf_len; + int fd; + char *fn; + gboolean ui_wants_data; }; static file_transfer_t *next_ft; struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); -static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) { + struct file_transfer *ft = data; + struct prpl_xfer_data *px = ft->data; + struct stat fs; + off_t tx_bytes; + + fprintf( stderr, "write_to_ui\n" ); + + /* If we don't have the file opened yet, there's no data so wait. */ + if( px->fd < 0 || !px->ui_wants_data ) + return FALSE; + + tx_bytes = lseek( px->fd, 0, SEEK_CUR ); + fstat( px->fd, &fs ); + + fprintf( stderr, "write_to_ui %zd %zd %zd\n", fs.st_size, tx_bytes, px->xfer->size ); + + if( fs.st_size > tx_bytes ) + { + char buf[1024]; + size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) ); + + if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) ) + { + fprintf( stderr, "Wrote %zd bytes\n", n ); + px->ui_wants_data = FALSE; + } + else + { + purple_xfer_cancel_local( px->xfer ); + imcb_file_canceled( ft, "Read error" ); + } + } + + if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) + { + purple_xfer_end( px->xfer ); + imcb_file_finished( ft ); + } + return FALSE; } -static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +/* UI calls this when its buffer is empty and wants more data to send to the user. */ +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) { struct prpl_xfer_data *px = ft->data; + fprintf( stderr, "wrq\n" ); + + px->ui_wants_data = TRUE; + try_write_to_ui( ft, 0, 0 ); + + return FALSE; +} + +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ return FALSE; } @@ -77,8 +127,14 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) { if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) { - /* This should suppress the stupid file dialog. */ - purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + xfer->ui_data = px; + px->xfer = xfer; + px->fn = mktemp( g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ) ); + px->fd = -1; + + purple_xfer_set_local_filename( xfer, px->fn ); /* Sadly the xfer struct is still empty ATM so come back after the caller is done. */ @@ -89,6 +145,7 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) /* struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + px->fd = -1; px->ft = next_ft; px->ft->data = px; px->xfer = xfer; @@ -106,7 +163,7 @@ static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_conditi { PurpleXfer *xfer = data; struct im_connection *ic = purple_ic_by_pa( xfer->account ); - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + struct prpl_xfer_data *px = xfer->ui_data; PurpleBuddy *buddy; const char *who; @@ -117,8 +174,6 @@ static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_conditi remove the evil cast below. */ px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); px->ft->data = px; - px->xfer = data; - px->xfer->ui_data = px; px->ft->accept = prpl_xfer_accept; px->ft->canceled = prpl_xfer_canceled; @@ -127,9 +182,43 @@ static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_conditi return FALSE; } +static void prplcb_xfer_destroy( PurpleXfer *xfer ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + + g_free( px->fn ); + if( px->fd >= 0 ) + close( px->fd ); + g_free( px ); +} + static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { - fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); + struct prpl_xfer_data *px = xfer->ui_data; + + fprintf( stderr, "prplcb_xfer_progress 0x%p %f\n", xfer, percent ); + + if( px->fd == -1 && percent > 0 ) + { + /* Weeeeeeeee, we're getting data! That means the file exists + by now so open it and start sending to the UI. */ + px->fd = open( px->fn, O_RDONLY ); + + /* Unlink it now, because we don't need it after this. */ + //unlink( px->fn ); + } + + if( percent < 1 ) + try_write_to_ui( px->ft, 0, 0 ); + else + b_timeout_add( 0, try_write_to_ui, px->ft ); +} + +static void prplcb_xfer_cancel_remote( PurpleXfer *xfer ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + + imcb_file_canceled( px->ft, "Canceled by remote end" ); } static void prplcb_xfer_dbg( PurpleXfer *xfer ) @@ -140,18 +229,16 @@ static void prplcb_xfer_dbg( PurpleXfer *xfer ) PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, - prplcb_xfer_dbg, + prplcb_xfer_destroy, prplcb_xfer_dbg, prplcb_xfer_progress, prplcb_xfer_dbg, - prplcb_xfer_dbg, + prplcb_xfer_cancel_remote, NULL, NULL, prplcb_xfer_dbg, }; -static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); - void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) { PurpleAccount *pa = ic->proto_data; -- cgit v1.2.3 From 5d1b3a9529f7aadab62026be0eb9f4ca0f6311ce Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 18 May 2010 00:38:39 +0100 Subject: purple_conv_chat_invite_user() is libpurple >= 2.6.0, so use serv_chat_invite() instead. --- protocols/purple/purple.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b01fb991..eb3a4eb5 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -448,7 +448,10 @@ void purple_chat_invite( struct groupchat *gc, char *who, char *message ) PurpleConversation *pc = gc->data; PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc ); - purple_conv_chat_invite_user( pcc, who, message && *message ? message : "Please join my chat", FALSE ); + serv_chat_invite( purple_account_get_connection( gc->ic->proto_data ), + purple_conv_chat_get_id( pcc ), + message && *message ? message : "Please join my chat", + who ); } void purple_chat_leave( struct groupchat *gc, char *who ) -- cgit v1.2.3 From c96c72f16ea48ca769400ff91bd2eb434da19f6e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 18 May 2010 01:08:17 +0100 Subject: Little cleanup. Less compiler warnings, and removing tempfile at the beginning of the download already to make sure it doesn't stick around. --- protocols/purple/ft.c | 158 ++++++++++++++++++++++++---------------------- protocols/purple/purple.c | 7 +- 2 files changed, 85 insertions(+), 80 deletions(-) diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index 57d45beb..8cfa60dd 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -45,69 +45,11 @@ struct prpl_xfer_data static file_transfer_t *next_ft; struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ); +static gboolean prpl_xfer_write_request( struct file_transfer *ft ); -gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) -{ - struct file_transfer *ft = data; - struct prpl_xfer_data *px = ft->data; - struct stat fs; - off_t tx_bytes; - - fprintf( stderr, "write_to_ui\n" ); - - /* If we don't have the file opened yet, there's no data so wait. */ - if( px->fd < 0 || !px->ui_wants_data ) - return FALSE; - - tx_bytes = lseek( px->fd, 0, SEEK_CUR ); - fstat( px->fd, &fs ); - - fprintf( stderr, "write_to_ui %zd %zd %zd\n", fs.st_size, tx_bytes, px->xfer->size ); - - if( fs.st_size > tx_bytes ) - { - char buf[1024]; - size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) ); - - if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) ) - { - fprintf( stderr, "Wrote %zd bytes\n", n ); - px->ui_wants_data = FALSE; - } - else - { - purple_xfer_cancel_local( px->xfer ); - imcb_file_canceled( ft, "Read error" ); - } - } - - if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) - { - purple_xfer_end( px->xfer ); - imcb_file_finished( ft ); - } - - return FALSE; -} - -/* UI calls this when its buffer is empty and wants more data to send to the user. */ -static gboolean prpl_xfer_write_request( struct file_transfer *ft ) -{ - struct prpl_xfer_data *px = ft->data; - - fprintf( stderr, "wrq\n" ); - - px->ui_wants_data = TRUE; - try_write_to_ui( ft, 0, 0 ); - - return FALSE; -} - -static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) -{ - return FALSE; -} +/* Receiving files (IM->UI): */ static void prpl_xfer_accept( struct file_transfer *ft ) { struct prpl_xfer_data *px = ft->data; @@ -121,8 +63,6 @@ static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) purple_xfer_request_denied( px->xfer ); } -static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ); - static void prplcb_xfer_new( PurpleXfer *xfer ) { if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) @@ -182,6 +122,58 @@ static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_conditi return FALSE; } +gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) +{ + struct file_transfer *ft = data; + struct prpl_xfer_data *px = ft->data; + struct stat fs; + off_t tx_bytes; + + /* If we don't have the file opened yet, there's no data so wait. */ + if( px->fd < 0 || !px->ui_wants_data ) + return FALSE; + + tx_bytes = lseek( px->fd, 0, SEEK_CUR ); + fstat( px->fd, &fs ); + + if( fs.st_size > tx_bytes ) + { + char buf[1024]; + size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) ); + + if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) ) + { + px->ui_wants_data = FALSE; + } + else + { + purple_xfer_cancel_local( px->xfer ); + imcb_file_canceled( ft, "Read error" ); + } + } + + if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) + { + purple_xfer_end( px->xfer ); + imcb_file_finished( ft ); + } + + return FALSE; +} + +/* UI calls this when its buffer is empty and wants more data to send to the user. */ +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + + px->ui_wants_data = TRUE; + try_write_to_ui( ft, 0, 0 ); + + return FALSE; +} + + +/* Generic (IM<>UI): */ static void prplcb_xfer_destroy( PurpleXfer *xfer ) { struct prpl_xfer_data *px = xfer->ui_data; @@ -196,8 +188,6 @@ static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { struct prpl_xfer_data *px = xfer->ui_data; - fprintf( stderr, "prplcb_xfer_progress 0x%p %f\n", xfer, percent ); - if( px->fd == -1 && percent > 0 ) { /* Weeeeeeeee, we're getting data! That means the file exists @@ -205,12 +195,16 @@ static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) px->fd = open( px->fn, O_RDONLY ); /* Unlink it now, because we don't need it after this. */ - //unlink( px->fn ); + unlink( px->fn ); } if( percent < 1 ) try_write_to_ui( px->ft, 0, 0 ); else + /* Another nice problem: If we have the whole file, it only + gets closed when we return. Problem: There may still be + stuff buffered and not written, we'll only see it after + the caller close()s the file. So poll the file after that. */ b_timeout_add( 0, try_write_to_ui, px->ft ); } @@ -226,18 +220,12 @@ static void prplcb_xfer_dbg( PurpleXfer *xfer ) fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); } -PurpleXferUiOps bee_xfer_uiops = + +/* Sending files (UI->IM): */ +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) { - prplcb_xfer_new, - prplcb_xfer_destroy, - prplcb_xfer_dbg, - prplcb_xfer_progress, - prplcb_xfer_dbg, - prplcb_xfer_cancel_remote, - NULL, - NULL, - prplcb_xfer_dbg, -}; + return FALSE; +} void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) { @@ -254,3 +242,19 @@ void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, cha px = ft->data; imcb_file_recv_start( ft ); } + + + + +PurpleXferUiOps bee_xfer_uiops = +{ + prplcb_xfer_new, + prplcb_xfer_destroy, + prplcb_xfer_dbg, + prplcb_xfer_progress, + prplcb_xfer_dbg, + prplcb_xfer_cancel_remote, + NULL, + NULL, + prplcb_xfer_dbg, +}; diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index eb3a4eb5..2507bfc2 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -141,10 +141,11 @@ static void purple_init( account_t *acc ) { PurpleKeyValuePair *kv = io->data; opts = g_slist_append( opts, kv->value ); + /* TODO: kv->value is not a char*, WTF? */ if( strcmp( kv->value, kv->key ) != 0 ) - g_string_append_printf( help, "%s (%s), ", kv->value, kv->key ); + g_string_append_printf( help, "%s (%s), ", (char*) kv->value, kv->key ); else - g_string_append_printf( help, "%s, ", kv->value ); + g_string_append_printf( help, "%s, ", (char*) kv->value ); } g_string_truncate( help, help->len - 2 ); eval = set_eval_list; @@ -454,7 +455,7 @@ void purple_chat_invite( struct groupchat *gc, char *who, char *message ) who ); } -void purple_chat_leave( struct groupchat *gc, char *who ) +void purple_chat_leave( struct groupchat *gc ) { PurpleConversation *pc = gc->data; -- cgit v1.2.3 From 31fc06fbb48b6217186ca441eb9b95add5f783ce Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 18 May 2010 01:42:02 +0100 Subject: Suppress auto-reconnect when required (auth errors and concurrent logins probably, not sure what sets the wants_to_die flag). --- protocols/purple/purple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 2507bfc2..fee93b27 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -497,7 +497,7 @@ static void prplcb_conn_disconnected( PurpleConnection *gc ) if( ic != NULL ) { - imc_logout( ic, TRUE ); + imc_logout( ic, !gc->wants_to_die ); } } -- cgit v1.2.3 From e7dc02a89d846d27b63719a5093c2e2a295fc232 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 19 May 2010 01:57:58 +0100 Subject: Similar hacky code to send files. This indirect sending stuff sucks badly for numerous reasons. Maybe libpurple 2.7.0 is less crappy and will eventually allow (working) direct ft's again. This somewhat works, but filename info is lost with some protocols. --- protocols/purple/ft.c | 113 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index 8cfa60dd..ab637a94 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -37,8 +37,9 @@ struct prpl_xfer_data { PurpleXfer *xfer; file_transfer_t *ft; + struct im_connection *ic; int fd; - char *fn; + char *fn, *orig_fn, *handle; gboolean ui_wants_data; }; @@ -82,20 +83,16 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) } else { - /* - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + struct file_transfer *ft = next_ft; + struct prpl_xfer_data *px = ft->data; - px->fd = -1; - px->ft = next_ft; - px->ft->data = px; + xfer->ui_data = px; px->xfer = xfer; - px->xfer->ui_data = px; - purple_xfer_set_filename( xfer, px->ft->file_name ); - purple_xfer_set_size( xfer, px->ft->file_size ); + purple_xfer_set_filename( xfer, px->orig_fn ); + purple_xfer_set_local_filename( xfer, px->fn ); next_ft = NULL; - */ } } @@ -179,6 +176,8 @@ static void prplcb_xfer_destroy( PurpleXfer *xfer ) struct prpl_xfer_data *px = xfer->ui_data; g_free( px->fn ); + g_free( px->orig_fn ); + g_free( px->handle ); if( px->fd >= 0 ) close( px->fd ); g_free( px ); @@ -188,6 +187,20 @@ static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { struct prpl_xfer_data *px = xfer->ui_data; + if( px == NULL ) + return; + + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) + { + if( *px->fn ) + { + //unlink( px->fn ); + *px->fn = '\0'; + } + + return; + } + if( px->fd == -1 && percent > 0 ) { /* Weeeeeeeee, we're getting data! That means the file exists @@ -215,6 +228,16 @@ static void prplcb_xfer_cancel_remote( PurpleXfer *xfer ) imcb_file_canceled( px->ft, "Canceled by remote end" ); } +static void prplcb_xfer_add( PurpleXfer *xfer ) +{ + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) + { + struct prpl_xfer_data *px = xfer->ui_data; + + purple_xfer_set_filename( xfer, px->orig_fn ); + } +} + static void prplcb_xfer_dbg( PurpleXfer *xfer ) { fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); @@ -222,27 +245,77 @@ static void prplcb_xfer_dbg( PurpleXfer *xfer ) /* Sending files (UI->IM): */ -static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ); +static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) { - return FALSE; + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + ft->data = px; + px->ft = ft; + px->fn = g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ); + px->fd = mkstemp( px->fn ); + + px->ic = ic; + px->handle = g_strdup( handle ); + px->orig_fn = g_strdup( ft->file_name ); + + imcb_log( ic, "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait..." ); + + b_timeout_add( 0, purple_transfer_request_cb, ft ); } -void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +static void purple_transfer_forward( struct file_transfer *ft ) { - PurpleAccount *pa = ic->proto_data; - struct prpl_xfer_data *px; + struct prpl_xfer_data *px = ft->data; + PurpleAccount *pa = px->ic->proto_data; /* xfer_new() will pick up this variable. It's a hack but we're not multi-threaded anyway. */ next_ft = ft; - serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); + serv_send_file( purple_account_get_connection( pa ), px->handle, px->fn ); +} + +static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ + file_transfer_t *ft = data; + + if( ft->write == NULL ) + { + ft->write = prpl_xfer_write; + imcb_file_recv_start( ft ); + } - ft->write = prpl_xfer_write; + ft->write_request( ft ); - px = ft->data; - imcb_file_recv_start( ft ); + return FALSE; } +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ + struct prpl_xfer_data *px = ft->data; + + if( write( px->fd, buffer, len ) != len ) + { + imcb_file_canceled( ft, "Error while writing temporary file" ); + return FALSE; + } + + if( lseek( px->fd, 0, SEEK_CUR ) >= ft->file_size ) + { + close( px->fd ); + px->fd = -1; + + purple_transfer_forward( ft ); + imcb_file_finished( ft ); + px->ft = NULL; + } + else + b_timeout_add( 0, purple_transfer_request_cb, ft ); + + return TRUE; +} @@ -250,7 +323,7 @@ PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, prplcb_xfer_destroy, - prplcb_xfer_dbg, + prplcb_xfer_add, prplcb_xfer_progress, prplcb_xfer_dbg, prplcb_xfer_cancel_remote, -- cgit v1.2.3 From 75c3ff711698ea069f33ffc460c2e7aec650e875 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 21 May 2010 01:09:29 +0100 Subject: Fixed sending with proper filenames by creating a temporary directory with the file in it; protocol modules are mostly hardcoded to use the filename from the filesystem with no way to override this. Also improved robustness a little bit. --- protocols/purple/ft.c | 64 +++++++++++++++++++++++++++++++---------------- protocols/purple/purple.c | 2 +- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index ab637a94..e3a89524 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -39,7 +39,7 @@ struct prpl_xfer_data file_transfer_t *ft; struct im_connection *ic; int fd; - char *fn, *orig_fn, *handle; + char *fn, *handle; gboolean ui_wants_data; }; @@ -89,9 +89,6 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) xfer->ui_data = px; px->xfer = xfer; - purple_xfer_set_filename( xfer, px->orig_fn ); - purple_xfer_set_local_filename( xfer, px->fn ); - next_ft = NULL; } } @@ -151,7 +148,7 @@ gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) { - purple_xfer_end( px->xfer ); + /*purple_xfer_end( px->xfer );*/ imcb_file_finished( ft ); } @@ -176,7 +173,6 @@ static void prplcb_xfer_destroy( PurpleXfer *xfer ) struct prpl_xfer_data *px = xfer->ui_data; g_free( px->fn ); - g_free( px->orig_fn ); g_free( px->handle ); if( px->fd >= 0 ) close( px->fd ); @@ -194,7 +190,14 @@ static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { if( *px->fn ) { - //unlink( px->fn ); + char *slash; + + unlink( px->fn ); + if( ( slash = strrchr( px->fn, '/' ) ) ) + { + *slash = '\0'; + rmdir( px->fn ); + } *px->fn = '\0'; } @@ -225,17 +228,11 @@ static void prplcb_xfer_cancel_remote( PurpleXfer *xfer ) { struct prpl_xfer_data *px = xfer->ui_data; - imcb_file_canceled( px->ft, "Canceled by remote end" ); -} - -static void prplcb_xfer_add( PurpleXfer *xfer ) -{ - if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) - { - struct prpl_xfer_data *px = xfer->ui_data; - - purple_xfer_set_filename( xfer, px->orig_fn ); - } + if( px->ft ) + imcb_file_canceled( px->ft, "Canceled by remote end" ); + else + /* px->ft == NULL for sends, because of the two stages. :-/ */ + imcb_error( px->ic, "File transfer cancelled by remote end" ); } static void prplcb_xfer_dbg( PurpleXfer *xfer ) @@ -251,15 +248,38 @@ static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_cond void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) { struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + char *dir, *basename; ft->data = px; px->ft = ft; - px->fn = g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ); - px->fd = mkstemp( px->fn ); + + dir = g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ); + if( !mkdtemp( dir ) ) + { + imcb_error( ic, "Could not create temporary file for file transfer" ); + g_free( px ); + g_free( dir ); + return; + } + + if( ( basename = strrchr( ft->file_name, '/' ) ) ) + basename++; + else + basename = ft->file_name; + px->fn = g_strdup_printf( "%s/%s", dir, basename ); + px->fd = open( px->fn, O_WRONLY | O_CREAT, 0600 ); + g_free( dir ); + + if( px->fd < 0 ) + { + imcb_error( ic, "Could not create temporary file for file transfer" ); + g_free( px ); + g_free( px->fn ); + return; + } px->ic = ic; px->handle = g_strdup( handle ); - px->orig_fn = g_strdup( ft->file_name ); imcb_log( ic, "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait..." ); @@ -323,7 +343,7 @@ PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, prplcb_xfer_destroy, - prplcb_xfer_add, + NULL, /* prplcb_xfer_add, */ prplcb_xfer_progress, prplcb_xfer_dbg, prplcb_xfer_cancel_remote, diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index fee93b27..799a8a80 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -840,7 +840,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - //purple_debug_set_ui_ops( &bee_debug_uiops ); + purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From 2c5fabcf9066752eed45ad0334fca232a7123629 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 00:26:21 +0100 Subject: Sigh. Enable debugging only if the BITLBEE_DEBUG variable is set. --- protocols/purple/purple.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 799a8a80..f11cefcb 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -840,7 +840,9 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - purple_debug_set_ui_ops( &bee_debug_uiops ); + + if( getenv( "BITLBEE_DEBUG" ) ) + purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From e77c2647c3f4d8d6518239f070f3989444003a08 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 01:58:59 +0100 Subject: Added support for the info command. --- protocols/purple/purple.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f11cefcb..1d17b012 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -358,6 +358,11 @@ static void purple_remove_buddy( struct im_connection *ic, char *who, char *grou } } +static void purple_get_info( struct im_connection *ic, char *who ) +{ + serv_get_info( purple_account_get_connection( ic->proto_data ), who ); +} + static void purple_keepalive( struct im_connection *ic ) { } @@ -824,10 +829,66 @@ static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, con return NULL; } +static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info ) +{ + struct im_connection *ic = purple_ic_by_gc( gc ); + GString *info = g_string_new( "" ); + GList *l = purple_notify_user_info_get_entries( user_info ); + char *key; + const char *value; + int n; + + while( l ) + { + PurpleNotifyUserInfoEntry *e = l->data; + + switch( purple_notify_user_info_entry_get_type( e ) ) + { + case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR: + case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER: + key = g_strdup( purple_notify_user_info_entry_get_label( e ) ); + value = purple_notify_user_info_entry_get_value( e ); + + if( key ) + { + strip_html( key ); + g_string_append_printf( info, "%s: ", key ); + + if( value ) + { + n = strlen( value ) - 1; + while( isspace( value[n] ) ) + n --; + g_string_append_len( info, value, n + 1 ); + } + g_string_append_c( info, '\n' ); + g_free( key ); + } + + break; + case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK: + g_string_append( info, "------------------------\n" ); + break; + } + + l = l->next; + } + + imcb_log( ic, "User %s info:\n%s", who, info->str ); + g_string_free( info, TRUE ); + + return NULL; +} + static PurpleNotifyUiOps bee_notify_uiops = { NULL, prplcb_notify_email, + NULL, + NULL, + NULL, + NULL, + prplcb_notify_userinfo, }; extern PurpleXferUiOps bee_xfer_uiops; @@ -885,6 +946,7 @@ void purple_initmodule() funcs.set_away = purple_set_away; funcs.add_buddy = purple_add_buddy; funcs.remove_buddy = purple_remove_buddy; + funcs.get_info = purple_get_info; funcs.keepalive = purple_keepalive; funcs.send_typing = purple_send_typing; funcs.handle_cmp = g_strcasecmp; -- cgit v1.2.3 From dca8effbf732557e28a6a03d2fcf785d69a5a1bd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 02:05:58 +0100 Subject: Return ui_info so jabber:iq:version responses will not say just libpurple. --- protocols/purple/purple.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 1d17b012..7b020cf7 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -471,12 +471,27 @@ void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, cha static void purple_ui_init(); +GHashTable *prplcb_ui_info() +{ + static GHashTable *ret; + + if( ret == NULL ) + { + ret = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert( ret, "name", "BitlBee" ); + g_hash_table_insert( ret, "version", BITLBEE_VERSION ); + } + + return ret; +} + static PurpleCoreUiOps bee_core_uiops = { NULL, NULL, purple_ui_init, NULL, + prplcb_ui_info, }; static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) -- cgit v1.2.3 From 05a8932daa3e8f6d3dd4c5177fa04d1f17254000 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 13:21:27 +0100 Subject: Enable changing and viewing of block/allow lists. --- protocols/purple/purple.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 7b020cf7..c9588d7a 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -358,6 +358,34 @@ static void purple_remove_buddy( struct im_connection *ic, char *who, char *grou } } +static void purple_add_permit( struct im_connection *ic, char *who ) +{ + PurpleAccount *pa = ic->proto_data; + + purple_privacy_permit_add( pa, who, FALSE ); +} + +static void purple_add_deny( struct im_connection *ic, char *who ) +{ + PurpleAccount *pa = ic->proto_data; + + purple_privacy_deny_add( pa, who, FALSE ); +} + +static void purple_rem_permit( struct im_connection *ic, char *who ) +{ + PurpleAccount *pa = ic->proto_data; + + purple_privacy_permit_remove( pa, who, FALSE ); +} + +static void purple_rem_deny( struct im_connection *ic, char *who ) +{ + PurpleAccount *pa = ic->proto_data; + + purple_privacy_deny_remove( pa, who, FALSE ); +} + static void purple_get_info( struct im_connection *ic, char *who ) { serv_get_info( purple_account_get_connection( ic->proto_data ), who ); @@ -800,6 +828,48 @@ static PurpleRequestUiOps bee_request_uiops = NULL, }; +static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + + if( !g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) + ic->permit = g_slist_prepend( ic->permit, g_strdup( name ) ); +} + +static void prplcb_privacy_permit_removed( PurpleAccount *account, const char *name ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + void *n; + + n = g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ); + ic->permit = g_slist_remove( ic->permit, n ); +} + +static void prplcb_privacy_deny_added( PurpleAccount *account, const char *name ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + + if( !g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) + ic->deny = g_slist_prepend( ic->deny, g_strdup( name ) ); +} + +static void prplcb_privacy_deny_removed( PurpleAccount *account, const char *name ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + void *n; + + n = g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ); + ic->deny = g_slist_remove( ic->deny, n ); +} + +static PurplePrivacyUiOps bee_privacy_uiops = +{ + prplcb_privacy_permit_added, + prplcb_privacy_permit_removed, + prplcb_privacy_deny_added, + prplcb_privacy_deny_removed, +}; + static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) { fprintf( stderr, "DEBUG %s: %s", category, arg_s ); @@ -916,6 +986,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); + purple_privacy_set_ui_ops( &bee_privacy_uiops ); if( getenv( "BITLBEE_DEBUG" ) ) purple_debug_set_ui_ops( &bee_debug_uiops ); @@ -961,6 +1032,10 @@ void purple_initmodule() funcs.set_away = purple_set_away; funcs.add_buddy = purple_add_buddy; funcs.remove_buddy = purple_remove_buddy; + funcs.add_permit = purple_add_permit; + funcs.add_deny = purple_add_deny; + funcs.rem_permit = purple_rem_permit; + funcs.rem_deny = purple_rem_deny; funcs.get_info = purple_get_info; funcs.keepalive = purple_keepalive; funcs.send_typing = purple_send_typing; -- cgit v1.2.3 From c3caa46bc5c5ac0a372a13c5fe0de79845a7dabf Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 14:46:25 +0100 Subject: Support for named groupchats, although not very solid. --- protocols/purple/purple.c | 52 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index c9588d7a..b91b7aca 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -495,6 +495,44 @@ void purple_chat_leave( struct groupchat *gc ) purple_conversation_destroy( pc ); } +struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password ) +{ + PurpleAccount *pa = ic->proto_data; + PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); + PurplePluginProtocolInfo *pi = prpl->info->extra_info; + GHashTable *chat_hash; + PurpleConversation *conv; + GList *info, *l; + + if( !pi->chat_info || !pi->chat_info_defaults || + !( info = pi->chat_info( purple_account_get_connection( pa ) ) ) ) + { + imcb_error( ic, "Joining chatrooms not supported by this protocol" ); + return NULL; + } + + if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_CHAT, room, pa ) ) ) + purple_conversation_destroy( conv ); + + chat_hash = pi->chat_info_defaults( purple_account_get_connection( pa ), room ); + + for( l = info; l; l = l->next ) + { + struct proto_chat_entry *pce = l->data; + + if( strcmp( pce->identifier, "handle" ) == 0 ) + g_hash_table_replace( chat_hash, "handle", g_strdup( nick ) ); + else if( strcmp( pce->identifier, "password" ) == 0 ) + g_hash_table_replace( chat_hash, "password", g_strdup( password ) ); + else if( strcmp( pce->identifier, "passwd" ) == 0 ) + g_hash_table_replace( chat_hash, "passwd", g_strdup( password ) ); + } + + serv_join_chat( purple_account_get_connection( pa ), chat_hash ); + + return NULL; +} + void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); static void purple_ui_init(); @@ -661,6 +699,11 @@ void prplcb_conv_new( PurpleConversation *conv ) gc = imcb_chat_new( ic, conv->name ); conv->ui_data = gc; gc->data = conv; + + /* libpurple brokenness: Whatever. Show that we join right away, + there's no clear "This is you!" signaling in _add_users so + don't even try. */ + imcb_chat_add_buddy( gc, gc->ic->acc->user ); } } @@ -676,14 +719,6 @@ void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean struct groupchat *gc = conv->ui_data; GList *b; - if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 ) - { - /* Work around the broken MSN module which fucks up the user's - handle completely when informing him/her that he just - successfully joined the room s/he just created (v2.6.6). */ - imcb_chat_add_buddy( gc, gc->ic->acc->user ); - } - for( b = cbuddies; b; b = b->next ) { PurpleConvChatBuddy *pcb = b->data; @@ -1045,6 +1080,7 @@ void purple_initmodule() funcs.chat_with = purple_chat_with; funcs.chat_invite = purple_chat_invite; funcs.chat_leave = purple_chat_leave; + funcs.chat_join = purple_chat_join; funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From f85e9d6846d7b4ec0109180e62ae8677e2421fa3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 24 May 2010 22:24:53 +0100 Subject: Read display names. Setting them is going to be an awesome hack. --- protocols/purple/purple.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b91b7aca..98cd2241 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -36,6 +36,8 @@ GSList *purple_connections; libpurple in daemon mode anyway. */ static irc_t *local_irc; +static char *set_eval_display_name( set_t *set, char *value ); + struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { GSList *i; @@ -172,6 +174,9 @@ static void purple_init( account_t *acc ) help_add_mem( &global.help, help_title, help->str ); g_string_free( help, TRUE ); + s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); + s->flags |= ACC_SET_ONLINE_ONLY; + if( pi->options & OPT_PROTO_MAIL_CHECK ) { s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); @@ -337,6 +342,14 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me g_list_free( args ); } +static char *set_eval_display_name( set_t *set, char *value ) +{ + account_t *acc = set->data; + struct im_connection *ic = acc->ic; + + return NULL; +} + static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) { PurpleBuddy *pb; @@ -570,9 +583,18 @@ static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t static void prplcb_conn_connected( PurpleConnection *gc ) { struct im_connection *ic = purple_ic_by_gc( gc ); + const char *dn; + set_t *s; imcb_connected( ic ); + if( ( dn = purple_connection_get_display_name( gc ) ) && + ( s = set_find( &ic->acc->set, "display_name" ) ) ) + { + g_free( s->value ); + s->value = g_strdup( dn ); + } + if( gc->flags & PURPLE_CONNECTION_HTML ) ic->flags |= OPT_DOES_HTML; } -- cgit v1.2.3 From d25ebea73aef7d6b2bbc17212af5109383277e6e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:04:55 +0100 Subject: GAIM_INPUT_* were renamed, at last. --- protocols/jabber/s5bytestream.c | 14 +++++++------- protocols/msn/invitation.c | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 58a6c2e4..14611a6f 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -405,7 +405,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_CONNECTED; - bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); + bt->tf->watch_out = b_input_add( fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt ); /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); @@ -432,7 +432,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_REQUEST; - bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_recv_handshake, bt ); + bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt ); bt->tf->watch_out = 0; return FALSE; @@ -588,7 +588,7 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) bt->sh->port ); tf->ft->data = tf; - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); + tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); tf->ft->write_request = jabber_bs_recv_write_request; reply = xt_new_node( "streamhost-used", NULL, NULL ); @@ -630,7 +630,7 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) if( ( ret == -1 ) && ( errno == EAGAIN ) ) { - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); + tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); return FALSE; } } @@ -706,7 +706,7 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l if( tf->byteswritten >= ft->file_size ) imcb_file_finished( ft ); else - bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); + bt->tf->watch_out = b_input_add( tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt ); return TRUE; } @@ -917,7 +917,7 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) strcpy( sh->port, port ); bt->streamhosts = g_slist_append( bt->streamhosts, sh ); - bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); } else { imcb_log( tf->ic, "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s", @@ -1054,7 +1054,7 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_CONNECTED; - bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); return FALSE; } case BS_PHASE_CONNECTED: diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index d2b2a5c8..9f8b9a6e 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -208,7 +208,7 @@ gboolean msn_ftps_connected( gpointer data, gint fd, b_input_condition cond ) fd = msn_file->fd; sock_make_nonblocking( fd ); - msn_file->r_event_id = b_input_add( fd, GAIM_INPUT_READ, msn_ftp_read, file ); + msn_file->r_event_id = b_input_add( fd, B_EV_IO_READ, msn_ftp_read, file ); return FALSE; } @@ -229,7 +229,7 @@ void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboar return; } - msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftps_connected, file ); + msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftps_connected, file ); g_snprintf( buf, sizeof( buf ), "IP-Address: %s\r\n" @@ -317,7 +317,7 @@ gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ) return FALSE; sock_make_nonblocking( msn_file->fd ); - msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftp_read, file ); + msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); return FALSE; } @@ -414,7 +414,7 @@ gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) if( ( msn_file->sbufpos < MSNFTP_PSIZE ) && ( msn_file->data_sent + msn_file->sbufpos - 3 < file->file_size ) ) { if( !msn_file->w_event_id ) - msn_file->w_event_id = b_input_add( msn_file->fd, GAIM_INPUT_WRITE, msn_ftp_send, file ); + msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); return TRUE; } @@ -451,7 +451,7 @@ gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) } else { /* we might already be listening if this is data from an overflow */ if( !msn_file->w_event_id ) - msn_file->w_event_id = b_input_add( msn_file->fd, GAIM_INPUT_WRITE, msn_ftp_send, file ); + msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); } return TRUE; @@ -616,7 +616,7 @@ gboolean msn_ftpr_write_request( file_transfer_t *file ) } msn_file->r_event_id = - b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftp_read, file ); + b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); return TRUE; } -- cgit v1.2.3 From f60079b74053a53da3720b992c603fddf75ff1dd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:26:54 +0100 Subject: Allow one to run the configure script from a different directory and put all build files in there. I need this to properly make Debian package variants (i.e. libpurple and native). --- Makefile | 2 +- bitlbee.h | 2 +- configure | 24 +++++++++++++++++++++++- doc/Makefile | 3 +++ doc/user-guide/Makefile | 4 ++++ lib/Makefile | 5 ++++- protocols/Makefile | 5 ++++- protocols/jabber/Makefile | 5 ++++- protocols/msn/Makefile | 5 ++++- protocols/oscar/Makefile | 6 +++++- protocols/purple/Makefile | 5 ++++- protocols/twitter/Makefile | 5 ++++- protocols/yahoo/Makefile | 5 ++++- tests/Makefile | 5 ++++- 14 files changed, 69 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 916f551a..2bbafc57 100644 --- a/Makefile +++ b/Makefile @@ -109,7 +109,7 @@ tar: $(subdirs): @$(MAKE) -C $@ $(MAKECMDGOALS) -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/bitlbee.h b/bitlbee.h index f4e03d76..1dc434ec 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -42,7 +42,7 @@ #define MAX_STRING 511 #if HAVE_CONFIG_H -#include "config.h" +#include #endif #include diff --git a/configure b/configure index e0cf6f18..54156a61 100755 --- a/configure +++ b/configure @@ -123,6 +123,28 @@ LFLAGS= EFLAGS= EOF +srcdir="$(dirname $0)" +if [ "$srcdir" != "." ]; then + echo + echo "configure script run from a different directory. Will create some symlinks..." + if [ ! -e Makefile -o -L Makefile ]; then + mkdir -p $(cd "$srcdir"; find . -type d) + find . -name Makefile -type l -print0 | xargs -0 rm 2> /dev/null + dst="$PWD" + cd "$srcdir" + for i in $(find . -name Makefile); do + ln -s "$PWD${i#.}" "$dst/$i"; + done + cd "$dst" + rm -rf .bzr + fi + + echo "SRCDIR=$srcdir/" >> Makefile.settings + CFLAGS="$CFLAGS -I${dst}" +else + srcdir=$PWD +fi + cat<config.h /* BitlBee settings, generated by configure @@ -160,7 +182,7 @@ else fi echo CFLAGS=$CFLAGS >> Makefile.settings -echo CFLAGS+=-I`pwd` -I`pwd`/lib -I`pwd`/protocols -I. >> Makefile.settings +echo CFLAGS+=-I${srcdir} -I${srcdir}/lib -I${srcdir}/protocols -I. >> Makefile.settings echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings diff --git a/doc/Makefile b/doc/Makefile index 9b473df3..aeebd901 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,4 +1,7 @@ -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)doc/ +endif all: # Only build the docs if this is a bzr checkout diff --git a/doc/user-guide/Makefile b/doc/user-guide/Makefile index 9841de8d..eaf2a5ff 100644 --- a/doc/user-guide/Makefile +++ b/doc/user-guide/Makefile @@ -1,4 +1,8 @@ -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)doc/user-guide/ +endif + EXTRAPARANEWLINE = 1 # EXTRAPARANEWLINE = 0 diff --git a/lib/Makefile b/lib/Makefile index b686f886..8fd9b19e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)lib/ +endif # [SH] Program variables objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o @@ -36,6 +39,6 @@ lib.o: $(objects) $(subdirs) $(objects): ../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/Makefile b/protocols/Makefile index 18d79e8d..57fcd7eb 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/ +endif # [SH] Program variables objects = nogaim.o @@ -48,6 +51,6 @@ protocols.o: $(objects) $(subdirs) $(objects): ../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index 78a02696..912ea702 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/jabber/ +endif # [SH] Program variables objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 5d199b9e..1de755a8 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/msn/ +endif # [SH] Program variables objects = invitation.o msn.o msn_util.o ns.o passport.o sb.o tables.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/oscar/Makefile b/protocols/oscar/Makefile index 2792f22a..0ec7436b 100644 --- a/protocols/oscar/Makefile +++ b/protocols/oscar/Makefile @@ -7,6 +7,10 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/oscar/ +CFLAGS += -I$(SRCDIR) +endif # [SH] Program variables objects = admin.o auth.o bos.o buddylist.o chat.o chatnav.o conn.o icq.o im.o info.o misc.o msgcookie.o rxhandlers.o rxqueue.o search.o service.o snac.o ssi.o stats.o tlv.o txqueue.o oscar_util.o oscar.o @@ -32,7 +36,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile index 403db799..97a5bb6a 100644 --- a/protocols/purple/Makefile +++ b/protocols/purple/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/purple/ +endif # [SH] Program variables objects = ft.o purple.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/twitter/Makefile b/protocols/twitter/Makefile index ca1e4695..8a4b97f9 100644 --- a/protocols/twitter/Makefile +++ b/protocols/twitter/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/twitter/ +endif # [SH] Program variables objects = twitter.o twitter_http.o twitter_lib.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile index b4fe56e2..20ecce71 100644 --- a/protocols/yahoo/Makefile +++ b/protocols/yahoo/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/yahoo/ +endif # [SH] Program variables objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_util.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/tests/Makefile b/tests/Makefile index 1bcf8f72..7c876cec 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,7 @@ -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)tests/ +endif LFLAGS +=-lcheck @@ -18,6 +21,6 @@ check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../l @echo '*' Linking $@ @$(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(EFLAGS) -%.o: %.c +%.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ -- cgit v1.2.3 From 4af305032ba6913449cbd9160db1b6ab228f0a9a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:41:52 +0100 Subject: install-* targets should also work now. Let's see how this works out in a deb. --- Makefile | 9 +++++---- doc/Makefile | 4 ++-- doc/user-guide/Makefile | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 2bbafc57..222737b2 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # Program variables objects = account.o bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o -headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h +headers = account.h bitlbee.h commands.h conf.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols ifeq ($(TARGET),i586-mingw32msvc) @@ -81,7 +81,8 @@ uninstall-bin: install-dev: mkdir -p $(DESTDIR)$(INCLUDEDIR) - install -m 0644 $(headers) $(DESTDIR)$(INCLUDEDIR) + install -m 0644 config.h $(DESTDIR)$(INCLUDEDIR) + for i in $(headers); do install -m 0644 $(SRCDIR)$$i $(DESTDIR)$(INCLUDEDIR); done mkdir -p $(DESTDIR)$(PCDIR) install -m 0644 bitlbee.pc $(DESTDIR)$(PCDIR) @@ -92,8 +93,8 @@ uninstall-dev: install-etc: mkdir -p $(DESTDIR)$(ETCDIR) - install -m 0644 motd.txt $(DESTDIR)$(ETCDIR)/motd.txt - install -m 0644 bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf + install -m 0644 $(SRCDIR)motd.txt $(DESTDIR)$(ETCDIR)/motd.txt + install -m 0644 $(SRCDIR)bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf uninstall-etc: rm -f $(DESTDIR)$(ETCDIR)/motd.txt diff --git a/doc/Makefile b/doc/Makefile index aeebd901..5f59879e 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -9,8 +9,8 @@ all: install: mkdir -p $(DESTDIR)$(MANDIR)/man8/ $(DESTDIR)$(MANDIR)/man5/ - install -m 0644 bitlbee.8 $(DESTDIR)$(MANDIR)/man8/ - install -m 0644 bitlbee.conf.5 $(DESTDIR)$(MANDIR)/man5/ + install -m 0644 $(SRCDIR)bitlbee.8 $(DESTDIR)$(MANDIR)/man8/ + install -m 0644 $(SRCDIR)bitlbee.conf.5 $(DESTDIR)$(MANDIR)/man5/ $(MAKE) -C user-guide $@ uninstall: diff --git a/doc/user-guide/Makefile b/doc/user-guide/Makefile index eaf2a5ff..2a80ea6c 100644 --- a/doc/user-guide/Makefile +++ b/doc/user-guide/Makefile @@ -41,7 +41,7 @@ install: mkdir -p $(DESTDIR)$(DATADIR) chmod 0755 $(DESTDIR)$(DATADIR) rm -f $(DESTDIR)$(DATADIR)/help.txt # Prevent help function from breaking in running sessions - install -m 0644 help.txt $(DESTDIR)$(DATADIR)/help.txt + install -m 0644 $(SRCDIR)help.txt $(DESTDIR)$(DATADIR)/help.txt uninstall: rm -f $(DESTDIR)$(DATADIR)/help.txt -- cgit v1.2.3 From 4469e7ecb15f57151452c59040360a483e4f5ee9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 3 Jun 2010 23:17:11 +0100 Subject: Support for the /topic command. --- irc_commands.c | 25 +++++++++++++------------ irc_im.c | 13 ++++++++++++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index 530e849b..24be35e3 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -528,26 +528,27 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) } } -#if 0 static void irc_cmd_topic( irc_t *irc, char **cmd ) { - char *channel = cmd[1]; - char *topic = cmd[2]; + irc_channel_t *ic = irc_channel_by_name( irc, cmd[1] ); + const char *new = cmd[2]; - if( topic ) + if( ic == NULL ) { - /* Send the topic */ - struct groupchat *c = irc_chat_by_channel( irc, channel ); - if( c && c->ic && c->ic->acc->prpl->chat_topic ) - c->ic->acc->prpl->chat_topic( c, topic ); + irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); + } + else if( new ) + { + if( ic->f->topic == NULL ) + irc_send_num( irc, 482, "%s :Can't change this channel's topic", ic->name ); + else if( ic->f->topic( ic, new ) ) + irc_send_topic( ic, TRUE ); } else { - /* Get the topic */ - irc_topic( irc, channel ); + irc_send_topic( ic, FALSE ); } } -#endif static void irc_cmd_away( irc_t *irc, char **cmd ) { @@ -636,8 +637,8 @@ static const command_t irc_commands[] = { { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, #if 0 { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, - { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, #endif + { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "deaf", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, diff --git a/irc_im.c b/irc_im.c index 6e257b03..bdfbc597 100644 --- a/irc_im.c +++ b/irc_im.c @@ -476,7 +476,18 @@ static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg ) static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new ) { - return TRUE; + struct groupchat *c = ic->data; + char *topic = g_strdup( new ); /* TODO: Need more const goodness here, sigh */ + + if( c->ic->acc->prpl->chat_topic == NULL ) + irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name ); + else + { + c->ic->acc->prpl->chat_topic( c, topic ); + return TRUE; + } + + return FALSE; } static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu ) -- cgit v1.2.3 From 2b8473c72b730085c8d6aef3df8fef55c7002080 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 01:20:12 +0100 Subject: Put the control channel settings into user-changeable settings. --- irc_channel.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/irc_channel.c b/irc_channel.c index a3a7e1ee..8ef7f7d1 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -25,6 +25,7 @@ #include "bitlbee.h" +static char *set_eval_channel_type( set_t *set, char *value ); static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ ); static const struct irc_channel_funcs control_channel_funcs; static const struct irc_channel_funcs groupchat_stub_funcs; @@ -48,17 +49,12 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) irc->channels = g_slist_prepend( irc->channels, ic ); + set_add( &ic->set, "type", "control", set_eval_channel_type, ic ); + if( name[0] == '&' ) - ic->f = &control_channel_funcs; + set_setstr( &ic->set, "type", "control" ); else /* if( name[0] == '#' ) */ - ic->f = &groupchat_stub_funcs; - - if( ic->f->_init ) - if( !ic->f->_init( ic ) ) - { - irc_channel_free( ic ); - return NULL; - } + set_setstr( &ic->set, "type", "chat" ); return ic; } @@ -99,6 +95,30 @@ int irc_channel_free( irc_channel_t *ic ) return 1; } +static char *set_eval_channel_type( set_t *set, char *value ) +{ + struct irc_channel *ic = set->data; + const struct irc_channel_funcs *new; + + if( strcmp( value, "control" ) == 0 ) + new = &control_channel_funcs; + else if( strcmp( value, "chat" ) == 0 ) + new = &groupchat_stub_funcs; + else + return SET_INVALID; + + /* TODO: Return values. */ + if( ic->f && ic->f->_free ) + ic->f->_free( ic ); + + ic->f = new; + + if( ic->f && ic->f->_init ) + ic->f->_init( ic ); + + return value; +} + int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) { irc_channel_user_t *icu; @@ -266,19 +286,87 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) return TRUE; } +static char *set_eval_by_account( set_t *set, char *value ); +static char *set_eval_fill_by( set_t *set, char *value ); +static char *set_eval_by_group( set_t *set, char *value ); + static gboolean control_channel_init( irc_channel_t *ic ) { struct irc_control_channel *icc; + set_add( &ic->set, "account", NULL, set_eval_by_account, ic ); + set_add( &ic->set, "fill_by", "all", set_eval_fill_by, ic ); + set_add( &ic->set, "group", NULL, set_eval_by_group, ic ); + ic->data = icc = g_new0( struct irc_control_channel, 1 ); icc->type = IRC_CC_TYPE_DEFAULT; - if( ( icc->group = bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) ) ) + if( bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) ) + { + set_setstr( &ic->set, "group", ic->name + 1 ); + set_setstr( &ic->set, "fill_by", "group" ); + } + else if( set_setstr( &ic->set, "account", ic->name + 1 ) ) + { + set_setstr( &ic->set, "fill_by", "account" ); + } + + return TRUE; +} + +static char *set_eval_by_account( set_t *set, char *value ) +{ + struct irc_channel *ic = set->data; + struct irc_control_channel *icc = ic->data; + account_t *acc; + + if( !( acc = account_get( ic->irc->b, value ) ) ) + return SET_INVALID; + + icc->account = acc; + bee_irc_channel_update( ic->irc, ic, NULL ); + return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user ); +} + +static char *set_eval_fill_by( set_t *set, char *value ) +{ + struct irc_channel *ic = set->data; + struct irc_control_channel *icc = ic->data; + + if( strcmp( value, "all" ) == 0 ) + icc->type = IRC_CC_TYPE_DEFAULT; + else if( strcmp( value, "rest" ) == 0 ) + icc->type = IRC_CC_TYPE_REST; + else if( strcmp( value, "group" ) == 0 ) icc->type = IRC_CC_TYPE_GROUP; - else if( ( icc->account = account_get( ic->irc->b, ic->name + 1 ) ) ) + else if( strcmp( value, "account" ) == 0 ) icc->type = IRC_CC_TYPE_ACCOUNT; + else + return SET_INVALID; + + return value; +} + +static char *set_eval_by_group( set_t *set, char *value ) +{ + struct irc_channel *ic = set->data; + struct irc_control_channel *icc = ic->data; + icc->group = bee_group_by_name( ic->irc->b, value, TRUE ); bee_irc_channel_update( ic->irc, ic, NULL ); + return g_strdup( icc->group->name ); +} + +static gboolean control_channel_free( irc_channel_t *ic ) +{ + struct irc_control_channel *icc = ic->data; + + set_del( &ic->set, "account" ); + set_del( &ic->set, "fill_by" ); + set_del( &ic->set, "group" ); + + g_free( icc ); + ic->data = NULL; return TRUE; } @@ -291,6 +379,7 @@ static const struct irc_channel_funcs control_channel_funcs = { NULL, control_channel_init, + control_channel_free, }; /* Groupchat stub: Only handles /INVITE at least for now. */ -- cgit v1.2.3 From 7e83e8e47ff1ba031928e1e46bf16ec53ca53117 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 01:35:17 +0100 Subject: Inform the UI about group changes. This is important if the user has group channels. --- irc_im.c | 11 +++++++++++ protocols/bee.h | 1 + protocols/nogaim.c | 3 +++ 3 files changed, 15 insertions(+) diff --git a/irc_im.c b/irc_im.c index bdfbc597..818495dd 100644 --- a/irc_im.c +++ b/irc_im.c @@ -258,6 +258,16 @@ static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) return TRUE; } +static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu ) +{ + irc_user_t *iu = (irc_user_t *) bu->ui_data; + irc_t *irc = (irc_t *) bee->ui_data; + + bee_irc_channel_update( irc, NULL, iu ); + + return TRUE; +} + /* IRC->IM calls */ static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg ) @@ -543,6 +553,7 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_user_new, bee_irc_user_free, bee_irc_user_fullname, + bee_irc_user_group, bee_irc_user_status, bee_irc_user_msg, bee_irc_user_typing, diff --git a/protocols/bee.h b/protocols/bee.h index e421db57..5701ea60 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -82,6 +82,7 @@ typedef struct bee_ui_funcs gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); gboolean (*user_free)( bee_t *bee, struct bee_user *bu ); gboolean (*user_fullname)( bee_t *bee, bee_user_t *bu ); + gboolean (*user_group)( bee_t *bee, bee_user_t *bu ); gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old ); gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ); gboolean (*user_typing)( bee_t *bee, bee_user_t *bu, guint32 flags ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 00fe0ebf..df97393d 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -378,6 +378,9 @@ void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *g bu = bee_user_new( bee, ic, handle, 0 ); bu->group = bee_group_by_name( bee, group, TRUE ); + + if( bee->ui->user_group ) + bee->ui->user_group( bee, bu ); } void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *fullname ) -- cgit v1.2.3 From c133d4b82b205b515e0c43be9f9b92ac3dbcd9ce Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 02:06:26 +0100 Subject: Added "channel set" command, mostly so I can test all that stuff. Needs more work. --- root_commands.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/root_commands.c b/root_commands.c index 65f3de9a..cf1c169c 100644 --- a/root_commands.c +++ b/root_commands.c @@ -485,6 +485,30 @@ static void cmd_account( irc_t *irc, char **cmd ) } } +static set_t **cmd_channel_set_findhead( irc_t *irc, char *id ) +{ + irc_channel_t *ic; + + if( ( ic = irc_channel_by_name( irc, id ) ) ) + return &ic->set; + else + return NULL; +} + +static void cmd_channel( irc_t *irc, char **cmd ) +{ + if( g_strcasecmp( cmd[1], "set" ) == 0 ) + { + MIN_ARGS( 2 ); + + cmd_set_real( irc, cmd + 1, cmd_channel_set_findhead, NULL ); + } + else + { + irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] ); + } +} + static void cmd_add( irc_t *irc, char **cmd ) { account_t *a; @@ -942,7 +966,6 @@ static void cmd_qlist( irc_t *irc, char **cmd ) static void cmd_chat( irc_t *irc, char **cmd ) { account_t *acc; - struct chat *c; if( g_strcasecmp( cmd[1], "add" ) == 0 ) { @@ -1080,6 +1103,8 @@ const command_t commands[] = { { "allow", 1, cmd_allow, 0 }, { "blist", 0, cmd_blist, 0 }, { "block", 1, cmd_block, 0 }, + { "channel", 1, cmd_channel, 0 }, + { "chat", 1, cmd_chat, 0 }, { "drop", 1, cmd_drop, 0 }, { "ft", 0, cmd_transfer, 0 }, { "help", 0, cmd_help, 0 }, @@ -1094,6 +1119,5 @@ const command_t commands[] = { { "set", 0, cmd_set, 0 }, { "transfer", 0, cmd_transfer, 0 }, { "yes", 0, cmd_yesno, 0 }, - { "chat", 1, cmd_chat, 0 }, { NULL } }; -- cgit v1.2.3 From cf1a9790b4b933f7727c1362285f529f45a755c0 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 02:07:04 +0100 Subject: Bug fixes, control channel behaviour is mostly okay again. --- irc_channel.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/irc_channel.c b/irc_channel.c index 8ef7f7d1..64bbd614 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -310,6 +310,10 @@ static gboolean control_channel_init( irc_channel_t *ic ) { set_setstr( &ic->set, "fill_by", "account" ); } + else + { + bee_irc_channel_update( ic->irc, ic, NULL ); + } return TRUE; } @@ -324,7 +328,8 @@ static char *set_eval_by_account( set_t *set, char *value ) return SET_INVALID; icc->account = acc; - bee_irc_channel_update( ic->irc, ic, NULL ); + if( icc->type == IRC_CC_TYPE_ACCOUNT ) + bee_irc_channel_update( ic->irc, ic, NULL ); return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user ); } @@ -344,6 +349,7 @@ static char *set_eval_fill_by( set_t *set, char *value ) else return SET_INVALID; + bee_irc_channel_update( ic->irc, ic, NULL ); return value; } @@ -353,7 +359,8 @@ static char *set_eval_by_group( set_t *set, char *value ) struct irc_control_channel *icc = ic->data; icc->group = bee_group_by_name( ic->irc->b, value, TRUE ); - bee_irc_channel_update( ic->irc, ic, NULL ); + if( icc->type == IRC_CC_TYPE_GROUP ) + bee_irc_channel_update( ic->irc, ic, NULL ); return g_strdup( icc->group->name ); } -- cgit v1.2.3 From 04dc563f2a30476bf96e72ce3e66cb5a8b2606d3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 11:32:02 +0100 Subject: A few more configure script tweaks: Don't enable libpurple by default anymore (in preparation for a merge), and limit the directory replication stuff. --- configure | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 54156a61..232e0cdc 100755 --- a/configure +++ b/configure @@ -26,7 +26,7 @@ jabber=1 oscar=1 yahoo=1 twitter=1 -purple=1 +purple=0 debug=0 strip=1 @@ -128,11 +128,12 @@ if [ "$srcdir" != "." ]; then echo echo "configure script run from a different directory. Will create some symlinks..." if [ ! -e Makefile -o -L Makefile ]; then - mkdir -p $(cd "$srcdir"; find . -type d) + COPYDIRS="doc lib protocols tests utils" + mkdir -p $(cd "$srcdir"; find $COPYDIRS -type d) find . -name Makefile -type l -print0 | xargs -0 rm 2> /dev/null dst="$PWD" cd "$srcdir" - for i in $(find . -name Makefile); do + for i in $(find . -name Makefile -type f); do ln -s "$PWD${i#.}" "$dst/$i"; done cd "$dst" -- cgit v1.2.3 From 095a5f046c36c4cff689305fef81533a9e9603fc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 15:47:54 +0100 Subject: Redid debian/rules using debhelper, with good results. This creates mostly fine debs. Need to do some more checks, and make sure bitlbee-libpurple gets the same maintainer scripts as bitlbee. --- debian/bitlbee-common.docs | 6 ++ debian/bitlbee-common.examples | 1 + debian/changelog | 9 +++ debian/compat | 1 + debian/conffiles | 3 - debian/control | 29 ++++++- debian/patches/bitlbee.conf.diff | 4 +- debian/rules | 163 ++++++++++++++++++--------------------- 8 files changed, 120 insertions(+), 96 deletions(-) create mode 100644 debian/bitlbee-common.docs create mode 100644 debian/bitlbee-common.examples create mode 100644 debian/compat delete mode 100644 debian/conffiles diff --git a/debian/bitlbee-common.docs b/debian/bitlbee-common.docs new file mode 100644 index 00000000..72ff657c --- /dev/null +++ b/debian/bitlbee-common.docs @@ -0,0 +1,6 @@ +doc/user-guide/user-guide.txt +doc/user-guide/user-guide.html +doc/AUTHORS +doc/CREDITS +doc/FAQ +doc/README diff --git a/debian/bitlbee-common.examples b/debian/bitlbee-common.examples new file mode 100644 index 00000000..81562b9e --- /dev/null +++ b/debian/bitlbee-common.examples @@ -0,0 +1 @@ +utils/* diff --git a/debian/changelog b/debian/changelog index 56d0a551..ffb23ed8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +bitlbee (1.3-0) unstable; urgency=low + + * Setting some bogus version number, fix that later. + * Now using debhelper to improve maintainability. + * Added a bitlbee-libpurple package, and split off docs and stuff into + bitlbee-common. + + -- Wilmer van der Gaast Sat, 05 Jun 2010 15:16:38 +0100 + bitlbee (1.2.6a-1) unstable; urgency=low * New upstream version. diff --git a/debian/compat b/debian/compat new file mode 100644 index 00000000..7f8f011e --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/conffiles b/debian/conffiles deleted file mode 100644 index dcb4078e..00000000 --- a/debian/conffiles +++ /dev/null @@ -1,3 +0,0 @@ -/etc/bitlbee/motd.txt -/etc/bitlbee/bitlbee.conf -/etc/init.d/bitlbee diff --git a/debian/control b/debian/control index 8f861aeb..dfef8e39 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Section: net Priority: optional Maintainer: Wilmer van der Gaast Uploaders: Jelmer Vernooij -Standards-Version: 3.8.0 +Standards-Version: 3.8.4 Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf, libpurple-dev Homepage: http://www.bitlbee.org/ Vcs-Bzr: http://code.bitlbee.org/bitlbee/ @@ -11,11 +11,36 @@ DM-Upload-Allowed: yes Package: bitlbee Architecture: any -Depends: ${shlibs:Depends}, adduser, net-tools, ${debconf-depends}, debianutils (>= 1.16) +Depends: ${shlibs:Depends}, adduser, net-tools, debianutils (>= 1.16), bitlbee-common (>= ${source:Version}), bitlbee-common (<< ${source:Version}.1~) Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. +Package: bitlbee-libpurple +Architecture: any +Depends: ${shlibs:Depends}, adduser, net-tools, debianutils (>= 1.16), bitlbee-common (>= ${source:Version}), bitlbee-common (<< ${source:Version}.1~) +Description: An IRC to other chat networks gateway + This program can be used as an IRC server which forwards everything you + say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. + . + This package contains a version of BitlBee that uses the libpurple instant + messaging library instead of built-in code, which adds support for more IM + protocols (all protocols supported by Pidgin/Finch) and features (like file + transfers), at the price of being less lightweight. + . + This variant may not be very suitable for BitlBee instances used by many + (tens or hundreds) of clients. + +Package: bitlbee-common +Architecture: all +Replaces: bitlbee (<= 1.3) +Description: An IRC to other chat networks gateway + This program can be used as an IRC server which forwards everything you + say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. + . + This package contains common files (mostly documentation) for bitlbee and + bitlbee-libpurple. + Package: bitlbee-dev Architecture: all Depends: bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) diff --git a/debian/patches/bitlbee.conf.diff b/debian/patches/bitlbee.conf.diff index c98fa546..339ccd4a 100644 --- a/debian/patches/bitlbee.conf.diff +++ b/debian/patches/bitlbee.conf.diff @@ -1,5 +1,5 @@ ---- debian/bitlbee/etc/bitlbee/bitlbee.conf 2009-06-01 00:20:24.000000000 +0100 -+++ debian/bitlbee/etc/bitlbee/bitlbee.conf 2009-06-07 21:16:19.000000000 +0100 +--- bitlbee.conf 2009-06-01 00:20:24.000000000 +0100 ++++ bitlbee.conf 2009-06-07 21:16:19.000000000 +0100 @@ -23,13 +23,18 @@ ## If BitlBee is started by root as a daemon, it can drop root privileges, ## and change to the specified user. diff --git a/debian/rules b/debian/rules index df129f98..bbb368f9 100755 --- a/debian/rules +++ b/debian/rules @@ -1,4 +1,11 @@ #!/usr/bin/make -f +# +# Finally switching to debhelper. +# +# Not using debhelper was an exercise suggested to me by my AM (Gergely +# Nagy). It was educating at the time but I finally decided that the +# exercise is over now. +# BITLBEE_CONFIGURE_FLAGS ?= DEBUG ?= 0 @@ -7,104 +14,82 @@ ifdef BITLBEE_VERSION BITLBEE_FORCE_VERSION=1 else # Want to use the full package version number instead of just the release. -BITLBEE_VERSION ?= "$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')" -export BITLBEE_VERSION +BITLBEE_CONFIGURE_VERSION ?= BITLBEE_VERSION=\"$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')\" endif -build-arch: build-arch-stamp -build-arch-stamp: - [ -d debian ] - ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS) - $(MAKE) -# $(MAKE) -C doc/ all - touch build-arch-stamp +build: build-stamp +build-stamp: + dh_testdir -clean: - [ "`whoami`" = "root" -a -d debian ] - rm -rf build-arch-stamp debian/bitlbee debian/*.substvars debian/files debian/bitlbee-dev - $(MAKE) distclean -# -$(MAKE) -C doc/ clean - - -install-arch: build-arch - [ "`whoami`" = "root" -a -d debian ] - mkdir -p debian/bitlbee/DEBIAN/ - $(MAKE) install install-etc DESTDIR=`pwd`/debian/bitlbee - - mkdir -p debian/bitlbee/usr/share/doc/bitlbee/ - cp doc/user-guide/user-guide.txt debian/bitlbee/usr/share/doc/bitlbee/ - cp doc/user-guide/user-guide.html debian/bitlbee/usr/share/doc/bitlbee/ - -install-indep: install-arch - [ "`whoami`" = "root" -a -d debian ] - mkdir -p debian/bitlbee-dev/DEBIAN/ - $(MAKE) install-dev DESTDIR=`pwd`/debian/bitlbee-dev - - mkdir -p debian/bitlbee-dev/usr/share/doc/bitlbee-dev/ - -binary-arch: build-arch install-arch - [ "`whoami`" = "root" -a -d debian ] - - chmod 755 debian/post* debian/pre* debian/config debian/bitlbee.init - - mkdir -p debian/bitlbee/usr/share/doc/bitlbee/examples/ debian/bitlbee/etc/init.d/ - -cp doc/RELEASE-SPEECH* debian/bitlbee/usr/share/doc/bitlbee/ && gzip -9 debian/bitlbee/usr/share/doc/bitlbee/RELEASE-SPEECH* - cp doc/CREDITS doc/AUTHORS doc/README doc/FAQ debian/README.Debian debian/bitlbee/usr/share/doc/bitlbee/ - cp debian/changelog debian/bitlbee/usr/share/doc/bitlbee/changelog.Debian - cp debian/copyright debian/bitlbee/usr/share/doc/bitlbee/copyright - cp doc/CHANGES debian/bitlbee/usr/share/doc/bitlbee/changelog - cp utils/* debian/bitlbee/usr/share/doc/bitlbee/examples/ - cp debian/bitlbee.init debian/bitlbee/etc/init.d/bitlbee - patch -p0 < debian/patches/bitlbee.conf.diff - cd debian/bitlbee/usr/share/; \ - gzip -9 doc/bitlbee/changelog.Debian doc/bitlbee/changelog doc/bitlbee/user-guide.txt \ - doc/bitlbee/examples/* man/man8/bitlbee.8 man/man5/bitlbee.conf.5 - - chown -R root:root debian/bitlbee/ - find debian/bitlbee/usr/share/ -type d -exec chmod 755 {} \; - find debian/bitlbee/usr/share/ -type f -exec chmod 644 {} \; - - cp debian/prerm debian/bitlbee/DEBIAN/ - cp debian/postinst debian/bitlbee/DEBIAN/ - cp debian/postrm debian/bitlbee/DEBIAN/ - cp debian/config debian/bitlbee/DEBIAN/ - - po2debconf debian/templates > debian/bitlbee/DEBIAN/templates - cp debian/conffiles debian/bitlbee/DEBIAN/ - - if [ "$(DEBUG)" = "0" ]; then strip -R .comment -R .note debian/bitlbee/usr/sbin/bitlbee; fi - - cd debian/bitlbee; \ - find usr -type f -exec md5sum {} \; > DEBIAN/md5sums - dpkg-shlibdeps -Tdebian/bitlbee.substvars -dDepends debian/bitlbee/usr/sbin/bitlbee -ifdef BITLBEE_FORCE_VERSION - dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -v1:$(BITLBEE_VERSION)-0 -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0' -else - dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0' -endif + mkdir debian/build-native + ROOT=$$PWD; cd debian/build-native; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS) + $(MAKE) -C debian/build-native - dpkg --build debian/bitlbee .. + mkdir debian/build-libpurple + ROOT=$$PWD; cd debian/build-libpurple; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --purple=1 $(BITLBEE_CONFIGURE_FLAGS) + $(MAKE) -C debian/build-libpurple -binary-indep: install-indep - [ "`whoami`" = "root" -a -d debian ] + touch build-stamp - chown -R root.root debian/bitlbee-dev/ - find debian/bitlbee-dev/usr/share/ -type d -exec chmod 755 {} \; - find debian/bitlbee-dev/usr/share/ -type f -exec chmod 644 {} \; +clean: + dh_testdir + dh_testroot + rm -f build-stamp - cp debian/changelog debian/bitlbee-dev/usr/share/doc/bitlbee-dev/changelog.Debian - gzip -9 debian/bitlbee-dev/usr/share/doc/bitlbee-dev/changelog.Debian - cp debian/copyright debian/bitlbee-dev/usr/share/doc/bitlbee-dev/copyright + rm -rf build-arch-stamp debian/build-* + $(MAKE) distclean - cd debian/bitlbee-dev; \ - find usr -type f -exec md5sum {} \; > DEBIAN/md5sums + dh_clean + +install: build + dh_testdir + dh_testroot + dh_prep + dh_installdirs + + $(MAKE) -C debian/build-native install install-etc DESTDIR=`pwd`/debian/bitlbee + $(MAKE) -C debian/build-libpurple install install-etc DESTDIR=`pwd`/debian/bitlbee-libpurple + $(MAKE) -C debian/build-native install-dev DESTDIR=`pwd`/debian/bitlbee-dev + + patch debian/bitlbee/etc/bitlbee/bitlbee.conf debian/patches/bitlbee.conf.diff + patch debian/bitlbee-libpurple/etc/bitlbee/bitlbee.conf debian/patches/bitlbee.conf.diff + + mkdir -p debian/bitlbee-common/usr + mv debian/bitlbee/usr/share debian/bitlbee-common/usr + rm -rf debian/bitlbee-libpurple/usr/share + +binary-common: + dh_testdir + dh_testroot + + dh_installdocs --link-doc=bitlbee-common + dh_installchangelogs + dh_installexamples + dh_installdebconf + dh_installinit + dh_installman + dh_strip + dh_link + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps +ifdef BITLBEE_FORCE_VERSION + dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 +else + dh_gencontrol +endif + dh_md5sums + dh_builddeb - dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev -v1:$(BITLBEE_VERSION)-0 +binary-indep: build install + $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common - dpkg --build debian/bitlbee-dev .. +binary-arch: build install + $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common -binary: binary-arch binary-indep -build: build-arch -install: install-arch install-indep +binary-%: build install + make -f debian/rules binary-common DH_OPTIONS=-p$* -.PHONY: build-arch build clean binary-arch binary install-arch install binary-indep install-indep +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary-common binary install -- cgit v1.2.3 From 4c0388134ff88b72e343bd07013554480c2a4ef7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 17:39:58 +0100 Subject: Remaining fixes: All looks good now, apt and dpkg seem to do the right thing with the -common transition, etc. --- debian/bitlbee-common.config | 25 ++++++++++ debian/bitlbee-common.templates | 8 ++++ debian/bitlbee.init | 0 debian/bitlbee.postinst | 102 ++++++++++++++++++++++++++++++++++++++++ debian/bitlbee.postrm | 14 ++++++ debian/bitlbee.prerm | 17 +++++++ debian/config | 25 ---------- debian/control | 15 ++++-- debian/postinst | 102 ---------------------------------------- debian/postrm | 14 ------ debian/prerm | 17 ------- debian/rules | 12 +++-- debian/templates | 8 ---- 13 files changed, 184 insertions(+), 175 deletions(-) create mode 100644 debian/bitlbee-common.config create mode 100644 debian/bitlbee-common.templates mode change 100755 => 100644 debian/bitlbee.init create mode 100644 debian/bitlbee.postinst create mode 100644 debian/bitlbee.postrm create mode 100644 debian/bitlbee.prerm delete mode 100755 debian/config delete mode 100755 debian/postinst delete mode 100755 debian/postrm delete mode 100755 debian/prerm delete mode 100644 debian/templates diff --git a/debian/bitlbee-common.config b/debian/bitlbee-common.config new file mode 100644 index 00000000..9bb78237 --- /dev/null +++ b/debian/bitlbee-common.config @@ -0,0 +1,25 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule +[ -f /etc/default/bitlbee ] && . /etc/default/bitlbee + +db_title BitlBee + +if [ -n "$BITLBEE_PORT" ]; then + db_set bitlbee/serveport "$BITLBEE_PORT" +else + db_get bitlbee/serveport + if [ "$RET" = "stillhavetoask" ]; then + listens=$(netstat -ltn | awk '{print $4}') + for port in 6667 6666 6668 6669; do + if [ $(expr "$listens " : ".*:$port\s") = "0" ]; then + break + fi + done + db_set bitlbee/serveport $port; + fi +fi + +if db_input medium bitlbee/serveport; then + db_go; +fi diff --git a/debian/bitlbee-common.templates b/debian/bitlbee-common.templates new file mode 100644 index 00000000..0cd04426 --- /dev/null +++ b/debian/bitlbee-common.templates @@ -0,0 +1,8 @@ +Template: bitlbee/serveport +Type: string +Default: stillhavetoask +_Description: On what TCP port should BitlBee listen for connections? + BitlBee normally listens on the regular IRC port, 6667. This might not be + a very good idea when you're running a real IRC daemon as well. 6668 might + be a good alternative. Leaving this value blank means that BitlBee will not + be run automatically. diff --git a/debian/bitlbee.init b/debian/bitlbee.init old mode 100755 new mode 100644 diff --git a/debian/bitlbee.postinst b/debian/bitlbee.postinst new file mode 100644 index 00000000..db541f6c --- /dev/null +++ b/debian/bitlbee.postinst @@ -0,0 +1,102 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule + +db_get bitlbee/serveport +PORT="$RET" + +CONFDIR=/var/lib/bitlbee/ + +update-rc.d bitlbee defaults > /dev/null 2>&1 + +## Load default option. Don't want to put this in debconf (yet?) +BITLBEE_OPTS=-F +BITLBEE_DISABLED=0 +BITLBEE_UPGRADE_DONT_RESTART=0 +[ -r /etc/default/bitlbee ] && . /etc/default/bitlbee + +if [ "$BITLBEE_DISABLED" = "0" ] && type update-inetd > /dev/null 2> /dev/null && + ( expr "$2" : '0\..*' > /dev/null || expr "$2" : '1\.0\..*' > /dev/null ); then + ## Make sure the inetd entry is gone (can still be there from a + ## previous version. + update-inetd --remove '.*/usr/sbin/bitlbee' + if grep -q /usr/sbin/bitlbee /etc/inetd.conf 2> /dev/null; then + # Thanks for breaking update-inetd! (bugs.debian.org/311111) + # I hope that it works at least with xinetd, because this + # emergency hack doesn't: + perl -pi -e 's:^[^#].*/usr/sbin/bitlbee$:## Now using daemon mode\: # $&:' /etc/inetd.conf + killall -HUP inetd || true + fi +fi + +cat</etc/default/bitlbee +## /etc/default/bitlbee: Auto-generated/updated script. +## +## If running in (fork)daemon mode, listen on this TCP port. +BITLBEE_PORT="$PORT" + +## Use single-process or forking daemon mode? Can't be changed from debconf, +## but maintainer scripts will save your changes here. +BITLBEE_OPTS="$BITLBEE_OPTS" + +## In case you want to stick with inetd mode (or if you just want to disable +## the init scripts for some other reason), you can disable the init script +## here. (Just set it to 1) +BITLBEE_DISABLED=$BITLBEE_DISABLED + +## As a server operator, you can use the RESTART command to restart only the +## master process while keeping all the child processes and their IPC +## connections. By enabling this, the maintainer scripts won't restart +## BitlBee during upgrades so you can restart the master process by hand. +BITLBEE_UPGRADE_DONT_RESTART=$BITLBEE_UPGRADE_DONT_RESTART +EOF + +## Bye-bye DebConf, we don't need you anymore. +db_stop + +## Restore the helpfile in case we weren't upgrading but just reconfiguring: +if [ -e /usr/share/bitlbee/help.upgrading ]; then + if [ -e /usr/share/bitlbee/help.txt ]; then + rm -f /usr/share/bitlbee/help.upgrading + else + mv /usr/share/bitlbee/help.upgrading /usr/share/bitlbee/help.txt + fi +fi + +if [ -n "$2" -a "$BITLBEE_UPGRADE_DONT_RESTART" != "1" ]; then + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitlbee restart + else + /etc/init.d/bitlbee restart + fi +fi + +## If we're upgrading, we'll probably skip this next part +if [ -d $CONFDIR ] && chown -R bitlbee: $CONFDIR; then + echo 'BitlBee (probably) already installed, skipping user/configdir installation' + exit 0 +fi + +adduser --system --group --disabled-login --disabled-password --home /var/lib/bitlbee/ bitlbee +chmod 700 /var/lib/bitlbee/ + +## Can't do this in packaging phase: Don't know the UID yet. Access to +## the file should be limited, now that it stores passwords. Added +## --group later for a little more security, but have to see if I can +## apply this change to existing installations on upgrades. Will think +## about that later. +if getent group bitlbee > /dev/null; then + chmod 640 /etc/bitlbee/bitlbee.conf + chown root:bitlbee /etc/bitlbee/bitlbee.conf +else + chmod 600 /etc/bitlbee/bitlbee.conf + chown bitlbee /etc/bitlbee/bitlbee.conf +fi + +if [ -z "$2" ]; then + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitlbee start + else + /etc/init.d/bitlbee start + fi +fi diff --git a/debian/bitlbee.postrm b/debian/bitlbee.postrm new file mode 100644 index 00000000..5c3b4b2e --- /dev/null +++ b/debian/bitlbee.postrm @@ -0,0 +1,14 @@ +#!/bin/sh -e + +[ "$1" = "purge" ] || exit 0 + +if [ -e /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule; + db_purge; +fi + +update-rc.d bitlbee remove > /dev/null 2>&1 || true +rm -f /etc/default/bitlbee + +deluser --system --remove-home bitlbee || true +rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var diff --git a/debian/bitlbee.prerm b/debian/bitlbee.prerm new file mode 100644 index 00000000..687c2cc1 --- /dev/null +++ b/debian/bitlbee.prerm @@ -0,0 +1,17 @@ +#!/bin/sh -e + +if [ "$1" = "upgrade" ]; then + ## To prevent the help function from breaking in currently running + ## BitlBee processes. Have to do it like this because dpkg-reconfigure + ## looks a lot like an upgrade and we don't want to lose help.txt... + if [ -e /usr/share/bitlbee/help.txt ]; then + rm -f /usr/share/bitlbee/help.upgrading + mv /usr/share/bitlbee/help.txt /usr/share/bitlbee/help.upgrading + fi +else + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitlbee stop || exit 0 + else + /etc/init.d/bitlbee stop || exit 0 + fi +fi diff --git a/debian/config b/debian/config deleted file mode 100755 index 9bb78237..00000000 --- a/debian/config +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -e - -. /usr/share/debconf/confmodule -[ -f /etc/default/bitlbee ] && . /etc/default/bitlbee - -db_title BitlBee - -if [ -n "$BITLBEE_PORT" ]; then - db_set bitlbee/serveport "$BITLBEE_PORT" -else - db_get bitlbee/serveport - if [ "$RET" = "stillhavetoask" ]; then - listens=$(netstat -ltn | awk '{print $4}') - for port in 6667 6666 6668 6669; do - if [ $(expr "$listens " : ".*:$port\s") = "0" ]; then - break - fi - done - db_set bitlbee/serveport $port; - fi -fi - -if db_input medium bitlbee/serveport; then - db_go; -fi diff --git a/debian/control b/debian/control index dfef8e39..62cb2ec0 100644 --- a/debian/control +++ b/debian/control @@ -4,21 +4,25 @@ Priority: optional Maintainer: Wilmer van der Gaast Uploaders: Jelmer Vernooij Standards-Version: 3.8.4 -Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf, libpurple-dev +Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), po-debconf, libpurple-dev, debhelper (>= 7) Homepage: http://www.bitlbee.org/ Vcs-Bzr: http://code.bitlbee.org/bitlbee/ DM-Upload-Allowed: yes Package: bitlbee Architecture: any -Depends: ${shlibs:Depends}, adduser, net-tools, debianutils (>= 1.16), bitlbee-common (>= ${source:Version}), bitlbee-common (<< ${source:Version}.1~) +Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${source:Version}) +Conflicts: bitlbee-libpurple +Replaces: bitlbee-libpurple Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. Package: bitlbee-libpurple Architecture: any -Depends: ${shlibs:Depends}, adduser, net-tools, debianutils (>= 1.16), bitlbee-common (>= ${source:Version}), bitlbee-common (<< ${source:Version}.1~) +Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${source:Version}) +Conflicts: bitlbee +Replaces: bitlbee Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. @@ -33,7 +37,8 @@ Description: An IRC to other chat networks gateway Package: bitlbee-common Architecture: all -Replaces: bitlbee (<= 1.3) +Depends: ${misc:Depends}, net-tools +Replaces: bitlbee Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. @@ -43,7 +48,7 @@ Description: An IRC to other chat networks gateway Package: bitlbee-dev Architecture: all -Depends: bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) +Depends: ${misc:Depends}, bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. diff --git a/debian/postinst b/debian/postinst deleted file mode 100755 index db541f6c..00000000 --- a/debian/postinst +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/sh -e - -. /usr/share/debconf/confmodule - -db_get bitlbee/serveport -PORT="$RET" - -CONFDIR=/var/lib/bitlbee/ - -update-rc.d bitlbee defaults > /dev/null 2>&1 - -## Load default option. Don't want to put this in debconf (yet?) -BITLBEE_OPTS=-F -BITLBEE_DISABLED=0 -BITLBEE_UPGRADE_DONT_RESTART=0 -[ -r /etc/default/bitlbee ] && . /etc/default/bitlbee - -if [ "$BITLBEE_DISABLED" = "0" ] && type update-inetd > /dev/null 2> /dev/null && - ( expr "$2" : '0\..*' > /dev/null || expr "$2" : '1\.0\..*' > /dev/null ); then - ## Make sure the inetd entry is gone (can still be there from a - ## previous version. - update-inetd --remove '.*/usr/sbin/bitlbee' - if grep -q /usr/sbin/bitlbee /etc/inetd.conf 2> /dev/null; then - # Thanks for breaking update-inetd! (bugs.debian.org/311111) - # I hope that it works at least with xinetd, because this - # emergency hack doesn't: - perl -pi -e 's:^[^#].*/usr/sbin/bitlbee$:## Now using daemon mode\: # $&:' /etc/inetd.conf - killall -HUP inetd || true - fi -fi - -cat</etc/default/bitlbee -## /etc/default/bitlbee: Auto-generated/updated script. -## -## If running in (fork)daemon mode, listen on this TCP port. -BITLBEE_PORT="$PORT" - -## Use single-process or forking daemon mode? Can't be changed from debconf, -## but maintainer scripts will save your changes here. -BITLBEE_OPTS="$BITLBEE_OPTS" - -## In case you want to stick with inetd mode (or if you just want to disable -## the init scripts for some other reason), you can disable the init script -## here. (Just set it to 1) -BITLBEE_DISABLED=$BITLBEE_DISABLED - -## As a server operator, you can use the RESTART command to restart only the -## master process while keeping all the child processes and their IPC -## connections. By enabling this, the maintainer scripts won't restart -## BitlBee during upgrades so you can restart the master process by hand. -BITLBEE_UPGRADE_DONT_RESTART=$BITLBEE_UPGRADE_DONT_RESTART -EOF - -## Bye-bye DebConf, we don't need you anymore. -db_stop - -## Restore the helpfile in case we weren't upgrading but just reconfiguring: -if [ -e /usr/share/bitlbee/help.upgrading ]; then - if [ -e /usr/share/bitlbee/help.txt ]; then - rm -f /usr/share/bitlbee/help.upgrading - else - mv /usr/share/bitlbee/help.upgrading /usr/share/bitlbee/help.txt - fi -fi - -if [ -n "$2" -a "$BITLBEE_UPGRADE_DONT_RESTART" != "1" ]; then - if which invoke-rc.d >/dev/null 2>&1; then - invoke-rc.d bitlbee restart - else - /etc/init.d/bitlbee restart - fi -fi - -## If we're upgrading, we'll probably skip this next part -if [ -d $CONFDIR ] && chown -R bitlbee: $CONFDIR; then - echo 'BitlBee (probably) already installed, skipping user/configdir installation' - exit 0 -fi - -adduser --system --group --disabled-login --disabled-password --home /var/lib/bitlbee/ bitlbee -chmod 700 /var/lib/bitlbee/ - -## Can't do this in packaging phase: Don't know the UID yet. Access to -## the file should be limited, now that it stores passwords. Added -## --group later for a little more security, but have to see if I can -## apply this change to existing installations on upgrades. Will think -## about that later. -if getent group bitlbee > /dev/null; then - chmod 640 /etc/bitlbee/bitlbee.conf - chown root:bitlbee /etc/bitlbee/bitlbee.conf -else - chmod 600 /etc/bitlbee/bitlbee.conf - chown bitlbee /etc/bitlbee/bitlbee.conf -fi - -if [ -z "$2" ]; then - if which invoke-rc.d >/dev/null 2>&1; then - invoke-rc.d bitlbee start - else - /etc/init.d/bitlbee start - fi -fi diff --git a/debian/postrm b/debian/postrm deleted file mode 100755 index 5c3b4b2e..00000000 --- a/debian/postrm +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -e - -[ "$1" = "purge" ] || exit 0 - -if [ -e /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule; - db_purge; -fi - -update-rc.d bitlbee remove > /dev/null 2>&1 || true -rm -f /etc/default/bitlbee - -deluser --system --remove-home bitlbee || true -rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var diff --git a/debian/prerm b/debian/prerm deleted file mode 100755 index 687c2cc1..00000000 --- a/debian/prerm +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -e - -if [ "$1" = "upgrade" ]; then - ## To prevent the help function from breaking in currently running - ## BitlBee processes. Have to do it like this because dpkg-reconfigure - ## looks a lot like an upgrade and we don't want to lose help.txt... - if [ -e /usr/share/bitlbee/help.txt ]; then - rm -f /usr/share/bitlbee/help.upgrading - mv /usr/share/bitlbee/help.txt /usr/share/bitlbee/help.upgrading - fi -else - if which invoke-rc.d >/dev/null 2>&1; then - invoke-rc.d bitlbee stop || exit 0 - else - /etc/init.d/bitlbee stop || exit 0 - fi -fi diff --git a/debian/rules b/debian/rules index bbb368f9..8ccb181b 100755 --- a/debian/rules +++ b/debian/rules @@ -10,9 +10,7 @@ BITLBEE_CONFIGURE_FLAGS ?= DEBUG ?= 0 -ifdef BITLBEE_VERSION -BITLBEE_FORCE_VERSION=1 -else +ifndef BITLBEE_VERSION # Want to use the full package version number instead of just the release. BITLBEE_CONFIGURE_VERSION ?= BITLBEE_VERSION=\"$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')\" endif @@ -67,14 +65,20 @@ binary-common: dh_installexamples dh_installdebconf dh_installinit +ifeq ($(DH_OPTIONS),-a) + cp -a debian/bitlbee/etc debian/bitlbee-libpurple +endif dh_installman dh_strip dh_link dh_compress dh_fixperms dh_installdeb +ifeq ($(DH_OPTIONS),-a) + cp -a debian/bitlbee/DEBIAN/{post,pre}* debian/bitlbee-libpurple/DEBIAN +endif dh_shlibdeps -ifdef BITLBEE_FORCE_VERSION +ifdef BITLBEE_VERSION dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 else dh_gencontrol diff --git a/debian/templates b/debian/templates deleted file mode 100644 index 0cd04426..00000000 --- a/debian/templates +++ /dev/null @@ -1,8 +0,0 @@ -Template: bitlbee/serveport -Type: string -Default: stillhavetoask -_Description: On what TCP port should BitlBee listen for connections? - BitlBee normally listens on the regular IRC port, 6667. This might not be - a very good idea when you're running a real IRC daemon as well. 6668 might - be a good alternative. Leaving this value blank means that BitlBee will not - be run automatically. -- cgit v1.2.3 From 477250038a91c6877838cbd7f0d32b839f64824d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 18:35:39 +0100 Subject: Put in the necessary hacks to make version number spoofing work again. --- debian/control | 6 +++--- debian/rules | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/debian/control b/debian/control index 62cb2ec0..d197dcdd 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ DM-Upload-Allowed: yes Package: bitlbee Architecture: any -Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${source:Version}) +Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version}) Conflicts: bitlbee-libpurple Replaces: bitlbee-libpurple Description: An IRC to other chat networks gateway @@ -20,7 +20,7 @@ Description: An IRC to other chat networks gateway Package: bitlbee-libpurple Architecture: any -Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${source:Version}) +Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version}) Conflicts: bitlbee Replaces: bitlbee Description: An IRC to other chat networks gateway @@ -48,7 +48,7 @@ Description: An IRC to other chat networks gateway Package: bitlbee-dev Architecture: all -Depends: ${misc:Depends}, bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) +Depends: ${misc:Depends}, bitlbee (>= ${bee:Version}), bitlbee (<< ${bee:Version}.1~) Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. diff --git a/debian/rules b/debian/rules index 8ccb181b..f2ede2cf 100755 --- a/debian/rules +++ b/debian/rules @@ -19,11 +19,11 @@ build: build-stamp build-stamp: dh_testdir - mkdir debian/build-native + mkdir -p debian/build-native ROOT=$$PWD; cd debian/build-native; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS) $(MAKE) -C debian/build-native - mkdir debian/build-libpurple + mkdir -p debian/build-libpurple ROOT=$$PWD; cd debian/build-libpurple; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --purple=1 $(BITLBEE_CONFIGURE_FLAGS) $(MAKE) -C debian/build-libpurple @@ -61,7 +61,7 @@ binary-common: dh_testroot dh_installdocs --link-doc=bitlbee-common - dh_installchangelogs + dh_installchangelogs doc/CHANGES dh_installexamples dh_installdebconf dh_installinit @@ -79,9 +79,10 @@ ifeq ($(DH_OPTIONS),-a) endif dh_shlibdeps ifdef BITLBEE_VERSION - dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 + echo source:Version=1:$(BITLBEE_VERSION)-0 > debian/substvars + dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 -Vbee:Version=1:$(BITLBEE_VERSION)-0 else - dh_gencontrol + dh_gencontrol -- -Vbee:Version=$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}' | sed -e 's/+[^+]*$$//') endif dh_md5sums dh_builddeb -- cgit v1.2.3 From e774815bc621af90bb64ca314b84367659c5a005 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 18:43:38 +0100 Subject: Updated short descriptions and fixed po-debconf files. --- debian/control | 8 ++++---- debian/po/POTFILES.in | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/debian/control b/debian/control index d197dcdd..05689940 100644 --- a/debian/control +++ b/debian/control @@ -14,7 +14,7 @@ Architecture: any Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version}) Conflicts: bitlbee-libpurple Replaces: bitlbee-libpurple -Description: An IRC to other chat networks gateway +Description: An IRC to other chat networks gateway (default version) This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. @@ -23,7 +23,7 @@ Architecture: any Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version}) Conflicts: bitlbee Replaces: bitlbee -Description: An IRC to other chat networks gateway +Description: An IRC to other chat networks gateway (using libpurple) This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. . @@ -39,7 +39,7 @@ Package: bitlbee-common Architecture: all Depends: ${misc:Depends}, net-tools Replaces: bitlbee -Description: An IRC to other chat networks gateway +Description: An IRC to other chat networks gateway (common files/docs) This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. . @@ -49,7 +49,7 @@ Description: An IRC to other chat networks gateway Package: bitlbee-dev Architecture: all Depends: ${misc:Depends}, bitlbee (>= ${bee:Version}), bitlbee (<< ${bee:Version}.1~) -Description: An IRC to other chat networks gateway +Description: An IRC to other chat networks gateway (dev files) This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. . diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in index cef83a34..8d2b570f 100644 --- a/debian/po/POTFILES.in +++ b/debian/po/POTFILES.in @@ -1 +1 @@ -[type: gettext/rfc822deb] templates +[type: gettext/rfc822deb] bitlbee-common.templates -- cgit v1.2.3 From f5d87ea5e4db1864cc9dd95c295f166af9944014 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 19:26:17 +0100 Subject: Pick up group changes in the middle of a Jabber session, now that we care about that info. --- protocols/jabber/iq.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 55c678aa..5166e322 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -390,9 +390,8 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node * { if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) ) { - if( initial || bee_user_by_handle( ic->bee, ic, jid ) == NULL ) - imcb_add_buddy( ic, jid, ( group && group->text_len ) ? - group->text : NULL ); + imcb_add_buddy( ic, jid, ( group && group->text_len ) ? + group->text : NULL ); if( name ) imcb_rename_buddy( ic, jid, name ); -- cgit v1.2.3 From 5a75d1586478f78446b6c78b161572fc7cabe4d9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 23:32:36 +0100 Subject: Chatroom improvements. Merged chatroom stub into normal chatroom stuff, restored "chat add" behaviour a little bit better (don't clean up a channel when its room disappears, just disconnect it from the groupchat). --- irc.h | 6 --- irc_channel.c | 50 ++--------------------- irc_commands.c | 4 +- irc_im.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++------ root_commands.c | 21 +++++++--- 5 files changed, 131 insertions(+), 74 deletions(-) diff --git a/irc.h b/irc.h index 03600f1e..eeb9b1b2 100644 --- a/irc.h +++ b/irc.h @@ -194,12 +194,6 @@ struct irc_control_channel struct account *account; }; -struct irc_groupchat_stub -{ - struct account *acc; - char *room; -}; - extern const struct bee_ui_funcs irc_ui_funcs; /* irc.c */ diff --git a/irc_channel.c b/irc_channel.c index 64bbd614..bceb067c 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -28,7 +28,8 @@ static char *set_eval_channel_type( set_t *set, char *value ); static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ ); static const struct irc_channel_funcs control_channel_funcs; -static const struct irc_channel_funcs groupchat_stub_funcs; + +extern const struct irc_channel_funcs irc_channel_im_chat_funcs; irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) { @@ -103,7 +104,7 @@ static char *set_eval_channel_type( set_t *set, char *value ) if( strcmp( value, "control" ) == 0 ) new = &control_channel_funcs; else if( strcmp( value, "chat" ) == 0 ) - new = &groupchat_stub_funcs; + new = &irc_channel_im_chat_funcs; else return SET_INVALID; @@ -388,48 +389,3 @@ static const struct irc_channel_funcs control_channel_funcs = { control_channel_init, control_channel_free, }; - -/* Groupchat stub: Only handles /INVITE at least for now. */ -static gboolean groupchat_stub_invite( irc_channel_t *ic, irc_user_t *iu ) -{ - bee_user_t *bu = iu->bu; - - if( iu->bu->ic->acc->prpl->chat_with ) - { - ic->flags |= IRC_CHANNEL_CHAT_PICKME; - iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle ); - ic->flags &= ~IRC_CHANNEL_CHAT_PICKME; - return TRUE; - } - else - { - irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name ); - return FALSE; - } -} - -static gboolean groupchat_stub_join( irc_channel_t *ic ) -{ - struct irc_groupchat_stub *igs = ic->data; - - if( igs && igs->acc->ic && igs->acc->prpl->chat_join ) - { - ic->flags |= IRC_CHANNEL_CHAT_PICKME; - igs->acc->prpl->chat_join( igs->acc->ic, igs->room, ic->irc->user->nick, NULL ); - ic->flags &= ~IRC_CHANNEL_CHAT_PICKME; - return FALSE; - } - else - { - irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name ); - return FALSE; - } -} - -static const struct irc_channel_funcs groupchat_stub_funcs = { - NULL, - groupchat_stub_join, - NULL, - NULL, - groupchat_stub_invite, -}; diff --git a/irc_commands.c b/irc_commands.c index 24be35e3..ccfc2171 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -398,9 +398,7 @@ static void irc_cmd_invite( irc_t *irc, char **cmd ) return; } - if( ic->f->invite ) - ic->f->invite( ic, iu ); - else + if( !ic->f->invite || !ic->f->invite( ic, iu ) ) irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] ); } diff --git a/irc_im.c b/irc_im.c index 818495dd..7828bb4a 100644 --- a/irc_im.c +++ b/irc_im.c @@ -317,7 +317,7 @@ static const struct irc_user_funcs irc_user_im_funcs = { /* IM->IRC: Groupchats */ -static const struct irc_channel_funcs irc_channel_im_chat_funcs; +const struct irc_channel_funcs irc_channel_im_chat_funcs; static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) { @@ -340,7 +340,7 @@ static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) if( l == NULL ) for( i = 0; i <= 999; i ++ ) { char name[16]; - sprintf( name, "&chat_%03d", i ); + sprintf( name, "#chat_%03d", i ); if( ( ic = irc_channel_new( irc, name ) ) ) break; } @@ -350,7 +350,6 @@ static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) c->ui_data = ic; ic->data = c; - ic->f = &irc_channel_im_chat_funcs; topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); irc_channel_set_topic( ic, topic, irc->root ); @@ -366,7 +365,10 @@ static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) if( ic->flags & IRC_CHANNEL_JOINED ) irc_channel_printf( ic, "Cleaning up channel, bye!" ); - irc_channel_free( ic ); + /* irc_channel_free( ic ); */ + + irc_channel_del_user( ic, ic->irc->user ); + ic->data = NULL; return TRUE; } @@ -467,33 +469,70 @@ static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg { struct groupchat *c = ic->data; + if( c == NULL ) + return FALSE; + bee_chat_msg( ic->irc->b, c, msg, 0 ); return TRUE; +} + +static gboolean bee_irc_channel_chat_join( irc_channel_t *ic ) +{ + char *acc_s, *room; + account_t *acc; + if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 ) + return TRUE; + + if( ( acc_s = set_getstr( &ic->set, "account" ) ) && + ( room = set_getstr( &ic->set, "room" ) ) && + ( acc = account_get( ic->irc->b, acc_s ) ) && + acc->ic && acc->prpl->chat_join ) + { + char *nick; + + if( !( nick = set_getstr( &ic->set, "nick" ) ) ) + nick = ic->irc->user->nick; + + ic->flags |= IRC_CHANNEL_CHAT_PICKME; + acc->prpl->chat_join( acc->ic, room, nick, NULL ); + ic->flags &= ~IRC_CHANNEL_CHAT_PICKME; + + return FALSE; + } + else + { + irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name ); + return FALSE; + } } static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg ) { struct groupchat *c = ic->data; - if( c->ic->acc->prpl->chat_leave ) + if( c && c->ic->acc->prpl->chat_leave ) c->ic->acc->prpl->chat_leave( c ); return TRUE; - } static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new ) { struct groupchat *c = ic->data; - char *topic = g_strdup( new ); /* TODO: Need more const goodness here, sigh */ + + if( c == NULL ) + return FALSE; if( c->ic->acc->prpl->chat_topic == NULL ) irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name ); else { + /* TODO: Need more const goodness here, sigh */ + char *topic = g_strdup( new ); c->ic->acc->prpl->chat_topic( c, topic ); + g_free( topic ); return TRUE; } @@ -503,23 +542,82 @@ static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new ) static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu ) { struct groupchat *c = ic->data; + bee_user_t *bu = iu->bu; + + if( bu == NULL ) + return FALSE; - if( iu->bu->ic != c->ic ) - irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name ); - else if( c->ic->acc->prpl->chat_invite ) - c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL ); + if( c ) + { + if( iu->bu->ic != c->ic ) + irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name ); + else if( c->ic->acc->prpl->chat_invite ) + c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL ); + else + irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name ); + } + else if( bu->ic->acc->prpl->chat_with && + strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 ) + { + ic->flags |= IRC_CHANNEL_CHAT_PICKME; + iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle ); + ic->flags &= ~IRC_CHANNEL_CHAT_PICKME; + } else + { irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name ); + } return TRUE; } -static const struct irc_channel_funcs irc_channel_im_chat_funcs = { +static char *set_eval_room_account( set_t *set, char *value ); + +static gboolean bee_irc_channel_init( irc_channel_t *ic ) +{ + set_add( &ic->set, "account", NULL, set_eval_room_account, ic ); + set_add( &ic->set, "chat_type", "groupchat", NULL, ic ); + set_add( &ic->set, "nick", NULL, NULL, ic ); + set_add( &ic->set, "room", NULL, NULL, ic ); + + return TRUE; +} + +static char *set_eval_room_account( set_t *set, char *value ) +{ + struct irc_channel *ic = set->data; + account_t *acc; + + if( !( acc = account_get( ic->irc->b, value ) ) ) + return SET_INVALID; + else if( !acc->prpl->chat_join ) + { + irc_usermsg( ic->irc, "Named chatrooms not supported on that account." ); + return SET_INVALID; + } + + return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user ); +} + +static gboolean bee_irc_channel_free( irc_channel_t *ic ) +{ + set_del( &ic->set, "account" ); + set_del( &ic->set, "chat_type" ); + set_del( &ic->set, "nick" ); + set_del( &ic->set, "room" ); + + return TRUE; +} + +const struct irc_channel_funcs irc_channel_im_chat_funcs = { bee_irc_channel_chat_privmsg, - NULL, /* join */ + bee_irc_channel_chat_join, bee_irc_channel_chat_part, bee_irc_channel_chat_topic, bee_irc_channel_chat_invite, + + bee_irc_channel_init, + bee_irc_channel_free, }; diff --git a/root_commands.c b/root_commands.c index cf1c169c..7c54e272 100644 --- a/root_commands.c +++ b/root_commands.c @@ -979,6 +979,11 @@ static void cmd_chat( irc_t *irc, char **cmd ) irc_usermsg( irc, "Invalid account" ); return; } + else if( !acc->prpl->chat_join ) + { + irc_usermsg( irc, "Named chatrooms not supported on that account." ); + return; + } if( cmd[4] == NULL ) { @@ -998,13 +1003,19 @@ static void cmd_chat( irc_t *irc, char **cmd ) channel = s; } - if( ( ic = irc_channel_new( irc, channel ) ) ) + if( ( ic = irc_channel_new( irc, channel ) ) && + set_setstr( &ic->set, "chat_type", "room" ) && + set_setstr( &ic->set, "account", cmd[2] ) && + set_setstr( &ic->set, "room", cmd[3] ) ) + { + irc_usermsg( irc, "Chatroom successfully added." ); + } + else { - struct irc_groupchat_stub *igs; + if( ic ) + irc_channel_free( ic ); - ic->data = igs = g_new0( struct irc_groupchat_stub, 1 ); - igs->acc = acc; - igs->room = g_strdup( cmd[3] ); + irc_usermsg( irc, "Could not add chatroom." ); } } /* -- cgit v1.2.3 From c1a8a163703a903c7a69e06286013f12d6bf865e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 6 Jun 2010 00:16:52 +0100 Subject: Restored the "chat with" command, let's keep it around at least for now. Maybe it's nice to keep since it's only one command, as opposed to /join + /invite. --- root_commands.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/root_commands.c b/root_commands.c index 7c54e272..eb42c888 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1018,19 +1018,19 @@ static void cmd_chat( irc_t *irc, char **cmd ) irc_usermsg( irc, "Could not add chatroom." ); } } - /* else if( g_strcasecmp( cmd[1], "with" ) == 0 ) { - user_t *u; + irc_user_t *iu; MIN_ARGS( 2 ); - if( ( u = user_find( irc, cmd[2] ) ) && u->ic && u->ic->acc->prpl->chat_with ) + if( ( iu = irc_user_by_name( irc, cmd[2] ) ) && + iu->bu && iu->bu->ic->acc->prpl->chat_with ) { - if( !u->ic->acc->prpl->chat_with( u->ic, u->handle ) ) + if( !iu->bu->ic->acc->prpl->chat_with( iu->bu->ic, iu->bu->handle ) ) { irc_usermsg( irc, "(Possible) failure while trying to open " - "a groupchat with %s.", u->nick ); + "a groupchat with %s.", iu->nick ); } } else @@ -1038,7 +1038,6 @@ static void cmd_chat( irc_t *irc, char **cmd ) irc_usermsg( irc, "Can't open a groupchat with %s.", cmd[2] ); } } - */ else { irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] ); -- cgit v1.2.3 From 18da20bf7690ca3c1e9cf4b70c2749a11c75b339 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 6 Jun 2010 01:33:33 +0100 Subject: Added /part msgs, and the ability to silently remove users from channels (when sending a /quit instead, for example). --- irc.h | 2 +- irc_channel.c | 8 ++++---- irc_commands.c | 2 +- irc_im.c | 6 +++--- irc_send.c | 2 +- irc_user.c | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/irc.h b/irc.h index eeb9b1b2..2e6fff4f 100644 --- a/irc.h +++ b/irc.h @@ -221,7 +221,7 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ); irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ); int irc_channel_free( irc_channel_t *ic ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); -int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ); +int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, const char *msg ); irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ); diff --git a/irc_channel.c b/irc_channel.c index bceb067c..b3904d22 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -80,7 +80,7 @@ int irc_channel_free( irc_channel_t *ic ) irc_t *irc = ic->irc; if( ic->flags & IRC_CHANNEL_JOINED ) - irc_channel_del_user( ic, irc->user ); + irc_channel_del_user( ic, irc->user, FALSE, "Cleaning up channel" ); irc->channels = g_slist_remove( irc->channels, ic ); while( ic->users ) @@ -141,7 +141,7 @@ int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) return 1; } -int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) +int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, const char *msg ) { irc_channel_user_t *icu; @@ -151,8 +151,8 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu ) ic->users = g_slist_remove( ic->users, icu ); g_free( icu ); - if( ic->flags & IRC_CHANNEL_JOINED ) - irc_send_part( ic, iu, "" ); + if( ic->flags & IRC_CHANNEL_JOINED && !silent ) + irc_send_part( ic, iu, msg ); if( iu == ic->irc->user ) ic->flags &= ~IRC_CHANNEL_JOINED; diff --git a/irc_commands.c b/irc_commands.c index ccfc2171..4ea53299 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -176,7 +176,7 @@ static void irc_cmd_part( irc_t *irc, char **cmd ) { irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); } - else if( irc_channel_del_user( ic, irc->user ) ) + else if( irc_channel_del_user( ic, irc->user, FALSE, cmd[2] ) ) { if( ic->f->part ) ic->f->part( ic, NULL ); diff --git a/irc_im.c b/irc_im.c index 7828bb4a..973cce8e 100644 --- a/irc_im.c +++ b/irc_im.c @@ -159,7 +159,7 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) if( !show ) { - irc_channel_del_user( ic, iu ); + irc_channel_del_user( ic, iu, FALSE, NULL ); } else { @@ -367,7 +367,7 @@ static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) /* irc_channel_free( ic ); */ - irc_channel_del_user( ic, ic->irc->user ); + irc_channel_del_user( ic, ic->irc->user, FALSE, "Chatroom closed by server" ); ic->data = NULL; return TRUE; @@ -411,7 +411,7 @@ static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_u { irc_t *irc = bee->ui_data; - irc_channel_del_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data ); + irc_channel_del_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data, FALSE, NULL ); return TRUE; } diff --git a/irc_send.c b/irc_send.c index b3283152..72a90724 100644 --- a/irc_send.c +++ b/irc_send.c @@ -149,7 +149,7 @@ void irc_send_join( irc_channel_t *ic, irc_user_t *iu ) void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ) { - irc_write( ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason ); + irc_write( ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason ? : "" ); } void irc_send_names( irc_channel_t *ic ) diff --git a/irc_user.c b/irc_user.c index 1884e66e..e18e67cc 100644 --- a/irc_user.c +++ b/irc_user.c @@ -57,7 +57,7 @@ int irc_user_free( irc_t *irc, irc_user_t *iu ) g_hash_table_remove( irc->nick_user_hash, iu->key ); for( l = irc->channels; l; l = l->next ) - irc_channel_del_user( (irc_channel_t*) l->data, iu ); + irc_channel_del_user( (irc_channel_t*) l->data, iu, FALSE, NULL ); g_free( iu->nick ); if( iu->nick != iu->user ) g_free( iu->user ); -- cgit v1.2.3 From 1f0224cdfd238060810679b3d6ba1a2bc49e4493 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 6 Jun 2010 02:11:13 +0100 Subject: Send one /QUIT instead of one or more /PARTs for a user that is being removed. Also restored netsplit simulation. --- irc.h | 1 + irc_send.c | 5 +++++ irc_user.c | 42 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/irc.h b/irc.h index 2e6fff4f..a959dcda 100644 --- a/irc.h +++ b/irc.h @@ -238,6 +238,7 @@ void irc_send_motd( irc_t *irc ); void irc_usermsg( irc_t *irc, char *format, ... ); void irc_send_join( irc_channel_t *ic, irc_user_t *iu ); void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ); +void irc_send_quit( irc_user_t *iu, const char *reason ); void irc_send_names( irc_channel_t *ic ); void irc_send_topic( irc_channel_t *ic, gboolean topic_change ); void irc_send_whois( irc_user_t *iu ); diff --git a/irc_send.c b/irc_send.c index 72a90724..41214428 100644 --- a/irc_send.c +++ b/irc_send.c @@ -152,6 +152,11 @@ void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ) irc_write( ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason ? : "" ); } +void irc_send_quit( irc_user_t *iu, const char *reason ) +{ + irc_write( iu->irc, ":%s!%s@%s QUIT :%s", iu->nick, iu->user, iu->host, reason ? : "" ); +} + void irc_send_names( irc_channel_t *ic ) { GSList *l; diff --git a/irc_user.c b/irc_user.c index e18e67cc..88db86d7 100644 --- a/irc_user.c +++ b/irc_user.c @@ -49,6 +49,7 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ) int irc_user_free( irc_t *irc, irc_user_t *iu ) { GSList *l; + gboolean send_quit = FALSE; if( !iu ) return 0; @@ -57,7 +58,46 @@ int irc_user_free( irc_t *irc, irc_user_t *iu ) g_hash_table_remove( irc->nick_user_hash, iu->key ); for( l = irc->channels; l; l = l->next ) - irc_channel_del_user( (irc_channel_t*) l->data, iu, FALSE, NULL ); + send_quit |= irc_channel_del_user( (irc_channel_t*) l->data, iu, TRUE, NULL ); + + if( send_quit ) + { + static struct im_connection *last_ic; + static char *msg; + + if( iu->bu && + ( iu->bu->ic->flags & OPT_LOGGING_OUT ) && + iu->bu->ic != last_ic ) + { + char host_prefix[] = "bitlbee."; + char *s; + + /* Irssi recognises netsplits by quitmsgs with two + hostnames, where a hostname is a "word" with one + of more dots. Mangle no-dot hostnames a bit. */ + if( strchr( irc->root->host, '.' ) ) + *host_prefix = '\0'; + + last_ic = iu->bu->ic; + g_free( msg ); + if( !set_getbool( &irc->b->set, "simulate_netsplit" ) ) + msg = g_strdup( "Account off-line" ); + else if( ( s = strchr( iu->bu->ic->acc->user, '@' ) ) ) + msg = g_strdup_printf( "%s%s %s", host_prefix, + irc->root->host, s + 1 ); + else + msg = g_strdup_printf( "%s%s %s.%s", + host_prefix, irc->root->host, + iu->bu->ic->acc->prpl->name, irc->root->host ); + } + else if( !iu->bu || !( iu->bu->ic->flags & OPT_LOGGING_OUT ) ) + { + g_free( msg ); + msg = g_strdup( "Removed" ); + last_ic = NULL; + } + irc_send_quit( iu, msg ); + } g_free( iu->nick ); if( iu->nick != iu->user ) g_free( iu->user ); -- cgit v1.2.3 From 88eaf4b49855a8069fce79296f2d271b6c6c654c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 6 Jun 2010 02:24:13 +0100 Subject: buddy_sendbuffer should be renamed, since it has to work for users and chats. For smooth transitions, add an old_key attribute to settings which can be used as a (temporary) alias. --- irc.c | 6 ++++-- set.c | 8 +++++--- set.h | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/irc.c b/irc.c index 13a77a72..6b95461e 100644 --- a/irc.c +++ b/irc.c @@ -98,8 +98,6 @@ irc_t *irc_new( int fd ) b->ui = &irc_ui_funcs; s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc ); - s = set_add( &b->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); - s = set_add( &b->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); //s = set_add( &b->set, "control_channel", irc->channel, NULL/*set_eval_control_channel*/, irc ); s = set_add( &b->set, "default_target", "root", NULL, irc ); @@ -108,6 +106,10 @@ irc_t *irc_new( int fd ) s = set_add( &b->set, "handle_unknown", "root", NULL, irc ); s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); s = set_add( &b->set, "ops", "both", NULL/*set_eval_ops*/, irc ); + s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc ); + s->old_key = g_strdup( "buddy_sendbuffer" ); + s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc ); + s->old_key = g_strdup( "buddy_sendbuffer_delay" ); s = set_add( &b->set, "private", "true", set_eval_bool, irc ); s = set_add( &b->set, "query_order", "lifo", NULL, irc ); s = set_add( &b->set, "root_nick", ROOT_NICK, NULL/*set_eval_root_nick*/, irc ); diff --git a/set.c b/set.c index 8ecc9690..48a9289b 100644 --- a/set.c +++ b/set.c @@ -68,7 +68,8 @@ set_t *set_find( set_t **head, char *key ) while( s ) { - if( g_strcasecmp( s->key, key ) == 0 ) + if( g_strcasecmp( s->key, key ) == 0 || + ( s->old_key && g_strcasecmp( s->old_key, key ) == 0 ) ) break; s = s->next; } @@ -175,8 +176,9 @@ void set_del( set_t **head, char *key ) *head = s->next; g_free( s->key ); - if( s->value ) g_free( s->value ); - if( s->def ) g_free( s->def ); + g_free( s->old_key ); + g_free( s->value ); + g_free( s->def ); g_free( s ); } } diff --git a/set.h b/set.h index 19ea73fb..5d03dc70 100644 --- a/set.h +++ b/set.h @@ -53,6 +53,7 @@ typedef struct set object this settings belongs to. */ char *key; + char *old_key; /* Previously known as; for smooth upgrades. */ char *value; char *def; /* Default value. If the set_setstr() function notices a new value is exactly the same as -- cgit v1.2.3 From 16834a53f85be092acc16dee70cb72451b2230f9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 6 Jun 2010 02:30:45 +0100 Subject: Set handle_unknown to add_channel by default. --- doc/user-guide/commands.xml | 2 +- irc.c | 3 +-- root_commands.c | 14 -------------- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 4746e2c3..97c54bae 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -592,7 +592,7 @@ - root + add_channel root, add, add_private, add_channel, ignore diff --git a/irc.c b/irc.c index 6b95461e..49395ee4 100644 --- a/irc.c +++ b/irc.c @@ -99,11 +99,10 @@ irc_t *irc_new( int fd ) s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc ); s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); - //s = set_add( &b->set, "control_channel", irc->channel, NULL/*set_eval_control_channel*/, irc ); s = set_add( &b->set, "default_target", "root", NULL, irc ); s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc ); s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc ); - s = set_add( &b->set, "handle_unknown", "root", NULL, irc ); + s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc ); s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); s = set_add( &b->set, "ops", "both", NULL/*set_eval_ops*/, irc ); s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc ); diff --git a/root_commands.c b/root_commands.c index eb42c888..cdc1ccdb 100644 --- a/root_commands.c +++ b/root_commands.c @@ -680,20 +680,6 @@ char *set_eval_root_nick( set_t *set, char *new_nick ) return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : SET_INVALID; } - -char *set_eval_control_channel( set_t *set, char *new_name ) -{ - irc_t *irc = set->data; - - if( strcmp( irc->channel, new_name ) != 0 ) - { - char *cmd[] = { "set_rename", irc->channel, new_name, NULL }; - - cmd_rename( irc, cmd ); - } - - return strcmp( irc->channel, new_name ) == 0 ? new_name : SET_INVALID; -} #endif static void cmd_block( irc_t *irc, char **cmd ) -- cgit v1.2.3 From bc49ec26b131054d8de5941e49cfce7e026a3941 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 6 Jun 2010 10:55:07 +0100 Subject: Make this build work even from fresh bzr branches. Also stop using {x,y} stuff which somehow magically doesn't work on my laptop. --- debian/rules | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index f2ede2cf..a00fdedf 100755 --- a/debian/rules +++ b/debian/rules @@ -27,6 +27,8 @@ build-stamp: ROOT=$$PWD; cd debian/build-libpurple; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --purple=1 $(BITLBEE_CONFIGURE_FLAGS) $(MAKE) -C debian/build-libpurple + $(MAKE) -C doc + touch build-stamp clean: @@ -75,7 +77,7 @@ endif dh_fixperms dh_installdeb ifeq ($(DH_OPTIONS),-a) - cp -a debian/bitlbee/DEBIAN/{post,pre}* debian/bitlbee-libpurple/DEBIAN + cp -a debian/bitlbee/DEBIAN/post* debian/bitlbee/DEBIAN/pre* debian/bitlbee-libpurple/DEBIAN endif dh_shlibdeps ifdef BITLBEE_VERSION -- cgit v1.2.3 From 560d0a0c4cc61e8ed68a80a04491af024254684c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 6 Jun 2010 20:01:14 +0100 Subject: Some more fixes to make this work with older distros (Hardy's stuck with debhelper 6) and on de code.b.o build bot (a substvars file I wasn't using anymore caused troubles). --- debian/compat | 2 +- debian/control | 2 +- debian/rules | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/debian/compat b/debian/compat index 7f8f011e..1e8b3149 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -7 +6 diff --git a/debian/control b/debian/control index 689f83f2..436bef6d 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Wilmer van der Gaast Uploaders: Jelmer Vernooij Standards-Version: 3.8.4 -Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), po-debconf, libpurple-dev, debhelper (>= 7) +Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), po-debconf, libpurple-dev, debhelper (>= 6) Homepage: http://www.bitlbee.org/ Vcs-Bzr: http://code.bitlbee.org/bitlbee/ DM-Upload-Allowed: yes diff --git a/debian/rules b/debian/rules index a00fdedf..9736e078 100755 --- a/debian/rules +++ b/debian/rules @@ -44,7 +44,7 @@ clean: install: build dh_testdir dh_testroot - dh_prep + dh_clean -k dh_installdirs $(MAKE) -C debian/build-native install install-etc DESTDIR=`pwd`/debian/bitlbee @@ -62,9 +62,12 @@ binary-common: dh_testdir dh_testroot - dh_installdocs --link-doc=bitlbee-common dh_installchangelogs doc/CHANGES dh_installexamples + dh_installdocs #--link-doc=bitlbee-common + # TODO: Restore --link-doc up here and remove the hack below once + # Hardy and Lenny are deprecated. + for p in bitlbee bitlbee-libpurple bitlbee-dev; do rm -rf debian/$$p/usr/share/doc/$$p; ln -s bitlbee-common debian/$$p/usr/share/doc/$$p; done dh_installdebconf dh_installinit ifeq ($(DH_OPTIONS),-a) @@ -81,7 +84,6 @@ ifeq ($(DH_OPTIONS),-a) endif dh_shlibdeps ifdef BITLBEE_VERSION - echo source:Version=1:$(BITLBEE_VERSION)-0 > debian/substvars dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 -Vbee:Version=1:$(BITLBEE_VERSION)-0 else dh_gencontrol -- -Vbee:Version=$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}' | sed -e 's/+[^+]*$$//') -- cgit v1.2.3 From 92cb8c4c251fb04a483b29e7108e7a52388f14dd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 00:47:46 +0100 Subject: Complete (hopefully) fix for nickname changes: Add flags to the identify command to allow identifying without loading any new settings. With some documentation hints. --- doc/user-guide/commands.xml | 10 +++++++++- doc/user-guide/misc.xml | 17 +++++++++++++++++ irc_commands.c | 4 ++++ root_commands.c | 42 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 97c54bae..bba2df73 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -1113,7 +1113,7 @@ - identify <password> + identify [-noload|-force] <password> Identify yourself with your password @@ -1124,6 +1124,14 @@ Once you're registered, you can change your password using set password <password>. + + + The -noload and -force flags can be used to identify when you're logged into some IM accounts already. -force will let you identify yourself and load all saved accounts (and keep the accounts you're logged into already). + + + + -noload will log you in but not load any accounts and settings saved under your current nickname. These will be overwritten once you save your settings (i.e. when you disconnect). + diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml index a926775a..2427ef69 100644 --- a/doc/user-guide/misc.xml +++ b/doc/user-guide/misc.xml @@ -116,4 +116,21 @@ If you want to set an away state for only one of your connections, you can use t + +Changing your nickname + + +BitlBee now allows you to change your nickname. So far this was not possible because it made managing saved accounts more complicated. + + + +The restriction no longer exists now though. When you change your nick (just using the /nick command), your logged-in status will be reset, which means any changes made to your settings/accounts will not be saved. + + + +To restore your logged-in status, you need to either use the register command to create an account under the new nickname, or use identify -noload to re-identify yourself under the new nickname. The -noload flag tells the command to verify your password and log you in, but not load any new settings. See help identify for more information. + + + + diff --git a/irc_commands.c b/irc_commands.c index 4ea53299..4ce68f07 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -88,6 +88,10 @@ static void irc_cmd_nick( irc_t *irc, char **cmd ) irc_setpass( irc, NULL ); irc->status &= ~USTATUS_IDENTIFIED; irc_umode_set( irc, "-R", 1 ); + irc_usermsg( irc, "Changing nicks resets your identify status. " + "Re-identify or register a new account if you want " + "your configuration to be saved. See \x02help " + "nick_changes\x02." ); } irc_user_set_nick( irc->user, cmd[1] ); diff --git a/root_commands.c b/root_commands.c index cdc1ccdb..bb9b26cd 100644 --- a/root_commands.c +++ b/root_commands.c @@ -104,15 +104,46 @@ static void cmd_account( irc_t *irc, char **cmd ); static void cmd_identify( irc_t *irc, char **cmd ) { - storage_status_t status = storage_load( irc, cmd[1] ); + storage_status_t status; char *account_on[] = { "account", "on", NULL }; + gboolean load = TRUE; + char *password = cmd[1]; - if( strchr( irc->umode, 'R' ) != NULL ) + if( irc->status & USTATUS_IDENTIFIED ) { irc_usermsg( irc, "You're already logged in." ); return; } + if( strncmp( cmd[1], "-no", 3 ) == 0 ) + { + load = FALSE; + password = cmd[2]; + } + else if( strncmp( cmd[1], "-force", 6 ) == 0 ) + { + password = cmd[2]; + } + else if( irc->b->accounts != NULL ) + { + irc_usermsg( irc, + "You're trying to identify yourself, but already have " + "at least one IM account set up. " + "Use \x02identify -noload\x02 or \x02identify -force\x02 " + "instead (see \x02help identify\x02)." ); + return; + } + + if( password == NULL ) + { + MIN_ARGS( 2 ); + } + + if( load ) + status = storage_load( irc, password ); + else + status = storage_check_pass( irc->user->nick, password ); + switch (status) { case STORAGE_INVALID_PASSWORD: irc_usermsg( irc, "Incorrect password" ); @@ -121,11 +152,12 @@ static void cmd_identify( irc_t *irc, char **cmd ) irc_usermsg( irc, "The nick is (probably) not registered" ); break; case STORAGE_OK: - irc_usermsg( irc, "Password accepted, settings and accounts loaded" ); - irc_setpass( irc, cmd[1] ); + irc_usermsg( irc, "Password accepted%s", + load ? ", settings and accounts loaded" : "" ); + irc_setpass( irc, password ); irc->status |= USTATUS_IDENTIFIED; irc_umode_set( irc, "+R", 1 ); - if( set_getbool( &irc->b->set, "auto_connect" ) ) + if( load && set_getbool( &irc->b->set, "auto_connect" ) ) cmd_account( irc, account_on ); break; case STORAGE_OTHER_ERROR: -- cgit v1.2.3 From 36562b0f1a82bd2a5e9a24a7091845847e4242b4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 01:44:45 +0100 Subject: Added "channel list" command and the ability to use only part of the channel name or a number in "chan set"/etc. --- irc.h | 1 + irc_channel.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- root_commands.c | 22 +++++++++++++++++++++- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/irc.h b/irc.h index a959dcda..30d47b87 100644 --- a/irc.h +++ b/irc.h @@ -219,6 +219,7 @@ void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv ); /* irc_channel.c */ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ); irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ); +irc_channel_t *irc_channel_get( irc_t *irc, char *id ); int irc_channel_free( irc_channel_t *ic ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, const char *msg ); diff --git a/irc_channel.c b/irc_channel.c index b3904d22..8abc0486 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -48,7 +48,7 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) strcmp( set_getstr( &irc->b->set, "ops" ), "root" ) == 0 ) irc_channel_user_set_mode( ic, irc->root, IRC_CHANNEL_USER_OP ); - irc->channels = g_slist_prepend( irc->channels, ic ); + irc->channels = g_slist_append( irc->channels, ic ); set_add( &ic->set, "type", "control", set_eval_channel_type, ic ); @@ -68,13 +68,53 @@ irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ) { irc_channel_t *ic = l->data; - if( name[0] == ic->name[0] && nick_cmp( name + 1, ic->name + 1 ) == 0 ) + if( g_strcasecmp( name, ic->name ) == 0 ) return ic; } return NULL; } +irc_channel_t *irc_channel_get( irc_t *irc, char *id ) +{ + irc_channel_t *ic, *ret = NULL; + GSList *l; + int nr; + + if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) + { + for( l = irc->channels; l; l = l->next ) + { + ic = l->data; + if( ( nr-- ) == 0 ) + return ic; + } + + return NULL; + } + + /* Exact match first: Partial match only sucks if there's a channel + #aa and #aabb */ + if( ( ret = irc_channel_by_name( irc, id ) ) ) + return ret; + + for( l = irc->channels; l; l = l->next ) + { + ic = l->data; + + if( strstr( ic->name, id ) ) + { + /* Make sure it's a unique match. */ + if( !ret ) + ret = ic; + else + return NULL; + } + } + + return ret; +} + int irc_channel_free( irc_channel_t *ic ) { irc_t *irc = ic->irc; diff --git a/root_commands.c b/root_commands.c index bb9b26cd..72dec6f1 100644 --- a/root_commands.c +++ b/root_commands.c @@ -521,7 +521,7 @@ static set_t **cmd_channel_set_findhead( irc_t *irc, char *id ) { irc_channel_t *ic; - if( ( ic = irc_channel_by_name( irc, id ) ) ) + if( ( ic = irc_channel_get( irc, id ) ) ) return &ic->set; else return NULL; @@ -535,6 +535,26 @@ static void cmd_channel( irc_t *irc, char **cmd ) cmd_set_real( irc, cmd + 1, cmd_channel_set_findhead, NULL ); } + else if( g_strcasecmp( cmd[1], "list" ) == 0 ) + { + GSList *l; + int i = 0; + + if( strchr( irc->umode, 'b' ) ) + irc_usermsg( irc, "Channel list:" ); + + for( l = irc->channels; l; l = l->next ) + { + irc_channel_t *ic = l->data; + + irc_usermsg( irc, "%2d. %s, %s channel%s", i, ic->name, + set_getstr( &ic->set, "type" ), + ic->flags & IRC_CHANNEL_JOINED ? " (joined)" : "" ); + + i ++; + } + irc_usermsg( irc, "End of channel list" ); + } else { irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] ); -- cgit v1.2.3 From 0d9d53ed0b3eb068cf57355a4d1465beaf191f8a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 01:58:07 +0100 Subject: Fixed "set password" and "set auto_reconnect_delay". --- irc.c | 18 ++++++++++++++++++ protocols/bee.c | 4 +--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/irc.c b/irc.c index 49395ee4..6862b6fb 100644 --- a/irc.c +++ b/irc.c @@ -30,6 +30,7 @@ GSList *irc_connection_list; static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ); static char *set_eval_charset( set_t *set, char *value ); +static char *set_eval_password( set_t *set, char *value ); irc_t *irc_new( int fd ) { @@ -109,6 +110,8 @@ irc_t *irc_new( int fd ) s->old_key = g_strdup( "buddy_sendbuffer" ); s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc ); s->old_key = g_strdup( "buddy_sendbuffer_delay" ); + s = set_add( &b->set, "password", NULL, set_eval_password, irc ); + s->flags |= SET_NULL_OK; s = set_add( &b->set, "private", "true", set_eval_bool, irc ); s = set_add( &b->set, "query_order", "lifo", NULL, irc ); s = set_add( &b->set, "root_nick", ROOT_NICK, NULL/*set_eval_root_nick*/, irc ); @@ -274,6 +277,21 @@ void irc_setpass (irc_t *irc, const char *pass) } } +static char *set_eval_password( set_t *set, char *value ) +{ + irc_t *irc = set->data; + + if( irc->status & USTATUS_IDENTIFIED && value ) + { + irc_setpass( irc, value ); + return NULL; + } + else + { + return SET_INVALID; + } +} + static char **irc_splitlines( char *buffer ); void irc_process( irc_t *irc ) diff --git a/protocols/bee.c b/protocols/bee.c index 471ce02a..c5eeee17 100644 --- a/protocols/bee.c +++ b/protocols/bee.c @@ -37,10 +37,8 @@ bee_t *bee_new() s->flags |= SET_NULL_OK; s = set_add( &b->set, "auto_connect", "true", set_eval_bool, b ); s = set_add( &b->set, "auto_reconnect", "true", set_eval_bool, b ); - s = set_add( &b->set, "auto_reconnect_delay", "5*3<900", NULL/*set_eval_account_reconnect_delay*/, b ); + s = set_add( &b->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, b ); s = set_add( &b->set, "debug", "false", set_eval_bool, b ); - s = set_add( &b->set, "password", NULL, NULL/*set_eval_password*/, b ); - s->flags |= SET_NULL_OK; s = set_add( &b->set, "save_on_quit", "true", set_eval_bool, b ); s = set_add( &b->set, "status", NULL, set_eval_away_status, b ); s->flags |= SET_NULL_OK; -- cgit v1.2.3 From 56699f009a608ecff3a247a08b3d0105a5e17153 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 16:11:09 +0100 Subject: Show idle + login time info in /WHOIS (if available). --- irc_send.c | 8 ++++++++ protocols/bee.h | 4 +++- protocols/bee_user.c | 12 ++++++++++++ protocols/oscar/oscar.c | 2 +- protocols/purple/purple.c | 4 ++++ protocols/yahoo/yahoo.c | 4 +--- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/irc_send.c b/irc_send.c index 41214428..3617d088 100644 --- a/irc_send.c +++ b/irc_send.c @@ -239,6 +239,14 @@ void irc_send_whois( irc_user_t *iu ) { irc_send_num( irc, 301, "%s :%s", iu->nick, "User is offline" ); } + + if( bu->idle_time || bu->login_time ) + { + irc_send_num( irc, 317, "%s %d %d :seconds idle, signon time", + iu->nick, + bu->idle_time ? (int) ( time( NULL ) - bu->idle_time ) : 0, + (int) bu->login_time ); + } } else { diff --git a/protocols/bee.h b/protocols/bee.h index 5701ea60..c57b4ab5 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -67,6 +67,8 @@ typedef struct bee_user char *status; char *status_msg; + time_t login_time, idle_time; + bee_t *bee; void *ui_data; } bee_user_t; @@ -122,7 +124,7 @@ void bee_group_free( bee_t *bee ); * OPT_LOGGED_IN and OPT_AWAY. * - 'state' and 'message' can be NULL */ G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); -/* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); +G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, guint32 flags, time_t sent_at ); diff --git a/protocols/bee_user.c b/protocols/bee_user.c index fd2e8635..28235a6d 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -191,6 +191,18 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, g_free( old ); } +void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ) +{ + bee_t *bee = ic->bee; + bee_user_t *bu; + + if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) + return; + + bu->login_time = login; + bu->idle_time = idle; +} + void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) { bee_t *bee = ic->bee; diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index f5b5c114..acae6433 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -937,7 +937,7 @@ static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) { tmp = normalize(info->sn); imcb_buddy_status(ic, tmp, flags, state_string, NULL); - /* imcb_buddy_times(ic, tmp, signon, time_idle); */ + imcb_buddy_times(ic, tmp, signon, time_idle); return 1; diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 16ca01de..2804fdf3 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -686,6 +686,10 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ), purple_status_get_attr_string( as, "message" ) ); + + imcb_buddy_times( ic, bud->name, + purple_presence_get_login_time( bud->presence ), + purple_presence_get_idle_time( bud->presence ) ); } } diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index d9f90fe0..bf577496 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -612,10 +612,8 @@ void ext_yahoo_status_changed( int id, const char *who, int stat, const char *ms imcb_buddy_status( ic, who, flags, state_string, msg ); - /* Not implemented yet... if( stat == YAHOO_STATUS_IDLE ) - imcb_buddy_times( ic, who, 0, away ); - */ + imcb_buddy_times( ic, who, 0, idle ); } void ext_yahoo_got_im( int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8 ) -- cgit v1.2.3 From 0e8b3e855dfa370fe559729224b3bff1d4cf5b87 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 16:21:21 +0100 Subject: Changing away_devoice will change current voice statuses in all channels. --- irc.c | 19 +++++++++++++++++- irc_im.c | 2 ++ protocols/nogaim.c | 59 ------------------------------------------------------ 3 files changed, 20 insertions(+), 60 deletions(-) diff --git a/irc.c b/irc.c index 61060796..4d528004 100644 --- a/irc.c +++ b/irc.c @@ -99,7 +99,7 @@ irc_t *irc_new( int fd ) b->ui_data = irc; b->ui = &irc_ui_funcs; - s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc ); + s = set_add( &b->set, "away_devoice", "true", set_eval_away_devoice, irc ); s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); s = set_add( &b->set, "default_target", "root", NULL, irc ); s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc ); @@ -818,3 +818,20 @@ static char *set_eval_charset( set_t *set, char *value ) return value; } + +char *set_eval_away_devoice( set_t *set, char *value ) +{ + irc_t *irc = set->data; + + if( !is_bool( value ) ) + return SET_INVALID; + + /* The usual problem: The setting isn't actually changed at this + point and we need it to be, so do it by hand. */ + g_free( set->value ); + set->value = g_strdup( value ); + + bee_irc_channel_update( irc, NULL, NULL ); + + return value; +} diff --git a/irc_im.c b/irc_im.c index 973cce8e..ee2712a3 100644 --- a/irc_im.c +++ b/irc_im.c @@ -168,6 +168,8 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) if( set_getbool( &irc->b->set, "away_devoice" ) ) irc_channel_user_set_mode( ic, iu, ( iu->bu->flags & BEE_USER_AWAY ) ? 0 : IRC_CHANNEL_USER_VOICE ); + else + irc_channel_user_set_mode( ic, iu, 0 ); } } diff --git a/protocols/nogaim.c b/protocols/nogaim.c index be85b8ba..499e4d1d 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -536,65 +536,6 @@ struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *han return bee_user_by_handle( ic->bee, ic, handle ); } - -/* Misc. BitlBee stuff which shouldn't really be here */ -#if 0 -char *set_eval_away_devoice( set_t *set, char *value ) -{ - irc_t *irc = set->data; - int st; - - if( !is_bool( value ) ) - return SET_INVALID; - - st = bool2int( value ); - - /* Horror.... */ - - if( st != set_getbool( &irc->b->set, "away_devoice" ) ) - { - char list[80] = ""; - user_t *u = irc->users; - int i = 0, count = 0; - char pm; - char v[80]; - - if( st ) - pm = '+'; - else - pm = '-'; - - while( u ) - { - if( u->ic && u->online && !u->away ) - { - if( ( strlen( list ) + strlen( u->nick ) ) >= 79 ) - { - for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0; - irc_write( irc, ":%s MODE %s %c%s%s", - irc->myhost, - irc->channel, pm, v, list ); - - *list = 0; - count = 0; - } - - sprintf( list + strlen( list ), " %s", u->nick ); - count ++; - } - u = u->next; - } - - /* $v = 'v' x $i */ - for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0; - irc_write( irc, ":%s MODE %s %c%s%s", irc->myhost, - irc->channel, pm, v, list ); - } - - return value; -} -#endif - /* The plan is to not allow straight calls to prpl functions anymore, but do them all from some wrappers. We'll start to define some down here: */ -- cgit v1.2.3 From c5aefa4463135b3f8720785804fe183817829b00 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 16:39:53 +0100 Subject: Restore "ops" command completely, and set user op status *just* before s/he joins. --- irc.c | 6 +----- irc.h | 2 ++ irc_channel.c | 32 ++++++++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/irc.c b/irc.c index 4d528004..b6a10fe0 100644 --- a/irc.c +++ b/irc.c @@ -106,7 +106,7 @@ irc_t *irc_new( int fd ) s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc ); s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc ); s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); - s = set_add( &b->set, "ops", "both", NULL/*set_eval_ops*/, irc ); + s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc ); s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc ); s->old_key = g_strdup( "buddy_sendbuffer" ); s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc ); @@ -662,10 +662,6 @@ int irc_check_login( irc_t *irc ) irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); irc_channel_add_user( ic, irc->user ); - if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 || - strcmp( set_getstr( &irc->b->set, "ops" ), "user" ) == 0 ) - irc_channel_user_set_mode( ic, irc->user, IRC_CHANNEL_USER_OP ); - irc->last_root_cmd = g_strdup( ROOT_CHAN ); irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN, diff --git a/irc.h b/irc.h index 30d47b87..45fe131f 100644 --- a/irc.h +++ b/irc.h @@ -228,6 +228,8 @@ int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_ void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ); void irc_channel_printf( irc_channel_t *ic, char *format, ... ); gboolean irc_channel_name_ok( const char *name ); +void irc_channel_update_ops( irc_channel_t *ic, char *value ); +char *set_eval_irc_channel_ops( struct set *set, char *value ); /* irc_commands.c */ void irc_exec( irc_t *irc, char **cmd ); diff --git a/irc_channel.c b/irc_channel.c index 8abc0486..d98d0652 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -44,9 +44,6 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) strcpy( ic->mode, CMODE ); irc_channel_add_user( ic, irc->root ); - if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 || - strcmp( set_getstr( &irc->b->set, "ops" ), "root" ) == 0 ) - irc_channel_user_set_mode( ic, irc->root, IRC_CHANNEL_USER_OP ); irc->channels = g_slist_append( irc->channels, ic ); @@ -172,6 +169,8 @@ int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) ic->users = g_slist_insert_sorted( ic->users, icu, irc_channel_user_cmp ); + irc_channel_update_ops( ic, set_getstr( &ic->irc->b->set, "ops" ) ); + if( iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED ) { ic->flags |= IRC_CHANNEL_JOINED; @@ -238,7 +237,7 @@ void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_u { irc_channel_user_t *icu = irc_channel_has_user( ic, iu ); - if( icu->flags == flags ) + if( !icu || icu->flags == flags ) return; if( ic->flags & IRC_CHANNEL_JOINED ) @@ -283,6 +282,31 @@ static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ ) return irc_user_cmp( a->iu, b->iu ); } +void irc_channel_update_ops( irc_channel_t *ic, char *value ) +{ + irc_channel_user_set_mode( ic, ic->irc->root, + ( strcmp( value, "both" ) == 0 || + strcmp( value, "root" ) == 0 ) ? IRC_CHANNEL_USER_OP : 0 ); + irc_channel_user_set_mode( ic, ic->irc->user, + ( strcmp( value, "both" ) == 0 || + strcmp( value, "user" ) == 0 ) ? IRC_CHANNEL_USER_OP : 0 ); +} + +char *set_eval_irc_channel_ops( set_t *set, char *value ) +{ + irc_t *irc = set->data; + GSList *l; + + if( strcmp( value, "both" ) != 0 && strcmp( value, "none" ) != 0 && + strcmp( value, "user" ) != 0 && strcmp( value, "root" ) != 0 ) + return SET_INVALID; + + for( l = irc->channels; l; l = l->next ) + irc_channel_update_ops( l->data, value ); + + return value; +} + /* Channel-type dependent functions, for control channels: */ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) { -- cgit v1.2.3 From 70f69ecc5f80f060d1780110ed9792f9e19d2507 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 16:45:42 +0100 Subject: Restoring auto-identification using server password. --- irc.c | 20 ++++++++++---------- irc_commands.c | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/irc.c b/irc.c index b6a10fe0..281b1fd5 100644 --- a/irc.c +++ b/irc.c @@ -643,16 +643,6 @@ int irc_check_login( irc_t *irc ) irc->status |= USTATUS_LOGGED_IN; - /* This is for bug #209 (use PASS to identify to NickServ). */ - if( irc->password != NULL ) - { - char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; - - /*irc_setpass( irc, NULL );*/ - /*root_command( irc, send_cmd );*/ - g_free( send_cmd[1] ); - } - irc_send_login( irc ); irc->umode[0] = '\0'; @@ -672,6 +662,16 @@ int irc_check_login( irc_t *irc ) "If you already have an account on this server, just use the " "\x02identify\x02 command to identify yourself.", NULL ); + /* This is for bug #209 (use PASS to identify to NickServ). */ + if( irc->password != NULL ) + { + char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; + + irc_setpass( irc, NULL ); + root_command( irc, send_cmd ); + g_free( send_cmd[1] ); + } + return 1; } } diff --git a/irc_commands.c b/irc_commands.c index 4ce68f07..6c425dee 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -57,7 +57,7 @@ static void irc_cmd_pass( irc_t *irc, char **cmd ) else { /* Remember the password and try to identify after USER/NICK. */ - /*irc_setpass( irc, cmd[1] ); */ + irc_setpass( irc, cmd[1] ); irc_check_login( irc ); } } -- cgit v1.2.3 From 0a6e5d1fd4e0c33e0529db7f94aae66b3f995f84 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 16:51:31 +0100 Subject: Restore "set root_nick" behaviour. All disabled set evaluators are back now. Getting *very* close to "feature parity" now! --- irc.c | 2 +- root_commands.c | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/irc.c b/irc.c index 281b1fd5..4593db8d 100644 --- a/irc.c +++ b/irc.c @@ -115,7 +115,7 @@ irc_t *irc_new( int fd ) s->flags |= SET_NULL_OK; s = set_add( &b->set, "private", "true", set_eval_bool, irc ); s = set_add( &b->set, "query_order", "lifo", NULL, irc ); - s = set_add( &b->set, "root_nick", ROOT_NICK, NULL/*set_eval_root_nick*/, irc ); + s = set_add( &b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc ); s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc ); s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc ); s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc ); diff --git a/root_commands.c b/root_commands.c index 72dec6f1..a9106781 100644 --- a/root_commands.c +++ b/root_commands.c @@ -718,21 +718,19 @@ static void cmd_rename( irc_t *irc, char **cmd ) } } -#if 0 char *set_eval_root_nick( set_t *set, char *new_nick ) { irc_t *irc = set->data; - if( strcmp( irc->mynick, new_nick ) != 0 ) + if( strcmp( irc->root->nick, new_nick ) != 0 ) { - char *cmd[] = { "set_rename", irc->mynick, new_nick, NULL }; + char *cmd[] = { "set_rename", irc->root->nick, new_nick, NULL }; cmd_rename( irc, cmd ); } - return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : SET_INVALID; + return strcmp( irc->root->nick, new_nick ) == 0 ? new_nick : SET_INVALID; } -#endif static void cmd_block( irc_t *irc, char **cmd ) { -- cgit v1.2.3 From a4d920bfc34db9ca9161ff3817385423e52b8c90 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 17:48:11 +0100 Subject: Added "channel del". --- root_commands.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/root_commands.c b/root_commands.c index a9106781..12e89b32 100644 --- a/root_commands.c +++ b/root_commands.c @@ -555,6 +555,24 @@ static void cmd_channel( irc_t *irc, char **cmd ) } irc_usermsg( irc, "End of channel list" ); } + else if( g_strcasecmp( cmd[1], "del" ) == 0 ) + { + irc_channel_t *ic; + + MIN_ARGS( 2 ); + + if( ( ic = irc_channel_get( irc, cmd[2] ) ) && + !( ic->flags & IRC_CHANNEL_JOINED ) && + ic != ic->irc->default_channel ) + { + irc_usermsg( irc, "Channel %s deleted.", ic->name ); + irc_channel_free( ic ); + } + else + irc_usermsg( irc, "Couldn't remove channel (main channel %s or " + "channels you're still in cannot be deleted).", + ic->irc->default_channel->name ); + } else { irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] ); -- cgit v1.2.3 From 619dd1882deb7f507882748982744cc275dd92a9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 19:40:08 +0100 Subject: Paste buffer functionality is back, now for users *and* rooms. --- irc.h | 9 ++++---- irc_im.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- irc_user.c | 4 ++-- 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/irc.h b/irc.h index 45fe131f..f694420d 100644 --- a/irc.h +++ b/irc.h @@ -108,10 +108,8 @@ typedef struct irc_user irc_user_flags_t flags; - char *sendbuf; - int sendbuf_len; - guint sendbuf_timer; - //int sendbuf_flags; + GString *pastebuf; + guint pastebuf_timer; struct bee_user *bu; @@ -150,6 +148,9 @@ typedef struct irc_channel GSList *users; struct set *set; + GString *pastebuf; + guint pastebuf_timer; + const struct irc_channel_funcs *f; void *data; } irc_channel_t; diff --git a/irc_im.c b/irc_im.c index ee2712a3..0ee8c88f 100644 --- a/irc_im.c +++ b/irc_im.c @@ -272,12 +272,46 @@ static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu ) /* IRC->IM calls */ +static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond ); + static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg ) { - if( iu->bu ) - return bee_user_msg( iu->irc->b, iu->bu, msg, 0 ); - else + if( iu->bu == NULL ) return FALSE; + else if( set_getbool( &iu->irc->b->set, "paste_buffer" ) ) + { + int delay; + + if( iu->pastebuf == NULL ) + iu->pastebuf = g_string_new( msg ); + else + { + b_event_remove( iu->pastebuf_timer ); + g_string_append_printf( iu->pastebuf, "\n%s", msg ); + } + + if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 ) + delay *= 1000; + + iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu ); + + return TRUE; + } + else + return bee_user_msg( iu->irc->b, iu->bu, msg, 0 ); +} + +static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond ) +{ + irc_user_t *iu = data; + + bee_user_msg( iu->irc->b, iu->bu, iu->pastebuf->str, 0 ); + + g_string_free( iu->pastebuf, TRUE ); + iu->pastebuf = 0; + iu->pastebuf_timer = 0; + + return FALSE; } static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp ) @@ -467,18 +501,52 @@ static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const c } /* IRC->IM */ +static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond ); + static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg ) { struct groupchat *c = ic->data; if( c == NULL ) return FALSE; - - bee_chat_msg( ic->irc->b, c, msg, 0 ); + else if( set_getbool( &ic->irc->b->set, "paste_buffer" ) ) + { + int delay; + + if( ic->pastebuf == NULL ) + ic->pastebuf = g_string_new( msg ); + else + { + b_event_remove( ic->pastebuf_timer ); + g_string_append_printf( ic->pastebuf, "\n%s", msg ); + } + + if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 ) + delay *= 1000; + + ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic ); + + return TRUE; + } + else + bee_chat_msg( ic->irc->b, c, msg, 0 ); return TRUE; } +static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond ) +{ + irc_channel_t *ic = data; + + bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 ); + + g_string_free( ic->pastebuf, TRUE ); + ic->pastebuf = 0; + ic->pastebuf_timer = 0; + + return FALSE; +} + static gboolean bee_irc_channel_chat_join( irc_channel_t *ic ) { char *acc_s, *room; diff --git a/irc_user.c b/irc_user.c index 88db86d7..3305b072 100644 --- a/irc_user.c +++ b/irc_user.c @@ -103,8 +103,8 @@ int irc_user_free( irc_t *irc, irc_user_t *iu ) if( iu->nick != iu->user ) g_free( iu->user ); if( iu->nick != iu->host ) g_free( iu->host ); if( iu->nick != iu->fullname ) g_free( iu->fullname ); - g_free( iu->sendbuf ); - if( iu->sendbuf_timer ) b_event_remove( iu->sendbuf_timer ); + g_free( iu->pastebuf ); + if( iu->pastebuf_timer ) b_event_remove( iu->pastebuf_timer ); g_free( iu->key ); return 1; -- cgit v1.2.3 From 6ef906557277b71fc278e3f612542bd4d7d75ab5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 7 Jun 2010 22:09:33 +0100 Subject: Restored nick_hint/nick_source functionality. --- irc_im.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- protocols/bee.h | 1 + protocols/nogaim.c | 38 ++++++-------------------------------- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/irc_im.c b/irc_im.c index 0ee8c88f..3dca5c3d 100644 --- a/irc_im.c +++ b/irc_im.c @@ -219,6 +219,8 @@ static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags return TRUE; } +static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint ); + static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) { irc_user_t *iu = (irc_user_t *) bu->ui_data; @@ -252,7 +254,7 @@ static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) name[i] = '\0'; } - imcb_buddy_nick_hint( bu->ic, bu->handle, name ); + bee_irc_user_nick_hint( bee, bu, name ); g_free( name ); } @@ -260,6 +262,47 @@ static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) return TRUE; } +static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint ) +{ + irc_user_t *iu = bu->ui_data; + char newnick[MAX_NICK_LENGTH+1], *translit; + + if( bu->flags & BEE_USER_ONLINE ) + /* Ignore if the user is visible already. */ + return TRUE; + + if( nick_saved( bu->ic->acc, bu->handle ) ) + /* The user already assigned a nickname to this person. */ + return TRUE; + + /* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT should + do lossy/approximate conversions, so letters with accents don't + just get stripped. Note that it depends on LC_CTYPE being set to + something other than C/POSIX. */ + translit = g_convert( hint, -1, "ASCII//TRANSLIT//IGNORE", "UTF-8", + NULL, NULL, NULL ); + + strncpy( newnick, translit ? : hint, MAX_NICK_LENGTH ); + newnick[MAX_NICK_LENGTH] = 0; + g_free( translit ); + + /* Some processing to make sure this string is a valid IRC nickname. */ + nick_strip( newnick ); + if( set_getbool( &bee->set, "lcnicks" ) ) + nick_lc( newnick ); + + if( strcmp( iu->nick, newnick ) != 0 ) + { + /* Only do this if newnick is different from the current one. + If rejoining a channel, maybe we got this nick already + (and dedupe would only add an underscore. */ + nick_dedupe( bu->ic->acc, bu->handle, newnick ); + irc_user_set_nick( iu, newnick ); + } + + return TRUE; +} + static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu ) { irc_user_t *iu = (irc_user_t *) bu->ui_data; @@ -721,6 +764,7 @@ const struct bee_ui_funcs irc_ui_funcs = { bee_irc_user_new, bee_irc_user_free, bee_irc_user_fullname, + bee_irc_user_nick_hint, bee_irc_user_group, bee_irc_user_status, bee_irc_user_msg, diff --git a/protocols/bee.h b/protocols/bee.h index c57b4ab5..c3230f47 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -84,6 +84,7 @@ typedef struct bee_ui_funcs gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); gboolean (*user_free)( bee_t *bee, struct bee_user *bu ); gboolean (*user_fullname)( bee_t *bee, bee_user_t *bu ); + gboolean (*user_nick_hint)( bee_t *bee, bee_user_t *bu, const char *hint ); gboolean (*user_group)( bee_t *bee, bee_user_t *bu ); gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old ); gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 499e4d1d..6ecdfe12 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -417,39 +417,13 @@ void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *grou modules to suggest a nickname for a handle. */ void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick ) { -#if 0 - user_t *u = user_findhandle( ic, handle ); - char newnick[MAX_NICK_LENGTH+1], *orig_nick; + bee_t *bee = ic->bee; + bee_user_t *bu = bee_user_by_handle( bee, ic, handle ); - if( u && !u->online && !nick_saved( ic->acc, handle ) ) - { - /* Only do this if the person isn't online yet (which should - be the case if we just added it) and if the user hasn't - assigned a nickname to this buddy already. */ - - strncpy( newnick, nick, MAX_NICK_LENGTH ); - newnick[MAX_NICK_LENGTH] = 0; - - /* Some processing to make sure this string is a valid IRC nickname. */ - nick_strip( newnick ); - if( set_getbool( &ic->bee->set, "lcnicks" ) ) - nick_lc( newnick ); - - if( strcmp( u->nick, newnick ) != 0 ) - { - /* Only do this if newnick is different from the current one. - If rejoining a channel, maybe we got this nick already - (and dedupe would only add an underscore. */ - nick_dedupe( ic->acc, handle, newnick ); - - /* u->nick will be freed halfway the process, so it can't be - passed as an argument. */ - orig_nick = g_strdup( u->nick ); - user_rename( ic->irc, orig_nick, newnick ); - g_free( orig_nick ); - } - } -#endif + if( !bu || !nick ) return; + + if( bee->ui->user_nick_hint ) + bee->ui->user_nick_hint( bee, bu, nick ); } -- cgit v1.2.3 From d7db3468f95d6b8ed2a161c71cb5b6ab1a7b5895 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 8 Jun 2010 23:44:16 +0100 Subject: Some cleanup improvements. --- irc_channel.c | 7 +++++++ irc_user.c | 1 + protocols/bee_user.c | 1 + root_commands.c | 1 + 4 files changed, 10 insertions(+) diff --git a/irc_channel.c b/irc_channel.c index d98d0652..c676afe6 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -119,6 +119,12 @@ int irc_channel_free( irc_channel_t *ic ) if( ic->flags & IRC_CHANNEL_JOINED ) irc_channel_del_user( ic, irc->user, FALSE, "Cleaning up channel" ); + if( ic->f->_free ) + ic->f->_free( ic ); + + while( ic->set ) + set_del( &ic->set, ic->set->key ); + irc->channels = g_slist_remove( irc->channels, ic ); while( ic->users ) { @@ -128,6 +134,7 @@ int irc_channel_free( irc_channel_t *ic ) g_free( ic->name ); g_free( ic->topic ); + g_free( ic->topic_who ); g_free( ic ); return 1; diff --git a/irc_user.c b/irc_user.c index 3305b072..9758350a 100644 --- a/irc_user.c +++ b/irc_user.c @@ -106,6 +106,7 @@ int irc_user_free( irc_t *irc, irc_user_t *iu ) g_free( iu->pastebuf ); if( iu->pastebuf_timer ) b_event_remove( iu->pastebuf_timer ); g_free( iu->key ); + g_free( iu ); return 1; } diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 28235a6d..faa2acb7 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -61,6 +61,7 @@ int bee_user_free( bee_t *bee, bee_user_t *bu ) g_free( bu->fullname ); g_free( bu->status ); g_free( bu->status_msg ); + g_free( bu ); bee->users = g_slist_remove( bee->users, bu ); diff --git a/root_commands.c b/root_commands.c index 12e89b32..ebcb6896 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1071,6 +1071,7 @@ static void cmd_chat( irc_t *irc, char **cmd ) irc_usermsg( irc, "Could not add chatroom." ); } + g_free( channel ); } else if( g_strcasecmp( cmd[1], "with" ) == 0 ) { -- cgit v1.2.3 From 46d215d562f8e1aba2b24e2d1feab27337956d50 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 9 Jun 2010 00:43:13 +0100 Subject: Allow moving contacts around between groups. Works with at least Jabber, will check the others now. --- irc_channel.c | 22 +++++++++++++++++++++- irc_commands.c | 4 +++- protocols/jabber/iq.c | 4 +++- protocols/jabber/jabber.c | 2 +- protocols/jabber/jabber.h | 2 +- root_commands.c | 2 +- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/irc_channel.c b/irc_channel.c index c676afe6..54e68577 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -358,6 +358,26 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) return TRUE; } +static gboolean control_channel_invite( irc_channel_t *ic, irc_user_t *iu ) +{ + struct irc_control_channel *icc = ic->data; + bee_user_t *bu = iu->bu; + + if( bu == NULL ) + return FALSE; + + if( icc->type != IRC_CC_TYPE_GROUP ) + { + irc_send_num( ic->irc, 482, "%s :Invitations are only possible to fill_by=group channels", ic->name ); + return FALSE; + } + + bu->ic->acc->prpl->add_buddy( bu->ic, bu->handle, + icc->group ? icc->group->name : NULL ); + + return TRUE; +} + static char *set_eval_by_account( set_t *set, char *value ); static char *set_eval_fill_by( set_t *set, char *value ); static char *set_eval_by_group( set_t *set, char *value ); @@ -455,7 +475,7 @@ static const struct irc_channel_funcs control_channel_funcs = { NULL, NULL, NULL, - NULL, + control_channel_invite, control_channel_init, control_channel_free, diff --git a/irc_commands.c b/irc_commands.c index 6c425dee..ac851adf 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -402,8 +402,10 @@ static void irc_cmd_invite( irc_t *irc, char **cmd ) return; } - if( !ic->f->invite || !ic->f->invite( ic, iu ) ) + if( !ic->f->invite ) irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] ); + else if( ic->f->invite( ic, iu ) ) + irc_send_num( irc, 341, "%s %s", iu->nick, ic->name ); } static void irc_cmd_userhost( irc_t *irc, char **cmd ) diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 5166e322..2930e75f 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -554,7 +554,7 @@ static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_no static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); -int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ) +int jabber_add_to_roster( struct im_connection *ic, const char *handle, const char *name, const char *group ) { struct xt_node *node; int st; @@ -564,6 +564,8 @@ int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ) xt_add_attr( node, "jid", handle ); if( name ) xt_add_attr( node, "name", name ); + if( group ) + xt_add_child( node, xt_new_node( "group", group, NULL ) ); /* And pack it into a roster-add packet */ node = xt_new_node( "query", NULL, node ); diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index cf491f81..d5948958 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -412,7 +412,7 @@ static void jabber_add_buddy( struct im_connection *ic, char *who, char *group ) return; } - if( jabber_add_to_roster( ic, who, NULL ) ) + if( jabber_add_to_roster( ic, who, NULL, group ) ) presence_send_request( ic, who, "subscribe" ); } diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index b3638597..45a1c5c1 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -234,7 +234,7 @@ int jabber_init_iq_auth( struct im_connection *ic ); xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); int jabber_get_roster( struct im_connection *ic ); int jabber_get_vcard( struct im_connection *ic, char *bare_jid ); -int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); +int jabber_add_to_roster( struct im_connection *ic, const char *handle, const char *name, const char *group ); int jabber_remove_from_roster( struct im_connection *ic, char *handle ); xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ); xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns ); diff --git a/root_commands.c b/root_commands.c index ebcb6896..f6f95599 100644 --- a/root_commands.c +++ b/root_commands.c @@ -621,7 +621,7 @@ static void cmd_add( irc_t *irc, char **cmd ) } if( add_on_server ) - a->ic->acc->prpl->add_buddy( a->ic, cmd[2], NULL ); + a->prpl->add_buddy( a->ic, cmd[2], NULL ); else /* Only for add -tmp. For regular adds, this callback will be called once the IM server confirms. */ -- cgit v1.2.3 From 6acc0332708b8c844f721dbda9c7ecb94b8dd232 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 9 Jun 2010 01:35:13 +0100 Subject: Moving MSN contacts between groups is now possible, but no support yet for creating new groups. --- protocols/msn/msn.c | 6 +++--- protocols/msn/msn.h | 2 +- protocols/msn/msn_util.c | 22 +++++++++++++++++----- protocols/msn/ns.c | 8 +++++++- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index d6a4b158..a5807318 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -181,7 +181,7 @@ static void msn_get_info(struct im_connection *ic, char *who) static void msn_add_buddy( struct im_connection *ic, char *who, char *group ) { - msn_buddy_list_add( ic, "FL", who, who ); + msn_buddy_list_add( ic, "FL", who, who, group ); } static void msn_remove_buddy( struct im_connection *ic, char *who, char *group ) @@ -251,7 +251,7 @@ static void msn_keepalive( struct im_connection *ic ) static void msn_add_permit( struct im_connection *ic, char *who ) { - msn_buddy_list_add( ic, "AL", who, who ); + msn_buddy_list_add( ic, "AL", who, who, NULL ); } static void msn_rem_permit( struct im_connection *ic, char *who ) @@ -263,7 +263,7 @@ static void msn_add_deny( struct im_connection *ic, char *who ) { struct msn_switchboard *sb; - msn_buddy_list_add( ic, "BL", who, who ); + msn_buddy_list_add( ic, "BL", who, who, NULL ); /* If there's still a conversation with this person, close it. */ if( ( sb = msn_sb_by_handle( ic, who ) ) ) diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index f3cb8635..e4abcb60 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -159,7 +159,7 @@ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ); /* msn_util.c */ int msn_write( struct im_connection *ic, char *s, int len ); int msn_logged_in( struct im_connection *ic ); -int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname ); +int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ); int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who ); void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname ); char *msn_findheader( char *text, char *header, int len ); diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index a8d24b30..f9c10e72 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -50,14 +50,26 @@ int msn_logged_in( struct im_connection *ic ) return( 0 ); } -int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname_ ) +int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ) { struct msn_data *md = ic->proto_data; - char buf[1024], *realname; + char buf[1024], *realname, groupid[8]; realname = msn_http_encode( realname_ ); - g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s\r\n", ++md->trId, list, who, realname ); + *groupid = '\0'; + if( group ) + { + int i; + for( i = 0; i < md->groupcount; i ++ ) + if( g_strcasecmp( md->grouplist[i], group ) == 0 ) + { + g_snprintf( groupid, sizeof( groupid ), " %d", i ); + break; + } + } + + g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid ); if( msn_write( ic, buf, strlen( buf ) ) ) { g_free( realname ); @@ -93,7 +105,7 @@ static void msn_buddy_ask_yes( void *data ) { struct msn_buddy_ask_data *bla = data; - msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname ); + msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname, NULL ); imcb_ask_add( bla->ic, bla->handle, NULL ); @@ -106,7 +118,7 @@ static void msn_buddy_ask_no( void *data ) { struct msn_buddy_ask_data *bla = data; - msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname ); + msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname, NULL ); g_free( bla->handle ); g_free( bla->realname ); diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 2b0600a3..8afcbe23 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -532,8 +532,14 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 ) { + const char *group = NULL; + int num; + + if( cmd[6] != NULL && sscanf( cmd[6], "%d", &num ) == 1 && num < md->groupcount ) + group = md->grouplist[num]; + http_decode( cmd[5] ); - imcb_add_buddy( ic, cmd[4], NULL ); + imcb_add_buddy( ic, cmd[4], group ); imcb_rename_buddy( ic, cmd[4], cmd[5] ); } } -- cgit v1.2.3 From 95c3ea9002af8f59a580f887b59413a0b924e1fc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 10 Jun 2010 17:36:23 +0100 Subject: Fixing hostname in "exiting" opermsgs. --- irc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/irc.c b/irc.c index 4593db8d..2660e58b 100644 --- a/irc.c +++ b/irc.c @@ -167,7 +167,7 @@ void irc_abort( irc_t *irc, int immed, char *format, ... ) irc_write( irc, "ERROR :Closing link: %s", reason ); ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", - irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, reason ); + irc->user->nick ? irc->user->nick : "(NONE)", irc->user->host, reason ); g_free( reason ); } @@ -177,7 +177,7 @@ void irc_abort( irc_t *irc, int immed, char *format, ... ) irc_write( irc, "ERROR :Closing link" ); ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", - irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, "No reason given" ); + irc->user->nick ? irc->user->nick : "(NONE)", irc->user->host, "No reason given" ); } irc->status |= USTATUS_SHUTDOWN; -- cgit v1.2.3 From 52663546cc019ebdf4a7d56f1425408e20e1d4cb Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 10 Jun 2010 17:39:17 +0100 Subject: Fixed extremely stupid NULLptr dereference. --- root_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/root_commands.c b/root_commands.c index f6f95599..10ce68e1 100644 --- a/root_commands.c +++ b/root_commands.c @@ -571,7 +571,7 @@ static void cmd_channel( irc_t *irc, char **cmd ) else irc_usermsg( irc, "Couldn't remove channel (main channel %s or " "channels you're still in cannot be deleted).", - ic->irc->default_channel->name ); + irc->default_channel->name ); } else { -- cgit v1.2.3 From 70ac4771ebf3358d7bec9bd6fe37cde4c23223bc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 11 Jun 2010 13:34:39 +0200 Subject: Create new MSN groups when necessary. --- protocols/msn/msn.c | 17 +++++++++++++++++ protocols/msn/msn.h | 8 +++++++- protocols/msn/msn_util.c | 46 ++++++++++++++++++++++++++++++++++++---------- protocols/msn/ns.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 11 deletions(-) diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index a5807318..8b4f1a81 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -103,6 +103,15 @@ static void msn_logout( struct im_connection *ic ) g_free( md->grouplist[--md->groupcount] ); g_free( md->grouplist ); + while( md->grpq ) + { + struct msn_groupadd *ga = md->grpq->data; + g_free( ga->group ); + g_free( ga->who ); + g_free( ga ); + md->grpq = g_slist_remove( md->grpq, ga ); + } + g_free( md ); } @@ -121,6 +130,14 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in { struct msn_switchboard *sb; +#ifdef DEBUG + if( strcmp( who, "raw" ) == 0 ) + { + msn_write( ic, message, strlen( message ) ); + msn_write( ic, "\r\n", 2 ); + } + else +#endif if( ( sb = msn_sb_by_handle( ic, who ) ) ) { return( msn_sb_sendmessage( sb, message ) ); diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index e4abcb60..59036e37 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -69,7 +69,7 @@ struct msn_data int trId; - GSList *msgq; + GSList *msgq, *grpq; GSList *switchboards; int sb_failures; time_t first_sb_failure; @@ -120,6 +120,12 @@ struct msn_message char *text; }; +struct msn_groupadd +{ + char *who; + char *group; +}; + struct msn_handler_data { int fd; diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index f9c10e72..24f7e10e 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -55,8 +55,6 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char * struct msn_data *md = ic->proto_data; char buf[1024], *realname, groupid[8]; - realname = msn_http_encode( realname_ ); - *groupid = '\0'; if( group ) { @@ -67,19 +65,47 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char * g_snprintf( groupid, sizeof( groupid ), " %d", i ); break; } - } - - g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid ); - if( msn_write( ic, buf, strlen( buf ) ) ) - { - g_free( realname ); - return( 1 ); + if( *groupid == '\0' ) + { + /* Have to create this group, it doesn't exist yet. */ + struct msn_groupadd *ga; + GSList *l; + + for( l = md->grpq; l; l = l->next ) + { + ga = l->data; + if( g_strcasecmp( ga->group, group ) == 0 ) + break; + } + + ga = g_new0( struct msn_groupadd, 1 ); + ga->who = g_strdup( who ); + ga->group = g_strdup( group ); + md->grpq = g_slist_prepend( md->grpq, ga ); + + if( l == NULL ) + { + char *groupname = msn_http_encode( group ); + g_snprintf( buf, sizeof( buf ), "ADG %d %s %d\r\n", ++md->trId, groupname, 0 ); + g_free( groupname ); + return msn_write( ic, buf, strlen( buf ) ); + } + else + { + /* This can happen if the user's doing lots of adds to a + new group at once; we're still waiting for the server + to confirm group creation. */ + return 1; + } + } } + realname = msn_http_encode( realname_ ); + g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid ); g_free( realname ); - return( 0 ); + return msn_write( ic, buf, strlen( buf ) ); } int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who ) diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 8afcbe23..0be9e727 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -611,6 +611,50 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) return( 0 ); } } + else if( strcmp( cmd[0], "ADG" ) == 0 ) + { + char *group = g_strdup( cmd[3] ); + int groupnum, i; + GSList *l, *next; + + http_decode( group ); + if( sscanf( cmd[4], "%d", &groupnum ) == 1 ) + { + if( groupnum >= md->groupcount ) + { + md->grouplist = g_renew( char *, md->grouplist, groupnum + 1 ); + for( i = md->groupcount; i <= groupnum; i ++ ) + md->grouplist[i] = NULL; + md->groupcount = groupnum + 1; + } + g_free( md->grouplist[groupnum] ); + md->grouplist[groupnum] = group; + } + else + { + /* Shouldn't happen, but if it does, give up on the group. */ + g_free( group ); + imcb_error( ic, "Syntax error" ); + imc_logout( ic, TRUE ); + return 0; + } + + for( l = md->grpq; l; l = next ) + { + struct msn_groupadd *ga = l->data; + next = l->next; + if( g_strcasecmp( ga->group, group ) == 0 ) + { + if( !msn_buddy_list_add( ic, "FL", ga->who, ga->who, group ) ) + return 0; + + g_free( ga->group ); + g_free( ga->who ); + g_free( ga ); + md->grpq = g_slist_remove( md->grpq, ga ); + } + } + } else if( isdigit( cmd[0][0] ) ) { int num = atoi( cmd[0] ); -- cgit v1.2.3 From 1c8e5f7fba87b6096a7fd86508ca1821876abb54 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 11 Jun 2010 17:12:27 +0200 Subject: Added away_reply_timeout setting so BitlBee will suppress away messages sent in response to PRIVMSG if one was sent recently - some IRC clients including irssi don't do this very well (when talking to >1 people who are away for example). --- doc/user-guide/commands.xml | 14 ++++++++++++++ irc.c | 1 + irc.h | 7 ++++--- irc_im.c | 16 +++++++++++++++- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index bba2df73..0989118e 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -483,6 +483,20 @@ + + 3600 + + + + Most IRC servers send a user's away message every time s/he gets a private message, to inform the sender that they may not get a response immediately. With this setting set to 0, BitlBee will also behave like this. + + + + Since not all IRC clients do an excellent job at suppressing these messages, this setting lets BitlBee do it instead. BitlBee will wait this many seconds (or until the away state/message changes) before re-informing you that the person's away. + + + + false diff --git a/irc.c b/irc.c index 2660e58b..fb720088 100644 --- a/irc.c +++ b/irc.c @@ -100,6 +100,7 @@ irc_t *irc_new( int fd ) b->ui = &irc_ui_funcs; s = set_add( &b->set, "away_devoice", "true", set_eval_away_devoice, irc ); + s = set_add( &b->set, "away_reply_timeout", "3600", set_eval_int, irc ); s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); s = set_add( &b->set, "default_target", "root", NULL, irc ); s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc ); diff --git a/irc.h b/irc.h index f694420d..1bbb564a 100644 --- a/irc.h +++ b/irc.h @@ -108,8 +108,9 @@ typedef struct irc_user irc_user_flags_t flags; - GString *pastebuf; + GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */ guint pastebuf_timer; + time_t away_reply_timeout; /* Only send a 301 if this time passed. */ struct bee_user *bu; @@ -145,10 +146,10 @@ typedef struct irc_channel char *topic_who; time_t topic_time; - GSList *users; + GSList *users; /* struct irc_channel_user */ struct set *set; - GString *pastebuf; + GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */ guint pastebuf_timer; const struct irc_channel_funcs *f; diff --git a/irc_im.c b/irc_im.c index 3dca5c3d..ee86d4b1 100644 --- a/irc_im.c +++ b/irc_im.c @@ -113,6 +113,9 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old } } + /* Reset this one since the info may have changed. */ + iu->away_reply_timeout = 0; + bee_irc_channel_update( irc, NULL, iu ); return TRUE; @@ -319,9 +322,20 @@ static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_conditi static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg ) { + const char *away; + if( iu->bu == NULL ) return FALSE; - else if( set_getbool( &iu->irc->b->set, "paste_buffer" ) ) + + if( ( away = irc_user_get_away( iu ) ) && + time( NULL ) >= iu->away_reply_timeout ) + { + irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away ); + iu->away_reply_timeout = time( NULL ) + + set_getint( &iu->irc->b->set, "away_reply_timeout" ); + } + + if( set_getbool( &iu->irc->b->set, "paste_buffer" ) ) { int delay; -- cgit v1.2.3 From 12b29dbdce259ad7c65b4b890b5d9a1bd44b60b4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 13 Jun 2010 00:40:12 +0200 Subject: Small doc update on multi-channel stuff. The "channel" command is not documented yet. --- doc/user-guide/help.xml | 1 + doc/user-guide/misc.xml | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/doc/user-guide/help.xml b/doc/user-guide/help.xml index 7487a841..0684512e 100644 --- a/doc/user-guide/help.xml +++ b/doc/user-guide/help.xml @@ -13,6 +13,7 @@ These are the available help subjects: quickstartA short introduction into BitlBee commandsAll available commands and settings + channelsAbout creating and customizing channels awayAbout setting away states smileysA summary of some non-standard smileys you might find and fail to understand groupchatsHow to work with groupchats on BitlBee diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml index 2427ef69..9a0bb67f 100644 --- a/doc/user-guide/misc.xml +++ b/doc/user-guide/misc.xml @@ -133,4 +133,50 @@ To restore your logged-in status, you need to either use the register< + +Dealing with channels + + +You can have as many channels in BitlBee as you want. You maintain your channel list using the channel command. You can create new channels by just joining them, like on regular IRC networks. + + + +You can create two kinds of channels. Control channels, and groupchat channels. By default, BitlBee will set up new channels as control channels if their name starts with an &, and as chat channels if it starts with a #. + + + +Control channels are where you see your contacts. By default, you will have one control channel called &bitlbee, containing all your contacts. But you can create more, if you want, and divide your contact list accross several channels. + + + +For example, you can have one channel with all contacts from your MSN Messenger account in it. Or all contacts from the group called "Work". + + + +Type help channels2 to read more. + + + + + +Creating a channel + + +When you create a new channel, BitlBee will try to guess from its name which contacts to fill it with. For example, if the channel name (excluding the &) matches the name of a group in which you have one or more contacts, the channel will contain all those contacts. + + + +Any valid account ID (so a number, protocol name or part of screenname, as long as it's unique) can also be used as a channel name. So if you just join &msn, it will contain all your MSN contacts (as long as you have only one MSN account set up). And if you have a Facebook account set up, you can see its contacts by just joining &facebook. + + + +To start a simple group chat, you simply join a channel which a name starting with #, and invite people into it. All people you invite have to be on the same IM network and contact list. + + + +If you want to configure your own channels, you can use the channel set. + + + + -- cgit v1.2.3 From e5b521d07dbe197c2dd7552f0036bdcac2116cde Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 14 Jun 2010 10:28:32 +0200 Subject: s/buddy_sendbuffer/paste_buffer/ in the docs as well. --- doc/user-guide/commands.xml | 66 ++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 0989118e..ce60b6db 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -497,39 +497,6 @@ - - false - - - - By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data. - - - - Using the buddy_sendbuffer_delay setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent. - - - - Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases. - - - - - - 200 - - - - - Tell BitlBee after how many (mili)seconds a buffered message should be sent. Values greater than 5 will be interpreted as miliseconds, 5 and lower as seconds. - - - - See also the buddy_sendbuffer setting. - - - - utf-8 you can get a list of all possible values by doing 'iconv -l' in a shell @@ -784,6 +751,39 @@ + + + false + + + + By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data. + + + + Using the paste_buffer_delay setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent. + + + + Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases. + + + + + + 200 + + + + + Tell BitlBee after how many (mili)seconds a buffered message should be sent. Values greater than 5 will be interpreted as miliseconds, 5 and lower as seconds. + + + + See also the paste_buffer setting. + + + -- cgit v1.2.3 From 6b90431eba3820aaa5535523622ba45ca65055f4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 16 Jun 2010 10:31:40 +0200 Subject: More correct handling of channel names (according to RFC 1459). Pretty much any 8-bit character is allowed in there - while nicknames are very restricted. --- irc.h | 1 + irc_channel.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++---------- irc_commands.c | 46 --------------------------------------------- 3 files changed, 50 insertions(+), 56 deletions(-) diff --git a/irc.h b/irc.h index 1bbb564a..82652013 100644 --- a/irc.h +++ b/irc.h @@ -230,6 +230,7 @@ int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_ void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ); void irc_channel_printf( irc_channel_t *ic, char *format, ... ); gboolean irc_channel_name_ok( const char *name ); +int irc_channel_name_cmp( const char *a_, const char *b_ ); void irc_channel_update_ops( irc_channel_t *ic, char *value ); char *set_eval_irc_channel_ops( struct set *set, char *value ); diff --git a/irc_channel.c b/irc_channel.c index 54e68577..133a6de9 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -65,7 +65,7 @@ irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ) { irc_channel_t *ic = l->data; - if( g_strcasecmp( name, ic->name ) == 0 ) + if( irc_channel_name_cmp( name, ic->name ) == 0 ) return ic; } @@ -266,20 +266,59 @@ void irc_channel_printf( irc_channel_t *ic, char *format, ... ) g_free( text ); } -gboolean irc_channel_name_ok( const char *name ) +gboolean irc_channel_name_ok( const char *name_ ) { - char name_[strlen(name)+1]; + const unsigned char *name = (unsigned char*) name_; + int i; /* Check if the first character is in CTYPES (#&) */ - if( strchr( CTYPES, name[0] ) == NULL ) + if( strchr( CTYPES, name_[0] ) == NULL ) return FALSE; - /* Check the rest of the name. Just checking name + 1 doesn't work - since it will fail if the first character is a number, or if - it's a one-char channel name - both of which are legal. */ - name_[0] = '_'; - strcpy( name_ + 1, name + 1 ); - return nick_ok( name_ ); + /* RFC 1459 keeps amazing me: While only a "few" chars are allowed + in nicknames, channel names can be pretty much anything as long + as they start with # or &. I'll be a little bit more strict and + disallow all non-printable characters. */ + for( i = 1; name[i]; i ++ ) + if( name[i] <= ' ' || name[i] == ',' ) + return FALSE; + + return TRUE; +} + +int irc_channel_name_cmp( const char *a_, const char *b_ ) +{ + static unsigned char case_map[256]; + const unsigned char *a = (unsigned char*) a_, *b = (unsigned char*) b_; + int i; + + if( case_map['A'] == '\0' ) + { + for( i = 33; i < 256; i ++ ) + if( i != ',' ) + case_map[i] = i; + + for( i = 0; i < 26; i ++ ) + case_map['A'+i] = 'a' + i; + + case_map['['] = '{'; + case_map[']'] = '}'; + case_map['~'] = '`'; + case_map['\\'] = '|'; + } + + if( !irc_channel_name_ok( a_ ) || !irc_channel_name_ok( b_ ) ) + return -1; + + for( i = 0; a[i] && b[i] && case_map[a[i]] && case_map[b[i]]; i ++ ) + { + if( case_map[a[i]] == case_map[b[i]] ) + continue; + else + return case_map[a[i]] - case_map[b[i]]; + } + + return case_map[a[i]] - case_map[b[i]]; } static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ ) diff --git a/irc_commands.c b/irc_commands.c index ac851adf..b1fc74bf 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -313,52 +313,6 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) { irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] ); } - - -#if 0 - else if( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) - { - } - else - { - if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) - { - unsigned int i; - char *t = set_getstr( &irc->set, "default_target" ); - - if( g_strcasecmp( t, "last" ) == 0 && irc->last_target ) - cmd[1] = irc->last_target; - else if( g_strcasecmp( t, "root" ) == 0 ) - cmd[1] = irc->mynick; - - for( i = 0; i < strlen( cmd[2] ); i ++ ) - { - if( cmd[2][i] == ' ' ) break; - if( cmd[2][i] == ':' || cmd[2][i] == ',' ) - { - cmd[1] = cmd[2]; - cmd[2] += i; - *cmd[2] = 0; - while( *(++cmd[2]) == ' ' ); - break; - } - } - - irc->is_private = 0; - - if( cmd[1] != irc->last_target ) - { - g_free( irc->last_target ); - irc->last_target = g_strdup( cmd[1] ); - } - } - else - { - irc->is_private = 1; - } - irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? OPT_AWAY : 0 ); - } -#endif } static void irc_cmd_nickserv( irc_t *irc, char **cmd ) -- cgit v1.2.3 From 7cd2e8a6c0ba091f56e6ee8bc087c799faee3662 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 23 Jun 2010 00:43:36 +0100 Subject: Automatically call the "channel" command for the now-gone chat subcommands. --- root_commands.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/root_commands.c b/root_commands.c index 10ce68e1..ea110467 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1093,6 +1093,13 @@ static void cmd_chat( irc_t *irc, char **cmd ) irc_usermsg( irc, "Can't open a groupchat with %s.", cmd[2] ); } } + else if( g_strcasecmp( cmd[1], "list" ) == 0 || + g_strcasecmp( cmd[1], "set" ) == 0 || + g_strcasecmp( cmd[1], "del" ) == 0 ) + { + irc_usermsg( irc, "Warning: The \002chat\002 command was mostly replaced with the \002channel\002 command." ); + cmd_channel( irc, cmd ); + } else { irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] ); -- cgit v1.2.3 From 92c8d410eb1d26bfe876ae119734772f46c9a7da Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 26 Jun 2010 22:26:41 +0100 Subject: Remember in which channel the user talked to someone and show responses in that same channel. --- irc.h | 9 ++++++--- irc_channel.c | 2 +- irc_commands.c | 2 +- irc_im.c | 24 ++++++++++-------------- irc_user.c | 5 ++++- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/irc.h b/irc.h index 82652013..e6a4dcf9 100644 --- a/irc.h +++ b/irc.h @@ -65,7 +65,9 @@ typedef struct irc struct irc_user *root; struct irc_user *user; - char *last_root_cmd; + char *last_root_cmd; /* Either the nickname from which the last root + msg came, or the last channel root was talked + to. */ char *password; /* HACK: Used to save the user's password, but before logging in, this may contain a password we should @@ -79,7 +81,7 @@ typedef struct irc GSList *users, *channels; struct irc_channel *default_channel; GHashTable *nick_user_hash; - GHashTable *watches; + GHashTable *watches; /* See irc_cmd_watch() */ gint r_watch_source_id; gint w_watch_source_id; @@ -90,7 +92,7 @@ typedef struct irc typedef enum { - IRC_USER_PRIVATE = 1, + /* Replaced with iu->last_channel IRC_USER_PRIVATE = 1, */ IRC_USER_AWAY = 2, } irc_user_flags_t; @@ -107,6 +109,7 @@ typedef struct irc_user char *key; irc_user_flags_t flags; + struct irc_channel *last_channel; GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */ guint pastebuf_timer; diff --git a/irc_channel.c b/irc_channel.c index 133a6de9..da6abbe4 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -374,7 +374,7 @@ static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg ) iu = irc_user_by_name( irc, to ); if( iu && iu->f->privmsg ) { - iu->flags &= ~IRC_USER_PRIVATE; + iu->last_channel = ic; iu->f->privmsg( iu, s ); } else diff --git a/irc_commands.c b/irc_commands.c index b1fc74bf..5a0843ec 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -305,7 +305,7 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) } else if( iu->f->privmsg ) { - iu->flags |= IRC_USER_PRIVATE; + iu->last_channel = NULL; iu->f->privmsg( iu, cmd[2] ); } } diff --git a/irc_im.c b/irc_im.c index ee86d4b1..ada92b16 100644 --- a/irc_im.c +++ b/irc_im.c @@ -33,12 +33,13 @@ static const struct irc_user_funcs irc_user_im_funcs; static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) { irc_user_t *iu; + irc_t *irc = (irc_t*) bee->ui_data; char nick[MAX_NICK_LENGTH+1], *s; memset( nick, 0, MAX_NICK_LENGTH + 1 ); strcpy( nick, nick_get( bu->ic->acc, bu->handle ) ); - bu->ui_data = iu = irc_user_new( (irc_t*) bee->ui_data, nick ); + bu->ui_data = iu = irc_user_new( irc, nick ); iu->bu = bu; if( ( s = strchr( bu->handle, '@' ) ) ) @@ -62,21 +63,17 @@ static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) iu->user = g_strdup( bu->handle ); } - if( set_getbool( &bee->set, "private" ) ) - iu->flags |= IRC_USER_PRIVATE; - if( bu->flags & BEE_USER_LOCAL ) { char *s = set_getstr( &bee->set, "handle_unknown" ); if( strcmp( s, "add_private" ) == 0 ) - iu->flags |= IRC_USER_PRIVATE; + iu->last_channel = NULL; else if( strcmp( s, "add_channel" ) == 0 ) - iu->flags &= ~IRC_USER_PRIVATE; + iu->last_channel = irc->default_channel; } iu->f = &irc_user_im_funcs; - //iu->last_typing_notice = 0; return TRUE; } @@ -179,7 +176,6 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ) { irc_t *irc = bee->ui_data; - irc_channel_t *ic = irc->default_channel; irc_user_t *iu = (irc_user_t *) bu->ui_data; char *dst, *prefix = NULL; char *wrapped, *ts = NULL; @@ -187,16 +183,16 @@ static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, t if( sent_at > 0 && set_getbool( &irc->b->set, "display_timestamps" ) ) ts = irc_format_timestamp( irc, sent_at ); - if( iu->flags & IRC_USER_PRIVATE ) + if( iu->last_channel ) { - dst = irc->user->nick; - prefix = ts; - ts = NULL; + dst = iu->last_channel->name; + prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" ); } else { - dst = ic->name; - prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" ); + dst = irc->user->nick; + prefix = ts; + ts = NULL; } wrapped = word_wrap( msg, 425 ); diff --git a/irc_user.c b/irc_user.c index 9758350a..2706eb67 100644 --- a/irc_user.c +++ b/irc_user.c @@ -33,7 +33,10 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ) iu->nick = g_strdup( nick ); iu->user = iu->host = iu->fullname = iu->nick; - iu->flags = set_getbool( &irc->b->set, "private" ) ? IRC_USER_PRIVATE : 0; + if( set_getbool( &irc->b->set, "private" ) ) + iu->last_channel = NULL; + else + iu->last_channel = irc->default_channel; iu->key = g_strdup( nick ); nick_lc( iu->key ); -- cgit v1.2.3 From e907683afea9e2789e0ac6a1eb55bda9c896c255 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 27 Jun 2010 00:39:31 +0100 Subject: Changed account set (and other account commands) syntax. Instead of "account set acc/key value" you now do "account acc set key value". So just the regular set syntax with a "account acc" prefix. The slash has been a source of confusion for long enough now. For consistency, commands like "account on acc" should now also be "account acc on" instead. Same for the new "channel" comand, of course. --- doc/user-guide/commands.xml | 63 ++--------- root_commands.c | 257 +++++++++++++++++++------------------------- 2 files changed, 118 insertions(+), 202 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 96d54530..edf89d46 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -5,7 +5,7 @@ IM-account list maintenance - account <action> [<arguments>] + account [<account id>] <action> [<arguments>] @@ -98,7 +98,7 @@ - account del <account id> + account <account id> del @@ -113,7 +113,7 @@ - account on [<account id>] + account [<account id>] on @@ -128,7 +128,7 @@ - account off [<account id>] + account [<account id>] off @@ -152,10 +152,10 @@ - account set <account id> - account set <account id>/<setting> - account set <account id>/<setting> <value> - account set -del <account id>/<setting> + account <account id> set + account <account id> set <setting> + account <account id> set <setting> <value> + account <account id> set -del <setting> @@ -180,7 +180,7 @@ - Available actions: add, del, list, with and set. See help chat <action> for more information. + Available actions: add, with. See help chat <action> for more information. @@ -204,30 +204,6 @@ - - chat del <chat id> - - - - This commands deletes an chatroom from your list. - - - - The room ID can be a number (see chat list), or (part of) the name of the room/channel. - - - - - - chat list - - - - This command gives you a list of all the chatrooms known by BitlBee. - - - - chat with <nickname> @@ -237,27 +213,6 @@ - - - chat set <chat id> - chat set <chat id>/<setting> - chat set <chat id>/<setting> <value> - chat set -del <chat id>/<setting> - - - - This command can be used to change various settings for chatrooms. - - - - For more infomation about a setting, see help set <setting>. - - - - The room ID can be a number (see chat list), or (part of) the name of the room/channel. - - - diff --git a/root_commands.c b/root_commands.c index ea110467..63f1c867 100644 --- a/root_commands.c +++ b/root_commands.c @@ -240,49 +240,24 @@ static void cmd_showset( irc_t *irc, set_t **head, char *key ) typedef set_t** (*cmd_set_findhead)( irc_t*, char* ); typedef int (*cmd_set_checkflags)( irc_t*, set_t *set ); -static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead, cmd_set_checkflags checkflags ) +static int cmd_set_real( irc_t *irc, char **cmd, set_t **head, cmd_set_checkflags checkflags ) { - char *set_full = NULL, *set_name = NULL, *tmp; - set_t **head; + char *set_name = NULL, *value = NULL; + gboolean del = FALSE; if( cmd[1] && g_strncasecmp( cmd[1], "-del", 4 ) == 0 ) { MIN_ARGS( 2, 0 ); - set_full = cmd[2]; + set_name = cmd[2]; + del = TRUE; } else - set_full = cmd[1]; - - if( findhead == NULL ) - { - set_name = set_full; - - head = &irc->b->set; - } - else { - char *id; - - if( ( tmp = strchr( set_full, '/' ) ) ) - { - id = g_strndup( set_full, ( tmp - set_full ) ); - set_name = tmp + 1; - } - else - { - id = g_strdup( set_full ); - } - - if( ( head = findhead( irc, id ) ) == NULL ) - { - g_free( id ); - irc_usermsg( irc, "Could not find setting." ); - return 0; - } - g_free( id ); + set_name = cmd[1]; + value = cmd[2]; } - if( cmd[1] && cmd[2] && set_name ) + if( set_name && ( value || del ) ) { set_t *s = set_find( head, set_name ); int st; @@ -290,13 +265,16 @@ static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead, cmd_ if( s && checkflags && checkflags( irc, s ) == 0 ) return 0; - if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 ) + if( del ) st = set_reset( head, set_name ); else - st = set_setstr( head, set_name, cmd[2] ); + st = set_setstr( head, set_name, value ); if( set_getstr( head, set_name ) == NULL ) { + /* This happens when changing the passwd, for example. + Showing these msgs instead gives slightly clearer + feedback. */ if( st ) irc_usermsg( irc, "Setting changed successfully" ); else @@ -324,16 +302,6 @@ static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead, cmd_ return 1; } -static set_t **cmd_account_set_findhead( irc_t *irc, char *id ) -{ - account_t *a; - - if( ( a = account_get( irc->b, id ) ) ) - return &a->set; - else - return NULL; -} - static int cmd_account_set_checkflags( irc_t *irc, set_t *s ) { account_t *a = s->data; @@ -385,24 +353,8 @@ static void cmd_account( irc_t *irc, char **cmd ) } irc_usermsg( irc, "Account successfully added" ); - } - else if( g_strcasecmp( cmd[1], "del" ) == 0 ) - { - MIN_ARGS( 2 ); - - if( !( a = account_get( irc->b, cmd[2] ) ) ) - { - irc_usermsg( irc, "Invalid account" ); - } - else if( a->ic ) - { - irc_usermsg( irc, "Account is still logged in, can't delete" ); - } - else - { - account_del( irc->b, a ); - irc_usermsg( irc, "Account deleted" ); - } + + return; } else if( g_strcasecmp( cmd[1], "list" ) == 0 ) { @@ -429,113 +381,112 @@ static void cmd_account( irc_t *irc, char **cmd ) i ++; } irc_usermsg( irc, "End of account list" ); + + return; + } + else if( cmd[2] ) + { + /* Try the following two only if cmd[2] == NULL */ } else if( g_strcasecmp( cmd[1], "on" ) == 0 ) { - if( cmd[2] ) + if ( irc->b->accounts ) { - if( ( a = account_get( irc->b, cmd[2] ) ) ) - { - if( a->ic ) - { - irc_usermsg( irc, "Account already online" ); - return; - } - else - { + irc_usermsg( irc, "Trying to get all accounts connected..." ); + + for( a = irc->b->accounts; a; a = a->next ) + if( !a->ic && a->auto_connect ) account_on( irc->b, a ); - } - } - else - { - irc_usermsg( irc, "Invalid account" ); - return; - } - } + } else { - if ( irc->b->accounts ) - { - irc_usermsg( irc, "Trying to get all accounts connected..." ); - - for( a = irc->b->accounts; a; a = a->next ) - if( !a->ic && a->auto_connect ) - account_on( irc->b, a ); - } - else - { - irc_usermsg( irc, "No accounts known. Use `account add' to add one." ); - } + irc_usermsg( irc, "No accounts known. Use `account add' to add one." ); } + + return; } else if( g_strcasecmp( cmd[1], "off" ) == 0 ) { - if( !cmd[2] ) - { - irc_usermsg( irc, "Deactivating all active (re)connections..." ); - - for( a = irc->b->accounts; a; a = a->next ) - { - if( a->ic ) - account_off( irc->b, a ); - else if( a->reconnect ) - cancel_auto_reconnect( a ); - } - } - else if( ( a = account_get( irc->b, cmd[2] ) ) ) + irc_usermsg( irc, "Deactivating all active (re)connections..." ); + + for( a = irc->b->accounts; a; a = a->next ) { if( a->ic ) - { account_off( irc->b, a ); - } else if( a->reconnect ) - { cancel_auto_reconnect( a ); - irc_usermsg( irc, "Reconnect cancelled" ); - } - else - { - irc_usermsg( irc, "Account already offline" ); - return; - } + } + + return; + } + + MIN_ARGS( 2 ); + + /* At least right now, don't accept on/off/set/del as account IDs even + if they're a proper match, since people not familiar with the new + syntax yet may get a confusing/nasty surprise. */ + if( g_strcasecmp( cmd[1], "on" ) == 0 || + g_strcasecmp( cmd[1], "off" ) == 0 || + g_strcasecmp( cmd[1], "set" ) == 0 || + g_strcasecmp( cmd[1], "del" ) == 0 || + ( a = account_get( irc->b, cmd[1] ) ) == NULL ) + { + irc_usermsg( irc, "Could not find account `%s'. Note that the syntax " + "of the account command changed, see \x02help account\x02.", cmd[1] ); + + return; + } + + if( g_strcasecmp( cmd[2], "del" ) == 0 ) + { + if( a->ic ) + { + irc_usermsg( irc, "Account is still logged in, can't delete" ); } else { - irc_usermsg( irc, "Invalid account" ); - return; + account_del( irc->b, a ); + irc_usermsg( irc, "Account deleted" ); } } - else if( g_strcasecmp( cmd[1], "set" ) == 0 ) + else if( g_strcasecmp( cmd[2], "on" ) == 0 ) { - MIN_ARGS( 2 ); - - cmd_set_real( irc, cmd + 1, cmd_account_set_findhead, cmd_account_set_checkflags ); + if( a->ic ) + irc_usermsg( irc, "Account already online" ); + else + account_on( irc->b, a ); + } + else if( g_strcasecmp( cmd[2], "off" ) == 0 ) + { + if( a->ic ) + { + account_off( irc->b, a ); + } + else if( a->reconnect ) + { + cancel_auto_reconnect( a ); + irc_usermsg( irc, "Reconnect cancelled" ); + } + else + { + irc_usermsg( irc, "Account already offline" ); + } + } + else if( g_strcasecmp( cmd[2], "set" ) == 0 ) + { + cmd_set_real( irc, cmd + 2, &a->set, cmd_account_set_checkflags ); } else { - irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[1] ); + irc_usermsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[2] ); } } -static set_t **cmd_channel_set_findhead( irc_t *irc, char *id ) +static void cmd_channel( irc_t *irc, char **cmd ) { irc_channel_t *ic; - if( ( ic = irc_channel_get( irc, id ) ) ) - return &ic->set; - else - return NULL; -} - -static void cmd_channel( irc_t *irc, char **cmd ) -{ - if( g_strcasecmp( cmd[1], "set" ) == 0 ) - { - MIN_ARGS( 2 ); - - cmd_set_real( irc, cmd + 1, cmd_channel_set_findhead, NULL ); - } - else if( g_strcasecmp( cmd[1], "list" ) == 0 ) + if( g_strcasecmp( cmd[1], "list" ) == 0 ) { GSList *l; int i = 0; @@ -554,15 +505,25 @@ static void cmd_channel( irc_t *irc, char **cmd ) i ++; } irc_usermsg( irc, "End of channel list" ); + + return; } - else if( g_strcasecmp( cmd[1], "del" ) == 0 ) + + MIN_ARGS( 2 ); + + if( ( ic = irc_channel_get( irc, cmd[1] ) ) == NULL ) { - irc_channel_t *ic; - - MIN_ARGS( 2 ); - - if( ( ic = irc_channel_get( irc, cmd[2] ) ) && - !( ic->flags & IRC_CHANNEL_JOINED ) && + irc_usermsg( irc, "Could not find channel `%s'", cmd[1] ); + return; + } + + if( g_strcasecmp( cmd[2], "set" ) == 0 ) + { + cmd_set_real( irc, cmd + 2, &ic->set, NULL ); + } + else if( g_strcasecmp( cmd[2], "del" ) == 0 ) + { + if( !( ic->flags & IRC_CHANNEL_JOINED ) && ic != ic->irc->default_channel ) { irc_usermsg( irc, "Channel %s deleted.", ic->name ); @@ -575,7 +536,7 @@ static void cmd_channel( irc_t *irc, char **cmd ) } else { - irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] ); + irc_usermsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] ); } } @@ -911,7 +872,7 @@ static void cmd_yesno( irc_t *irc, char **cmd ) static void cmd_set( irc_t *irc, char **cmd ) { - cmd_set_real( irc, cmd, NULL, NULL ); + cmd_set_real( irc, cmd, &irc->b->set, NULL ); } static void cmd_blist( irc_t *irc, char **cmd ) -- cgit v1.2.3 From 84c3a72604a292c41348d678eccf1875263cb8dd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 27 Jun 2010 13:39:07 +0100 Subject: Import chatrooms configured in older BitlBee versions. Settings are currently ignored though. Also removing the old chat.[ch] files since they're really not important anymore. --- irc_commands.c | 1 - protocols/account.c | 1 - protocols/chat.c | 192 ---------------------------------------------------- protocols/chat.h | 51 -------------- protocols/nogaim.c | 1 - root_commands.c | 1 - storage_xml.c | 51 +++++++------- 7 files changed, 24 insertions(+), 274 deletions(-) delete mode 100644 protocols/chat.c delete mode 100644 protocols/chat.h diff --git a/irc_commands.c b/irc_commands.c index 5a0843ec..30518a99 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -26,7 +26,6 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "ipc.h" -#include "chat.h" static void irc_cmd_pass( irc_t *irc, char **cmd ) { diff --git a/protocols/account.c b/protocols/account.c index 0bacea74..ba309b38 100644 --- a/protocols/account.c +++ b/protocols/account.c @@ -26,7 +26,6 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "account.h" -#include "chat.h" account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) { diff --git a/protocols/chat.c b/protocols/chat.c deleted file mode 100644 index 8c5ce0bc..00000000 --- a/protocols/chat.c +++ /dev/null @@ -1,192 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2008 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Keep track of chatrooms the user is interested in */ - -/* - 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 -*/ - -#include "bitlbee.h" -#include "chat.h" - -struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ) -{ - struct chat *c, *l; - set_t *s; - - if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) || - chat_chancmp( channel, irc->channel ) == 0 ) - { - return NULL; - } - - for( c = irc->chatrooms; c; c = c->next ) - { - if( chat_chancmp( channel, c->channel ) == 0 ) - return NULL; - - if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) - return NULL; - - l = c; - } - - if( irc->chatrooms == NULL ) - irc->chatrooms = c = g_new0( struct chat, 1 ); - else - l->next = c = g_new0( struct chat, 1 ); - - c->acc = acc; - c->handle = g_strdup( handle ); - c->channel = g_strdup( channel ); - - s = set_add( &c->set, "auto_join", "false", set_eval_bool, c ); - /* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */ - s = set_add( &c->set, "nick", NULL, NULL, c ); - s->flags |= SET_NULL_OK; - - return c; -} - -struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ) -{ - struct chat *c; - - for( c = irc->chatrooms; c; c = c->next ) - { - if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) - break; - } - - return c; -} - -struct chat *chat_bychannel( irc_t *irc, char *channel ) -{ - struct chat *c; - - for( c = irc->chatrooms; c; c = c->next ) - { - if( chat_chancmp( channel, c->channel ) == 0 ) - break; - } - - return c; -} - -struct chat *chat_get( irc_t *irc, char *id ) -{ - struct chat *c, *ret = NULL; - int nr; - - if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) - { - for( c = irc->chatrooms; c; c = c->next ) - if( ( nr-- ) == 0 ) - return c; - - return NULL; - } - - for( c = irc->chatrooms; c; c = c->next ) - { - if( strstr( c->handle, id ) ) - { - if( !ret ) - ret = c; - else - return NULL; - } - else if( strstr( c->channel, id ) ) - { - if( !ret ) - ret = c; - else - return NULL; - } - } - - return ret; -} - -int chat_del( irc_t *irc, struct chat *chat ) -{ - struct chat *c, *l = NULL; - - for( c = irc->chatrooms; c; c = (l=c)->next ) - if( c == chat ) - break; - - if( c == NULL ) - return 0; - else if( l == NULL ) - irc->chatrooms = c->next; - else - l->next = c->next; - - while( c->set ) - set_del( &c->set, c->set->key ); - - g_free( c->handle ); - g_free( c->channel ); - g_free( c ); - - return 1; -} - -int chat_chancmp( char *a, char *b ) -{ - if( !chat_chanok( a ) || !chat_chanok( b ) ) - return 0; - - if( a[0] == b[0] ) - return nick_cmp( a + 1, b + 1 ); - else - return -1; -} - -int chat_chanok( char *a ) -{ - if( strchr( CTYPES, a[0] ) != NULL ) - return nick_ok( a + 1 ); - else - return 0; -} - -int chat_join( irc_t *irc, struct chat *c, const char *password ) -{ - struct groupchat *gc; - char *nick = set_getstr( &c->set, "nick" ); - - if( c->acc->ic == NULL || c->acc->prpl->chat_join == NULL ) - return 0; - - if( nick == NULL ) - nick = irc->nick; - - if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, password ) ) ) - { - g_free( gc->channel ); - gc->channel = g_strdup( c->channel ); - return 1; - } - - return 0; -} diff --git a/protocols/chat.h b/protocols/chat.h deleted file mode 100644 index 7196aea8..00000000 --- a/protocols/chat.h +++ /dev/null @@ -1,51 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2008 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Keep track of chatrooms the user is interested in */ - -/* - 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 -*/ - -#ifndef _CHAT_H -#define _CHAT_H - -struct chat -{ - account_t *acc; - - char *handle; - char *channel; - set_t *set; - - struct chat *next; -}; - -struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ); -struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ); -struct chat *chat_bychannel( irc_t *irc, char *channel ); -struct chat *chat_get( irc_t *irc, char *id ); -int chat_del( irc_t *irc, struct chat *chat ); - -int chat_chancmp( char *a, char *b ); -int chat_chanok( char *a ); - -int chat_join( irc_t *irc, struct chat *c, const char *password ); - -#endif diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 6ecdfe12..f88ec693 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -35,7 +35,6 @@ #include #include "nogaim.h" -#include "chat.h" GSList *connections; diff --git a/root_commands.c b/root_commands.c index 63f1c867..e075816a 100644 --- a/root_commands.c +++ b/root_commands.c @@ -27,7 +27,6 @@ #include "commands.h" #include "bitlbee.h" #include "help.h" -#include "chat.h" #include diff --git a/storage_xml.c b/storage_xml.c index 3ce85713..73d77ade 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -28,7 +28,6 @@ #include "base64.h" #include "arc.h" #include "md5.h" -#include "chat.h" #if GLIB_CHECK_VERSION(2,8,0) #include @@ -54,7 +53,7 @@ struct xml_parsedata irc_t *irc; char *current_setting; account_t *current_account; - struct chat *current_chat; + irc_channel_t *current_channel; set_t **current_set_head; char *given_nick; char *given_pass; @@ -175,9 +174,12 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) ) { + /* if( xd->current_chat != NULL ) xd->current_set_head = &xd->current_chat->set; - else if( xd->current_account != NULL ) + else + */ + if( xd->current_account != NULL ) xd->current_set_head = &xd->current_account->set; else xd->current_set_head = &xd->irc->b->set; @@ -214,7 +216,24 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na if( xd->current_account && handle && channel ) { - //xd->current_chat = chat_add( xd->irc, xd->current_account, handle, channel ); + irc_channel_t *ic; + char *acc; + + acc = g_strdup_printf( "%s(%s)", + xd->current_account->prpl->name, + xd->current_account->user ); + + if( ( ic = irc_channel_new( irc, channel ) ) && + set_setstr( &ic->set, "chat_type", "room" ) && + set_setstr( &ic->set, "account", acc ) && + set_setstr( &ic->set, "room", handle ) ) + { + /* Nothing else to do for now, really. */ + } + else if( ic ) + irc_channel_free( ic ); + + g_free( acc ); } else { @@ -244,7 +263,7 @@ static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name } else if( g_strcasecmp( element_name, "chat" ) == 0 ) { - xd->current_chat = NULL; + /* xd->current_chat = NULL; */ } } @@ -436,7 +455,6 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) unsigned char *pass_cr; char *pass_b64; int pass_len; - struct chat *c; pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password, 12 ); pass_b64 = base64_encode( pass_cr, pass_len ); @@ -469,27 +487,6 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) if( g_hash_table_find( acc->nicks, xml_save_nick, & fd ) ) goto write_error; -#if 0 - for( c = irc->chatrooms; c; c = c->next ) - { - if( c->acc != acc ) - continue; - - if( !xml_printf( fd, 2, "\n", - c->handle, c->channel, "room" ) ) - goto write_error; - - for( set = c->set; set; set = set->next ) - if( set->value && !( set->flags & ACC_SET_NOSAVE ) ) - if( !xml_printf( fd, 3, "%s\n", - set->key, set->value ) ) - goto write_error; - - if( !xml_printf( fd, 2, "\n" ) ) - goto write_error; - } -#endif - if( !xml_printf( fd, 1, "\n" ) ) goto write_error; } -- cgit v1.2.3 From 547ea687b733113e06d7b941096b60632ac24de9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 27 Jun 2010 16:32:12 +0100 Subject: Save and load channels. Also fixing a bug in creating non-control channels with a &-name. --- root_commands.c | 1 + storage_xml.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/root_commands.c b/root_commands.c index e075816a..b47ce078 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1018,6 +1018,7 @@ static void cmd_chat( irc_t *irc, char **cmd ) } if( ( ic = irc_channel_new( irc, channel ) ) && + set_setstr( &ic->set, "type", "chat" ) && set_setstr( &ic->set, "chat_type", "room" ) && set_setstr( &ic->set, "account", cmd[2] ) && set_setstr( &ic->set, "room", cmd[3] ) ) diff --git a/storage_xml.c b/storage_xml.c index 73d77ade..d20469af 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -174,12 +174,9 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) ) { - /* - if( xd->current_chat != NULL ) - xd->current_set_head = &xd->current_chat->set; - else - */ - if( xd->current_account != NULL ) + if( xd->current_channel != NULL ) + xd->current_set_head = &xd->current_channel->set; + else if( xd->current_account != NULL ) xd->current_set_head = &xd->current_account->set; else xd->current_set_head = &xd->irc->b->set; @@ -207,6 +204,26 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na "Missing attributes for %s element", element_name ); } } + else if( g_strcasecmp( element_name, "channel" ) == 0 ) + { + char *name, *type; + + name = xml_attr( attr_names, attr_values, "name" ); + type = xml_attr( attr_names, attr_values, "type" ); + + if( !name || !type ) + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Missing attributes for %s element", element_name ); + return; + } + + if( ( xd->current_channel = irc_channel_by_name( irc, name ) ) || + ( xd->current_channel = irc_channel_new( irc, name ) ) ) + set_setstr( &xd->current_channel->set, "type", type ); + } + /* Backward compatibility: Keep this around for a while for people + switching from BitlBee 1.2.4+. */ else if( g_strcasecmp( element_name, "chat" ) == 0 ) { char *handle, *channel; @@ -224,11 +241,13 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na xd->current_account->user ); if( ( ic = irc_channel_new( irc, channel ) ) && + set_setstr( &ic->set, "type", "chat" ) && set_setstr( &ic->set, "chat_type", "room" ) && set_setstr( &ic->set, "account", acc ) && set_setstr( &ic->set, "room", handle ) ) { - /* Nothing else to do for now, really. */ + /* Try to pick up some settings where possible. */ + xd->current_channel = ic; } else if( ic ) irc_channel_free( ic ); @@ -261,9 +280,10 @@ static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name { xd->current_account = NULL; } - else if( g_strcasecmp( element_name, "chat" ) == 0 ) + else if( g_strcasecmp( element_name, "channel" ) == 0 || + g_strcasecmp( element_name, "chat" ) == 0 ) { - /* xd->current_chat = NULL; */ + xd->current_channel = NULL; } } @@ -413,6 +433,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) int fd; md5_byte_t pass_md5[21]; md5_state_t md5_state; + GSList *l; path2 = g_strdup( irc->user->nick ); nick_lc( path2 ); @@ -491,6 +512,23 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) goto write_error; } + for( l = irc->channels; l; l = l->next ) + { + irc_channel_t *ic = l->data; + + if( !xml_printf( fd, 1, "\n", + ic->name, set_getstr( &ic->set, "type" ) ) ) + goto write_error; + + for( set = ic->set; set; set = set->next ) + if( set->value && strcmp( set->key, "type" ) != 0 ) + if( !xml_printf( fd, 2, "%s\n", set->key, set->value ) ) + goto write_error; + + if( !xml_printf( fd, 1, "\n" ) ) + goto write_error; + } + if( !xml_printf( fd, 0, "\n" ) ) goto write_error; -- cgit v1.2.3 From 134a02cd563c395d0026d9d1b07eb136394798ca Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 27 Jun 2010 17:04:28 +0100 Subject: irc_channel_name_strip() instead of nick_strip(). --- irc.h | 1 + irc_channel.c | 11 +++++++++++ irc_im.c | 4 ++-- root_commands.c | 2 ++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/irc.h b/irc.h index e6a4dcf9..74e66075 100644 --- a/irc.h +++ b/irc.h @@ -233,6 +233,7 @@ int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_ void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ); void irc_channel_printf( irc_channel_t *ic, char *format, ... ); gboolean irc_channel_name_ok( const char *name ); +void irc_channel_name_strip( char *name ); int irc_channel_name_cmp( const char *a_, const char *b_ ); void irc_channel_update_ops( irc_channel_t *ic, char *value ); char *set_eval_irc_channel_ops( struct set *set, char *value ); diff --git a/irc_channel.c b/irc_channel.c index da6abbe4..f0e564bc 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -286,6 +286,17 @@ gboolean irc_channel_name_ok( const char *name_ ) return TRUE; } +void irc_channel_name_strip( char *name ) +{ + int i, j; + + for( i = j = 0; name[i]; i ++ ) + if( name[i] > ' ' && name[i] != ',' ) + name[j++] = name[i]; + + name[j] = '\0'; +} + int irc_channel_name_cmp( const char *a_, const char *b_ ) { static unsigned char case_map[256]; diff --git a/irc_im.c b/irc_im.c index ada92b16..f4c5f390 100644 --- a/irc_im.c +++ b/irc_im.c @@ -534,11 +534,11 @@ static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const c strncpy( stripped, name, MAX_NICK_LENGTH ); stripped[MAX_NICK_LENGTH] = '\0'; - nick_strip( stripped ); + irc_channel_name_strip( stripped ); if( set_getbool( &bee->set, "lcnicks" ) ) nick_lc( stripped ); - full_name = g_strdup_printf( "&%s", stripped ); + full_name = g_strdup_printf( "#%s", stripped ); if( stripped[0] && irc_channel_by_name( irc, full_name ) == NULL ) { diff --git a/root_commands.c b/root_commands.c index b47ce078..470b2536 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1015,6 +1015,8 @@ static void cmd_chat( irc_t *irc, char **cmd ) s = g_strdup_printf( "#%s", channel ); g_free( channel ); channel = s; + + irc_channel_name_strip( channel ); } if( ( ic = irc_channel_new( irc, channel ) ) && -- cgit v1.2.3 From 1c40aa73b52e4507404c82056170069a859fb0cb Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 28 Jun 2010 01:07:46 +0100 Subject: Mark nameless groupchat channels as temporary so they don't stick around forever. --- irc.h | 4 +++- irc_channel.c | 5 +++++ irc_im.c | 29 +++++++++++++++++++++++++---- storage_xml.c | 3 +++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/irc.h b/irc.h index 74e66075..b9257645 100644 --- a/irc.h +++ b/irc.h @@ -131,7 +131,9 @@ extern const struct irc_user_funcs irc_user_self_funcs; typedef enum { - IRC_CHANNEL_JOINED = 1, + IRC_CHANNEL_JOINED = 1, /* The user is currently in the channel. */ + IRC_CHANNEL_TEMP = 2, /* Erase the channel when the user leaves, + and don't save it. */ /* Hack: Set this flag right before jumping into IM when we expect a call to imcb_chat_new(). */ diff --git a/irc_channel.c b/irc_channel.c index f0e564bc..534f49c4 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -201,7 +201,12 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, co irc_send_part( ic, iu, msg ); if( iu == ic->irc->user ) + { ic->flags &= ~IRC_CHANNEL_JOINED; + + if( ic->flags & IRC_CHANNEL_TEMP ) + irc_channel_free( ic ); + } return 1; } diff --git a/irc_im.c b/irc_im.c index f4c5f390..6572be44 100644 --- a/irc_im.c +++ b/irc_im.c @@ -454,10 +454,8 @@ static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) if( ic->flags & IRC_CHANNEL_JOINED ) irc_channel_printf( ic, "Cleaning up channel, bye!" ); - /* irc_channel_free( ic ); */ - - irc_channel_del_user( ic, ic->irc->user, FALSE, "Chatroom closed by server" ); ic->data = NULL; + irc_channel_del_user( ic, ic->irc->user, FALSE, "Chatroom closed by server" ); return TRUE; } @@ -500,6 +498,9 @@ static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_u { irc_t *irc = bee->ui_data; + /* TODO: Possible bug here: If a module removes $user here instead of just + using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into + a broken state around here. */ irc_channel_del_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data, FALSE, NULL ); return TRUE; @@ -695,14 +696,18 @@ static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu ) } static char *set_eval_room_account( set_t *set, char *value ); +static char *set_eval_chat_type( set_t *set, char *value ); static gboolean bee_irc_channel_init( irc_channel_t *ic ) { set_add( &ic->set, "account", NULL, set_eval_room_account, ic ); - set_add( &ic->set, "chat_type", "groupchat", NULL, ic ); + set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic ); set_add( &ic->set, "nick", NULL, NULL, ic ); set_add( &ic->set, "room", NULL, NULL, ic ); + /* chat_type == groupchat */ + ic->flags |= IRC_CHANNEL_TEMP; + return TRUE; } @@ -722,6 +727,20 @@ static char *set_eval_room_account( set_t *set, char *value ) return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user ); } +static char *set_eval_chat_type( set_t *set, char *value ) +{ + struct irc_channel *ic = set->data; + + if( strcmp( value, "groupchat" ) == 0 ) + ic->flags |= IRC_CHANNEL_TEMP; + else if( strcmp( value, "room" ) == 0 ) + ic->flags &= ~IRC_CHANNEL_TEMP; + else + return NULL; + + return value; +} + static gboolean bee_irc_channel_free( irc_channel_t *ic ) { set_del( &ic->set, "account" ); @@ -729,6 +748,8 @@ static gboolean bee_irc_channel_free( irc_channel_t *ic ) set_del( &ic->set, "nick" ); set_del( &ic->set, "room" ); + ic->flags &= ~IRC_CHANNEL_TEMP; + return TRUE; } diff --git a/storage_xml.c b/storage_xml.c index d20469af..f97b2cd5 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -516,6 +516,9 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) { irc_channel_t *ic = l->data; + if( ic->flags & IRC_CHANNEL_TEMP ) + continue; + if( !xml_printf( fd, 1, "\n", ic->name, set_getstr( &ic->set, "type" ) ) ) goto write_error; -- cgit v1.2.3 From c7eb7719b4642b72669d280d4c24c54109ed0d28 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 28 Jun 2010 01:18:40 +0100 Subject: Hacky support for short subcommands (i.e. "ac l" instead of "account list".). --- root_commands.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/root_commands.c b/root_commands.c index 470b2536..127570e6 100644 --- a/root_commands.c +++ b/root_commands.c @@ -322,6 +322,7 @@ static int cmd_account_set_checkflags( irc_t *irc, set_t *s ) static void cmd_account( irc_t *irc, char **cmd ) { account_t *a; + int len; if( global.conf->authmode == AUTHMODE_REGISTERED && !( irc->status & USTATUS_IDENTIFIED ) ) { @@ -329,7 +330,9 @@ static void cmd_account( irc_t *irc, char **cmd ) return; } - if( g_strcasecmp( cmd[1], "add" ) == 0 ) + len = strlen( cmd[1] ); + + if( len >= 1 && g_strncasecmp( cmd[1], "add", len ) == 0 ) { struct prpl *prpl; @@ -355,7 +358,7 @@ static void cmd_account( irc_t *irc, char **cmd ) return; } - else if( g_strcasecmp( cmd[1], "list" ) == 0 ) + else if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 ) { int i = 0; @@ -387,7 +390,7 @@ static void cmd_account( irc_t *irc, char **cmd ) { /* Try the following two only if cmd[2] == NULL */ } - else if( g_strcasecmp( cmd[1], "on" ) == 0 ) + else if( len >= 2 && g_strncasecmp( cmd[1], "on", len ) == 0 ) { if ( irc->b->accounts ) { @@ -404,7 +407,7 @@ static void cmd_account( irc_t *irc, char **cmd ) return; } - else if( g_strcasecmp( cmd[1], "off" ) == 0 ) + else if( len >= 2 && g_strncasecmp( cmd[1], "off", len ) == 0 ) { irc_usermsg( irc, "Deactivating all active (re)connections..." ); @@ -420,6 +423,7 @@ static void cmd_account( irc_t *irc, char **cmd ) } MIN_ARGS( 2 ); + len = strlen( cmd[2] ); /* At least right now, don't accept on/off/set/del as account IDs even if they're a proper match, since people not familiar with the new @@ -436,7 +440,7 @@ static void cmd_account( irc_t *irc, char **cmd ) return; } - if( g_strcasecmp( cmd[2], "del" ) == 0 ) + if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 ) { if( a->ic ) { @@ -448,14 +452,14 @@ static void cmd_account( irc_t *irc, char **cmd ) irc_usermsg( irc, "Account deleted" ); } } - else if( g_strcasecmp( cmd[2], "on" ) == 0 ) + else if( len >= 2 && g_strncasecmp( cmd[2], "on", len ) == 0 ) { if( a->ic ) irc_usermsg( irc, "Account already online" ); else account_on( irc->b, a ); } - else if( g_strcasecmp( cmd[2], "off" ) == 0 ) + else if( len >= 2 && g_strncasecmp( cmd[2], "off", len ) == 0 ) { if( a->ic ) { @@ -471,7 +475,7 @@ static void cmd_account( irc_t *irc, char **cmd ) irc_usermsg( irc, "Account already offline" ); } } - else if( g_strcasecmp( cmd[2], "set" ) == 0 ) + else if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 ) { cmd_set_real( irc, cmd + 2, &a->set, cmd_account_set_checkflags ); } @@ -484,8 +488,11 @@ static void cmd_account( irc_t *irc, char **cmd ) static void cmd_channel( irc_t *irc, char **cmd ) { irc_channel_t *ic; + int len; + + len = strlen( cmd[1] ); - if( g_strcasecmp( cmd[1], "list" ) == 0 ) + if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 ) { GSList *l; int i = 0; @@ -509,6 +516,7 @@ static void cmd_channel( irc_t *irc, char **cmd ) } MIN_ARGS( 2 ); + len = strlen( cmd[2] ); if( ( ic = irc_channel_get( irc, cmd[1] ) ) == NULL ) { @@ -516,11 +524,11 @@ static void cmd_channel( irc_t *irc, char **cmd ) return; } - if( g_strcasecmp( cmd[2], "set" ) == 0 ) + if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 ) { cmd_set_real( irc, cmd + 2, &ic->set, NULL ); } - else if( g_strcasecmp( cmd[2], "del" ) == 0 ) + else if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 ) { if( !( ic->flags & IRC_CHANNEL_JOINED ) && ic != ic->irc->default_channel ) -- cgit v1.2.3 From ab6006cfec72ae8669c68f2892cf1c9994740c92 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 28 Jun 2010 10:03:39 +0100 Subject: Callers shouldn't have to expect that irc_channel_del_user() frees the channel so if it wants to (temporary channels), do it via a timer. --- irc.h | 1 + irc_channel.c | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/irc.h b/irc.h index b9257645..9bfb23b1 100644 --- a/irc.h +++ b/irc.h @@ -228,6 +228,7 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ); irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name ); irc_channel_t *irc_channel_get( irc_t *irc, char *id ); int irc_channel_free( irc_channel_t *ic ); +void irc_channel_free_soon( irc_channel_t *ic ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, const char *msg ); irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); diff --git a/irc_channel.c b/irc_channel.c index 534f49c4..fc89ddd3 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -140,6 +140,40 @@ int irc_channel_free( irc_channel_t *ic ) return 1; } +struct irc_channel_free_data +{ + irc_t *irc; + irc_channel_t *ic; + char *name; +}; + +static gboolean irc_channel_free_callback( gpointer data, gint fd, b_input_condition cond ) +{ + struct irc_channel_free_data *d = data; + + if( g_slist_find( irc_connection_list, d->irc ) && + irc_channel_by_name( d->irc, d->name ) == d->ic && + !( d->ic->flags & IRC_CHANNEL_JOINED ) ) + irc_channel_free( d->ic ); + + g_free( d->name ); + g_free( d ); + return FALSE; +} + +/* Free the channel, but via the event loop, so after finishing whatever event + we're currently handling. */ +void irc_channel_free_soon( irc_channel_t *ic ) +{ + struct irc_channel_free_data *d = g_new0( struct irc_channel_free_data, 1 ); + + d->irc = ic->irc; + d->ic = ic; + d->name = g_strdup( ic->name ); + + b_timeout_add( 0, irc_channel_free_callback, d ); +} + static char *set_eval_channel_type( set_t *set, char *value ) { struct irc_channel *ic = set->data; @@ -205,7 +239,7 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, co ic->flags &= ~IRC_CHANNEL_JOINED; if( ic->flags & IRC_CHANNEL_TEMP ) - irc_channel_free( ic ); + irc_channel_free_soon( ic ); } return 1; -- cgit v1.2.3 From a670aeb34e4f3c4a1fd910aa5944fed4f9c20d7a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 28 Jun 2010 10:37:01 +0100 Subject: An empty string is not a valid channel name. --- irc_channel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/irc_channel.c b/irc_channel.c index fc89ddd3..a241847d 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -310,6 +310,9 @@ gboolean irc_channel_name_ok( const char *name_ ) const unsigned char *name = (unsigned char*) name_; int i; + if( name_[0] == '\0' ) + return FALSE; + /* Check if the first character is in CTYPES (#&) */ if( strchr( CTYPES, name_[0] ) == NULL ) return FALSE; -- cgit v1.2.3 From 58646d9244557320ec811ee0b67519027170eede Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 28 Jun 2010 10:37:11 +0100 Subject: Allow joining multiple channels at once. Although BitlBee doesn't advertise support for this, irssi tries to do this anyway. --- irc_commands.c | 61 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index 30518a99..bcb2fef2 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -127,30 +127,49 @@ static void irc_cmd_pong( irc_t *irc, char **cmd ) static void irc_cmd_join( irc_t *irc, char **cmd ) { - irc_channel_t *ic; - - if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL ) - ic = irc_channel_new( irc, cmd[1] ); + char *comma, *s = cmd[1]; - if( ic == NULL ) + while( s ) { - irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] ); - return; + irc_channel_t *ic; + + if( ( comma = strchr( s, ',' ) ) ) + *comma = '\0'; + + if( ( ic = irc_channel_by_name( irc, s ) ) == NULL ) + ic = irc_channel_new( irc, s ); + + if( ic == NULL ) + { + irc_send_num( irc, 479, "%s :Invalid channel name", s ); + goto next; + } + + if( ic->flags & IRC_CHANNEL_JOINED ) + /* Dude, you're already there... + RFC doesn't have any reply for that though? */ + goto next; + + if( ic->f->join && !ic->f->join( ic ) ) + /* The story is: FALSE either means the handler + showed an error message, or is doing some work + before the join should be confirmed. (In the + latter case, the caller should take care of that + confirmation.) TRUE means all's good, let the + user join the channel right away. */ + goto next; + + irc_channel_add_user( ic, irc->user ); + +next: + if( comma ) + { + s = comma + 1; + *comma = ','; + } + else + break; } - - if( ic->flags & IRC_CHANNEL_JOINED ) - return; /* Dude, you're already there... - RFC doesn't have any reply for that though? */ - - if( ic->f->join && !ic->f->join( ic ) ) - /* The story is: FALSE either means the handler showed an error - message, or is doing some work before the join should be - confirmed. (In the latter case, the caller should take care - of that confirmation.) - TRUE means all's good, let the user join the channel right away. */ - return; - - irc_channel_add_user( ic, irc->user ); } static void irc_cmd_names( irc_t *irc, char **cmd ) -- cgit v1.2.3 From 7a6ba50d4824c4e33ebf1154e97038c9331f4f58 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 30 Jun 2010 00:44:17 +0100 Subject: Also allow selecting contacts for a channel by protocol instead of account. If someone has two MSN accts and wants contacts from both in one channel, this is now possible. --- irc.h | 2 ++ irc_channel.c | 22 ++++++++++++++++++++++ irc_im.c | 2 ++ storage_xml.c | 4 ++++ 4 files changed, 30 insertions(+) diff --git a/irc.h b/irc.h index 9bfb23b1..4663c5b9 100644 --- a/irc.h +++ b/irc.h @@ -192,6 +192,7 @@ typedef enum IRC_CC_TYPE_REST, IRC_CC_TYPE_GROUP, IRC_CC_TYPE_ACCOUNT, + IRC_CC_TYPE_PROTOCOL, } irc_control_channel_type_t; struct irc_control_channel @@ -199,6 +200,7 @@ struct irc_control_channel irc_control_channel_type_t type; struct bee_group *group; struct account *account; + struct prpl *protocol; }; extern const struct bee_ui_funcs irc_ui_funcs; diff --git a/irc_channel.c b/irc_channel.c index a241847d..f5e140e0 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -473,6 +473,7 @@ static gboolean control_channel_invite( irc_channel_t *ic, irc_user_t *iu ) static char *set_eval_by_account( set_t *set, char *value ); static char *set_eval_fill_by( set_t *set, char *value ); static char *set_eval_by_group( set_t *set, char *value ); +static char *set_eval_by_protocol( set_t *set, char *value ); static gboolean control_channel_init( irc_channel_t *ic ) { @@ -481,6 +482,7 @@ static gboolean control_channel_init( irc_channel_t *ic ) set_add( &ic->set, "account", NULL, set_eval_by_account, ic ); set_add( &ic->set, "fill_by", "all", set_eval_fill_by, ic ); set_add( &ic->set, "group", NULL, set_eval_by_group, ic ); + set_add( &ic->set, "protocol", NULL, set_eval_by_protocol, ic ); ic->data = icc = g_new0( struct irc_control_channel, 1 ); icc->type = IRC_CC_TYPE_DEFAULT; @@ -514,6 +516,7 @@ static char *set_eval_by_account( set_t *set, char *value ) icc->account = acc; if( icc->type == IRC_CC_TYPE_ACCOUNT ) bee_irc_channel_update( ic->irc, ic, NULL ); + return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user ); } @@ -530,6 +533,8 @@ static char *set_eval_fill_by( set_t *set, char *value ) icc->type = IRC_CC_TYPE_GROUP; else if( strcmp( value, "account" ) == 0 ) icc->type = IRC_CC_TYPE_ACCOUNT; + else if( strcmp( value, "protocol" ) == 0 ) + icc->type = IRC_CC_TYPE_PROTOCOL; else return SET_INVALID; @@ -545,9 +550,26 @@ static char *set_eval_by_group( set_t *set, char *value ) icc->group = bee_group_by_name( ic->irc->b, value, TRUE ); if( icc->type == IRC_CC_TYPE_GROUP ) bee_irc_channel_update( ic->irc, ic, NULL ); + return g_strdup( icc->group->name ); } +static char *set_eval_by_protocol( set_t *set, char *value ) +{ + struct irc_channel *ic = set->data; + struct irc_control_channel *icc = ic->data; + struct prpl *prpl; + + if( !( prpl = find_protocol( value ) ) ) + return SET_INVALID; + + icc->protocol = prpl; + if( icc->type == IRC_CC_TYPE_PROTOCOL ) + bee_irc_channel_update( ic->irc, ic, NULL ); + + return value; +} + static gboolean control_channel_free( irc_channel_t *ic ) { struct irc_control_channel *icc = ic->data; diff --git a/irc_im.c b/irc_im.c index 6572be44..268fb176 100644 --- a/irc_im.c +++ b/irc_im.c @@ -156,6 +156,8 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) show = iu->bu->group == icc->group; else if( icc->type == IRC_CC_TYPE_ACCOUNT ) show = iu->bu->ic->acc == icc->account; + else if( icc->type == IRC_CC_TYPE_PROTOCOL ) + show = iu->bu->ic->acc->prpl == icc->protocol; if( !show ) { diff --git a/storage_xml.c b/storage_xml.c index f97b2cd5..a60769bb 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -218,6 +218,10 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na return; } + /* The channel may exist already, for example if it's &bitlbee. + Also, it's possible that the user just reconnected and the + IRC client already rejoined all channels it was in. They + should still get the right settings. */ if( ( xd->current_channel = irc_channel_by_name( irc, name ) ) || ( xd->current_channel = irc_channel_new( irc, name ) ) ) set_setstr( &xd->current_channel->set, "type", type ); -- cgit v1.2.3 From 217bf4e0401435dce8dec87d2644fcadc986fa93 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 30 Jun 2010 20:30:42 +0100 Subject: Also, automatically set up per-protocol channels if the name's a protocol name. --- irc_channel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/irc_channel.c b/irc_channel.c index f5e140e0..a0aaaccf 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -492,6 +492,10 @@ static gboolean control_channel_init( irc_channel_t *ic ) set_setstr( &ic->set, "group", ic->name + 1 ); set_setstr( &ic->set, "fill_by", "group" ); } + else if( set_setstr( &ic->set, "protocol", ic->name + 1 ) ) + { + set_setstr( &ic->set, "fill_by", "protocol" ); + } else if( set_setstr( &ic->set, "account", ic->name + 1 ) ) { set_setstr( &ic->set, "fill_by", "account" ); -- cgit v1.2.3 From d7f850037c6288b5a2eebae789ed0247d0e5f7b3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 1 Jul 2010 00:01:33 +0100 Subject: Support /NOTICE, although for now just to yourself - some IRC clients use this to measure lag. --- irc_commands.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index bcb2fef2..0a9b2c61 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -333,6 +333,20 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) } } +static void irc_cmd_notice( irc_t *irc, char **cmd ) +{ + if( !cmd[2] ) + { + irc_send_num( irc, 412, ":No text to send" ); + return; + } + + /* At least for now just echo. IIRC some IRC clients use self-notices + for lag checks, so try to support that. */ + if( nick_cmp( cmd[1], irc->user->nick ) == 0 ) + irc_send_msg( irc->user, "NOTICE", irc->user->nick, cmd[2], NULL ); +} + static void irc_cmd_nickserv( irc_t *irc, char **cmd ) { /* [SH] This aliases the NickServ command to PRIVMSG root */ @@ -602,6 +616,7 @@ static const command_t irc_commands[] = { { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, { "who", 0, irc_cmd_who, IRC_CMD_LOGGED_IN }, { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, + { "notice", 1, irc_cmd_notice, IRC_CMD_LOGGED_IN }, { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, @@ -611,9 +626,6 @@ static const command_t irc_commands[] = { { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, -#if 0 - { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, -#endif { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, -- cgit v1.2.3 From 9052bc147b30a08c8df6799df09b01f922c10be7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 1 Jul 2010 00:15:44 +0100 Subject: Flush channels when the user leaves them. Also, don't update a control channel if the user isn't in it. --- irc_channel.c | 11 +++++++++++ irc_im.c | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/irc_channel.c b/irc_channel.c index a0aaaccf..b597e809 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -240,6 +240,16 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, co if( ic->flags & IRC_CHANNEL_TEMP ) irc_channel_free_soon( ic ); + else + { + /* Flush userlist now. The user won't see it anyway. */ + while( ic->users ) + { + g_free( ic->users->data ); + ic->users = g_slist_remove( ic->users, ic->users->data ); + } + irc_channel_add_user( ic, ic->irc->root ); + } } return 1; @@ -581,6 +591,7 @@ static gboolean control_channel_free( irc_channel_t *ic ) set_del( &ic->set, "account" ); set_del( &ic->set, "fill_by" ); set_del( &ic->set, "group" ); + set_del( &ic->set, "protocol" ); g_free( icc ); ic->data = NULL; diff --git a/irc_im.c b/irc_im.c index 268fb176..5f33e579 100644 --- a/irc_im.c +++ b/irc_im.c @@ -130,7 +130,8 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) { ic = l->data; /* TODO: Just add a type flag or so.. */ - if( ic->f == irc->default_channel->f ) + if( ic->f == irc->default_channel->f && + ( ic->flags & IRC_CHANNEL_JOINED ) ) bee_irc_channel_update( irc, ic, iu ); } return; -- cgit v1.2.3 From 06f9548d5f3b0909c8cdbd092dd3118c661be67b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 1 Jul 2010 00:30:27 +0100 Subject: Disable the code added in the previous change during shutdown, since it's pointless at that stage and may cause crashes. --- irc_channel.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/irc_channel.c b/irc_channel.c index b597e809..ffcfbffb 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -238,8 +238,14 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, co { ic->flags &= ~IRC_CHANNEL_JOINED; - if( ic->flags & IRC_CHANNEL_TEMP ) + if( ic->irc->status & USTATUS_SHUTDOWN ) + { + /* Don't do anything fancy when we're shutting down anyway. */ + } + else if( ic->flags & IRC_CHANNEL_TEMP ) + { irc_channel_free_soon( ic ); + } else { /* Flush userlist now. The user won't see it anyway. */ -- cgit v1.2.3 From 52a252173062bbce66040ef2b79c15dc2e2c03e6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 1 Jul 2010 00:46:55 +0100 Subject: When receiving a channel name hint, and a channel with that name already exists, remove it if it's fully unused, instead of failing immediately. (Fixes #639.) --- irc_im.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/irc_im.c b/irc_im.c index 5f33e579..f9c1b865 100644 --- a/irc_im.c +++ b/irc_im.c @@ -420,7 +420,7 @@ static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c ) int i; /* Try to find a channel that expects to receive a groupchat. - This flag is set by groupchat_stub_invite(). */ + This flag is set earlier in our current call trace. */ for( l = irc->channels; l; l = l->next ) { ic = l->data; @@ -529,7 +529,7 @@ static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name ) { irc_t *irc = bee->ui_data; - irc_channel_t *ic = c->ui_data; + irc_channel_t *ic = c->ui_data, *oic; char stripped[MAX_NICK_LENGTH+1], *full_name; /* Don't rename a channel if the user's in it already. */ @@ -542,18 +542,37 @@ static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const c if( set_getbool( &bee->set, "lcnicks" ) ) nick_lc( stripped ); - full_name = g_strdup_printf( "#%s", stripped ); + if( stripped[0] == '\0' ) + return FALSE; - if( stripped[0] && irc_channel_by_name( irc, full_name ) == NULL ) - { - g_free( ic->name ); - ic->name = full_name; - } - else + full_name = g_strdup_printf( "#%s", stripped ); + if( ( oic = irc_channel_by_name( irc, full_name ) ) ) { - g_free( full_name ); + char *type, *chat_type; + + type = set_getstr( &oic->set, "type" ); + chat_type = set_getstr( &oic->set, "chat_type" ); + + if( type && chat_type && oic->data == FALSE && + strcmp( type, "chat" ) == 0 && + strcmp( chat_type, "groupchat" ) == 0 ) + { + /* There's a channel with this name already, but it looks + like it's not in use yet. Most likely the IRC client + rejoined the channel after a reconnect. Remove it so + we can reuse its name. */ + irc_channel_free( oic ); + } + else + { + g_free( full_name ); + return FALSE; + } } + g_free( ic->name ); + ic->name = full_name; + return TRUE; } -- cgit v1.2.3 From e1f3f94eaf2f18f4707dc48db9adc541f16d1679 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 3 Jul 2010 18:39:37 +0100 Subject: Fix handling of outgoing CTCP ACTIONs. --- irc_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc_commands.c b/irc_commands.c index 0a9b2c61..197a7e6e 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -296,7 +296,7 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) if( g_strncasecmp( cmd[2], "\001ACTION", 7 ) == 0 ) { cmd[2] += 4; - strcpy( cmd[2], "/me" ); + memcpy( cmd[2], "/me", 3 ); if( cmd[2][strlen(cmd[2])-1] == '\001' ) cmd[2][strlen(cmd[2])-1] = '\0'; } -- cgit v1.2.3 From bc4b4694d8ec852ac2e192b794bfcb7b62fbc3fc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 3 Jul 2010 18:57:31 +0100 Subject: Initialize the show variable in channel_update. This shouldn't have caused any troubles, only unpredictable behaviour for users trying out unimplemented functionality. --- irc_im.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc_im.c b/irc_im.c index f9c1b865..dcba6f8f 100644 --- a/irc_im.c +++ b/irc_im.c @@ -122,7 +122,7 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) { struct irc_control_channel *icc; GSList *l; - gboolean show; + gboolean show = FALSE; if( ic == NULL ) { -- cgit v1.2.3 From 917a83ee526b8e5baaf451888cd0a3149894697b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 3 Jul 2010 21:38:53 +0100 Subject: Use irc_send_msg instead of irc_send_msg_raw() to echo messages to self, to make sure CTCP ACTIONs work. Not important, just more correct. Other CTCPs sent to oneself are dropped, but why CTCP yourself anyway? --- irc_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc_user.c b/irc_user.c index 2706eb67..98145ae1 100644 --- a/irc_user.c +++ b/irc_user.c @@ -242,7 +242,7 @@ const struct irc_user_funcs irc_user_root_funcs = { /* Echo to yourself: */ static gboolean self_privmsg( irc_user_t *iu, const char *msg ) { - irc_send_msg_raw( iu, "PRIVMSG", iu->nick, msg ); + irc_send_msg( iu, "PRIVMSG", iu->nick, msg, NULL ); return TRUE; } -- cgit v1.2.3 From 0bd948edfea280cf9f05e21cd5bef4b7fdf3335c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 3 Jul 2010 22:16:41 +0100 Subject: Show a user going offline as a QUIT, not as one or more PARTs, like in the old-style BitlBee. This so that the IRC client will show the notification in query windows as well. Make it a setting though, for bug #539. --- irc.c | 1 + irc.h | 1 + irc_im.c | 8 ++++++ irc_user.c | 92 +++++++++++++++++++++++++++++++++----------------------------- 4 files changed, 59 insertions(+), 43 deletions(-) diff --git a/irc.c b/irc.c index fb720088..650917fe 100644 --- a/irc.c +++ b/irc.c @@ -107,6 +107,7 @@ irc_t *irc_new( int fd ) s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc ); s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc ); s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); + s = set_add( &b->set, "offline_user_quits", "true", set_eval_bool, irc ); s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc ); s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc ); s->old_key = g_strdup( "buddy_sendbuffer" ); diff --git a/irc.h b/irc.h index 4663c5b9..96b05d44 100644 --- a/irc.h +++ b/irc.h @@ -272,6 +272,7 @@ irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ); int irc_user_set_nick( irc_user_t *iu, const char *new ); gint irc_user_cmp( gconstpointer a_, gconstpointer b_ ); const char *irc_user_get_away( irc_user_t *iu ); +void irc_user_quit( irc_user_t *iu, const char *msg ); /* irc_util.c */ char *set_eval_timezone( struct set *set, char *value ); diff --git a/irc_im.c b/irc_im.c index dcba6f8f..caf0e38e 100644 --- a/irc_im.c +++ b/irc_im.c @@ -107,6 +107,14 @@ static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old if( g_hash_table_lookup( irc->watches, iu->key ) ) irc_send_num( irc, 601, "%s %s %s %d :%s", iu->nick, iu->user, iu->host, (int) time( NULL ), "logged offline" ); + + /* Send a QUIT since those will also show up in any + query windows the user may have, plus it's only + one QUIT instead of possibly many (in case of + multiple control chans). If there's a channel that + shows offline people, a JOIN will follow. */ + if( set_getbool( &bee->set, "offline_user_quits" ) ) + irc_user_quit( iu, "Leaving..." ); } } diff --git a/irc_user.c b/irc_user.c index 98145ae1..fa509a45 100644 --- a/irc_user.c +++ b/irc_user.c @@ -51,56 +51,47 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ) int irc_user_free( irc_t *irc, irc_user_t *iu ) { - GSList *l; - gboolean send_quit = FALSE; + static struct im_connection *last_ic; + static char *msg; if( !iu ) return 0; - irc->users = g_slist_remove( irc->users, iu ); - g_hash_table_remove( irc->nick_user_hash, iu->key ); - - for( l = irc->channels; l; l = l->next ) - send_quit |= irc_channel_del_user( (irc_channel_t*) l->data, iu, TRUE, NULL ); - - if( send_quit ) + if( iu->bu && + ( iu->bu->ic->flags & OPT_LOGGING_OUT ) && + iu->bu->ic != last_ic ) { - static struct im_connection *last_ic; - static char *msg; + char host_prefix[] = "bitlbee."; + char *s; - if( iu->bu && - ( iu->bu->ic->flags & OPT_LOGGING_OUT ) && - iu->bu->ic != last_ic ) - { - char host_prefix[] = "bitlbee."; - char *s; - - /* Irssi recognises netsplits by quitmsgs with two - hostnames, where a hostname is a "word" with one - of more dots. Mangle no-dot hostnames a bit. */ - if( strchr( irc->root->host, '.' ) ) - *host_prefix = '\0'; - - last_ic = iu->bu->ic; - g_free( msg ); - if( !set_getbool( &irc->b->set, "simulate_netsplit" ) ) - msg = g_strdup( "Account off-line" ); - else if( ( s = strchr( iu->bu->ic->acc->user, '@' ) ) ) - msg = g_strdup_printf( "%s%s %s", host_prefix, - irc->root->host, s + 1 ); - else - msg = g_strdup_printf( "%s%s %s.%s", - host_prefix, irc->root->host, - iu->bu->ic->acc->prpl->name, irc->root->host ); - } - else if( !iu->bu || !( iu->bu->ic->flags & OPT_LOGGING_OUT ) ) - { - g_free( msg ); - msg = g_strdup( "Removed" ); - last_ic = NULL; - } - irc_send_quit( iu, msg ); + /* Irssi recognises netsplits by quitmsgs with two + hostnames, where a hostname is a "word" with one + of more dots. Mangle no-dot hostnames a bit. */ + if( strchr( irc->root->host, '.' ) ) + *host_prefix = '\0'; + + last_ic = iu->bu->ic; + g_free( msg ); + if( !set_getbool( &irc->b->set, "simulate_netsplit" ) ) + msg = g_strdup( "Account off-line" ); + else if( ( s = strchr( iu->bu->ic->acc->user, '@' ) ) ) + msg = g_strdup_printf( "%s%s %s", host_prefix, + irc->root->host, s + 1 ); + else + msg = g_strdup_printf( "%s%s %s.%s", + host_prefix, irc->root->host, + iu->bu->ic->acc->prpl->name, irc->root->host ); + } + else if( !iu->bu || !( iu->bu->ic->flags & OPT_LOGGING_OUT ) ) + { + g_free( msg ); + msg = g_strdup( "Removed" ); + last_ic = NULL; } + irc_user_quit( iu, msg ); + + irc->users = g_slist_remove( irc->users, iu ); + g_hash_table_remove( irc->nick_user_hash, iu->key ); g_free( iu->nick ); if( iu->nick != iu->user ) g_free( iu->user ); @@ -204,6 +195,21 @@ const char *irc_user_get_away( irc_user_t *iu ) return NULL; } +void irc_user_quit( irc_user_t *iu, const char *msg ) +{ + GSList *l; + gboolean send_quit = FALSE; + + if( !iu ) + return; + + for( l = iu->irc->channels; l; l = l->next ) + send_quit |= irc_channel_del_user( (irc_channel_t*) l->data, iu, TRUE, NULL ); + + if( send_quit ) + irc_send_quit( iu, msg ); +} + /* User-type dependent functions, for root/NickServ: */ static gboolean root_privmsg( irc_user_t *iu, const char *msg ) { -- cgit v1.2.3 From c8eeadd72286b87cc1638e388669e00e8c8b9f1e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Jul 2010 11:16:07 +0100 Subject: Added automatic joining of channels. Auto-rejoin functionality for groupchats not reimplemented yet but that's the next step. --- irc.c | 3 ++- irc.h | 1 + irc_channel.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- root_commands.c | 1 + 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/irc.c b/irc.c index 650917fe..cb1d0867 100644 --- a/irc.c +++ b/irc.c @@ -652,7 +652,8 @@ int irc_check_login( irc_t *irc ) ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN ); irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); - irc_channel_add_user( ic, irc->user ); + set_setstr( &ic->set, "auto_join", "true" ); + irc_channel_auto_joins( irc, NULL ); irc->last_root_cmd = g_strdup( ROOT_CHAN ); diff --git a/irc.h b/irc.h index 96b05d44..e909d6b9 100644 --- a/irc.h +++ b/irc.h @@ -236,6 +236,7 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, co irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ); +void irc_channel_auto_joins( irc_t *irc, struct account *acc ); void irc_channel_printf( irc_channel_t *ic, char *format, ... ); gboolean irc_channel_name_ok( const char *name ); void irc_channel_name_strip( char *name ); diff --git a/irc_channel.c b/irc_channel.c index ffcfbffb..4aec9077 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -47,6 +47,7 @@ irc_channel_t *irc_channel_new( irc_t *irc, const char *name ) irc->channels = g_slist_append( irc->channels, ic ); + set_add( &ic->set, "auto_join", "false", set_eval_bool, ic ); set_add( &ic->set, "type", "control", set_eval_channel_type, ic ); if( name[0] == '&' ) @@ -308,6 +309,45 @@ void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_u icu->flags = flags; } +void irc_channel_auto_joins( irc_t *irc, account_t *acc ) +{ + GSList *l; + + for( l = irc->channels; l; l = l->next ) + { + irc_channel_t *ic = l->data; + gboolean aj = set_getbool( &ic->set, "auto_join" ); + char *type; + + if( acc && + ( type = set_getstr( &ic->set, "chat_type" ) ) && + strcmp( type, "room" ) == 0 ) + { + /* Bit of an ugly special case: Handle chatrooms here, we + can only auto-join them if their account is online. */ + char *acc_s; + + if( !aj && !( ic->flags & IRC_CHANNEL_JOINED ) ) + /* Only continue if this one's marked as auto_join + or if we're in it already. (Possible if the + client auto-rejoined it before identyfing.) */ + continue; + else if( !( acc_s = set_getstr( &ic->set, "account" ) ) ) + continue; + else if( account_get( irc->b, acc_s ) != acc ) + continue; + else if( acc->ic == NULL || !( acc->ic->flags & OPT_LOGGED_IN ) ) + continue; + else + ic->f->join( ic ); + } + else if( aj ) + { + irc_channel_add_user( ic, irc->user ); + } + } +} + void irc_channel_printf( irc_channel_t *ic, char *format, ... ) { va_list params; @@ -524,6 +564,13 @@ static gboolean control_channel_init( irc_channel_t *ic ) return TRUE; } +static gboolean control_channel_join( irc_channel_t *ic ) +{ + bee_irc_channel_update( ic->irc, ic, NULL ); + + return TRUE; +} + static char *set_eval_by_account( set_t *set, char *value ) { struct irc_channel *ic = set->data; @@ -607,7 +654,7 @@ static gboolean control_channel_free( irc_channel_t *ic ) static const struct irc_channel_funcs control_channel_funcs = { control_channel_privmsg, - NULL, + control_channel_join, NULL, NULL, control_channel_invite, diff --git a/root_commands.c b/root_commands.c index 127570e6..62fe1e79 100644 --- a/root_commands.c +++ b/root_commands.c @@ -156,6 +156,7 @@ static void cmd_identify( irc_t *irc, char **cmd ) irc_setpass( irc, password ); irc->status |= USTATUS_IDENTIFIED; irc_umode_set( irc, "+R", 1 ); + irc_channel_auto_joins( irc, NULL ); if( load && set_getbool( &irc->b->set, "auto_connect" ) ) cmd_account( irc, account_on ); break; -- cgit v1.2.3 From 5c7b45cb652c73a8f2c827116786a1b21519d4b7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Jul 2010 14:36:08 +0100 Subject: Auto joins for chatrooms. --- irc_im.c | 15 +++++++++++++++ protocols/bee.h | 3 +++ protocols/nogaim.c | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/irc_im.c b/irc_im.c index caf0e38e..57ff893e 100644 --- a/irc_im.c +++ b/irc_im.c @@ -30,6 +30,18 @@ static const struct irc_user_funcs irc_user_im_funcs; +static void bee_irc_imc_connected( struct im_connection *ic ) +{ + irc_t *irc = (irc_t*) ic->bee->ui_data; + + irc_channel_auto_joins( irc, ic->acc ); +} + +static void bee_irc_imc_disconnected( struct im_connection *ic ) +{ + /* Maybe try to send /QUITs here instead of later on. */ +} + static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) { irc_user_t *iu; @@ -822,6 +834,9 @@ static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file } const struct bee_ui_funcs irc_ui_funcs = { + bee_irc_imc_connected, + bee_irc_imc_disconnected, + bee_irc_user_new, bee_irc_user_free, bee_irc_user_fullname, diff --git a/protocols/bee.h b/protocols/bee.h index c3230f47..e82913d6 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -81,6 +81,9 @@ typedef struct bee_group typedef struct bee_ui_funcs { + void (*imc_connected)( struct im_connection *ic ); + void (*imc_disconnected)( struct im_connection *ic ); + gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); gboolean (*user_free)( bee_t *bee, struct bee_user *bu ); gboolean (*user_fullname)( bee_t *bee, bee_user_t *bu ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index f88ec693..0998291b 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -286,6 +286,9 @@ void imcb_connected( struct im_connection *ic ) exponential backoff timer. */ ic->acc->auto_reconnect_delay = 0; + if( ic->bee->ui->imc_connected ) + ic->bee->ui->imc_connected( ic ); + /* for( c = irc->chatrooms; c; c = c->next ) { @@ -328,6 +331,9 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) else ic->flags |= OPT_LOGGING_OUT; + if( ic->bee->ui->imc_disconnected ) + ic->bee->ui->imc_disconnected( ic ); + imcb_log( ic, "Signing off.." ); b_event_remove( ic->keepalive ); -- cgit v1.2.3 From f537044f96de6b6553f042e2274252cb834680b3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Jul 2010 15:31:50 +0100 Subject: Documentation update. I think this covers most of the channel-related ui-fix changes. --- doc/user-guide/commands.xml | 151 ++++++++++++++++++++++++++++++++++++++++++-- doc/user-guide/help.xml | 7 +- doc/user-guide/misc.xml | 2 +- 3 files changed, 153 insertions(+), 7 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index edf89d46..889b6165 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -159,7 +159,7 @@ - This command can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing account set <account id>. + This command can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing account <account id> set. @@ -173,6 +173,65 @@ + + Channel list maintenance + channel [<account id>] <action> [<arguments>] + + + + Available actions: del, list, set. See help chat <action> for more information. + + + + There is no channel add command. To create a new channel, just use the IRC /join command. See also help channels and help groupchats. + + + + + channel <channel id> del + + + + Remove a channel and forget all its settings. You can only remove channels you're not currently in, and can't remove the main control channel. (You can, however, leave it.) + + + + + + + channel list + + + + This command gives you a list of all the channels you configured. + + + + + + + channel <channel id> set + channel <channel id> set <setting> + channel <channel id> set <setting> <value> + channel <channel id> set -del <setting> + + + + This command can be used to change various settings for channels. Different channel types support different settings. You can see the settings available for a channel by typing channel <channel id> set. + + + + For more infomation about a setting, see help set <setting>. + + + + The channel ID can be a number (see channel list), or (part of) its name, as long as it matches only one channel. + + + + + + Chatroom list maintenance chat <action> [<arguments>] @@ -352,6 +411,15 @@ + + + + + For control channels with fill_by set to account: Set this setting to the account id (numeric, or part of the username) of the account containing the contacts you want to see in this channel. + + + + true @@ -366,12 +434,12 @@ - + false - With this option enabled, BitlBee will automatically join this chatroom when you log in. + With this option enabled, BitlBee will automatically join this channel when you log in. @@ -478,6 +546,25 @@ + + groupchat + groupchat, room + + + + There are two kinds of chat channels: simple groupchats (basically normal IM chats with more than two participants) and names chatrooms, more similar to IRC channels. + + + + BitlBee supports both types. With this setting set to groupchat (the default), you can just invite people into the room and start talking. + + + + For setting up named chatrooms, it's currently easier to just use the chat add command. + + + + false @@ -527,6 +614,38 @@ + + all + all, group, account, protocol + + + + For control channels only: This setting determines which contacts the channel gets populated with. + + + + By default, control channels will contain all your contacts. You instead select contacts by buddy group, IM account or IM protocol. + + + + Change this setting and the corresponding account/group/protocol setting to set up this selection. + + + + Note that, when creating a new channel, BitlBee will try to preconfigure the channel for you, based on the channel name. See help channels. + + + + + + + + + For control channels with fill_by set to group: Set this setting to the name of the group containing the contacts you want to see in this channel. + + + + add_channel root, add, add_private, add_channel, ignore @@ -776,6 +895,15 @@ + + + + + For control channels with fill_by set to protocol: Set this setting to the name of the IM protocol of all contacts you want to see in this channel. + + + + lifo lifo, fifo @@ -964,6 +1092,21 @@ + + control + control, chat + + + + BitlBee supports two kinds of channels: control channels (usually with a name starting with a &) and chatroom channels (name usually starts with a #). + + + + See help channels for a full description of channel types in BitlBee. + + + + false @@ -1153,7 +1296,7 @@ - account set 1/display_name "The majestik møøse" + account 1 set display_name "The majestik møøse" display_name = `The majestik møøse' diff --git a/doc/user-guide/help.xml b/doc/user-guide/help.xml index 0684512e..48ed8a48 100644 --- a/doc/user-guide/help.xml +++ b/doc/user-guide/help.xml @@ -15,8 +15,9 @@ These are the available help subjects: commandsAll available commands and settings channelsAbout creating and customizing channels awayAbout setting away states - smileysA summary of some non-standard smileys you might find and fail to understand groupchatsHow to work with groupchats on BitlBee + nick_changesChanging your nickname without losing any settings + smileysA summary of some non-standard smileys you might find and fail to understand @@ -43,9 +44,11 @@ These are the available help subjects: quickstartA short introduction into BitlBee commandsAll available commands and settings + channelsAbout creating and customizing channels awayAbout setting away states - smileysA summary of some non-standard smileys you might find and fail to understand groupchatsHow to work with groupchats on BitlBee + nick_changesChanging your nickname without losing any settings + smileysA summary of some non-standard smileys you might find and fail to understand diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml index 9a0bb67f..175f0cd4 100644 --- a/doc/user-guide/misc.xml +++ b/doc/user-guide/misc.xml @@ -166,7 +166,7 @@ When you create a new channel, BitlBee will try to guess from its name which con -Any valid account ID (so a number, protocol name or part of screenname, as long as it's unique) can also be used as a channel name. So if you just join &msn, it will contain all your MSN contacts (as long as you have only one MSN account set up). And if you have a Facebook account set up, you can see its contacts by just joining &facebook. +Any valid account ID (so a number, protocol name or part of screenname, as long as it's unique) can also be used as a channel name. So if you just join &msn, it will contain all your MSN contacts. And if you have a Facebook account set up, you can see its contacts by just joining &facebook. -- cgit v1.2.3 From 006a84f999248d1bc1c1e36fa3437765d4bd1142 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Jul 2010 21:40:15 +0100 Subject: Kick the user instead of parting him/her when cleaning up a channel. This is what the older version also did so that Irssi won't clean up the window. --- irc.h | 10 +++++++++- irc_channel.c | 11 ++++++++--- irc_im.c | 6 +++--- irc_send.c | 6 ++++++ irc_user.c | 2 +- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/irc.h b/irc.h index e909d6b9..94738832 100644 --- a/irc.h +++ b/irc.h @@ -205,6 +205,13 @@ struct irc_control_channel extern const struct bee_ui_funcs irc_ui_funcs; +typedef enum +{ + IRC_CDU_SILENT, + IRC_CDU_PART, + IRC_CDU_KICK, +} irc_channel_del_user_type_t; + /* irc.c */ extern GSList *irc_connection_list; @@ -232,7 +239,7 @@ irc_channel_t *irc_channel_get( irc_t *irc, char *id ); int irc_channel_free( irc_channel_t *ic ); void irc_channel_free_soon( irc_channel_t *ic ); int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ); -int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, const char *msg ); +int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg ); irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu ); int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who ); void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags ); @@ -255,6 +262,7 @@ void irc_usermsg( irc_t *irc, char *format, ... ); void irc_send_join( irc_channel_t *ic, irc_user_t *iu ); void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason ); void irc_send_quit( irc_user_t *iu, const char *reason ); +void irc_send_kick( irc_channel_t *ic, irc_user_t *iu, irc_user_t *kicker, const char *reason ); void irc_send_names( irc_channel_t *ic ); void irc_send_topic( irc_channel_t *ic, gboolean topic_change ); void irc_send_whois( irc_user_t *iu ); diff --git a/irc_channel.c b/irc_channel.c index 4aec9077..70770bfb 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -118,7 +118,7 @@ int irc_channel_free( irc_channel_t *ic ) irc_t *irc = ic->irc; if( ic->flags & IRC_CHANNEL_JOINED ) - irc_channel_del_user( ic, irc->user, FALSE, "Cleaning up channel" ); + irc_channel_del_user( ic, irc->user, IRC_CDU_KICK, "Cleaning up channel" ); if( ic->f->_free ) ic->f->_free( ic ); @@ -222,7 +222,7 @@ int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu ) return 1; } -int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, const char *msg ) +int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg ) { irc_channel_user_t *icu; @@ -232,8 +232,13 @@ int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, gboolean silent, co ic->users = g_slist_remove( ic->users, icu ); g_free( icu ); - if( ic->flags & IRC_CHANNEL_JOINED && !silent ) + if( !( ic->flags & IRC_CHANNEL_JOINED ) || type == IRC_CDU_SILENT ) {} + /* Do nothing. The caller should promise it won't screw + up state of the IRC client. :-) */ + else if( type == IRC_CDU_PART ) irc_send_part( ic, iu, msg ); + else if( type == IRC_CDU_KICK ) + irc_send_kick( ic, iu, ic->irc->root, msg ); if( iu == ic->irc->user ) { diff --git a/irc_im.c b/irc_im.c index 57ff893e..c900e7ff 100644 --- a/irc_im.c +++ b/irc_im.c @@ -182,7 +182,7 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) if( !show ) { - irc_channel_del_user( ic, iu, FALSE, NULL ); + irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL ); } else { @@ -478,7 +478,7 @@ static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) irc_channel_printf( ic, "Cleaning up channel, bye!" ); ic->data = NULL; - irc_channel_del_user( ic, ic->irc->user, FALSE, "Chatroom closed by server" ); + irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" ); return TRUE; } @@ -524,7 +524,7 @@ static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_u /* TODO: Possible bug here: If a module removes $user here instead of just using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into a broken state around here. */ - irc_channel_del_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data, FALSE, NULL ); + irc_channel_del_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL ); return TRUE; } diff --git a/irc_send.c b/irc_send.c index 3617d088..b62d2011 100644 --- a/irc_send.c +++ b/irc_send.c @@ -157,6 +157,12 @@ void irc_send_quit( irc_user_t *iu, const char *reason ) irc_write( iu->irc, ":%s!%s@%s QUIT :%s", iu->nick, iu->user, iu->host, reason ? : "" ); } +void irc_send_kick( irc_channel_t *ic, irc_user_t *iu, irc_user_t *kicker, const char *reason ) +{ + irc_write( ic->irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, + kicker->host, ic->name, iu->nick, reason ? : "" ); +} + void irc_send_names( irc_channel_t *ic ) { GSList *l; diff --git a/irc_user.c b/irc_user.c index fa509a45..cb7ea1e7 100644 --- a/irc_user.c +++ b/irc_user.c @@ -204,7 +204,7 @@ void irc_user_quit( irc_user_t *iu, const char *msg ) return; for( l = iu->irc->channels; l; l = l->next ) - send_quit |= irc_channel_del_user( (irc_channel_t*) l->data, iu, TRUE, NULL ); + send_quit |= irc_channel_del_user( (irc_channel_t*) l->data, iu, IRC_CDU_SILENT, NULL ); if( send_quit ) irc_send_quit( iu, msg ); -- cgit v1.2.3 From 69b896b5967e5d13b1c60c68cb3bc7d4a0d5cd06 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 5 Jul 2010 13:01:28 +0100 Subject: When addressing people in a chatroom, try to translate the nickname to the original unstripped version (without ugly underscores, also). --- bitlbee.c | 3 ++- doc/user-guide/commands.xml | 14 ++++++++++++++ irc_im.c | 27 ++++++++++++++++++++++++++- protocols/bee.h | 1 + protocols/bee_user.c | 1 + protocols/nogaim.c | 3 +++ 6 files changed, 47 insertions(+), 2 deletions(-) diff --git a/bitlbee.c b/bitlbee.c index d0d95e67..5e93f733 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -369,7 +369,8 @@ 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 ); + irc_abort( irc_connection_list->data, FALSE, + "BitlBee server shutting down" ); /* We'll only reach this point when not running in inetd mode: */ b_main_quit(); diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 889b6165..4154fb27 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -1092,6 +1092,20 @@ + + true + + + + IRC's nickname namespace is quite limited compared to most IM protocols. Not any non-ASCII characters are allowed, in fact nicknames have to be mostly alpha-numeric. Also, BitlBee has to add underscores sometimes to avoid nickname collisions. + + + + While normally the BitlBee user is the only one seeing these names, they may be exposed to other chatroom participants for example when addressing someone in the channel (with or without tab completion). By default BitlBee will translate these stripped nicknames back to the original nick. If you don't want this, disable this setting. + + + + control control, chat diff --git a/irc_im.c b/irc_im.c index c900e7ff..d7906cd5 100644 --- a/irc_im.c +++ b/irc_im.c @@ -602,10 +602,31 @@ static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg ) { struct groupchat *c = ic->data; + char *trans = NULL, *s; if( c == NULL ) return FALSE; - else if( set_getbool( &ic->irc->b->set, "paste_buffer" ) ) + + if( set_getbool( &ic->set, "translate_to_nicks" ) ) + { + char nick[MAX_NICK_LENGTH+1]; + irc_user_t *iu; + + strncpy( nick, msg, MAX_NICK_LENGTH ); + nick[MAX_NICK_LENGTH] = '\0'; + if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) ) + { + *s = '\0'; + if( ( iu = irc_user_by_name( ic->irc, nick ) ) && + iu->bu->nick && irc_channel_has_user( ic, iu ) ) + { + trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL ); + msg = trans; + } + } + } + + if( set_getbool( &ic->irc->b->set, "paste_buffer" ) ) { int delay; @@ -622,11 +643,13 @@ static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic ); + g_free( trans ); return TRUE; } else bee_chat_msg( ic->irc->b, c, msg, 0 ); + g_free( trans ); return TRUE; } @@ -746,6 +769,7 @@ static gboolean bee_irc_channel_init( irc_channel_t *ic ) set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic ); set_add( &ic->set, "nick", NULL, NULL, ic ); set_add( &ic->set, "room", NULL, NULL, ic ); + set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic ); /* chat_type == groupchat */ ic->flags |= IRC_CHANNEL_TEMP; @@ -789,6 +813,7 @@ static gboolean bee_irc_channel_free( irc_channel_t *ic ) set_del( &ic->set, "chat_type" ); set_del( &ic->set, "nick" ); set_del( &ic->set, "room" ); + set_del( &ic->set, "translate_to_nicks" ); ic->flags &= ~IRC_CHANNEL_TEMP; diff --git a/protocols/bee.h b/protocols/bee.h index e82913d6..4b6a1f4a 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -61,6 +61,7 @@ typedef struct bee_user struct im_connection *ic; char *handle; char *fullname; + char *nick; struct bee_group *group; bee_user_flags_t flags; diff --git a/protocols/bee_user.c b/protocols/bee_user.c index faa2acb7..4399a566 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -59,6 +59,7 @@ int bee_user_free( bee_t *bee, bee_user_t *bu ) g_free( bu->handle ); g_free( bu->fullname ); + g_free( bu->nick ); g_free( bu->status ); g_free( bu->status_msg ); g_free( bu ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 0998291b..c23b0a3a 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -427,6 +427,9 @@ void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const c if( !bu || !nick ) return; + g_free( bu->nick ); + bu->nick = g_strdup( nick ); + if( bee->ui->user_nick_hint ) bee->ui->user_nick_hint( bee, bu, nick ); } -- cgit v1.2.3 From 6c2404e051cb6a235f985797c149af0791f44bbd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Jul 2010 22:44:52 +0100 Subject: First part of the handshake, including sending a file descriptor to the IPC master. --- bitlbee.c | 2 + bitlbee.h | 1 + ipc.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++----- ipc.h | 6 +++ irc.h | 1 + root_commands.c | 25 ++++++++++-- 6 files changed, 140 insertions(+), 14 deletions(-) diff --git a/bitlbee.c b/bitlbee.c index d0d95e67..78f4b4e5 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -317,10 +317,12 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition { struct bitlbee_child *child; + /* TODO: Stuff like this belongs in ipc.c. */ 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->to_fd = -1; child_list = g_slist_append( child_list, child ); log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid ); diff --git a/bitlbee.h b/bitlbee.h index 5610d95b..9b35810f 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -162,6 +162,7 @@ gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_co void root_command_string( irc_t *irc, char *command ); void root_command( irc_t *irc, char *command[] ); +gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond ); gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond ); char *set_eval_root_nick( set_t *set, char *new_nick ); diff --git a/ipc.c b/ipc.c index 81c5b8b4..cdf6539d 100644 --- a/ipc.c +++ b/ipc.c @@ -111,6 +111,31 @@ void ipc_master_cmd_restart( irc_t *data, char **cmd ) bitlbee_shutdown( NULL, -1, 0 ); } +void ipc_master_cmd_identify( irc_t *data, char **cmd ) +{ + struct bitlbee_child *child = (void*) data, *old = NULL; + GSList *l; + + if( strcmp( child->nick, cmd[1] ) != 0 ) + return; + + g_free( child->password ); + child->password = g_strdup( cmd[2] ); + + for( l = child_list; l; l = l->next ) + { + old = l->data; + if( nick_cmp( old->nick, child->nick ) == 0 && child != old && + old->password && strcmp( old->password, child->password ) ) + break; + } + + if( old == NULL ) + return; + + child->to_child = old; +} + static const command_t ipc_master_commands[] = { { "client", 3, ipc_master_cmd_client, 0 }, { "hello", 0, ipc_master_cmd_client, 0 }, @@ -122,6 +147,7 @@ static const command_t ipc_master_commands[] = { { "rehash", 0, ipc_master_cmd_rehash, 0 }, { "kill", 2, NULL, IPC_CMD_TO_CHILDREN }, { "restart", 0, ipc_master_cmd_restart, 0 }, + { "identify", 2, ipc_master_cmd_identify, 0 }, { NULL } }; @@ -201,6 +227,22 @@ static const command_t ipc_child_commands[] = { { NULL } }; +static gboolean ipc_send_fd( int fd, int send_fd ); + +gboolean ipc_child_identify( irc_t *irc ) +{ + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + { + if( !ipc_send_fd( global.listen_socket, irc->fd ) ) + ipc_child_disable(); + + ipc_to_master_str( "IDENTIFY %s :%s\r\n", irc->user->nick, irc->password ); + + return TRUE; + } + else + return FALSE; +} static void ipc_command_exec( void *data, char **cmd, const command_t *commands ) { @@ -229,8 +271,12 @@ static void ipc_command_exec( void *data, char **cmd, const command_t *commands /* Return just one line. Returns NULL if something broke, an empty string on temporary "errors" (EAGAIN and friends). */ -static char *ipc_readline( int fd ) +static char *ipc_readline( int fd, int *recv_fd ) { + struct msghdr msg; + struct iovec iov; + char ccmsg[CMSG_SPACE(sizeof(recv_fd))]; + struct cmsghdr *cmsg; char buf[513], *eol; int size; @@ -252,22 +298,44 @@ static char *ipc_readline( int fd ) else size = eol - buf + 2; - if( recv( fd, buf, size, 0 ) != size ) + iov.iov_base = buf; + iov.iov_len = size; + + memset( &msg, 0, sizeof( msg ) ); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof( ccmsg ); + + if( recvmsg( fd, &msg, 0 ) != size ) return NULL; - else - return g_strndup( buf, size - 2 ); + + if( recv_fd ) + for( cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) ) + if( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS ) + { + /* Getting more than one shouldn't happen but if it does, + make sure we don't leave them around. */ + if( *recv_fd != -1 ) + close( *recv_fd ); + + *recv_fd = *(int*) CMSG_DATA( cmsg ); + } + + return g_strndup( buf, size - 2 ); } gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond ) { + struct bitlbee_child *child = data; char *buf, **cmd; - if( ( buf = ipc_readline( source ) ) ) + if( ( buf = ipc_readline( source, &child->to_fd ) ) ) { cmd = irc_parse_line( buf ); if( cmd ) { - ipc_command_exec( data, cmd, ipc_master_commands ); + ipc_command_exec( child, cmd, ipc_master_commands ); g_free( cmd ); } g_free( buf ); @@ -283,8 +351,9 @@ gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond ) gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond ) { char *buf, **cmd; + int recv_fd = -1; - if( ( buf = ipc_readline( source ) ) ) + if( ( buf = ipc_readline( source, &recv_fd ) ) ) { cmd = irc_parse_line( buf ); if( cmd ) @@ -412,14 +481,43 @@ void ipc_to_children_str( char *format, ... ) g_free( msg_buf ); } +static gboolean ipc_send_fd( int fd, int send_fd ) +{ + struct msghdr msg; + struct iovec iov; + char ccmsg[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + + memset( &msg, 0, sizeof( msg ) ); + iov.iov_base = "0x90\r\n"; + iov.iov_len = 6; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof( ccmsg ); + cmsg = CMSG_FIRSTHDR( &msg ); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN( sizeof( send_fd ) ); + *(int*)CMSG_DATA( cmsg ) = send_fd; + msg.msg_controllen = cmsg->cmsg_len; + + return sendmsg( fd, &msg, 0 ) == 6; +} + void ipc_master_free_one( struct bitlbee_child *c ) { b_event_remove( c->ipc_inpa ); closesocket( c->ipc_fd ); + if( c->to_fd != -1 ) + close( c->to_fd ); + g_free( c->host ); g_free( c->nick ); g_free( c->realname ); + g_free( c->password ); g_free( c ); } @@ -505,8 +603,8 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio { struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 ); + child->to_fd = -1; child->ipc_fd = accept( serversock, NULL, 0 ); - if( child->ipc_fd == -1 ) { log_message( LOGLVL_WARNING, "Unable to accept connection on UNIX domain socket: %s", strerror(errno) ); @@ -515,7 +613,7 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio 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 ); + child_list = g_slist_prepend( child_list, child ); return TRUE; } @@ -597,8 +695,9 @@ int ipc_master_load_state( char *statefile ) return 0; } child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child ); + child->to_fd = -1; - child_list = g_slist_append( child_list, child ); + child_list = g_slist_prepend( child_list, child ); } ipc_to_children_str( "HELLO\r\n" ); diff --git a/ipc.h b/ipc.h index 0e71c520..bf5f0454 100644 --- a/ipc.h +++ b/ipc.h @@ -36,6 +36,12 @@ struct bitlbee_child char *host; char *nick; char *realname; + + char *password; + + /* For takeovers: */ + struct bitlbee_child *to_child; + int to_fd; }; diff --git a/irc.h b/irc.h index 94738832..3b5e244f 100644 --- a/irc.h +++ b/irc.h @@ -86,6 +86,7 @@ typedef struct irc gint r_watch_source_id; gint w_watch_source_id; gint ping_source_id; + gint login_source_id; /* To slightly delay some events at login time. */ struct bee *b; } irc_t; diff --git a/root_commands.c b/root_commands.c index 62fe1e79..5d84c6d4 100644 --- a/root_commands.c +++ b/root_commands.c @@ -104,7 +104,6 @@ static void cmd_account( irc_t *irc, char **cmd ); static void cmd_identify( irc_t *irc, char **cmd ) { storage_status_t status; - char *account_on[] = { "account", "on", NULL }; gboolean load = TRUE; char *password = cmd[1]; @@ -157,8 +156,16 @@ static void cmd_identify( irc_t *irc, char **cmd ) irc->status |= USTATUS_IDENTIFIED; irc_umode_set( irc, "+R", 1 ); irc_channel_auto_joins( irc, NULL ); - if( load && set_getbool( &irc->b->set, "auto_connect" ) ) - cmd_account( irc, account_on ); + + if( ipc_child_identify( irc ) ) + { + if( load && set_getbool( &irc->b->set, "auto_connect" ) ) + irc->login_source_id = b_timeout_add( 200, + cmd_identify_finish, irc ); + } + else if( load && set_getbool( &irc->b->set, "auto_connect" ) ) + cmd_identify_finish( irc, 0, 0 ); + break; case STORAGE_OTHER_ERROR: default: @@ -167,6 +174,16 @@ static void cmd_identify( irc_t *irc, char **cmd ) } } +gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond ) +{ + char *account_on[] = { "account", "on", NULL }; + irc_t *irc = data; + + cmd_account( irc, account_on ); + + return FALSE; +} + static void cmd_register( irc_t *irc, char **cmd ) { if( global.conf->authmode == AUTHMODE_REGISTERED ) @@ -671,7 +688,7 @@ static void cmd_rename( irc_t *irc, char **cmd ) } else if( iu == irc->user ) { - irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] ); + irc_usermsg( irc, "Use /nick to change your own nickname" ); } else if( !nick_ok( cmd[2] ) ) { -- cgit v1.2.3 From 0b09da0dbbdf294799bd5335b327c2601f263b01 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Jul 2010 00:10:17 +0100 Subject: It works! Fragile like hell though, and without any confirmation or whatever. --- ipc.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 8 deletions(-) diff --git a/ipc.c b/ipc.c index cdf6539d..5bc22463 100644 --- a/ipc.c +++ b/ipc.c @@ -32,6 +32,7 @@ #endif GSList *child_list = NULL; +static int ipc_child_recv_fd = -1; static void ipc_master_cmd_client( irc_t *data, char **cmd ) { @@ -114,6 +115,7 @@ void ipc_master_cmd_restart( irc_t *data, char **cmd ) void ipc_master_cmd_identify( irc_t *data, char **cmd ) { struct bitlbee_child *child = (void*) data, *old = NULL; + char *resp; GSList *l; if( strcmp( child->nick, cmd[1] ) != 0 ) @@ -126,14 +128,61 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) { old = l->data; if( nick_cmp( old->nick, child->nick ) == 0 && child != old && - old->password && strcmp( old->password, child->password ) ) + old->password && strcmp( old->password, child->password ) == 0 ) break; } - if( old == NULL ) - return; - child->to_child = old; + + if( l ) + { + resp = "TAKEOVER INIT\r\n"; + } + else + { + /* Won't need the fd since we can't send it anywhere. */ + close( child->to_fd ); + child->to_fd = -1; + resp = "TAKEOVER NO\r\n"; + } + + if( write( child->ipc_fd, resp, strlen( resp ) ) != strlen( resp ) ) + { + ipc_master_free_one( child ); + child_list = g_slist_remove( child_list, child ); + } +} + +static gboolean ipc_send_fd( int fd, int send_fd ); + +void ipc_master_cmd_takeover( irc_t *data, char **cmd ) +{ + struct bitlbee_child *child = (void*) data; + + /* TODO: Check if child->to_child is still valid, etc. */ + if( strcmp( cmd[1], "AUTH" ) == 0 ) + { + if( child->to_child && + child->nick && child->to_child->nick && cmd[2] && + child->password && child->to_child->password && cmd[3] && + strcmp( child->nick, child->to_child->nick ) == 0 && + strcmp( child->nick, cmd[2] ) == 0 && + strcmp( child->password, child->to_child->password ) == 0 && + strcmp( child->password, cmd[3] ) == 0 ) + { + char *s; + + ipc_send_fd( child->to_child->ipc_fd, child->to_fd ); + + s = irc_build_line( cmd ); + if( write( child->to_child->ipc_fd, s, strlen( s ) ) != strlen( s ) ) + { + ipc_master_free_one( child ); + child_list = g_slist_remove( child_list, child ); + } + g_free( s ); + } + } } static const command_t ipc_master_commands[] = { @@ -148,6 +197,7 @@ static const command_t ipc_master_commands[] = { { "kill", 2, NULL, IPC_CMD_TO_CHILDREN }, { "restart", 0, ipc_master_cmd_restart, 0 }, { "identify", 2, ipc_master_cmd_identify, 0 }, + { "takeover", 1, ipc_master_cmd_takeover, 0 }, { NULL } }; @@ -216,6 +266,46 @@ static void ipc_child_cmd_hello( irc_t *irc, char **cmd ) ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); } +static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) +{ + if( strcmp( cmd[1], "NO" ) == 0 ) + { + /* No takeover, finish the login. */ + } + else if( strcmp( cmd[1], "INIT" ) == 0 ) + { + ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n", + irc->user->nick, irc->password ); + + /* Drop credentials, we'll shut down soon and shouldn't overwrite + any settings. */ + /* TODO: irc_setpass() should do all of this. */ + irc_usermsg( irc, "Trying to take over existing session" ); + /** NOT YET + irc_setpass( irc, NULL ); + irc->status &= ~USTATUS_IDENTIFIED; + irc_umode_set( irc, "-R", 1 ); + */ + } + else if( strcmp( cmd[1], "AUTH" ) == 0 ) + { + if( irc->password && cmd[2] && cmd[3] && + ipc_child_recv_fd != -1 && + strcmp( irc->user->nick, cmd[2] ) == 0 && + strcmp( irc->password, cmd[3] ) == 0 ) + { + fprintf( stderr, "TO\n" ); + b_event_remove( irc->r_watch_source_id ); + closesocket( irc->fd ); + irc->fd = ipc_child_recv_fd; + irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); + ipc_child_recv_fd = -1; + } + fprintf( stderr, "%s %s %s\n", irc->password, cmd[2], cmd[3] ); + fprintf( stderr, "%d %s %s\n", ipc_child_recv_fd, irc->user->nick, irc->password ); + } +} + static const command_t ipc_child_commands[] = { { "die", 0, ipc_child_cmd_die, 0 }, { "wallops", 1, ipc_child_cmd_wallops, 0 }, @@ -224,11 +314,10 @@ static const command_t ipc_child_commands[] = { { "rehash", 0, ipc_child_cmd_rehash, 0 }, { "kill", 2, ipc_child_cmd_kill, 0 }, { "hello", 0, ipc_child_cmd_hello, 0 }, + { "takeover", 1, ipc_child_cmd_takeover, 0 }, { NULL } }; -static gboolean ipc_send_fd( int fd, int send_fd ); - gboolean ipc_child_identify( irc_t *irc ) { if( global.conf->runmode == RUNMODE_FORKDAEMON ) @@ -320,8 +409,10 @@ static char *ipc_readline( int fd, int *recv_fd ) close( *recv_fd ); *recv_fd = *(int*) CMSG_DATA( cmsg ); + fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd ); } + fprintf( stderr, "pid %d received: %s", (int) getpid(), buf ); return g_strndup( buf, size - 2 ); } @@ -351,9 +442,8 @@ gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond ) gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond ) { char *buf, **cmd; - int recv_fd = -1; - if( ( buf = ipc_readline( source, &recv_fd ) ) ) + if( ( buf = ipc_readline( source, &ipc_child_recv_fd ) ) ) { cmd = irc_parse_line( buf ); if( cmd ) -- cgit v1.2.3 From 9a9b520df6044cfc034f9736fb97660a46e879b9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Jul 2010 23:31:01 +0100 Subject: Allow nick changes if they're only different in capitalisation, fixed faulty responses in the NICK command, and fixing crash bug in nick changes before finishing login. --- irc_commands.c | 11 +++++++---- irc_user.c | 4 +++- root_commands.c | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index 197a7e6e..0573601d 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -71,16 +71,18 @@ static void irc_cmd_user( irc_t *irc, char **cmd ) static void irc_cmd_nick( irc_t *irc, char **cmd ) { - if( irc_user_by_name( irc, cmd[1] ) ) + irc_user_t *iu; + + if( ( iu = irc_user_by_name( irc, cmd[1] ) ) && iu != irc->user ) { - irc_send_num( irc, 433, ":This nick is already in use" ); + irc_send_num( irc, 433, "%s :This nick is already in use", cmd[1] ); } else if( !nick_ok( cmd[1] ) ) { /* [SH] Invalid characters. */ - irc_send_num( irc, 432, ":This nick contains invalid characters" ); + irc_send_num( irc, 432, "%s :This nick contains invalid characters", cmd[1] ); } - else if( irc->user->nick ) + else if( irc->status & USTATUS_LOGGED_IN ) { if( irc->status & USTATUS_IDENTIFIED ) { @@ -97,6 +99,7 @@ static void irc_cmd_nick( irc_t *irc, char **cmd ) } else { + g_free( irc->user->nick ); irc->user->nick = g_strdup( cmd[1] ); irc_check_login( irc ); diff --git a/irc_user.c b/irc_user.c index cb7ea1e7..7d29cae0 100644 --- a/irc_user.c +++ b/irc_user.c @@ -119,11 +119,13 @@ irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ) int irc_user_set_nick( irc_user_t *iu, const char *new ) { irc_t *irc = iu->irc; + irc_user_t *new_iu; char key[strlen(new)+1]; GSList *cl; strcpy( key, new ); - if( iu == NULL || !nick_lc( key ) || irc_user_by_name( irc, new ) ) + if( iu == NULL || !nick_lc( key ) || + ( ( new_iu = irc_user_by_name( irc, new ) ) && new_iu != iu ) ) return 0; for( cl = irc->channels; cl; cl = cl->next ) diff --git a/root_commands.c b/root_commands.c index 62fe1e79..5c5ead7b 100644 --- a/root_commands.c +++ b/root_commands.c @@ -661,7 +661,7 @@ static void cmd_info( irc_t *irc, char **cmd ) static void cmd_rename( irc_t *irc, char **cmd ) { - irc_user_t *iu; + irc_user_t *iu, *old; iu = irc_user_by_name( irc, cmd[1] ); @@ -677,7 +677,7 @@ static void cmd_rename( irc_t *irc, char **cmd ) { irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] ); } - else if( irc_user_by_name( irc, cmd[2] ) ) + else if( ( old = irc_user_by_name( irc, cmd[2] ) ) && old != iu ) { irc_usermsg( irc, "Nick `%s' already exists", cmd[2] ); } -- cgit v1.2.3 From f545372483cef6a2414bc5bf21260579151a627e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Jul 2010 00:10:43 +0100 Subject: Ask for confirmation. Generally working fairly well now, but definitely fragile. --- ipc.c | 121 +++++++++++++++++++++++++++++++++++++++++++++----------- root_commands.c | 1 + 2 files changed, 100 insertions(+), 22 deletions(-) diff --git a/ipc.c b/ipc.c index 5bc22463..6903c898 100644 --- a/ipc.c +++ b/ipc.c @@ -132,11 +132,11 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) break; } - child->to_child = old; - - if( l ) + if( l && !child->to_child && !old->to_child ) { resp = "TAKEOVER INIT\r\n"; + child->to_child = old; + old->to_child = child; } else { @@ -158,6 +158,7 @@ static gboolean ipc_send_fd( int fd, int send_fd ); void ipc_master_cmd_takeover( irc_t *data, char **cmd ) { struct bitlbee_child *child = (void*) data; + char *fwd = NULL; /* TODO: Check if child->to_child is still valid, etc. */ if( strcmp( cmd[1], "AUTH" ) == 0 ) @@ -170,19 +171,37 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) strcmp( child->password, child->to_child->password ) == 0 && strcmp( child->password, cmd[3] ) == 0 ) { - char *s; - ipc_send_fd( child->to_child->ipc_fd, child->to_fd ); - s = irc_build_line( cmd ); - if( write( child->to_child->ipc_fd, s, strlen( s ) ) != strlen( s ) ) + fwd = irc_build_line( cmd ); + if( write( child->to_child->ipc_fd, fwd, strlen( fwd ) ) != strlen( fwd ) ) { ipc_master_free_one( child ); child_list = g_slist_remove( child_list, child ); } - g_free( s ); + g_free( fwd ); } } + else if( strcmp( cmd[1], "DONE" ) == 0 || strcmp( cmd[1], "FAIL" ) == 0 ) + { + int fd; + + /* The copy was successful (or not), we don't need it anymore. */ + close( child->to_fd ); + child->to_fd = -1; + + /* Pass it through to the other party, and flush all state. */ + fwd = irc_build_line( cmd ); + fd = child->to_child->ipc_fd; + child->to_child->to_child = NULL; + child->to_child = NULL; + if( write( fd, fwd, strlen( fwd ) ) != strlen( fwd ) ) + { + ipc_master_free_one( child ); + child_list = g_slist_remove( child_list, child ); + } + g_free( fwd ); + } } static const command_t ipc_master_commands[] = { @@ -266,46 +285,104 @@ static void ipc_child_cmd_hello( irc_t *irc, char **cmd ) ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); } +static void ipc_child_cmd_takeover_yes( void *data ); +static void ipc_child_cmd_takeover_no( void *data ); + static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) { if( strcmp( cmd[1], "NO" ) == 0 ) { + /* Master->New connection */ /* No takeover, finish the login. */ } else if( strcmp( cmd[1], "INIT" ) == 0 ) { - ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n", - irc->user->nick, irc->password ); + /* Master->New connection */ + /* Offer to take over the old session, unless for some reason + we're already logging into IM connections. */ + if( irc->login_source_id != -1 ) + query_add( irc, NULL, + "You're already connected to this server. " + "Would you like to take over this session?", + ipc_child_cmd_takeover_yes, + ipc_child_cmd_takeover_no, irc ); - /* Drop credentials, we'll shut down soon and shouldn't overwrite - any settings. */ - /* TODO: irc_setpass() should do all of this. */ - irc_usermsg( irc, "Trying to take over existing session" ); - /** NOT YET - irc_setpass( irc, NULL ); - irc->status &= ~USTATUS_IDENTIFIED; - irc_umode_set( irc, "-R", 1 ); - */ + /* This one's going to connect to accounts, avoid that. */ + b_event_remove( irc->login_source_id ); + irc->login_source_id = -1; } else if( strcmp( cmd[1], "AUTH" ) == 0 ) { + /* Master->Old connection */ if( irc->password && cmd[2] && cmd[3] && ipc_child_recv_fd != -1 && strcmp( irc->user->nick, cmd[2] ) == 0 && strcmp( irc->password, cmd[3] ) == 0 ) { - fprintf( stderr, "TO\n" ); + GSList *l; + b_event_remove( irc->r_watch_source_id ); closesocket( irc->fd ); irc->fd = ipc_child_recv_fd; irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); ipc_child_recv_fd = -1; + + for( l = irc->channels; l; l = l->next ) + { + irc_channel_t *ic = l->data; + if( ic->flags & IRC_CHANNEL_JOINED ) + irc_send_join( ic, irc->user ); + } + irc_usermsg( irc, "You've successfully taken over your old session" ); + + ipc_to_master_str( "TAKEOVER DONE\r\n" ); } - fprintf( stderr, "%s %s %s\n", irc->password, cmd[2], cmd[3] ); - fprintf( stderr, "%d %s %s\n", ipc_child_recv_fd, irc->user->nick, irc->password ); + else + { + ipc_to_master_str( "TAKEOVER FAIL\r\n" ); + } + } + else if( strcmp( cmd[1], "DONE" ) == 0 ) + { + /* Master->New connection (now taken over by old process) */ + irc_free( irc ); + } + else if( strcmp( cmd[1], "FAIL" ) == 0 ) + { + /* Master->New connection */ + irc_usermsg( irc, "Could not take over old session" ); } } +static void ipc_child_cmd_takeover_yes( void *data ) +{ + irc_t *irc = data; + GSList *l; + + /* Master->New connection */ + ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n", + irc->user->nick, irc->password ); + + /* Drop credentials, we'll shut down soon and shouldn't overwrite + any settings. */ + irc_usermsg( irc, "Trying to take over existing session" ); + + /* TODO: irc_setpass() should do all of this. */ + irc_setpass( irc, NULL ); + irc->status &= ~USTATUS_IDENTIFIED; + irc_umode_set( irc, "-R", 1 ); + + for( l = irc->channels; l; l = l->next ) + irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK, + "Switching to old session" ); +} + +static void ipc_child_cmd_takeover_no( void *data ) +{ + ipc_to_master_str( "TAKEOVER NO\r\n" ); + cmd_identify_finish( data, 0, 0 ); +} + static const command_t ipc_child_commands[] = { { "die", 0, ipc_child_cmd_die, 0 }, { "wallops", 1, ipc_child_cmd_wallops, 0 }, diff --git a/root_commands.c b/root_commands.c index 5d84c6d4..daaebb95 100644 --- a/root_commands.c +++ b/root_commands.c @@ -181,6 +181,7 @@ gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond ) cmd_account( irc, account_on ); + irc->login_source_id = -1; return FALSE; } -- cgit v1.2.3 From 9595d2b9420afc1044c0d5ee93311b8c4ee6dec2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Jul 2010 09:25:25 +0100 Subject: Also sync umodes. --- ipc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ipc.c b/ipc.c index 6903c898..aad05bbf 100644 --- a/ipc.c +++ b/ipc.c @@ -321,12 +321,18 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) { GSList *l; + /* TODO: Move this all into irc_switch_fd() or so and + irc_sync() */ b_event_remove( irc->r_watch_source_id ); closesocket( irc->fd ); irc->fd = ipc_child_recv_fd; irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); ipc_child_recv_fd = -1; + irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick, + irc->user->user, irc->user->host, irc->user->nick, + irc->umode ); + for( l = irc->channels; l; l = l->next ) { irc_channel_t *ic = l->data; @@ -375,6 +381,10 @@ static void ipc_child_cmd_takeover_yes( void *data ) for( l = irc->channels; l; l = l->next ) irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK, "Switching to old session" ); + + irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick, + irc->user->user, irc->user->host, irc->user->nick, + irc->umode ); } static void ipc_child_cmd_takeover_no( void *data ) -- cgit v1.2.3 From debe871bbd96a1d4215983265d97b8f0df7a4fbf Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Jul 2010 22:06:38 +0100 Subject: Inform IPC master about nick changes. --- ipc.c | 13 +++++++++++++ irc_user.c | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/ipc.c b/ipc.c index aad05bbf..3c98c41e 100644 --- a/ipc.c +++ b/ipc.c @@ -49,11 +49,23 @@ static void ipc_master_cmd_client( irc_t *data, char **cmd ) child->realname = g_strdup( cmd[3] ); } + /* CLIENT == On initial connects, HELLO is after /RESTARTs. */ if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 ) ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n", (int) ( child ? child->pid : -1 ), cmd[2], cmd[1], cmd[3] ); } +static void ipc_master_cmd_nick( irc_t *data, char **cmd ) +{ + struct bitlbee_child *child = (void*) data; + + if( child && cmd[1] ) + { + g_free( child->nick ); + child->nick = g_strdup( cmd[1] ); + } +} + static void ipc_master_cmd_die( irc_t *data, char **cmd ) { if( global.conf->runmode == RUNMODE_FORKDAEMON ) @@ -207,6 +219,7 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) static const command_t ipc_master_commands[] = { { "client", 3, ipc_master_cmd_client, 0 }, { "hello", 0, ipc_master_cmd_client, 0 }, + { "nick", 1, ipc_master_cmd_nick, 0 }, { "die", 0, ipc_master_cmd_die, 0 }, { "deaf", 0, ipc_master_cmd_deaf, 0 }, { "wallops", 1, NULL, IPC_CMD_TO_CHILDREN }, diff --git a/irc_user.c b/irc_user.c index cb7ea1e7..70f324c7 100644 --- a/irc_user.c +++ b/irc_user.c @@ -24,6 +24,7 @@ */ #include "bitlbee.h" +#include "ipc.h" irc_user_t *irc_user_new( irc_t *irc, const char *nick ) { @@ -157,6 +158,9 @@ int irc_user_set_nick( irc_user_t *iu, const char *new ) g_hash_table_insert( irc->nick_user_hash, iu->key, iu ); irc->users = g_slist_insert_sorted( irc->users, iu, irc_user_cmp ); + if( iu == irc->user ) + ipc_to_master_str( "NICK :%s\r\n", new ); + return 1; } -- cgit v1.2.3 From c5bff810e6919dc3daf7f82f761197a27f04538b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Jul 2010 00:24:23 +0100 Subject: More state consistency checks/error handling. --- ipc.c | 79 +++++++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/ipc.c b/ipc.c index 3c98c41e..b4b31da1 100644 --- a/ipc.c +++ b/ipc.c @@ -34,6 +34,9 @@ GSList *child_list = NULL; static int ipc_child_recv_fd = -1; +static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both ); +static gboolean ipc_send_fd( int fd, int send_fd ); + static void ipc_master_cmd_client( irc_t *data, char **cmd ) { /* Normally data points at an irc_t block, but for the IPC master @@ -153,28 +156,28 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) else { /* Won't need the fd since we can't send it anywhere. */ - close( child->to_fd ); + closesocket( child->to_fd ); child->to_fd = -1; resp = "TAKEOVER NO\r\n"; } if( write( child->ipc_fd, resp, strlen( resp ) ) != strlen( resp ) ) - { ipc_master_free_one( child ); - child_list = g_slist_remove( child_list, child ); - } } -static gboolean ipc_send_fd( int fd, int send_fd ); void ipc_master_cmd_takeover( irc_t *data, char **cmd ) { struct bitlbee_child *child = (void*) data; char *fwd = NULL; - /* TODO: Check if child->to_child is still valid, etc. */ + if( child->to_child == NULL || + g_slist_find( child_list, child->to_child ) == NULL ) + return ipc_master_takeover_fail( child, FALSE ); + if( strcmp( cmd[1], "AUTH" ) == 0 ) { + /* New connection -> Master */ if( child->to_child && child->nick && child->to_child->nick && cmd[2] && child->password && child->to_child->password && cmd[3] && @@ -187,19 +190,19 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) fwd = irc_build_line( cmd ); if( write( child->to_child->ipc_fd, fwd, strlen( fwd ) ) != strlen( fwd ) ) - { ipc_master_free_one( child ); - child_list = g_slist_remove( child_list, child ); - } g_free( fwd ); } + else + return ipc_master_takeover_fail( child, TRUE ); } else if( strcmp( cmd[1], "DONE" ) == 0 || strcmp( cmd[1], "FAIL" ) == 0 ) { + /* Old connection -> Master */ int fd; /* The copy was successful (or not), we don't need it anymore. */ - close( child->to_fd ); + closesocket( child->to_fd ); child->to_fd = -1; /* Pass it through to the other party, and flush all state. */ @@ -208,10 +211,7 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) child->to_child->to_child = NULL; child->to_child = NULL; if( write( fd, fwd, strlen( fwd ) ) != strlen( fwd ) ) - { ipc_master_free_one( child ); - child_list = g_slist_remove( child_list, child ); - } g_free( fwd ); } } @@ -342,7 +342,7 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); ipc_child_recv_fd = -1; - irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick, + irc_write( irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick, irc->user->user, irc->user->host, irc->user->nick, irc->umode ); @@ -433,6 +433,29 @@ gboolean ipc_child_identify( irc_t *irc ) return FALSE; } +static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both ) +{ + if( child == NULL || g_slist_find( child_list, child ) == NULL ) + return; + + if( both && child->to_child != NULL ) + ipc_master_takeover_fail( child->to_child, FALSE ); + + if( child->to_fd > -1 ) + { + /* Send this error only to the new connection, which can be + recognised by to_fd being set. */ + if( write( child->ipc_fd, "TAKEOVER FAIL\r\n", 15 ) != 15 ) + { + ipc_master_free_one( child ); + return; + } + close( child->to_fd ); + child->to_fd = -1; + } + child->to_child = NULL; +} + static void ipc_command_exec( void *data, char **cmd, const command_t *commands ) { int i, j; @@ -650,10 +673,7 @@ void ipc_to_children_str( char *format, ... ) next = l->next; if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 ) - { ipc_master_free_one( c ); - child_list = g_slist_remove( child_list, c ); - } } } else if( global.conf->runmode == RUNMODE_DAEMON ) @@ -679,7 +699,7 @@ static gboolean ipc_send_fd( int fd, int send_fd ) struct cmsghdr *cmsg; memset( &msg, 0, sizeof( msg ) ); - iov.iov_base = "0x90\r\n"; + iov.iov_base = "0x90\r\n"; /* Ja, noppes */ iov.iov_len = 6; msg.msg_iov = &iov; msg.msg_iovlen = 1; @@ -698,6 +718,8 @@ static gboolean ipc_send_fd( int fd, int send_fd ) void ipc_master_free_one( struct bitlbee_child *c ) { + GSList *l; + b_event_remove( c->ipc_inpa ); closesocket( c->ipc_fd ); @@ -709,6 +731,17 @@ void ipc_master_free_one( struct bitlbee_child *c ) g_free( c->realname ); g_free( c->password ); g_free( c ); + + child_list = g_slist_remove( child_list, c ); + + /* Also, if any child has a reference to this one, remove it. */ + for( l = child_list; l; l = l->next ) + { + struct bitlbee_child *oc = l->data; + + if( oc->to_child == c ) + ipc_master_takeover_fail( oc, FALSE ); + } } void ipc_master_free_fd( int fd ) @@ -722,7 +755,6 @@ void ipc_master_free_fd( int fd ) if( c->ipc_fd == fd ) { ipc_master_free_one( c ); - child_list = g_slist_remove( child_list, c ); break; } } @@ -730,13 +762,8 @@ void ipc_master_free_fd( int fd ) void ipc_master_free_all() { - GSList *l; - - for( l = child_list; l; l = l->next ) - ipc_master_free_one( l->data ); - - g_slist_free( child_list ); - child_list = NULL; + while( child_list ) + ipc_master_free_one( child_list->data ); } void ipc_child_disable() -- cgit v1.2.3 From 534e4069e0cfa8aee82f82603dc3ca05562a85dd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Jul 2010 01:30:39 +0100 Subject: Translate "nick:" to "@nick" in Twitter rooms to make tab completion easier. Not working yet with stripped/mangled nicknames. --- protocols/twitter/twitter.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 2e3ab634..f3fe8922 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -315,8 +315,28 @@ static void twitter_remove_buddy( struct im_connection *ic, char *who, char *gro static void twitter_chat_msg( struct groupchat *c, char *message, int flags ) { - if( c && message && twitter_length_check(c->ic, message)) - twitter_post_status(c->ic, message); + if( c && message && twitter_length_check( c->ic, message ) ) + { + char *s, *new = NULL; + + if( ( s = strchr( message, ':' ) ) || + ( s = strchr( message, ',' ) ) ) + { + bee_user_t *bu; + + new = g_strdup( message ); + new[s-message] = '\0'; + if( ( bu = bee_user_by_handle( c->ic->bee, c->ic, new ) ) ) + { + sprintf( new, "@%s", bu->handle ); + new[s-message+1] = ' '; + message = new; + } + } + + twitter_post_status( c->ic, message ); + g_free( new ); + } } static void twitter_chat_invite( struct groupchat *c, char *who, char *message ) -- cgit v1.2.3 From f1c2b21af3a51796b747ca4dbc2cdec9611efc5d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Jul 2010 16:08:02 +0100 Subject: Cleanup. Move some code to a more appropriate location, and show the old connection a quit message instead of just breaking the connection. --- ipc.c | 32 +++-------------------- ipc.h | 2 ++ irc.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ irc.h | 5 ++++ root_commands.c | 3 +-- 5 files changed, 91 insertions(+), 30 deletions(-) diff --git a/ipc.c b/ipc.c index b4b31da1..a92d5a0f 100644 --- a/ipc.c +++ b/ipc.c @@ -332,27 +332,10 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) strcmp( irc->user->nick, cmd[2] ) == 0 && strcmp( irc->password, cmd[3] ) == 0 ) { - GSList *l; - - /* TODO: Move this all into irc_switch_fd() or so and - irc_sync() */ - b_event_remove( irc->r_watch_source_id ); - closesocket( irc->fd ); - irc->fd = ipc_child_recv_fd; - irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); - ipc_child_recv_fd = -1; - - irc_write( irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick, - irc->user->user, irc->user->host, irc->user->nick, - irc->umode ); - - for( l = irc->channels; l; l = l->next ) - { - irc_channel_t *ic = l->data; - if( ic->flags & IRC_CHANNEL_JOINED ) - irc_send_join( ic, irc->user ); - } + irc_switch_fd( irc, ipc_child_recv_fd ); + irc_sync( irc ); irc_usermsg( irc, "You've successfully taken over your old session" ); + ipc_child_recv_fd = -1; ipc_to_master_str( "TAKEOVER DONE\r\n" ); } @@ -376,7 +359,6 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) static void ipc_child_cmd_takeover_yes( void *data ) { irc_t *irc = data; - GSList *l; /* Master->New connection */ ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n", @@ -391,13 +373,7 @@ static void ipc_child_cmd_takeover_yes( void *data ) irc->status &= ~USTATUS_IDENTIFIED; irc_umode_set( irc, "-R", 1 ); - for( l = irc->channels; l; l = l->next ) - irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK, - "Switching to old session" ); - - irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick, - irc->user->user, irc->user->host, irc->user->nick, - irc->umode ); + irc_desync( irc ); } static void ipc_child_cmd_takeover_no( void *data ) diff --git a/ipc.h b/ipc.h index bf5f0454..068996a7 100644 --- a/ipc.h +++ b/ipc.h @@ -54,6 +54,8 @@ void ipc_master_free_all(); void ipc_child_disable(); +gboolean ipc_child_identify( irc_t *irc ); + void ipc_to_master( char **cmd ); void ipc_to_master_str( char *format, ... ) G_GNUC_PRINTF( 1, 2 ); void ipc_to_children( char **cmd ); diff --git a/irc.c b/irc.c index cb1d0867..f616ef40 100644 --- a/irc.c +++ b/irc.c @@ -618,6 +618,85 @@ void irc_vawrite( irc_t *irc, char *format, va_list params ) return; } +/* Flush sendbuffer if you can. If it fails, fail silently and let some + I/O event handler clean up. */ +void irc_flush( irc_t *irc ) +{ + ssize_t n; + size_t len; + + if( irc->sendbuffer == NULL ) + return; + + len = strlen( irc->sendbuffer ); + if( ( n = send( irc->fd, irc->sendbuffer, len, 0 ) ) == len ) + { + g_free( irc->sendbuffer ); + irc->sendbuffer = NULL; + + b_event_remove( irc->w_watch_source_id ); + irc->w_watch_source_id = 0; + } + else if( n > 0 ) + { + char *s = g_strdup( irc->sendbuffer + n ); + g_free( irc->sendbuffer ); + irc->sendbuffer = s; + } + /* Otherwise something went wrong and we don't currently care + what the error was. We may or may not succeed later, we + were just trying to flush the buffer immediately. */ +} + +/* Meant for takeover functionality. Transfer an IRC connection to a different + socket. */ +void irc_switch_fd( irc_t *irc, int fd ) +{ + irc_write( irc, "ERROR :Transferring session to a new connection" ); + irc_flush( irc ); /* Write it now or forget about it forever. */ + + if( irc->sendbuffer ) + { + b_event_remove( irc->w_watch_source_id ); + g_free( irc->sendbuffer ); + irc->sendbuffer = irc->w_watch_source_id = 0; + } + + b_event_remove( irc->r_watch_source_id ); + closesocket( irc->fd ); + irc->fd = fd; + irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); +} + +void irc_sync( irc_t *irc ) +{ + GSList *l; + + irc_write( irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick, + irc->user->user, irc->user->host, irc->user->nick, + irc->umode ); + + for( l = irc->channels; l; l = l->next ) + { + irc_channel_t *ic = l->data; + if( ic->flags & IRC_CHANNEL_JOINED ) + irc_send_join( ic, irc->user ); + } +} + +void irc_desync( irc_t *irc ) +{ + GSList *l; + + for( l = irc->channels; l; l = l->next ) + irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK, + "Switching to old session" ); + + irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick, + irc->user->user, irc->user->host, irc->user->nick, + irc->umode ); +} + int irc_check_login( irc_t *irc ) { if( irc->user->user && irc->user->nick ) diff --git a/irc.h b/irc.h index 3b5e244f..1d5bd113 100644 --- a/irc.h +++ b/irc.h @@ -229,6 +229,11 @@ void irc_write( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); void irc_write_all( int now, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); void irc_vawrite( irc_t *irc, char *format, va_list params ); +void irc_flush( irc_t *irc ); +void irc_switch_fd( irc_t *irc, int fd ); +void irc_sync( irc_t *irc ); +void irc_desync( irc_t *irc ); + int irc_check_login( irc_t *irc ); void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv ); diff --git a/root_commands.c b/root_commands.c index cf448e8d..6fa4fd54 100644 --- a/root_commands.c +++ b/root_commands.c @@ -27,8 +27,7 @@ #include "commands.h" #include "bitlbee.h" #include "help.h" - -#include +#include "ipc.h" void root_command_string( irc_t *irc, char *command ) { -- cgit v1.2.3 From e92c4f4f63ca0ba9ac6f959f7ff894ad2fc72a04 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Jul 2010 01:29:19 +0100 Subject: Takeover stuff now works in daemon mode as well. --- ipc.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- root_commands.c | 23 ++++++++++++++---- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/ipc.c b/ipc.c index a92d5a0f..32e58cb7 100644 --- a/ipc.c +++ b/ipc.c @@ -171,6 +171,11 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) struct bitlbee_child *child = (void*) data; char *fwd = NULL; + /* Normal daemon mode doesn't keep these and has simplified code for + takeovers. */ + if( child == NULL ) + return; + if( child->to_child == NULL || g_slist_find( child_list, child->to_child ) == NULL ) return ipc_master_takeover_fail( child, FALSE ); @@ -358,22 +363,58 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) static void ipc_child_cmd_takeover_yes( void *data ) { - irc_t *irc = data; + irc_t *irc = data, *old = NULL; + char *to_auth[] = { "TAKEOVER", "AUTH", irc->user->nick, irc->password, NULL }; /* Master->New connection */ ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n", irc->user->nick, irc->password ); + if( global.conf->runmode == RUNMODE_DAEMON ) + { + GSList *l; + + for( l = irc_connection_list; l; l = l->next ) + { + old = l->data; + + if( irc != old && + irc->user->nick && old->user->nick && + irc->password && old->password && + strcmp( irc->user->nick, old->user->nick ) == 0 && + strcmp( irc->password, old->password ) == 0 ) + break; + } + if( l == NULL ) + { + to_auth[1] = "FAIL"; + ipc_child_cmd_takeover( irc, to_auth ); + return; + } + } + /* Drop credentials, we'll shut down soon and shouldn't overwrite any settings. */ irc_usermsg( irc, "Trying to take over existing session" ); + irc_desync( irc ); + + if( old ) + { + ipc_child_recv_fd = dup( irc->fd ); + ipc_child_cmd_takeover( old, to_auth ); + } + /* TODO: irc_setpass() should do all of this. */ irc_setpass( irc, NULL ); irc->status &= ~USTATUS_IDENTIFIED; irc_umode_set( irc, "-R", 1 ); - irc_desync( irc ); + if( old ) + { + irc->status |= USTATUS_SHUTDOWN; + irc_abort( irc, FALSE, NULL ); + } } static void ipc_child_cmd_takeover_no( void *data ) @@ -405,6 +446,30 @@ gboolean ipc_child_identify( irc_t *irc ) return TRUE; } + else if( global.conf->runmode == RUNMODE_DAEMON ) + { + GSList *l; + irc_t *old; + char *to_init[] = { "TAKEOVER", "INIT", NULL }; + + for( l = irc_connection_list; l; l = l->next ) + { + old = l->data; + + if( irc != old && + irc->user->nick && old->user->nick && + irc->password && old->password && + strcmp( irc->user->nick, old->user->nick ) == 0 && + strcmp( irc->password, old->password ) == 0 ) + break; + } + if( l == NULL ) + return FALSE; + + ipc_child_cmd_takeover( irc, to_init ); + + return TRUE; + } else return FALSE; } @@ -508,10 +573,14 @@ static char *ipc_readline( int fd, int *recv_fd ) close( *recv_fd ); *recv_fd = *(int*) CMSG_DATA( cmsg ); + /* fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd ); + */ } + /* fprintf( stderr, "pid %d received: %s", (int) getpid(), buf ); + */ return g_strndup( buf, size - 2 ); } diff --git a/root_commands.c b/root_commands.c index 6fa4fd54..4aeb1470 100644 --- a/root_commands.c +++ b/root_commands.c @@ -154,16 +154,31 @@ static void cmd_identify( irc_t *irc, char **cmd ) irc_setpass( irc, password ); irc->status |= USTATUS_IDENTIFIED; irc_umode_set( irc, "+R", 1 ); - irc_channel_auto_joins( irc, NULL ); - if( ipc_child_identify( irc ) ) + /* The following code is a bit hairy now. With takeover + support, we shouldn't immediately auto_connect in case + we're going to offer taking over an existing session. + Do it in 200ms since that should give the parent process + enough time to come back to us. */ + if( load ) { - if( load && set_getbool( &irc->b->set, "auto_connect" ) ) + irc_channel_auto_joins( irc, NULL ); + if( set_getbool( &irc->b->set, "auto_connect" ) ) irc->login_source_id = b_timeout_add( 200, cmd_identify_finish, irc ); } - else if( load && set_getbool( &irc->b->set, "auto_connect" ) ) + + /* If ipc_child_identify() returns FALSE, it means we're + already sure that there's no takeover target (only + possible in 1-process daemon mode). Start auto_connect + immediately. */ + if( !ipc_child_identify( irc ) && load && + set_getbool( &irc->b->set, "auto_connect" ) ) + { + b_event_remove( irc->login_source_id ); + irc->login_source_id = -1; cmd_identify_finish( irc, 0, 0 ); + } break; case STORAGE_OTHER_ERROR: -- cgit v1.2.3 From 1e52e1ff518987092cfe94bc5c9c4479ed535019 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Jul 2010 11:30:27 +0100 Subject: When cleaning up queries, q->data is free()d. Even if it turns out to be the "struct irc" containing all data belonging to a session. Sanitise memory management a little bit here. (There are some memory leaks in here too that need to be fixed at some point.) --- ipc.c | 2 +- protocols/nogaim.c | 6 +++--- protocols/purple/purple.c | 2 +- query.c | 7 +++++-- query.h | 5 +++-- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ipc.c b/ipc.c index 32e58cb7..9a100465 100644 --- a/ipc.c +++ b/ipc.c @@ -323,7 +323,7 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) "You're already connected to this server. " "Would you like to take over this session?", ipc_child_cmd_takeover_yes, - ipc_child_cmd_takeover_no, irc ); + ipc_child_cmd_takeover_no, NULL, irc ); /* This one's going to connect to accounts, avoid that. */ b_event_remove( irc->login_source_id ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index c23b0a3a..7380c575 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -379,7 +379,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont ) { - query_add( (irc_t *) ic->bee->ui_data, ic, msg, doit, dont, data ); + query_add( (irc_t *) ic->bee->ui_data, ic, msg, doit, dont, g_free, data ); } void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group ) @@ -477,7 +477,7 @@ void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *re data->ic = ic; data->handle = g_strdup( handle ); query_add( (irc_t *) ic->bee->ui_data, ic, s, - imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data ); + imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, g_free, data ); } @@ -510,7 +510,7 @@ void imcb_ask_add( struct im_connection *ic, const char *handle, const char *rea data->ic = ic; data->handle = g_strdup( handle ); query_add( (irc_t *) ic->bee->ui_data, ic, s, - imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data ); + imcb_ask_add_cb_yes, imcb_ask_add_cb_no, g_free, data ); } struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *handle ) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 2804fdf3..b8d74ba1 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -876,7 +876,7 @@ static void *prplcb_request_action( const char *title, const char *primary, cons /* TODO: IRC stuff here :-( */ q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary ); pqad->bee_data = query_add( local_bee->ui_data, purple_ic_by_pa( account ), q, - prplcb_request_action_yes, prplcb_request_action_no, pqad ); + prplcb_request_action_yes, prplcb_request_action_no, g_free, pqad ); g_free( q ); diff --git a/query.c b/query.c index b60669f7..67382b79 100644 --- a/query.c +++ b/query.c @@ -30,7 +30,8 @@ static void query_display( irc_t *irc, query_t *q ); static query_t *query_default( irc_t *irc ); query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, - query_callback yes, query_callback no, void *data ) + query_callback yes, query_callback no, query_callback free, + void *data ) { query_t *q = g_new0( query_t, 1 ); @@ -38,6 +39,7 @@ query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, q->question = g_strdup( question ); q->yes = yes; q->no = no; + q->free = free; q->data = data; if( strchr( irc->umode, 'b' ) != NULL ) @@ -93,7 +95,8 @@ void query_del( irc_t *irc, query_t *q ) } g_free( q->question ); - if( q->data ) g_free( q->data ); /* Memory leak... */ + if( q->free && q->data ) + q->free( q->data ); g_free( q ); } diff --git a/query.h b/query.h index e0ca32ec..f0419573 100644 --- a/query.h +++ b/query.h @@ -32,13 +32,14 @@ typedef struct query { struct im_connection *ic; char *question; - query_callback yes, no; + query_callback yes, no, free; void *data; struct query *next; } query_t; query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, - query_callback yes, query_callback no, void *data ); + query_callback yes, query_callback no, query_callback free, + void *data ); void query_del( irc_t *irc, query_t *q ); void query_del_by_conn( irc_t *irc, struct im_connection *ic ); void query_answer( irc_t *irc, query_t *q, int ans ); -- cgit v1.2.3 From af9f2ca883354a47635d130ff5e7bd693a200a29 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Jul 2010 11:59:56 +0100 Subject: Added allow_takeover setting for people who don't like this new functionality. --- doc/user-guide/commands.xml | 10 ++++++++++ ipc.c | 13 +++++++++++-- irc.c | 4 +++- root_commands.c | 5 +---- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 4154fb27..be61e4db 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -420,6 +420,16 @@ + + true + + + + When you're already connected to a BitlBee server and you connect (and identify) again, BitlBee will offer to migrate your existing session to the new connection. If for whatever reason you don't want this, you can disable this setting. + + + + true diff --git a/ipc.c b/ipc.c index 9a100465..26fe7b30 100644 --- a/ipc.c +++ b/ipc.c @@ -316,6 +316,12 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) else if( strcmp( cmd[1], "INIT" ) == 0 ) { /* Master->New connection */ + if( !set_getbool( &irc->b->set, "allow_takeover" ) ) + { + ipc_child_cmd_takeover_no( irc ); + return; + } + /* Offer to take over the old session, unless for some reason we're already logging into IM connections. */ if( irc->login_source_id != -1 ) @@ -335,7 +341,8 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) if( irc->password && cmd[2] && cmd[3] && ipc_child_recv_fd != -1 && strcmp( irc->user->nick, cmd[2] ) == 0 && - strcmp( irc->password, cmd[3] ) == 0 ) + strcmp( irc->password, cmd[3] ) == 0 && + set_getbool( &irc->b->set, "allow_takeover" ) ) { irc_switch_fd( irc, ipc_child_recv_fd ); irc_sync( irc ); @@ -463,7 +470,9 @@ gboolean ipc_child_identify( irc_t *irc ) strcmp( irc->password, old->password ) == 0 ) break; } - if( l == NULL ) + if( l == NULL || + !set_getbool( &irc->b->set, "allow_takeover" ) || + !set_getbool( &old->b->set, "allow_takeover" ) ) return FALSE; ipc_child_cmd_takeover( irc, to_init ); diff --git a/irc.c b/irc.c index f616ef40..10fbea23 100644 --- a/irc.c +++ b/irc.c @@ -99,6 +99,7 @@ irc_t *irc_new( int fd ) b->ui_data = irc; b->ui = &irc_ui_funcs; + s = set_add( &b->set, "allow_takeover", "true", set_eval_bool, irc ); s = set_add( &b->set, "away_devoice", "true", set_eval_away_devoice, irc ); s = set_add( &b->set, "away_reply_timeout", "3600", set_eval_int, irc ); s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); @@ -658,8 +659,9 @@ void irc_switch_fd( irc_t *irc, int fd ) if( irc->sendbuffer ) { b_event_remove( irc->w_watch_source_id ); + irc->w_watch_source_id = 0; g_free( irc->sendbuffer ); - irc->sendbuffer = irc->w_watch_source_id = 0; + irc->sendbuffer = NULL; } b_event_remove( irc->r_watch_source_id ); diff --git a/root_commands.c b/root_commands.c index 4aeb1470..d19feae0 100644 --- a/root_commands.c +++ b/root_commands.c @@ -174,11 +174,7 @@ static void cmd_identify( irc_t *irc, char **cmd ) immediately. */ if( !ipc_child_identify( irc ) && load && set_getbool( &irc->b->set, "auto_connect" ) ) - { - b_event_remove( irc->login_source_id ); - irc->login_source_id = -1; cmd_identify_finish( irc, 0, 0 ); - } break; case STORAGE_OTHER_ERROR: @@ -195,6 +191,7 @@ gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond ) cmd_account( irc, account_on ); + b_event_remove( irc->login_source_id ); irc->login_source_id = -1; return FALSE; } -- cgit v1.2.3 From 133cdffff000275c3968b38e5e4cdde02dc400d3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Jul 2010 13:11:27 +0100 Subject: More careful pointer checking in the master. --- ipc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ipc.c b/ipc.c index 26fe7b30..aff6f994 100644 --- a/ipc.c +++ b/ipc.c @@ -133,7 +133,7 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) char *resp; GSList *l; - if( strcmp( child->nick, cmd[1] ) != 0 ) + if( !child || !child->nick || strcmp( child->nick, cmd[1] ) != 0 ) return; g_free( child->password ); @@ -142,7 +142,8 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) for( l = child_list; l; l = l->next ) { old = l->data; - if( nick_cmp( old->nick, child->nick ) == 0 && child != old && + if( child != old && + old->nick && nick_cmp( old->nick, child->nick ) == 0 && old->password && strcmp( old->password, child->password ) == 0 ) break; } -- cgit v1.2.3 From b1f818bcbd50eccd416127ed68346616295f54cd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Jul 2010 13:21:59 +0100 Subject: Use bee_user structs in all nick_* functions. Prepare for a nick_get() with more flexible nickname generation. --- irc_im.c | 6 +++--- nick.c | 44 +++++++++++++++++++++++++++----------------- nick.h | 12 +++++++----- root_commands.c | 6 +++--- storage_xml.c | 2 +- 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/irc_im.c b/irc_im.c index d7906cd5..48d2ada4 100644 --- a/irc_im.c +++ b/irc_im.c @@ -49,7 +49,7 @@ static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) char nick[MAX_NICK_LENGTH+1], *s; memset( nick, 0, MAX_NICK_LENGTH + 1 ); - strcpy( nick, nick_get( bu->ic->acc, bu->handle ) ); + strcpy( nick, nick_get( bu ) ); bu->ui_data = iu = irc_user_new( irc, nick ); iu->bu = bu; @@ -293,7 +293,7 @@ static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char * /* Ignore if the user is visible already. */ return TRUE; - if( nick_saved( bu->ic->acc, bu->handle ) ) + if( nick_saved( bu ) ) /* The user already assigned a nickname to this person. */ return TRUE; @@ -318,7 +318,7 @@ static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char * /* Only do this if newnick is different from the current one. If rejoining a channel, maybe we got this nick already (and dedupe would only add an underscore. */ - nick_dedupe( bu->ic->acc, bu->handle, newnick ); + nick_dedupe( bu, newnick ); irc_user_set_nick( iu, newnick ); } diff --git a/nick.c b/nick.c index 7188df14..b96c0b80 100644 --- a/nick.c +++ b/nick.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2007 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* Some stuff to fetch, save and handle nicknames for your buddies */ @@ -41,29 +41,34 @@ static char *clean_handle( const char *orig ) return new; } -void nick_set( account_t *acc, const char *handle, const char *nick ) +void nick_set_raw( account_t *acc, const char *handle, const char *nick ) { char *store_handle, *store_nick = g_malloc( MAX_NICK_LENGTH + 1 ); store_handle = clean_handle( handle ); - store_nick[MAX_NICK_LENGTH] = 0; + store_nick[MAX_NICK_LENGTH] = '\0'; strncpy( store_nick, nick, MAX_NICK_LENGTH ); nick_strip( store_nick ); g_hash_table_replace( acc->nicks, store_handle, store_nick ); } -char *nick_get( account_t *acc, const char *handle ) +void nick_set( bee_user_t *bu, const char *nick ) +{ + nick_set_raw( bu->ic->acc, bu->handle, nick ); +} + +char *nick_get( bee_user_t *bu ) { static char nick[MAX_NICK_LENGTH+1]; char *store_handle, *found_nick; memset( nick, 0, MAX_NICK_LENGTH + 1 ); - store_handle = clean_handle( handle ); + store_handle = clean_handle( bu->handle ); /* Find out if we stored a nick for this person already. If not, try to generate a sane nick automatically. */ - if( ( found_nick = g_hash_table_lookup( acc->nicks, store_handle ) ) ) + if( ( found_nick = g_hash_table_lookup( bu->ic->acc->nicks, store_handle ) ) ) { strncpy( nick, found_nick, MAX_NICK_LENGTH ); } @@ -71,27 +76,32 @@ char *nick_get( account_t *acc, const char *handle ) { char *s; - g_snprintf( nick, MAX_NICK_LENGTH, "%s", handle ); + g_snprintf( nick, MAX_NICK_LENGTH, "%s", bu->handle ); if( ( s = strchr( nick, '@' ) ) ) while( *s ) *(s++) = 0; nick_strip( nick ); - if( set_getbool( &acc->bee->set, "lcnicks" ) ) + if( set_getbool( &bu->bee->set, "lcnicks" ) ) nick_lc( nick ); } g_free( store_handle ); /* Make sure the nick doesn't collide with an existing one by adding underscores and that kind of stuff, if necessary. */ - nick_dedupe( acc, handle, nick ); + nick_dedupe( bu, nick ); return nick; } -void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+1] ) +char *nick_gen( bee_user_t *bu ) { - irc_t *irc = (irc_t*) acc->bee->ui_data; + return NULL; +} + +void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ) +{ + irc_t *irc = (irc_t*) bu->bee->ui_data; int inf_protection = 256; /* Now, find out if the nick is already in use at the moment, and make @@ -118,7 +128,7 @@ void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+ "If it does, please *do* send us a bug report! " "Please send all the following lines in your report:" ); - irc_usermsg( irc, "Trying to get a sane nick for handle %s", handle ); + irc_usermsg( irc, "Trying to get a sane nick for handle %s", bu->handle ); for( i = 0; i < MAX_NICK_LENGTH; i ++ ) irc_usermsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] ); @@ -135,20 +145,20 @@ void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+ /* Just check if there is a nickname set for this buddy or if we'd have to generate one. */ -int nick_saved( account_t *acc, const char *handle ) +int nick_saved( bee_user_t *bu ) { char *store_handle, *found; - store_handle = clean_handle( handle ); - found = g_hash_table_lookup( acc->nicks, store_handle ); + store_handle = clean_handle( bu->handle ); + found = g_hash_table_lookup( bu->ic->acc->nicks, store_handle ); g_free( store_handle ); return found != NULL; } -void nick_del( account_t *acc, const char *handle ) +void nick_del( bee_user_t *bu ) { - g_hash_table_remove( acc->nicks, handle ); + g_hash_table_remove( bu->ic->acc->nicks, bu->handle ); } diff --git a/nick.h b/nick.h index 31298275..e1b506e1 100644 --- a/nick.h +++ b/nick.h @@ -23,11 +23,13 @@ Suite 330, Boston, MA 02111-1307 USA */ -void nick_set( account_t *acc, const char *handle, const char *nick ); -char *nick_get( account_t *acc, const char *handle ); -void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+1] ); -int nick_saved( account_t *acc, const char *handle ); -void nick_del( account_t *acc, const char *handle ); +void nick_set_raw( account_t *acc, const char *handle, const char *nick ); +void nick_set( bee_user_t *bu, const char *nick ); +char *nick_get( bee_user_t *bu ); +char *nick_gen( bee_user_t *bu ); +void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ); +int nick_saved( bee_user_t *bu ); +void nick_del( bee_user_t *bu ); void nick_strip( char *nick ); int nick_ok( const char *nick ); diff --git a/root_commands.c b/root_commands.c index d19feae0..f4bb4b82 100644 --- a/root_commands.c +++ b/root_commands.c @@ -614,7 +614,7 @@ static void cmd_add( irc_t *irc, char **cmd ) } else { - nick_set( a, cmd[2], cmd[3] ); + nick_set_raw( a, cmd[2], cmd[3] ); } } @@ -642,7 +642,7 @@ static void cmd_remove( irc_t *irc, char **cmd ) s = g_strdup( bu->handle ); bu->ic->acc->prpl->remove_buddy( bu->ic, bu->handle, NULL ); - nick_del( bu->ic->acc, bu->handle ); + nick_del( bu ); //TODO(wilmer): bee_user_free() and/or let the IM mod do it? irc_user_free( irc, cmd[1] ); irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] ); @@ -727,7 +727,7 @@ static void cmd_rename( irc_t *irc, char **cmd ) } else if( iu->bu ) { - nick_set( iu->bu->ic->acc, iu->bu->handle, cmd[2] ); + nick_set( iu->bu, cmd[2] ); } irc_usermsg( irc, "Nick successfully changed" ); diff --git a/storage_xml.c b/storage_xml.c index a60769bb..7a10cea7 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -196,7 +196,7 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na if( xd->current_account && handle && nick ) { - nick_set( xd->current_account, handle, nick ); + nick_set_raw( xd->current_account, handle, nick ); } else { -- cgit v1.2.3 From 2e0eaac657b09f0698bd5779142fad5fe9db9eb6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Jul 2010 00:14:49 +0100 Subject: First version of the nick_format setting. --- irc.c | 1 + nick.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++----- protocols/account.c | 2 ++ 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/irc.c b/irc.c index 10fbea23..e2486c10 100644 --- a/irc.c +++ b/irc.c @@ -108,6 +108,7 @@ irc_t *irc_new( int fd ) s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc ); s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc ); s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); + s = set_add( &b->set, "nick_format", "%-@handle", NULL, irc ); s = set_add( &b->set, "offline_user_quits", "true", set_eval_bool, irc ); s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc ); s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc ); diff --git a/nick.c b/nick.c index b96c0b80..bc81d50e 100644 --- a/nick.c +++ b/nick.c @@ -26,6 +26,12 @@ #define BITLBEE_CORE #include "bitlbee.h" +/* Character maps, _lc_[x] == _uc_[x] (but uppercase), according to the RFC's. + With one difference, we allow dashes. These are used to do uc/lc conversions + and strip invalid chars. */ +static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^`-_|"; +static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~`-_\\"; + /* Store handles in lower case and strip spaces, because AIM is braindead. */ static char *clean_handle( const char *orig ) { @@ -72,8 +78,14 @@ char *nick_get( bee_user_t *bu ) { strncpy( nick, found_nick, MAX_NICK_LENGTH ); } + else if( ( found_nick = nick_gen( bu ) ) ) + { + strncpy( nick, found_nick, MAX_NICK_LENGTH ); + g_free( found_nick ); + } else { + /* Keep this fallback since nick_gen() can return NULL in some cases. */ char *s; g_snprintf( nick, MAX_NICK_LENGTH, "%s", bu->handle ); @@ -96,7 +108,73 @@ char *nick_get( bee_user_t *bu ) char *nick_gen( bee_user_t *bu ) { - return NULL; + gboolean ok = FALSE; /* Set to true once the nick contains something unique. */ + GString *ret = g_string_new( "" ); + char *fmt = set_getstr( &bu->ic->acc->set, "nick_format" ) ? : + set_getstr( &bu->bee->set, "nick_format" ); + + while( fmt && *fmt && ret->len < MAX_NICK_LENGTH ) + { + char *part, chop = '\0'; + + if( *fmt != '%' ) + { + g_string_append_c( ret, *fmt ); + fmt ++; + continue; + } + + fmt ++; + while( *fmt ) + { + /* -char means chop off everything from char */ + if( *fmt == '-' ) + { + chop = fmt[1]; + if( chop == '\0' ) + return NULL; + fmt += 2; + } + else if( g_strncasecmp( fmt, "handle", 6 ) == 0 ) + { + part = bu->handle; + fmt += 6; + ok |= TRUE; + break; + } + else if( g_strncasecmp( fmt, "full_name", 9 ) == 0 ) + { + part = bu->fullname; + fmt += 9; + ok |= part && *part; + break; + } + else if( g_strncasecmp( fmt, "first_name", 10 ) == 0 ) + { + part = bu->fullname; + fmt += 10; + ok |= part && *part; + chop = ' '; + break; + } + else + { + return NULL; + } + } + + while( part && *part && *part != chop ) + { + if( strchr( nick_lc_chars, *part ) || + strchr( nick_uc_chars, *part ) ) + g_string_append_c( ret, *part ); + + part ++; + } + } + + /* This returns NULL if the nick is empty or otherwise not ok. */ + return g_string_free( ret, ret->len == 0 || !ok ); } void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ) @@ -162,12 +240,6 @@ void nick_del( bee_user_t *bu ) } -/* Character maps, _lc_[x] == _uc_[x] (but uppercase), according to the RFC's. - With one difference, we allow dashes. */ - -static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^`-_|"; -static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~`-_\\"; - void nick_strip( char *nick ) { int i, j; diff --git a/protocols/account.c b/protocols/account.c index ba309b38..40cce2b8 100644 --- a/protocols/account.c +++ b/protocols/account.c @@ -53,6 +53,8 @@ account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); + s = set_add( &a->set, "nick_format", NULL, NULL, a ); + s = set_add( &a->set, "nick_source", "handle", NULL, a ); s = set_add( &a->set, "password", NULL, set_eval_account, a ); -- cgit v1.2.3 From 09dfb686be1297e9ff0a9b434ef865b779a60bc3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Jul 2010 21:27:11 +0100 Subject: Added %nick and %group. They don't work yet since nick_gen() is only called once. --- irc.c | 2 +- nick.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/irc.c b/irc.c index e2486c10..2093fa5b 100644 --- a/irc.c +++ b/irc.c @@ -108,7 +108,7 @@ irc_t *irc_new( int fd ) s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc ); s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc ); s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); - s = set_add( &b->set, "nick_format", "%-@handle", NULL, irc ); + s = set_add( &b->set, "nick_format", "%-@nick", NULL, irc ); s = set_add( &b->set, "offline_user_quits", "true", set_eval_bool, irc ); s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc ); s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc ); diff --git a/nick.c b/nick.c index bc81d50e..c8e4916f 100644 --- a/nick.c +++ b/nick.c @@ -135,6 +135,13 @@ char *nick_gen( bee_user_t *bu ) return NULL; fmt += 2; } + else if( g_strncasecmp( fmt, "nick", 4 ) == 0 ) + { + part = bu->nick ? : bu->handle; + fmt += 4; + ok |= TRUE; + break; + } else if( g_strncasecmp( fmt, "handle", 6 ) == 0 ) { part = bu->handle; @@ -157,6 +164,12 @@ char *nick_gen( bee_user_t *bu ) chop = ' '; break; } + else if( g_strncasecmp( fmt, "group", 5 ) == 0 ) + { + part = bu->group ? bu->group->name : NULL; + fmt += 5; + break; + } else { return NULL; -- cgit v1.2.3 From badd1484469ab6280de977eb179db00820868c03 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Jul 2010 00:22:53 +0100 Subject: Reformat nicks whenever fullname/nick/group changes (but at least for now still only for offline users). --- irc_im.c | 70 ++++++++++++++++++----------------------------------- nick.c | 15 ++++++++++-- protocols/account.c | 1 + unix.c | 5 ++++ 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/irc_im.c b/irc_im.c index 48d2ada4..150b8607 100644 --- a/irc_im.c +++ b/irc_im.c @@ -241,7 +241,7 @@ static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags return TRUE; } -static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint ); +static gboolean bee_irc_user_nick_update( irc_user_t *iu ); static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) { @@ -264,30 +264,33 @@ static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL ); } - s = set_getstr( &bu->ic->acc->set, "nick_source" ); - if( strcmp( s, "handle" ) != 0 ) - { - char *name = g_strdup( bu->fullname ); - - if( strcmp( s, "first_name" ) == 0 ) - { - int i; - for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {} - name[i] = '\0'; - } - - bee_irc_user_nick_hint( bee, bu, name ); - - g_free( name ); - } + bee_irc_user_nick_update( iu ); return TRUE; } static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint ) { - irc_user_t *iu = bu->ui_data; - char newnick[MAX_NICK_LENGTH+1], *translit; + bee_irc_user_nick_update( (irc_user_t*) bu->ui_data ); + + return TRUE; +} + +static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu ) +{ + irc_user_t *iu = (irc_user_t *) bu->ui_data; + irc_t *irc = (irc_t *) bee->ui_data; + + bee_irc_channel_update( irc, NULL, iu ); + bee_irc_user_nick_update( iu ); + + return TRUE; +} + +static gboolean bee_irc_user_nick_update( irc_user_t *iu ) +{ + bee_user_t *bu = iu->bu; + char *newnick; if( bu->flags & BEE_USER_ONLINE ) /* Ignore if the user is visible already. */ @@ -297,27 +300,10 @@ static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char * /* The user already assigned a nickname to this person. */ return TRUE; - /* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT should - do lossy/approximate conversions, so letters with accents don't - just get stripped. Note that it depends on LC_CTYPE being set to - something other than C/POSIX. */ - translit = g_convert( hint, -1, "ASCII//TRANSLIT//IGNORE", "UTF-8", - NULL, NULL, NULL ); - - strncpy( newnick, translit ? : hint, MAX_NICK_LENGTH ); - newnick[MAX_NICK_LENGTH] = 0; - g_free( translit ); - - /* Some processing to make sure this string is a valid IRC nickname. */ - nick_strip( newnick ); - if( set_getbool( &bee->set, "lcnicks" ) ) - nick_lc( newnick ); + newnick = nick_get( bu ); if( strcmp( iu->nick, newnick ) != 0 ) { - /* Only do this if newnick is different from the current one. - If rejoining a channel, maybe we got this nick already - (and dedupe would only add an underscore. */ nick_dedupe( bu, newnick ); irc_user_set_nick( iu, newnick ); } @@ -325,16 +311,6 @@ static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char * return TRUE; } -static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu ) -{ - irc_user_t *iu = (irc_user_t *) bu->ui_data; - irc_t *irc = (irc_t *) bee->ui_data; - - bee_irc_channel_update( irc, NULL, iu ); - - return TRUE; -} - /* IRC->IM calls */ static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond ); diff --git a/nick.c b/nick.c index c8e4916f..0b3fcfbd 100644 --- a/nick.c +++ b/nick.c @@ -115,7 +115,7 @@ char *nick_gen( bee_user_t *bu ) while( fmt && *fmt && ret->len < MAX_NICK_LENGTH ) { - char *part, chop = '\0'; + char *part, chop = '\0', *asc = NULL; if( *fmt != '%' ) { @@ -176,6 +176,14 @@ char *nick_gen( bee_user_t *bu ) } } + /* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT + should do lossy/approximate conversions, so letters with + accents don't just get stripped. Note that it depends on + LC_CTYPE being set to something other than C/POSIX. */ + if( part ) + part = asc = g_convert( part, -1, "ASCII//TRANSLIT//IGNORE", + "UTF-8", NULL, NULL, NULL ); + while( part && *part && *part != chop ) { if( strchr( nick_lc_chars, *part ) || @@ -184,6 +192,7 @@ char *nick_gen( bee_user_t *bu ) part ++; } + g_free( asc ); } /* This returns NULL if the nick is empty or otherwise not ok. */ @@ -194,10 +203,12 @@ void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ) { irc_t *irc = (irc_t*) bu->bee->ui_data; int inf_protection = 256; + irc_user_t *iu; /* Now, find out if the nick is already in use at the moment, and make subtle changes to make it unique. */ - while( !nick_ok( nick ) || irc_user_by_name( irc, nick ) ) + while( !nick_ok( nick ) || + ( ( iu = irc_user_by_name( irc, nick ) ) && iu->bu != bu ) ) { if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) { diff --git a/protocols/account.c b/protocols/account.c index 40cce2b8..2552b672 100644 --- a/protocols/account.c +++ b/protocols/account.c @@ -54,6 +54,7 @@ account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); s = set_add( &a->set, "nick_format", NULL, NULL, a ); + s->flags |= SET_NULL_OK; s = set_add( &a->set, "nick_source", "handle", NULL, a ); diff --git a/unix.c b/unix.c index f559705e..a9045c44 100644 --- a/unix.c +++ b/unix.c @@ -38,6 +38,7 @@ #include #include #include +#include global_t global; /* Against global namespace pollution */ @@ -51,6 +52,10 @@ int main( int argc, char *argv[] ) char *old_cwd = NULL; struct sigaction sig, old; + /* Required to make iconv to ASCII//TRANSLIT work. This makes BitlBee + system-locale-sensitive. :-( */ + setlocale( LC_CTYPE, "" ); + if( argc > 1 && strcmp( argv[1], "-x" ) == 0 ) return crypt_main( argc, argv ); -- cgit v1.2.3 From db2cef1ab11cc58cc92c16fd9bccd0fe17e413e4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Jul 2010 01:09:40 +0100 Subject: Document this complex new beast. --- doc/user-guide/commands.xml | 31 ++++++++++++++++++++++++++++++- doc/user-guide/misc.xml | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index be61e4db..d0ce485e 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -763,7 +763,6 @@ - You can use this option to set your nickname in a chatroom. You won't see this nickname yourself, but other people in the room will. By default, BitlBee will use your username as the chatroom nickname. @@ -771,6 +770,36 @@ + + %-@nick + + + + By default, BitlBee tries to derive sensible nicknames for all your contacts from their IM handles. In some cases, IM modules (ICQ for example) will provide a nickname suggestion, which will then be used instead. This setting lets you change this behaviour. + + + + Whenever this setting is set for an account, it will be used for all its contacts. If it's not set, the global value will be used. + + + + It's easier to describe this setting using a few examples: + + + + FB-%full_name will make all nicknames start with "FB-", followed by the person's full name. For example you can set this format for your Facebook account so all Facebook contacts are clearly marked. + + + + [%group]%-@nick will make all nicknames start with the group the contact is in between square brackets, followed by the nickname suggestions from the IM module if available, or otherwise the handle. Because of the "-@" part, everything from the first @ will be stripped. + + + + See help nick_format for more information. + + + + handle handle, full_name, first_name diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml index 175f0cd4..825d80ee 100644 --- a/doc/user-guide/misc.xml +++ b/doc/user-guide/misc.xml @@ -179,4 +179,38 @@ If you want to configure your own channels, you can use the channel se + +Nickname formatting + + +The nick_format setting can be set globally using the +set command, or per account using account +set (so that you can set a per-account suffix/prefix or have +nicknames generated from full names for certain accounts). + + + +The setting is basically some kind of format string. It can contain normal +text that will be copied to the nick, combined with several variables: + + + + %nickNickname suggested for this contact by the IM protocol, or just the handle if no nickname was suggested. + %handleThe handle/screenname of the contact. + %full_nameThe full name of the contact. + %first_nameThe first name of the contact (the full name up to the first space). + %groupThe name of the group this contact is a member of + + + +One modifier is currently available: %-@variable will remove all characters from the first @ in the string. + + + +In all cases, invalid characters (like spaces) will be stripped. Depending +on your locale settings, characters with accents will be converted to ASCII. + + + + -- cgit v1.2.3 From 06b39f28ca532b21c9ddb1ce36c17d3f7121c388 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Jul 2010 01:17:37 +0100 Subject: Automatically convert nick_source settings to their nick_convert equivalent. --- protocols/account.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/protocols/account.c b/protocols/account.c index 2552b672..cf9cbe71 100644 --- a/protocols/account.c +++ b/protocols/account.c @@ -27,6 +27,8 @@ #include "bitlbee.h" #include "account.h" +static char *set_eval_nick_source( set_t *set, char *value ); + account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) { account_t *a; @@ -56,7 +58,8 @@ account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) s = set_add( &a->set, "nick_format", NULL, NULL, a ); s->flags |= SET_NULL_OK; - s = set_add( &a->set, "nick_source", "handle", NULL, a ); + s = set_add( &a->set, "nick_source", "handle", set_eval_nick_source, a ); + s->flags |= ACC_SET_NOSAVE; /* Just for bw compatibility! */ s = set_add( &a->set, "password", NULL, set_eval_account, a ); s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; @@ -154,6 +157,21 @@ char *set_eval_account( set_t *set, char *value ) return SET_INVALID; } +/* For bw compatibility, have this write-only setting. */ +static char *set_eval_nick_source( set_t *set, char *value ) +{ + account_t *a = set->data; + + if( strcmp( value, "full_name" ) == 0 ) + set_setstr( &a->set, "nick_format", "%full_name" ); + else if( strcmp( value, "first_name" ) == 0 ) + set_setstr( &a->set, "nick_format", "%first_name" ); + else + set_setstr( &a->set, "nick_format", "%-@nick" ); + + return value; +} + account_t *account_get( bee_t *bee, char *id ) { account_t *a, *ret = NULL; -- cgit v1.2.3 From b1af3e8750e81869c8c1d2a12c9c27f6913aa58d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Jul 2010 23:50:06 +0100 Subject: Fixed cleaning up of channels. Something broke when changing the irc_channel_del_user() syntax. --- irc_commands.c | 2 +- irc_im.c | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/irc_commands.c b/irc_commands.c index 0573601d..d11d46d3 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -201,7 +201,7 @@ static void irc_cmd_part( irc_t *irc, char **cmd ) { irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); } - else if( irc_channel_del_user( ic, irc->user, FALSE, cmd[2] ) ) + else if( irc_channel_del_user( ic, irc->user, IRC_CDU_PART, cmd[2] ) ) { if( ic->f->part ) ic->f->part( ic, NULL ); diff --git a/irc_im.c b/irc_im.c index 150b8607..f467a666 100644 --- a/irc_im.c +++ b/irc_im.c @@ -450,6 +450,9 @@ static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c ) { irc_channel_t *ic = c->ui_data; + if( ic == NULL ) + return FALSE; + if( ic->flags & IRC_CHANNEL_JOINED ) irc_channel_printf( ic, "Cleaning up channel, bye!" ); @@ -463,6 +466,9 @@ static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *t { irc_channel_t *ic = c->ui_data; + if( ic == NULL ) + return FALSE; + irc_channel_printf( ic, "%s", text ); return TRUE; @@ -475,6 +481,9 @@ static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *b irc_channel_t *ic = c->ui_data; char *ts = NULL; + if( ic == NULL ) + return FALSE; + if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) ) ts = irc_format_timestamp( irc, sent_at ); @@ -487,8 +496,12 @@ static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *b static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) { irc_t *irc = bee->ui_data; + irc_channel_t *ic = c->ui_data; - irc_channel_add_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data ); + if( ic == NULL ) + return FALSE; + + irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data ); return TRUE; } @@ -496,20 +509,28 @@ static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu ) { irc_t *irc = bee->ui_data; + irc_channel_t *ic = c->ui_data; + + if( ic == NULL ) + return FALSE; /* TODO: Possible bug here: If a module removes $user here instead of just using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into a broken state around here. */ - irc_channel_del_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL ); + irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL ); return TRUE; } static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu ) { + irc_channel_t *ic = c->ui_data; irc_t *irc = bee->ui_data; irc_user_t *iu; + if( ic == NULL ) + return FALSE; + if( bu == NULL ) iu = irc->root; else if( bu == bee->user ) @@ -517,7 +538,7 @@ static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char else iu = bu->ui_data; - irc_channel_set_topic( c->ui_data, new, iu ); + irc_channel_set_topic( ic, new, iu ); return TRUE; } @@ -528,6 +549,9 @@ static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const c irc_channel_t *ic = c->ui_data, *oic; char stripped[MAX_NICK_LENGTH+1], *full_name; + if( ic == NULL ) + return FALSE; + /* Don't rename a channel if the user's in it already. */ if( ic->flags & IRC_CHANNEL_JOINED ) return FALSE; @@ -785,6 +809,8 @@ static char *set_eval_chat_type( set_t *set, char *value ) static gboolean bee_irc_channel_free( irc_channel_t *ic ) { + struct groupchat *c = ic->data; + set_del( &ic->set, "account" ); set_del( &ic->set, "chat_type" ); set_del( &ic->set, "nick" ); @@ -793,6 +819,10 @@ static gboolean bee_irc_channel_free( irc_channel_t *ic ) ic->flags &= ~IRC_CHANNEL_TEMP; + /* That one still points at this channel. Don't. */ + if( c ) + c->ui_data = NULL; + return TRUE; } -- cgit v1.2.3 From 324c378cd2d3f208b000e1279d462665d163edb7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Jul 2010 00:14:41 +0100 Subject: Move control channel autoconfiguration to irc_cmd_join() instead so that it only triggers on channels created by the user. (And not at identify time, which was causing odd problems on my test setup.) --- irc_channel.c | 18 ------------------ irc_commands.c | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/irc_channel.c b/irc_channel.c index 70770bfb..6f9de637 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -548,24 +548,6 @@ static gboolean control_channel_init( irc_channel_t *ic ) ic->data = icc = g_new0( struct irc_control_channel, 1 ); icc->type = IRC_CC_TYPE_DEFAULT; - if( bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) ) - { - set_setstr( &ic->set, "group", ic->name + 1 ); - set_setstr( &ic->set, "fill_by", "group" ); - } - else if( set_setstr( &ic->set, "protocol", ic->name + 1 ) ) - { - set_setstr( &ic->set, "fill_by", "protocol" ); - } - else if( set_setstr( &ic->set, "account", ic->name + 1 ) ) - { - set_setstr( &ic->set, "fill_by", "account" ); - } - else - { - bee_irc_channel_update( ic->irc, ic, NULL ); - } - return TRUE; } diff --git a/irc_commands.c b/irc_commands.c index d11d46d3..0bf20cfc 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -140,7 +140,31 @@ static void irc_cmd_join( irc_t *irc, char **cmd ) *comma = '\0'; if( ( ic = irc_channel_by_name( irc, s ) ) == NULL ) + { ic = irc_channel_new( irc, s ); + + if( strcmp( set_getstr( &ic->set, "type" ), "control" ) != 0 ) + { + /* Autoconfiguration is for control channels only ATM. */ + } + else if( bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) ) + { + set_setstr( &ic->set, "group", ic->name + 1 ); + set_setstr( &ic->set, "fill_by", "group" ); + } + else if( set_setstr( &ic->set, "protocol", ic->name + 1 ) ) + { + set_setstr( &ic->set, "fill_by", "protocol" ); + } + else if( set_setstr( &ic->set, "account", ic->name + 1 ) ) + { + set_setstr( &ic->set, "fill_by", "account" ); + } + else + { + bee_irc_channel_update( ic->irc, ic, NULL ); + } + } if( ic == NULL ) { -- cgit v1.2.3 From 83e2d3048639533acd1d56672dcb1ba76de65e36 Mon Sep 17 00:00:00 2001 From: Doug Luce Date: Wed, 14 Jul 2010 18:19:03 -0500 Subject: This makes Bitlbee compile on OpenBSD. --- ipc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ipc.h b/ipc.h index 068996a7..fc1c65b4 100644 --- a/ipc.h +++ b/ipc.h @@ -25,6 +25,7 @@ #define BITLBEE_CORE #include "bitlbee.h" +#include struct bitlbee_child -- cgit v1.2.3 From e43736627ab8fa174352ba6a0122a6dade08a8d7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 00:55:53 +0100 Subject: Fix possible crash on trying to use half-created OSCAR chatrooms. --- protocols/oscar/oscar.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 9a9f2999..11539852 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -2430,7 +2430,8 @@ void oscar_chat_msg(struct groupchat *c, char *message, int msgflags) guint16 flags; char *s; - ccon = c->data; + if (!(ccon = c->data)) + return; for (s = message; *s; s++) if (*s & 128) @@ -2471,7 +2472,10 @@ void oscar_chat_invite(struct groupchat *c, char *who, char *message) { struct im_connection *ic = c->ic; struct oscar_data * od = (struct oscar_data *)ic->proto_data; - struct chat_connection *ccon = c->data; + struct chat_connection *ccon; + + if (!(ccon = c->data)) + return; aim_chat_invite(od->sess, od->conn, who, message ? message : "", ccon->exchange, ccon->name, 0x0); @@ -2496,6 +2500,8 @@ void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc) void oscar_chat_leave(struct groupchat *c) { + if (!c->data) + return; oscar_chat_kill(c->ic, c->data); } @@ -2527,12 +2533,16 @@ struct groupchat *oscar_chat_with(struct im_connection * ic, char *who) struct oscar_data * od = (struct oscar_data *)ic->proto_data; struct groupchat *ret; static int chat_id = 0; - char * chatname; + char * chatname, *s; struct groupchat *c; chatname = g_strdup_printf("%s%s%d", isdigit(*ic->acc->user) ? "icq" : "", ic->acc->user, chat_id++); + for (s = chatname; *s; s ++) + if (!isalnum(*s)) + *s = '0'; + c = imcb_chat_new(ic, chatname); ret = oscar_chat_join(ic, chatname, NULL, NULL); aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0); -- cgit v1.2.3 From 177ffd7da1570485698f6c105374e86c4471c94a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 11:04:51 +0100 Subject: nick_gen() should also insert an underscore if the first character of a nick would otherwise be a digit. --- nick.c | 3 +++ protocols/purple/purple.c | 17 +++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/nick.c b/nick.c index 0b3fcfbd..d4c1891c 100644 --- a/nick.c +++ b/nick.c @@ -184,6 +184,9 @@ char *nick_gen( bee_user_t *bu ) part = asc = g_convert( part, -1, "ASCII//TRANSLIT//IGNORE", "UTF-8", NULL, NULL, NULL ); + if( ret->len == 0 && *part && isdigit( *part ) ) + g_string_append_c( ret, '_' ); + while( part && *part && *part != chop ) { if( strchr( nick_lc_chars, *part ) || diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b8d74ba1..2935609b 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -883,15 +883,20 @@ static void *prplcb_request_action( const char *title, const char *primary, cons return pqad; } +static void prplcb_request_test() +{ + fprintf( stderr, "bla\n" ); +} + static PurpleRequestUiOps bee_request_uiops = { - NULL, - NULL, + prplcb_request_test, + prplcb_request_test, prplcb_request_action, - NULL, - NULL, - NULL, - NULL, + prplcb_request_test, + prplcb_request_test, + prplcb_request_test, + prplcb_request_test, }; static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name ) -- cgit v1.2.3 From d0527c1845ed1230bbc3b0f94b8643cd8f9fddc3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 12:34:24 +0100 Subject: libpurple: Handle incoming authorization requests. --- protocols/nogaim.c | 6 +++++ protocols/nogaim.h | 1 + protocols/purple/purple.c | 58 +++++++++++++++++++++++++++++++++++------------ 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 7380c575..e628126f 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -382,6 +382,12 @@ void imcb_ask( struct im_connection *ic, char *msg, void *data, query_add( (irc_t *) ic->bee->ui_data, ic, msg, doit, dont, g_free, data ); } +void imcb_ask_with_free( struct im_connection *ic, char *msg, void *data, + query_callback doit, query_callback dont, query_callback myfree ) +{ + query_add( (irc_t *) ic->bee->ui_data, ic, msg, doit, dont, myfree, data ); +} + void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group ) { bee_user_t *bu; diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 5ce62742..e2933e4a 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -268,6 +268,7 @@ G_MODULE_EXPORT void imcb_error( struct im_connection *ic, char *format, ... ) G * - 'doit' or 'dont' will be called depending of the answer of the user. */ G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont ); +G_MODULE_EXPORT void imcb_ask_with_free( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont, query_callback myfree ); /* Two common questions you may want to ask: * - X added you to his contact list, allow? diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 2935609b..4729794d 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -883,20 +883,22 @@ static void *prplcb_request_action( const char *title, const char *primary, cons return pqad; } +/* static void prplcb_request_test() { fprintf( stderr, "bla\n" ); } +*/ static PurpleRequestUiOps bee_request_uiops = { - prplcb_request_test, - prplcb_request_test, + NULL, + NULL, prplcb_request_action, - prplcb_request_test, - prplcb_request_test, - prplcb_request_test, - prplcb_request_test, + NULL, + NULL, + NULL, + NULL, }; static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name ) @@ -1047,17 +1049,45 @@ static PurpleNotifyUiOps bee_notify_uiops = prplcb_notify_userinfo, }; +static void *prplcb_account_request_authorize( PurpleAccount *account, const char *remote_user, + const char *id, const char *alias, const char *message, gboolean on_list, + PurpleAccountRequestAuthorizationCb authorize_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + char *q; + + if( alias ) + q = g_strdup_printf( "%s (%s) wants to add you to his/her contact " + "list. (%s)", alias, remote_user, message ); + else + q = g_strdup_printf( "%s wants to add you to his/her contact " + "list. (%s)", remote_user, message ); + + imcb_ask_with_free( ic, q, user_data, authorize_cb, deny_cb, NULL ); + g_free( q ); +} + +static PurpleAccountUiOps bee_account_uiops = +{ + NULL, + NULL, + NULL, + prplcb_account_request_authorize, + NULL, +}; + extern PurpleXferUiOps bee_xfer_uiops; static void purple_ui_init() { - purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); + purple_blist_set_ui_ops( &bee_blist_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); purple_request_set_ui_ops( &bee_request_uiops ); + purple_privacy_set_ui_ops( &bee_privacy_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); + purple_accounts_set_ui_ops( &bee_account_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - purple_privacy_set_ui_ops( &bee_privacy_uiops ); if( getenv( "BITLBEE_DEBUG" ) ) purple_debug_set_ui_ops( &bee_debug_uiops ); @@ -1076,10 +1106,10 @@ void purple_initmodule() exit( 1 ); } - purple_util_set_user_dir("/tmp"); - purple_debug_set_enabled(FALSE); - purple_core_set_ui_ops(&bee_core_uiops); - purple_eventloop_set_ui_ops(&glib_eventloops); + purple_util_set_user_dir( "/tmp" ); + purple_debug_set_enabled( FALSE ); + purple_core_set_ui_ops( &bee_core_uiops ); + purple_eventloop_set_ui_ops( &glib_eventloops ); if( !purple_core_init( "BitlBee") ) { /* Initializing the core failed. Terminate. */ @@ -1088,7 +1118,7 @@ void purple_initmodule() } /* This seems like stateful shit we don't want... */ - purple_set_blist(purple_blist_new()); + purple_set_blist( purple_blist_new() ); purple_blist_load(); /* Meh? */ @@ -1119,7 +1149,7 @@ void purple_initmodule() funcs.chat_join = purple_chat_join; funcs.transfer_request = purple_transfer_request; - help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); + help = g_string_new( "BitlBee libpurple module supports the following IM protocols:\n" ); /* Add a protocol entry to BitlBee's structures for every protocol supported by this libpurple instance. */ -- cgit v1.2.3 From a08e8752a64e12f16a9ddc7e583195c3388b8d3f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 13:08:31 +0100 Subject: libpurple: Read group information of contacts. --- protocols/purple/purple.c | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 4729794d..d69bc82b 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -643,32 +643,12 @@ static PurpleConnectionUiOps bee_conn_uiops = prplcb_conn_report_disconnect_reason, }; -static void prplcb_blist_new( PurpleBlistNode *node ) -{ - PurpleBuddy *bud = (PurpleBuddy*) node; - - if( node->type == PURPLE_BLIST_BUDDY_NODE ) - { - struct im_connection *ic = purple_ic_by_pa( bud->account ); - - if( ic == NULL ) - return; - - imcb_add_buddy( ic, bud->name, NULL ); - if( bud->server_alias ) - { - imcb_rename_buddy( ic, bud->name, bud->server_alias ); - imcb_buddy_nick_hint( ic, bud->name, bud->server_alias ); - } - } -} - static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) { - PurpleBuddy *bud = (PurpleBuddy*) node; - if( node->type == PURPLE_BLIST_BUDDY_NODE ) { + PurpleBuddy *bud = (PurpleBuddy*) node; + PurpleGroup *group = purple_buddy_get_group( bud ); struct im_connection *ic = purple_ic_by_pa( bud->account ); PurpleStatus *as; int flags = 0; @@ -679,6 +659,9 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) if( bud->server_alias ) imcb_rename_buddy( ic, bud->name, bud->server_alias ); + if( group ) + imcb_add_buddy( ic, bud->name, purple_group_get_name( group ) ); + flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0; flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY; @@ -693,6 +676,22 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) } } +static void prplcb_blist_new( PurpleBlistNode *node ) +{ + if( node->type == PURPLE_BLIST_BUDDY_NODE ) + { + PurpleBuddy *bud = (PurpleBuddy*) node; + struct im_connection *ic = purple_ic_by_pa( bud->account ); + + if( ic == NULL ) + return; + + imcb_add_buddy( ic, bud->name, NULL ); + + prplcb_blist_update( NULL, node ); + } +} + static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { /* -- cgit v1.2.3 From a91550c216d39b4063941a4d0831fe3a30186d2e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 13:29:51 +0100 Subject: Try to pass a group when removing a contact. No idea what will happen if a contact is in multiple groups, for now I'm not supporting it. Also cleaning up query code to avoid calling NULL. --- protocols/purple/purple.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index d69bc82b..e960970c 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -370,7 +370,11 @@ static void purple_remove_buddy( struct im_connection *ic, char *who, char *grou pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who ); if( pb != NULL ) { - purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL ); + PurpleGroup *group; + + group = purple_buddy_get_group( pb ); + purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, group ); + purple_blist_remove_buddy( pb ); } } @@ -694,7 +698,7 @@ static void prplcb_blist_new( PurpleBlistNode *node ) static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { - /* +/* PurpleBuddy *bud = (PurpleBuddy*) node; if( node->type == PURPLE_BLIST_BUDDY_NODE ) @@ -706,7 +710,7 @@ static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) imcb_remove_buddy( ic, bud->name, NULL ); } - */ +*/ } static PurpleBlistUiOps bee_blist_uiops = @@ -827,7 +831,8 @@ static void prplcb_request_action_yes( void *data ) { struct prplcb_request_action_data *pqad = data; - pqad->yes( pqad->user_data, pqad->yes_i ); + if( pqad->yes ) + pqad->yes( pqad->user_data, pqad->yes_i ); g_free( pqad ); } @@ -835,7 +840,8 @@ static void prplcb_request_action_no( void *data ) { struct prplcb_request_action_data *pqad = data; - pqad->no( pqad->user_data, pqad->no_i ); + if( pqad->no ) + pqad->no( pqad->user_data, pqad->no_i ); g_free( pqad ); } @@ -858,7 +864,7 @@ static void *prplcb_request_action( const char *title, const char *primary, cons caption = va_arg( actions, char* ); fn = va_arg( actions, void* ); - if( strstr( caption, "Accept" ) ) + if( strstr( caption, "Accept" ) || strstr( caption, "OK" ) ) { pqad->yes = fn; pqad->yes_i = i; -- cgit v1.2.3 From 6ef19f7260711d9d28359492f196b0af4825882d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 15:02:47 +0100 Subject: Fixed bug in r712. Check part, not *part. --- nick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nick.c b/nick.c index d4c1891c..d8efd093 100644 --- a/nick.c +++ b/nick.c @@ -184,7 +184,7 @@ char *nick_gen( bee_user_t *bu ) part = asc = g_convert( part, -1, "ASCII//TRANSLIT//IGNORE", "UTF-8", NULL, NULL, NULL ); - if( ret->len == 0 && *part && isdigit( *part ) ) + if( ret->len == 0 && part && isdigit( *part ) ) g_string_append_c( ret, '_' ); while( part && *part && *part != chop ) -- cgit v1.2.3 From 5e98ff07af5a8d89b7c813f55dfd0972fca11297 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 15:23:20 +0100 Subject: Free a user structure when using the remove command. This disappeared while most IM modules don't call back when a removal was successful. --- root_commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/root_commands.c b/root_commands.c index f4bb4b82..e9b0e0ab 100644 --- a/root_commands.c +++ b/root_commands.c @@ -643,7 +643,8 @@ static void cmd_remove( irc_t *irc, char **cmd ) bu->ic->acc->prpl->remove_buddy( bu->ic, bu->handle, NULL ); nick_del( bu ); - //TODO(wilmer): bee_user_free() and/or let the IM mod do it? irc_user_free( irc, cmd[1] ); + if( g_slist_find( irc->users, iu ) ) + bee_user_free( irc->b, bu ); irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] ); g_free( s ); -- cgit v1.2.3 From 6f0ea5781ea4bd50324ee81758a9fd72ef780fcd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 15:37:33 +0100 Subject: Making nick_format a tiny bit more complicated: Allow truncating a variable to a certain length. --- doc/user-guide/misc.xml | 25 ++++++++++++++++++++++--- nick.c | 11 ++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml index 825d80ee..fcbdda0e 100644 --- a/doc/user-guide/misc.xml +++ b/doc/user-guide/misc.xml @@ -203,12 +203,31 @@ text that will be copied to the nick, combined with several variables: -One modifier is currently available: %-@variable will remove all characters from the first @ in the string. +Invalid characters (like spaces) will always be stripped. Depending on your +locale settings, characters with accents will be converted to ASCII. -In all cases, invalid characters (like spaces) will be stripped. Depending -on your locale settings, characters with accents will be converted to ASCII. +See set nick_format2 for some more information. + + + + + +Nickname formatting - modifiers + + +Two modifiers ares currently available: You can include only the first few +characters of a variable by putting a number right after the %. For example, +[%3group]%-@nick will include only the first three +characters of the group name in the nick. + + + +Also, you can truncate variables from a certain character using +the -modifier. For example, you may want to leave out +everything after the @. %-@handle will expand to +everything in the handle up to the first @. diff --git a/nick.c b/nick.c index d8efd093..c0e3a003 100644 --- a/nick.c +++ b/nick.c @@ -116,6 +116,7 @@ char *nick_gen( bee_user_t *bu ) while( fmt && *fmt && ret->len < MAX_NICK_LENGTH ) { char *part, chop = '\0', *asc = NULL; + int len = MAX_NICK_LENGTH; if( *fmt != '%' ) { @@ -135,6 +136,13 @@ char *nick_gen( bee_user_t *bu ) return NULL; fmt += 2; } + else if( isdigit( *fmt ) ) + { + len = 0; + /* Grab a number. */ + while( isdigit( *fmt ) ) + len = len * 10 + ( *(fmt++) - '0' ); + } else if( g_strncasecmp( fmt, "nick", 4 ) == 0 ) { part = bu->nick ? : bu->handle; @@ -187,13 +195,14 @@ char *nick_gen( bee_user_t *bu ) if( ret->len == 0 && part && isdigit( *part ) ) g_string_append_c( ret, '_' ); - while( part && *part && *part != chop ) + while( part && *part && *part != chop && len > 0 ) { if( strchr( nick_lc_chars, *part ) || strchr( nick_uc_chars, *part ) ) g_string_append_c( ret, *part ); part ++; + len --; } g_free( asc ); } -- cgit v1.2.3 From 0d691eaca734902fb6c0f476ad2dbddb72e6ba66 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 15:41:50 +0100 Subject: Although linebreaks are allowed in the .xml files, new lines should never start with XML tags, since they won't show up as whitespace in help.txt. --- doc/user-guide/misc.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml index fcbdda0e..330e18bb 100644 --- a/doc/user-guide/misc.xml +++ b/doc/user-guide/misc.xml @@ -183,8 +183,8 @@ If you want to configure your own channels, you can use the channel se Nickname formatting -The nick_format setting can be set globally using the -set command, or per account using account +The nick_format setting can be set globally using +the set command, or per account using account set (so that you can set a per-account suffix/prefix or have nicknames generated from full names for certain accounts). @@ -218,14 +218,14 @@ See set nick_format2 for some more information. Two modifiers ares currently available: You can include only the first few -characters of a variable by putting a number right after the %. For example, -[%3group]%-@nick will include only the first three -characters of the group name in the nick. +characters of a variable by putting a number right after the %. For +example, [%3group]%-@nick will include only the first +three characters of the group name in the nick. Also, you can truncate variables from a certain character using -the -modifier. For example, you may want to leave out +the - modifier. For example, you may want to leave out everything after the @. %-@handle will expand to everything in the handle up to the first @. -- cgit v1.2.3 From ffcdf1329ad0401ace5a0355160ac7c249869431 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 16:06:56 +0100 Subject: When using non-Twitter Twitter API services, prefix the channel and contact name with that service name, not always Twitter. This is especially useful when having multiple accounts on different sites with the same username. Also adding an "identica" protocol entry for convenience. Based on a patch from kensanata, bug #648. --- protocols/twitter/twitter.c | 37 ++++++++++++++++++++++++++++++------- protocols/twitter/twitter.h | 2 ++ protocols/twitter/twitter_lib.c | 4 ++-- protocols/twitter/twitter_lib.h | 1 + 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index f3fe8922..f718eeb7 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -118,7 +118,7 @@ static gboolean twitter_oauth_callback( struct oauth_info *info ) return FALSE; } - sprintf( name, "twitter_%s", ic->acc->user ); + sprintf( name, "%s_%s", td->prefix, ic->acc->user ); msg = g_strdup_printf( "To finish OAuth authentication, please visit " "%s and respond with the resulting PIN code.", info->auth_url ); @@ -171,8 +171,21 @@ static gboolean twitter_length_check( struct im_connection *ic, gchar *msg ) static void twitter_init( account_t *acc ) { set_t *s; + char *def_url; + char *def_oauth; - s = set_add( &acc->set, "base_url", TWITTER_API_URL, NULL, acc ); + if( strcmp( acc->prpl->name, "twitter" ) == 0 ) + { + def_url = TWITTER_API_URL; + def_oauth = "true"; + } + else /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ + { + def_url = IDENTICA_API_URL; + def_oauth = "false"; + } + + s = set_add( &acc->set, "base_url", def_url, NULL, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; s = set_add( &acc->set, "message_length", "140", set_eval_int, acc ); @@ -180,7 +193,7 @@ static void twitter_init( account_t *acc ) s = set_add( &acc->set, "mode", "one", set_eval_mode, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; - s = set_add( &acc->set, "oauth", "true", set_eval_bool, acc ); + s = set_add( &acc->set, "oauth", def_oauth, set_eval_bool, acc ); } /** @@ -213,12 +226,16 @@ static void twitter_login( account_t *acc ) td->url_path = g_strdup( url.file ); else td->url_path = g_strdup( "" ); + if( g_str_has_suffix( url.host, ".com" ) ) + td->prefix = g_strndup( url.host, strlen( url.host ) - 4 ); + else + td->prefix = g_strdup( url.host ); td->user = acc->user; if( strstr( acc->pass, "oauth_token=" ) ) td->oauth_info = oauth_from_string( acc->pass, &twitter_oauth ); - sprintf( name, "twitter_%s", acc->user ); + sprintf( name, "%s_%s", td->prefix, acc->user ); imcb_add_buddy( ic, name, NULL ); imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); @@ -246,6 +263,7 @@ static void twitter_logout( struct im_connection *ic ) if( td ) { oauth_info_free( td->oauth_info ); + g_free( td->prefix ); g_free( td->url_host ); g_free( td->url_path ); g_free( td->pass ); @@ -261,9 +279,10 @@ static void twitter_logout( struct im_connection *ic ) static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) { struct twitter_data *td = ic->proto_data; + int plen = strlen( td->prefix ); - if (g_strncasecmp(who, "twitter_", 8) == 0 && - g_strcasecmp(who + 8, ic->acc->user) == 0) + if (g_strncasecmp(who, td->prefix, plen) == 0 && who[plen] == '_' && + g_strcasecmp(who + plen + 1, ic->acc->user) == 0) { if( set_getbool( &ic->acc->set, "oauth" ) && td->oauth_info && td->oauth_info->token == NULL ) @@ -415,10 +434,14 @@ void twitter_initmodule() ret->rem_deny = twitter_rem_deny; ret->send_typing = twitter_send_typing; ret->handle_cmp = g_strcasecmp; + + register_protocol(ret); + /* And an identi.ca variant: */ + ret = g_memdup(ret, sizeof(struct prpl)); + ret->name = "identica"; register_protocol(ret); // Initialise the twitter_connections GSList. twitter_connections = NULL; } - diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index e61d32be..b7e41fc5 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -52,6 +52,8 @@ struct twitter_data int url_port; char *url_host; char *url_path; + + char *prefix; /* Used to generate contact + channel name. */ }; /** diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index b4b460d3..1bf5257c 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -458,7 +458,7 @@ static void twitter_groupchat_init(struct im_connection *ic) td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); - name_hint = g_strdup_printf( "Twitter_%s", ic->acc->user ); + name_hint = g_strdup_printf( "%s_%s", td->prefix, ic->acc->user ); imcb_chat_name_hint( gc, name_hint ); g_free( name_hint ); } @@ -518,7 +518,7 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list) if( mode_one ) { - g_snprintf( from, sizeof( from ) - 1, "twitter_%s", ic->acc->user ); + g_snprintf( from, sizeof( from ) - 1, "%s_%s", td->prefix, ic->acc->user ); from[MAX_STRING-1] = '\0'; } diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index 6b90f9bb..5a3c3f68 100644 --- a/protocols/twitter/twitter_lib.h +++ b/protocols/twitter/twitter_lib.h @@ -29,6 +29,7 @@ #include "twitter_http.h" #define TWITTER_API_URL "http://twitter.com" +#define IDENTICA_API_URL "http://identi.ca/api" /* Status URLs */ #define TWITTER_STATUS_UPDATE_URL "/statuses/update.xml" -- cgit v1.2.3 From 5c18a7632da2507ee9f8e63fafa46061163e3e3c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 16:51:01 +0100 Subject: Store exact Twitter usernames for all Twitter contacts when using mode=chat, so that xxx:->@xxx translation always works properly (even when the nick was stripped/etc). --- protocols/twitter/twitter_lib.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 1bf5257c..620850ab 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -124,7 +124,12 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char * imcb_add_buddy( ic, name, NULL ); imcb_rename_buddy( ic, name, fullname ); if (g_strcasecmp(mode, "chat") == 0) + { + /* Necessary so that nicks always get translated to the + exact Twitter username. */ + imcb_buddy_nick_hint( ic, name, name ); imcb_chat_add_buddy( td->home_timeline_gc, name ); + } else if (g_strcasecmp(mode, "many") == 0) imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); } -- cgit v1.2.3 From fe4f28f593231f93fe4a2e6365edd45c301a6596 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Jul 2010 17:11:09 +0100 Subject: Remove the user from default_channel if it has the auto_join setting disabled. --- root_commands.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/root_commands.c b/root_commands.c index e9b0e0ab..3ce26924 100644 --- a/root_commands.c +++ b/root_commands.c @@ -163,6 +163,10 @@ static void cmd_identify( irc_t *irc, char **cmd ) if( load ) { irc_channel_auto_joins( irc, NULL ); + if( !set_getbool( &irc->default_channel->set, "auto_join" ) ) + irc_channel_del_user( irc->default_channel, irc->user, + IRC_CDU_PART, "auto_join disabled " + "for this channel." ); if( set_getbool( &irc->b->set, "auto_connect" ) ) irc->login_source_id = b_timeout_add( 200, cmd_identify_finish, irc ); -- cgit v1.2.3 From 2bfe976616cc80439257959411c3bb20f2ab2e6b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 18 Jul 2010 15:44:55 +0100 Subject: The "transfers" command was renamed to "transfer". --- dcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dcc.c b/dcc.c index ecce7db4..a9e5b5f1 100644 --- a/dcc.c +++ b/dcc.c @@ -123,7 +123,7 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, irc_user_t *iu, cons imcb_log( ic, "File transfer request from %s for %s (%zd kb).\n" "Accept the file transfer if you'd like the file. If you don't, " - "issue the 'transfers reject' command.", + "issue the 'transfer reject' command.", iu->nick, file_name, file_size / 1024 ); return file; -- cgit v1.2.3 From f1d488e9d3f14e68e7df9686acf0840bf7307854 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 18 Jul 2010 16:04:19 +0100 Subject: Add some context sensitivity to the "add" command: If it's used in a group-specific control channel, automatically add the person to that group. Also added the "group list" command I planned for a while already. --- root_commands.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/root_commands.c b/root_commands.c index 3ce26924..3b607d22 100644 --- a/root_commands.c +++ b/root_commands.c @@ -623,13 +623,34 @@ static void cmd_add( irc_t *irc, char **cmd ) } if( add_on_server ) - a->prpl->add_buddy( a->ic, cmd[2], NULL ); + { + irc_channel_t *ic; + char *s, *group = NULL;; + + if( ( ic = irc_channel_by_name( irc, irc->last_root_cmd ) ) && + ( s = set_getstr( &ic->set, "fill_by" ) ) && + strcmp( s, "group" ) == 0 && + ( group = set_getstr( &ic->set, "group" ) ) ) + irc_usermsg( irc, "Adding `%s' to contact list (group %s)", + cmd[2], group ); + else + irc_usermsg( irc, "Adding `%s' to contact list", cmd[2] ); + + a->prpl->add_buddy( a->ic, cmd[2], group ); + } else + { + bee_user_t *bu; + irc_user_t *iu; + /* Only for add -tmp. For regular adds, this callback will be called once the IM server confirms. */ - bee_user_new( irc->b, a->ic, cmd[2], BEE_USER_LOCAL ); + if( ( bu = bee_user_new( irc->b, a->ic, cmd[2], BEE_USER_LOCAL ) ) && + ( iu = bu->ui_data ) ) + irc_usermsg( irc, "Temporarily assigned nickname `%s' " + "to contact `%s'", iu->nick, cmd[2] ); + } - irc_usermsg( irc, "Adding `%s' to your contact list", cmd[2] ); } static void cmd_remove( irc_t *irc, char **cmd ) @@ -809,7 +830,7 @@ static void cmd_block( irc_t *irc, char **cmd ) { imc_rem_allow( ic, cmd[2] ); imc_add_block( ic, cmd[2] ); - irc_usermsg( irc, "Buddy `%s' moved from your allow- to your block-list", cmd[2] ); + irc_usermsg( irc, "Buddy `%s' moved from allow- to block-list", cmd[2] ); } } @@ -870,7 +891,7 @@ static void cmd_allow( irc_t *irc, char **cmd ) imc_rem_block( ic, cmd[2] ); imc_add_allow( ic, cmd[2] ); - irc_usermsg( irc, "Buddy `%s' moved from your block- to your allow-list", cmd[2] ); + irc_usermsg( irc, "Buddy `%s' moved from block- to allow-list", cmd[2] ); } } @@ -1112,6 +1133,32 @@ static void cmd_chat( irc_t *irc, char **cmd ) } } +static void cmd_group( irc_t *irc, char **cmd ) +{ + GSList *l; + int len; + + len = strlen( cmd[1] ); + if( g_strncasecmp( cmd[1], "list", len ) == 0 ) + { + int n = 0; + + if( strchr( irc->umode, 'b' ) ) + irc_usermsg( irc, "Group list:" ); + + for( l = irc->b->groups; l; l = l->next ) + { + bee_group_t *bg = l->data; + irc_usermsg( irc, "%d. %s", n ++, bg->name ); + } + irc_usermsg( irc, "End of group list" ); + } + else + { + irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "group", cmd[1] ); + } +} + static void cmd_transfer( irc_t *irc, char **cmd ) { GSList *files = irc->file_transfers; @@ -1185,6 +1232,7 @@ const command_t commands[] = { { "chat", 1, cmd_chat, 0 }, { "drop", 1, cmd_drop, 0 }, { "ft", 0, cmd_transfer, 0 }, + { "group", 1, cmd_group, 0 }, { "help", 0, cmd_help, 0 }, { "identify", 1, cmd_identify, 0 }, { "info", 1, cmd_info, 0 }, -- cgit v1.2.3 From 2efb69bed8c96842368c1d82263909c74582399e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 18 Jul 2010 16:12:35 +0100 Subject: Doc update for the previous change. --- doc/user-guide/commands.xml | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 46d12a9c..2f5c961a 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -245,7 +245,7 @@ - chat add <account> <room> [<channel>] + chat add <account id> <room> [<channel>] @@ -276,8 +276,8 @@ Add a buddy to your contact list - add <connection> <handle> [<nick>] - add -tmp <connection> <handle> [<nick>] + add <account id> <handle> [<nick>] + add -tmp <account id> <handle> [<nick>] @@ -287,6 +287,10 @@ If you want, you can also tell BitlBee what nick to give the new contact. The -tmp option adds the buddy to the internal BitlBee structures only, not to the real contact list (like done by set handle_unknown add). This allows you to talk to people who are not in your contact list. This normally won't show you any presence notifications. + + + If you use this command in a control channel containing people from only one group, the new contact will be added to that group automatically. + @@ -1355,22 +1359,15 @@ - - Change friendly name, nick - nick <connection> [<new nick>] - nick <connection> + + Contact group management + group list - Deprecated: Use the per-account display_name setting to read and change this information. + Only the group list command is supported at the moment, which shows a list of all groups defined so far. - - - account 1 set display_name "The majestik møøse" - display_name = `The majestik møøse' - - -- cgit v1.2.3 From 3e59c8d79ae39d8c1412c2bbf8dced6ded74af6f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 18 Jul 2010 16:31:58 +0100 Subject: libpurple: Add contacts to groups when requested. Still not dealing well with contacts in multiple groups. --- protocols/purple/purple.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index e960970c..c7cfcfda 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -357,9 +357,16 @@ static char *set_eval_display_name( set_t *set, char *value ) static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) { PurpleBuddy *pb; + PurpleGroup *pg = NULL; + + if( group && !( pg = purple_find_group( group ) ) ) + { + pg = purple_group_new( group ); + purple_blist_add_group( pg, NULL ); + } pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL ); - purple_blist_add_buddy( pb, NULL, NULL, NULL ); + purple_blist_add_buddy( pb, NULL, pg, NULL ); purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb ); } @@ -1070,6 +1077,8 @@ static void *prplcb_account_request_authorize( PurpleAccount *account, const cha imcb_ask_with_free( ic, q, user_data, authorize_cb, deny_cb, NULL ); g_free( q ); + + return NULL; } static PurpleAccountUiOps bee_account_uiops = -- cgit v1.2.3 From 4f22a68c5d1dfd0d1da8b44c3a9d60a7754633b7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 18 Jul 2010 21:12:55 +0100 Subject: Automatically operate on the current channel if just using "channel set". --- doc/user-guide/commands.xml | 10 +++++----- root_commands.c | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 2f5c961a..ae2d76fb 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -210,10 +210,10 @@ - channel <channel id> set - channel <channel id> set <setting> - channel <channel id> set <setting> <value> - channel <channel id> set -del <setting> + channel [<channel id>] set + channel [<channel id>] set <setting> + channel [<channel id>] set <setting> <value> + channel [<channel id>] set -del <setting> @@ -225,7 +225,7 @@ - The channel ID can be a number (see channel list), or (part of) its name, as long as it matches only one channel. + The channel ID can be a number (see channel list), or (part of) its name, as long as it matches only one channel. If you want to change settings of the current channel, you can omit the channel ID. diff --git a/root_commands.c b/root_commands.c index 3b607d22..ddef9cde 100644 --- a/root_commands.c +++ b/root_commands.c @@ -549,15 +549,23 @@ static void cmd_channel( irc_t *irc, char **cmd ) return; } - MIN_ARGS( 2 ); - len = strlen( cmd[2] ); - if( ( ic = irc_channel_get( irc, cmd[1] ) ) == NULL ) { - irc_usermsg( irc, "Could not find channel `%s'", cmd[1] ); + /* If this doesn't match any channel, maybe this is the short + syntax (only works when used inside a channel). */ + if( ( len = strlen( cmd[1] ) ) && + g_strncasecmp( cmd[1], "set", len ) == 0 && + ( ic = irc_channel_by_name( irc, irc->last_root_cmd ) ) ) + cmd_set_real( irc, cmd + 1, &ic->set, NULL ); + else + irc_usermsg( irc, "Could not find channel `%s'", cmd[1] ); + return; } + MIN_ARGS( 2 ); + len = strlen( cmd[2] ); + if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 ) { cmd_set_real( irc, cmd + 2, &ic->set, NULL ); -- cgit v1.2.3 From 94d5da9cc78a89b6292d53c7784a3076500d67e2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 18 Jul 2010 23:12:19 +0100 Subject: One of the last few things I wanted to get done in this branch: combining show_offline and away_devoice and possibly other ideas into one setting called show_users. Documentation will come soon. :-P --- irc.h | 2 ++ irc_channel.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ irc_im.c | 35 +++++++++++++++++++++-------------- 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/irc.h b/irc.h index 1d5bd113..69f9dfd0 100644 --- a/irc.h +++ b/irc.h @@ -179,6 +179,7 @@ typedef enum IRC_CHANNEL_USER_OP = 1, IRC_CHANNEL_USER_HALFOP = 2, IRC_CHANNEL_USER_VOICE = 4, + IRC_CHANNEL_USER_NONE = 8, } irc_channel_user_flags_t; typedef struct irc_channel_user @@ -202,6 +203,7 @@ struct irc_control_channel struct bee_group *group; struct account *account; struct prpl *protocol; + char modes[4]; }; extern const struct bee_ui_funcs irc_ui_funcs; diff --git a/irc_channel.c b/irc_channel.c index 6f9de637..b3058e0e 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -535,6 +535,7 @@ static char *set_eval_by_account( set_t *set, char *value ); static char *set_eval_fill_by( set_t *set, char *value ); static char *set_eval_by_group( set_t *set, char *value ); static char *set_eval_by_protocol( set_t *set, char *value ); +static char *set_eval_show_users( set_t *set, char *value ); static gboolean control_channel_init( irc_channel_t *ic ) { @@ -544,10 +545,14 @@ static gboolean control_channel_init( irc_channel_t *ic ) set_add( &ic->set, "fill_by", "all", set_eval_fill_by, ic ); set_add( &ic->set, "group", NULL, set_eval_by_group, ic ); set_add( &ic->set, "protocol", NULL, set_eval_by_protocol, ic ); + set_add( &ic->set, "show_users", "online+,away", set_eval_show_users, ic ); ic->data = icc = g_new0( struct irc_control_channel, 1 ); icc->type = IRC_CC_TYPE_DEFAULT; + /* Have to run the evaluator to initialize icc->modes. */ + set_setstr( &ic->set, "show_users", set_getstr( &ic->set, "show_users" ) ); + return TRUE; } @@ -624,6 +629,49 @@ static char *set_eval_by_protocol( set_t *set, char *value ) return value; } +static char *set_eval_show_users( set_t *set, char *value ) +{ + struct irc_channel *ic = set->data; + struct irc_control_channel *icc = ic->data; + char **parts = g_strsplit( value, ",", 0 ), **part; + char modes[4]; + + memset( modes, 0, 4 ); + for( part = parts; *part; part ++ ) + { + char last, modechar = IRC_CHANNEL_USER_NONE; + + if( **part == '\0' ) + goto fail; + + last = (*part)[strlen(*part+1)]; + if( last == '+' ) + modechar = IRC_CHANNEL_USER_VOICE; + else if( last == '%' ) + modechar = IRC_CHANNEL_USER_HALFOP; + else if( last == '@' ) + modechar = IRC_CHANNEL_USER_OP; + + if( strncmp( *part, "offline", 7 ) == 0 ) + modes[0] = modechar; + else if( strncmp( *part, "away", 4 ) == 0 ) + modes[1] = modechar; + else if( strncmp( *part, "online", 6 ) == 0 ) + modes[2] = modechar; + else + goto fail; + } + memcpy( icc->modes, modes, 4 ); + bee_irc_channel_update( ic->irc, ic, NULL ); + + g_strfreev( parts ); + return value; + +fail: + g_strfreev( parts ); + return SET_INVALID; +} + static gboolean control_channel_free( irc_channel_t *ic ) { struct irc_control_channel *icc = ic->data; diff --git a/irc_im.c b/irc_im.c index f467a666..8a10cede 100644 --- a/irc_im.c +++ b/irc_im.c @@ -142,7 +142,7 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) { struct irc_control_channel *icc; GSList *l; - gboolean show = FALSE; + gboolean match = FALSE; if( ic == NULL ) { @@ -169,30 +169,37 @@ void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) icc = ic->data; - if( !( iu->bu->flags & BEE_USER_ONLINE ) ) - show = FALSE; - else if( icc->type == IRC_CC_TYPE_DEFAULT ) - show = TRUE; + if( icc->type == IRC_CC_TYPE_DEFAULT ) + match = TRUE; else if( icc->type == IRC_CC_TYPE_GROUP ) - show = iu->bu->group == icc->group; + match = iu->bu->group == icc->group; else if( icc->type == IRC_CC_TYPE_ACCOUNT ) - show = iu->bu->ic->acc == icc->account; + match = iu->bu->ic->acc == icc->account; else if( icc->type == IRC_CC_TYPE_PROTOCOL ) - show = iu->bu->ic->acc->prpl == icc->protocol; + match = iu->bu->ic->acc->prpl == icc->protocol; - if( !show ) + if( !match ) { irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL ); } else { - irc_channel_add_user( ic, iu ); + int mode = 0; - if( set_getbool( &irc->b->set, "away_devoice" ) ) - irc_channel_user_set_mode( ic, iu, ( iu->bu->flags & BEE_USER_AWAY ) ? - 0 : IRC_CHANNEL_USER_VOICE ); + if( !( iu->bu->flags & BEE_USER_ONLINE ) ) + mode = icc->modes[0]; + else if( iu->bu->flags & BEE_USER_AWAY ) + mode = icc->modes[1]; else - irc_channel_user_set_mode( ic, iu, 0 ); + mode = icc->modes[2]; + + if( !mode ) + irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL ); + else + { + irc_channel_add_user( ic, iu ); + irc_channel_user_set_mode( ic, iu, mode ); + } } } -- cgit v1.2.3 From 6d8cc053c4b247ad721a0760b13cd383d758c2c5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 19 Jul 2010 00:50:27 +0100 Subject: Adding easy migration from old show_offline/away_devoice settings, and documentation. --- doc/user-guide/commands.xml | 30 ++++++++++++++++++++++++++++++ irc.c | 34 ++++++++++++++++++++++++---------- protocols/nogaim.h | 1 - 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index ae2d76fb..4413ce49 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -517,6 +517,10 @@ With this option enabled, the root user devoices people when they go away (just away, not offline) and gives the voice back when they come back. You might dislike the voice-floods you'll get if your contact list is huge, so this option can be disabled. + + + Replaced with the show_users setting. See help show_users. + @@ -1040,6 +1044,32 @@ If enabled causes BitlBee to also show offline users in Channel. Online-users will get op, away-users voice and offline users none of both. This option takes effect as soon as you reconnect. + + + Replaced with the show_users setting. See help show_users. + + + + + + online+,away + + + + Comma-separated list of statuses of users you want in the channel, + and any modes they should have. The following statuses are currently + recognised: online (i.e. available, not + away), away, and offline. + + + + If a status is followed by a valid channel mode character + (@, % or +), it will be given to users with that status. + For example, online@,away+,offline will + show all users in the channel. Online people will + have +o, people who are online but away will have +v, + and others will have no special modes. + diff --git a/irc.c b/irc.c index 2093fa5b..1d2b729c 100644 --- a/irc.c +++ b/irc.c @@ -32,6 +32,7 @@ GSList *irc_connection_list; static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ); static char *set_eval_charset( set_t *set, char *value ); static char *set_eval_password( set_t *set, char *value ); +static char *set_eval_bw_compat( set_t *set, char *value ); irc_t *irc_new( int fd ) { @@ -100,7 +101,7 @@ irc_t *irc_new( int fd ) b->ui = &irc_ui_funcs; s = set_add( &b->set, "allow_takeover", "true", set_eval_bool, irc ); - s = set_add( &b->set, "away_devoice", "true", set_eval_away_devoice, irc ); + s = set_add( &b->set, "away_devoice", "true", set_eval_bw_compat, irc ); s = set_add( &b->set, "away_reply_timeout", "3600", set_eval_int, irc ); s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); s = set_add( &b->set, "default_target", "root", NULL, irc ); @@ -120,6 +121,7 @@ irc_t *irc_new( int fd ) s = set_add( &b->set, "private", "true", set_eval_bool, irc ); s = set_add( &b->set, "query_order", "lifo", NULL, irc ); s = set_add( &b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc ); + s = set_add( &b->set, "show_offline", "false", set_eval_bw_compat, irc ); s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc ); s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc ); s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc ); @@ -900,19 +902,31 @@ static char *set_eval_charset( set_t *set, char *value ) return value; } -char *set_eval_away_devoice( set_t *set, char *value ) +/* Mostly meant for upgrades. If one of these is set to the non-default, + set show_users of all channels to something with the same effect. */ +static char *set_eval_bw_compat( set_t *set, char *value ) { irc_t *irc = set->data; + char *val; + GSList *l; - if( !is_bool( value ) ) - return SET_INVALID; + irc_usermsg( irc, "Setting `%s' is obsolete, use the `show_users' " + "channel setting instead.", set->key ); - /* The usual problem: The setting isn't actually changed at this - point and we need it to be, so do it by hand. */ - g_free( set->value ); - set->value = g_strdup( value ); + if( strcmp( set->key, "away_devoice" ) == 0 && !bool2int( value ) ) + val = "online,away"; + else if( strcmp( set->key, "show_offline" ) == 0 && bool2int( value ) ) + val = "online@,away+,offline"; + else + return SET_INVALID; - bee_irc_channel_update( irc, NULL, NULL ); + for( l = irc->channels; l; l = l->next ) + { + irc_channel_t *ic = l->data; + /* No need to check channel type, if the setting doesn't exist it + will just be ignored. */ + set_setstr( &ic->set, "show_users", val ); + } - return value; + return SET_INVALID; } diff --git a/protocols/nogaim.h b/protocols/nogaim.h index e2933e4a..1d9ac71e 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -323,7 +323,6 @@ void imc_rem_block( struct im_connection *ic, char *handle ); /* Misc. stuff */ char *set_eval_timezone( set_t *set, char *value ); -char *set_eval_away_devoice( set_t *set, char *value ); gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ); void cancel_auto_reconnect( struct account *a ); -- cgit v1.2.3 From 5a61bf59dc372e795163dc5afa4cc91820f559f8 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 19 Jul 2010 22:18:13 +0100 Subject: Whoops. Small memory management screw-up. --- irc_channel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/irc_channel.c b/irc_channel.c index b3058e0e..536b2de1 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -545,13 +545,15 @@ static gboolean control_channel_init( irc_channel_t *ic ) set_add( &ic->set, "fill_by", "all", set_eval_fill_by, ic ); set_add( &ic->set, "group", NULL, set_eval_by_group, ic ); set_add( &ic->set, "protocol", NULL, set_eval_by_protocol, ic ); + + /* When changing the default, also change it below. */ set_add( &ic->set, "show_users", "online+,away", set_eval_show_users, ic ); ic->data = icc = g_new0( struct irc_control_channel, 1 ); icc->type = IRC_CC_TYPE_DEFAULT; /* Have to run the evaluator to initialize icc->modes. */ - set_setstr( &ic->set, "show_users", set_getstr( &ic->set, "show_users" ) ); + set_setstr( &ic->set, "show_users", "online+,away" ); return TRUE; } -- cgit v1.2.3 From 262bc7e0454346b81d28accb207289fcbbae029d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 21 Jul 2010 01:39:47 +0100 Subject: 1.3dev changelog. Should release that soon, sticking a version# on it makes more people use it. --- doc/CHANGES | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/doc/CHANGES b/doc/CHANGES index 25db8a70..0ea6ebe0 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -3,6 +3,53 @@ found in the bzr commit logs, for example you can try: http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on +Version 1.3dev: +- Loads of new stuff, mostly ready for a new major release, but starting with + a dev snapshot. A few changes that were planned for a long time already: +- Rewrote the IRC core, which brings: + * Support for multiple (control) channels, so you can have one channel per + buddy group, per IM account/protocol, or for example a &offline with all + offline contacts. See "help channels" for information on how to use this. + * You can also leave and rejoin all channels. Being in &bitlbee is no + longer required. + * Now you can start groupchats by just joining a new channel and inviting + contacts. (The "chat with" command still works as well.) + * You can change your nickname, whenever you want. + * Root commands: + + To hopefully resolve confusion about the "account set" syntax, the + ordering was changed slightly: "account set acc/setting value" + becomes "account acc set setting value". Obviously the same order + should now be used for other account subcommands. + + Shortcuts: Instead of "account list" you can say "acc li". + * /whois shows idle/login times of your contacts when available. + * paste_buffer (previously known as buddy_sendbuffer) now works for chats + as well. + * The nick_source setting was replaced with a nick_format setting, which + looks more or less like a format string, and lets you tweak how nicknames + for contacts are generated in more detail. + * A per-channel show_users setting lets you configure exactly which kinds + of contacts (online/away/offline) should show up in a channel and what + modes (none/voice/etc) they should have. + * If you connect (and identify) to a BitlBee server you're already + connected to, you can take over the existing session instead of starting + a new one. + * More correct validation of channel names: They can contain pretty much + any character, unlike nicknames. +- Support for using libpurple instead of BitlBee's built-in IM protocol + modules. This can be enabled by passing --purple=1 to the configure script. + * This adds support for many more IM protocols to BitlBee. + * And new functionality to existing protocols. + * This is and will always be optional and using it on public servers is + *not* recommended. It should be pretty stable, but costs more RAM/etc. + * When using this variant, type "help purple" to get a list of supported + protocols. +- Support for file transfers, in and out. /DCC SEND a file to a contact and + it becomes a file transfer, and incoming file transfers become /DCC SENDs + to you. Note that this is mostly useful when combined with libpurple, as + only the Jabber native protocol module currently supports file transfers. + +Finished ... 2010 + Version 1.2.8: - Now always using the AIM-style authentication method for OSCAR connections, even when connecting to ICQ. This solves login issues some people were -- cgit v1.2.3 From 938c30512f4dac4f084fd6bb8b7f41655de9bce4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 22 Jul 2010 08:43:21 +0100 Subject: Fixed crash on failed Jabber file transfers. --- protocols/jabber/s5bytestream.c | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index a8137271..6759b78b 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -568,7 +568,6 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) imcb_file_canceled( tf->ic, tf->ft, "couldn't connect to any streamhosts" ); - bt->tf->watch_in = 0; /* MUST always return FALSE! */ return FALSE; } -- cgit v1.2.3 From c36f73bd317dd55d7e70275a6425faa4be7bfd8c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 23 Jul 2010 15:35:45 +0100 Subject: This should mostly be a no-op, merging *loads* of whitespace changes from libyahoo2 so that I can see better what really changed. --- protocols/yahoo/libyahoo2.c | 1468 +++++++++++++++++++----------------- protocols/yahoo/yahoo2.h | 140 ++-- protocols/yahoo/yahoo2_callbacks.h | 187 ++--- protocols/yahoo/yahoo2_types.h | 1 + protocols/yahoo/yahoo_fn.h | 17 +- protocols/yahoo/yahoo_util.c | 6 +- protocols/yahoo/yahoo_util.h | 20 +- 7 files changed, 973 insertions(+), 866 deletions(-) diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index 1bfc2e59..c376a2de 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -2,6 +2,8 @@ * libyahoo2: libyahoo2.c * * Some code copyright (C) 2002-2004, Philip S Tellis + * YMSG16 code copyright (C) 2009, + * Siddhesh Poyarekar * * Yahoo Search copyright (C) 2003, Konstantin Klyagin * @@ -26,6 +28,8 @@ * Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto * * + * YMSG16 authentication code based mostly on write-up at: + * http://www.carbonize.co.uk/ymsg16.html * * 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 @@ -91,9 +95,9 @@ char *strchr (), *strrchr (); #include "http_client.h" #ifdef USE_STRUCT_CALLBACKS -struct yahoo_callbacks *yc=NULL; +struct yahoo_callbacks *yc = NULL; -void yahoo_register_callbacks(struct yahoo_callbacks * tyc) +void yahoo_register_callbacks(struct yahoo_callbacks *tyc) { yc = tyc; } @@ -105,19 +109,19 @@ void yahoo_register_callbacks(struct yahoo_callbacks * tyc) static int yahoo_send_data(int fd, void *data, int len); -int yahoo_log_message(char * fmt, ...) +int yahoo_log_message(char *fmt, ...) { char out[1024]; va_list ap; va_start(ap, fmt); vsnprintf(out, sizeof(out), fmt, ap); va_end(ap); - return YAHOO_CALLBACK(ext_yahoo_log)("%s", out); + return YAHOO_CALLBACK(ext_yahoo_log) ("%s", out); } -int yahoo_connect(char * host, int port) +int yahoo_connect(char *host, int port) { - return YAHOO_CALLBACK(ext_yahoo_connect)(host, port); + return YAHOO_CALLBACK(ext_yahoo_connect) (host, port); } static enum yahoo_log_level log_level = YAHOO_LOG_NONE; @@ -137,14 +141,14 @@ int yahoo_set_log_level(enum yahoo_log_level level) /* default values for servers */ static char pager_host[] = "scs.msg.yahoo.com"; static int pager_port = 5050; -static int fallback_ports[]={23, 25, 80, 20, 119, 8001, 8002, 5050, 0}; -static char filetransfer_host[]="filetransfer.msg.yahoo.com"; -static int filetransfer_port=80; -static char webcam_host[]="webcam.yahoo.com"; -static int webcam_port=5100; -static char webcam_description[]=""; -static char local_host[]=""; -static int conn_type=Y_WCM_DSL; +static int fallback_ports[] = { 23, 25, 80, 20, 119, 8001, 8002, 5050, 0 }; +static char filetransfer_host[] = "filetransfer.msg.yahoo.com"; +static int filetransfer_port = 80; +static char webcam_host[] = "webcam.yahoo.com"; +static int webcam_port = 5100; +static char webcam_description[] = ""; +static char local_host[] = ""; +static int conn_type = Y_WCM_DSL; static char profile_url[] = "http://profiles.yahoo.com/"; @@ -214,18 +218,18 @@ enum yahoo_service { /* these are easier to see in hex */ YAHOO_SERVICE_PICTURE = 0xbe, YAHOO_SERVICE_PICTURE_UPDATE = 0xc1, YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2, - YAHOO_SERVICE_Y6_VISIBILITY=0xc5, - YAHOO_SERVICE_Y6_STATUS_UPDATE=0xc6, - YAHOO_PHOTOSHARE_INIT=0xd2, - YAHOO_SERVICE_CONTACT_YMSG13=0xd6, - YAHOO_PHOTOSHARE_PREV=0xd7, - YAHOO_PHOTOSHARE_KEY=0xd8, - YAHOO_PHOTOSHARE_TRANS=0xda, - YAHOO_FILE_TRANSFER_INIT_YMSG13=0xdc, - YAHOO_FILE_TRANSFER_GET_YMSG13=0xdd, - YAHOO_FILE_TRANSFER_PUT_YMSG13=0xde, - YAHOO_SERVICE_YMSG15_STATUS=0xf0, - YAHOO_SERVICE_YMSG15_BUDDY_LIST=0xf1, + YAHOO_SERVICE_Y6_VISIBILITY = 0xc5, + YAHOO_SERVICE_Y6_STATUS_UPDATE = 0xc6, + YAHOO_PHOTOSHARE_INIT = 0xd2, + YAHOO_SERVICE_CONTACT_YMSG13 = 0xd6, + YAHOO_PHOTOSHARE_PREV = 0xd7, + YAHOO_PHOTOSHARE_KEY = 0xd8, + YAHOO_PHOTOSHARE_TRANS = 0xda, + YAHOO_FILE_TRANSFER_INIT_YMSG13 = 0xdc, + YAHOO_FILE_TRANSFER_GET_YMSG13 = 0xdd, + YAHOO_FILE_TRANSFER_PUT_YMSG13 = 0xde, + YAHOO_SERVICE_YMSG15_STATUS = 0xf0, + YAHOO_SERVICE_YMSG15_BUDDY_LIST = 0xf1, }; struct yahoo_pair { @@ -241,15 +245,15 @@ struct yahoo_packet { }; struct yahoo_search_state { - int lsearch_type; - char *lsearch_text; - int lsearch_gender; - int lsearch_agerange; - int lsearch_photo; - int lsearch_yahoo_only; - int lsearch_nstart; - int lsearch_nfound; - int lsearch_ntotal; + int lsearch_type; + char *lsearch_text; + int lsearch_gender; + int lsearch_agerange; + int lsearch_photo; + int lsearch_yahoo_only; + int lsearch_nstart; + int lsearch_nfound; + int lsearch_ntotal; }; struct data_queue { @@ -263,30 +267,30 @@ struct yahoo_input_data { struct yahoo_webcam_data *wcd; struct yahoo_search_state *ys; - int fd; + int fd; enum yahoo_connection_type type; - unsigned char *rxqueue; - int rxlen; - int read_tag; + unsigned char *rxqueue; + int rxlen; + int read_tag; YList *txqueues; - int write_tag; + int write_tag; }; struct yahoo_server_settings { char *pager_host; - int pager_port; + int pager_port; char *filetransfer_host; - int filetransfer_port; + int filetransfer_port; char *webcam_host; - int webcam_port; + int webcam_port; char *webcam_description; char *local_host; - int conn_type; + int conn_type; }; -static void * _yahoo_default_server_settings() +static void *_yahoo_default_server_settings() { struct yahoo_server_settings *yss = y_new0(struct yahoo_server_settings, 1); @@ -303,54 +307,54 @@ static void * _yahoo_default_server_settings() return yss; } -static void * _yahoo_assign_server_settings(va_list ap) +static void *_yahoo_assign_server_settings(va_list ap) { struct yahoo_server_settings *yss = _yahoo_default_server_settings(); char *key; char *svalue; - int nvalue; + int nvalue; - while(1) { + while (1) { key = va_arg(ap, char *); - if(key == NULL) + if (key == NULL) break; - if(!strcmp(key, "pager_host")) { + if (!strcmp(key, "pager_host")) { svalue = va_arg(ap, char *); free(yss->pager_host); yss->pager_host = strdup(svalue); - } else if(!strcmp(key, "pager_port")) { + } else if (!strcmp(key, "pager_port")) { nvalue = va_arg(ap, int); yss->pager_port = nvalue; - } else if(!strcmp(key, "filetransfer_host")) { + } else if (!strcmp(key, "filetransfer_host")) { svalue = va_arg(ap, char *); free(yss->filetransfer_host); yss->filetransfer_host = strdup(svalue); - } else if(!strcmp(key, "filetransfer_port")) { + } else if (!strcmp(key, "filetransfer_port")) { nvalue = va_arg(ap, int); yss->filetransfer_port = nvalue; - } else if(!strcmp(key, "webcam_host")) { + } else if (!strcmp(key, "webcam_host")) { svalue = va_arg(ap, char *); free(yss->webcam_host); yss->webcam_host = strdup(svalue); - } else if(!strcmp(key, "webcam_port")) { + } else if (!strcmp(key, "webcam_port")) { nvalue = va_arg(ap, int); yss->webcam_port = nvalue; - } else if(!strcmp(key, "webcam_description")) { + } else if (!strcmp(key, "webcam_description")) { svalue = va_arg(ap, char *); free(yss->webcam_description); yss->webcam_description = strdup(svalue); - } else if(!strcmp(key, "local_host")) { + } else if (!strcmp(key, "local_host")) { svalue = va_arg(ap, char *); free(yss->local_host); yss->local_host = strdup(svalue); - } else if(!strcmp(key, "conn_type")) { + } else if (!strcmp(key, "conn_type")) { nvalue = va_arg(ap, int); yss->conn_type = nvalue; } else { WARNING(("Unknown key passed to yahoo_init, " - "perhaps you didn't terminate the list " - "with NULL")); + "perhaps you didn't terminate the list " + "with NULL")); } } @@ -359,7 +363,7 @@ static void * _yahoo_assign_server_settings(va_list ap) static void yahoo_free_server_settings(struct yahoo_server_settings *yss) { - if(!yss) + if (!yss) return; free(yss->pager_host); @@ -371,20 +375,20 @@ static void yahoo_free_server_settings(struct yahoo_server_settings *yss) free(yss); } -static YList *conns=NULL; -static YList *inputs=NULL; -static int last_id=0; +static YList *conns = NULL; +static YList *inputs = NULL; +static int last_id = 0; static void add_to_list(struct yahoo_data *yd) { conns = y_list_prepend(conns, yd); } -static struct yahoo_data * find_conn_by_id(int id) +static struct yahoo_data *find_conn_by_id(int id) { YList *l; - for(l = conns; l; l = y_list_next(l)) { + for (l = conns; l; l = y_list_next(l)) { struct yahoo_data *yd = l->data; - if(yd->client_id == id) + if (yd->client_id == id) return yd; } return NULL; @@ -395,7 +399,7 @@ static void del_from_list(struct yahoo_data *yd) } /* call repeatedly to get the next one */ -static struct yahoo_input_data * find_input_by_id(int id) +static struct yahoo_input_data *find_input_by_id(int id) { YList *l; for(l = inputs; l; l = y_list_next(l)) { @@ -406,13 +410,13 @@ static struct yahoo_input_data * find_input_by_id(int id) return NULL; } -static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const char * who) +static struct yahoo_input_data *find_input_by_id_and_webcam_user(int id, const char *who) { YList *l; LOG(("find_input_by_id_and_webcam_user")); - for(l = inputs; l; l = y_list_next(l)) { + for (l = inputs; l; l = y_list_next(l)) { struct yahoo_input_data *yid = l->data; - if(yid->type == YAHOO_CONNECTION_WEBCAM && yid->yd->client_id == id + if (yid->type == YAHOO_CONNECTION_WEBCAM && yid->yd->client_id == id && yid->wcm && ((who && yid->wcm->user && !strcmp(who, yid->wcm->user)) || !(yid->wcm->user && !who))) @@ -421,25 +425,25 @@ static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const return NULL; } -static struct yahoo_input_data * find_input_by_id_and_type(int id, enum yahoo_connection_type type) +static struct yahoo_input_data *find_input_by_id_and_type(int id, enum yahoo_connection_type type) { YList *l; LOG(("find_input_by_id_and_type")); - for(l = inputs; l; l = y_list_next(l)) { + for (l = inputs; l; l = y_list_next(l)) { struct yahoo_input_data *yid = l->data; - if(yid->type == type && yid->yd->client_id == id) + if (yid->type == type && yid->yd->client_id == id) return yid; } return NULL; } -static struct yahoo_input_data * find_input_by_id_and_fd(int id, int fd) +static struct yahoo_input_data *find_input_by_id_and_fd(int id, int fd) { YList *l; LOG(("find_input_by_id_and_fd")); - for(l = inputs; l; l = y_list_next(l)) { + for (l = inputs; l; l = y_list_next(l)) { struct yahoo_input_data *yid = l->data; - if(yid->fd == fd && yid->yd->client_id == id) + if (yid->fd == fd && yid->yd->client_id == id) return yid; } return NULL; @@ -447,12 +451,12 @@ static struct yahoo_input_data * find_input_by_id_and_fd(int id, int fd) static int count_inputs_with_id(int id) { - int c=0; + int c = 0; YList *l; LOG(("counting %d", id)); - for(l = inputs; l; l = y_list_next(l)) { + for (l = inputs; l; l = y_list_next(l)) { struct yahoo_input_data *yid = l->data; - if(yid->yd->client_id == id) + if (yid->yd->client_id == id) c++; } LOG(("%d", c)); @@ -463,20 +467,19 @@ static int count_inputs_with_id(int id) extern char *yahoo_crypt(char *, char *); /* Free a buddy list */ -static void yahoo_free_buddies(YList * list) +static void yahoo_free_buddies(YList *list) { YList *l; - for(l = list; l; l = l->next) - { + for (l = list; l; l = l->next) { struct yahoo_buddy *bud = l->data; - if(!bud) + if (!bud) continue; FREE(bud->group); FREE(bud->id); FREE(bud->real_name); - if(bud->yab_entry) { + if (bud->yab_entry) { FREE(bud->yab_entry->fname); FREE(bud->yab_entry->lname); FREE(bud->yab_entry->nname); @@ -495,7 +498,7 @@ static void yahoo_free_buddies(YList * list) } /* Free an identities list */ -static void yahoo_free_identities(YList * list) +static void yahoo_free_identities(YList *list) { while (list) { YList *n = list; @@ -689,30 +692,29 @@ static void yahoo_dump_unhandled(struct yahoo_packet *pkt) } } - static void yahoo_packet_dump(unsigned char *data, int len) { - if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) { + if (yahoo_get_log_level() >= YAHOO_LOG_DEBUG) { int i; for (i = 0; i < len; i++) { if ((i % 8 == 0) && i) - YAHOO_CALLBACK(ext_yahoo_log)(" "); + YAHOO_CALLBACK(ext_yahoo_log) (" "); if ((i % 16 == 0) && i) - YAHOO_CALLBACK(ext_yahoo_log)("\n"); - YAHOO_CALLBACK(ext_yahoo_log)("%02x ", data[i]); + YAHOO_CALLBACK(ext_yahoo_log) ("\n"); + YAHOO_CALLBACK(ext_yahoo_log) ("%02x ", data[i]); } - YAHOO_CALLBACK(ext_yahoo_log)("\n"); + YAHOO_CALLBACK(ext_yahoo_log) ("\n"); for (i = 0; i < len; i++) { if ((i % 8 == 0) && i) - YAHOO_CALLBACK(ext_yahoo_log)(" "); + YAHOO_CALLBACK(ext_yahoo_log) (" "); if ((i % 16 == 0) && i) - YAHOO_CALLBACK(ext_yahoo_log)("\n"); + YAHOO_CALLBACK(ext_yahoo_log) ("\n"); if (isprint(data[i])) - YAHOO_CALLBACK(ext_yahoo_log)(" %c ", data[i]); + YAHOO_CALLBACK(ext_yahoo_log) (" %c ", data[i]); else - YAHOO_CALLBACK(ext_yahoo_log)(" . "); + YAHOO_CALLBACK(ext_yahoo_log) (" . "); } - YAHOO_CALLBACK(ext_yahoo_log)("\n"); + YAHOO_CALLBACK(ext_yahoo_log) ("\n"); } } @@ -722,7 +724,8 @@ static void to_y64(unsigned char *out, const unsigned char *in, int inlen) base64_encode_real(in, inlen, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"); } -static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, int length) +static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, + int length) { struct data_queue *tx = y_new0(struct data_queue, 1); tx->queue = y_new0(unsigned char, length); @@ -731,15 +734,17 @@ static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, in yid->txqueues = y_list_append(yid->txqueues, tx); - if(!yid->write_tag) - yid->write_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_WRITE, yid); + if (!yid->write_tag) + yid->write_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd-> + client_id, yid->fd, YAHOO_INPUT_WRITE, yid); } -static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet *pkt, int extra_pad) +static void yahoo_send_packet(struct yahoo_input_data *yid, + struct yahoo_packet *pkt, int extra_pad) { int pktlen = yahoo_packet_length(pkt); int len = YAHOO_PACKET_HDRLEN + pktlen; - unsigned char *data; int pos = 0; @@ -748,19 +753,20 @@ static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet data = y_new0(unsigned char, len + 1); - memcpy(data + pos, "YMSG", 4); pos += 4; - pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); - pos += yahoo_put16(data + pos, 0x0000); - pos += yahoo_put16(data + pos, pktlen + extra_pad); - pos += yahoo_put16(data + pos, pkt->service); - pos += yahoo_put32(data + pos, pkt->status); - pos += yahoo_put32(data + pos, pkt->id); + memcpy(data + pos, "YMSG", 4); + pos += 4; + pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); /* version [latest 12 0x000c] */ + pos += yahoo_put16(data + pos, 0x0000); /* HIWORD pkt length??? */ + pos += yahoo_put16(data + pos, pktlen + extra_pad); /* LOWORD pkt length? */ + pos += yahoo_put16(data + pos, pkt->service); /* service */ + pos += yahoo_put32(data + pos, pkt->status); /* status [4bytes] */ + pos += yahoo_put32(data + pos, pkt->id); /* session [4bytes] */ yahoo_packet_write(pkt, data + pos); yahoo_packet_dump(data, len); - - if( yid->type == YAHOO_CONNECTION_FT ) + + if (yid->type == YAHOO_CONNECTION_FT) yahoo_send_data(yid->fd, data, len); else yahoo_add_to_send_queue(yid, data, len); @@ -793,80 +799,82 @@ static int yahoo_send_data(int fd, void *data, int len) do { ret = write(fd, data, len); - } while(ret == -1 && errno==EINTR); - e=errno; + } while (ret == -1 && errno == EINTR); + e = errno; - if (ret == -1) { + if (ret == -1) { LOG(("wrote data: ERR %s", strerror(errno))); } else { LOG(("wrote data: OK")); } - errno=e; + errno = e; return ret; } -void yahoo_close(int id) +void yahoo_close(int id) { struct yahoo_data *yd = find_conn_by_id(id); - - if(!yd) + if (!yd) return; del_from_list(yd); yahoo_free_data(yd); - if(id == last_id) + if (id == last_id) last_id--; } -static void yahoo_input_close(struct yahoo_input_data *yid) +static void yahoo_input_close(struct yahoo_input_data *yid) { inputs = y_list_remove(inputs, yid); - LOG(("yahoo_input_close(read)")); - YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->read_tag); - LOG(("yahoo_input_close(write)")); - YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->write_tag); + LOG(("yahoo_input_close(read)")); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id, + yid->read_tag); + LOG(("yahoo_input_close(write)")); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id, + yid->write_tag); yid->read_tag = yid->write_tag = 0; - if(yid->fd) + if (yid->fd) close(yid->fd); yid->fd = 0; FREE(yid->rxqueue); - if(count_inputs_with_id(yid->yd->client_id) == 0) { + if (count_inputs_with_id(yid->yd->client_id) == 0) { LOG(("closing %d", yid->yd->client_id)); yahoo_close(yid->yd->client_id); } yahoo_free_webcam(yid->wcm); - if(yid->wcd) + if (yid->wcd) FREE(yid->wcd); - if(yid->ys) { + if (yid->ys) { FREE(yid->ys->lsearch_text); FREE(yid->ys); } FREE(yid); } -static int is_same_bud(const void * a, const void * b) { +static int is_same_bud(const void *a, const void *b) +{ const struct yahoo_buddy *subject = a; const struct yahoo_buddy *object = b; return strcmp(subject->id, object->id); } -static char * getcookie(char *rawcookie) +static char *getcookie(char *rawcookie) { - char * cookie=NULL; - char * tmpcookie; - char * cookieend; + char *cookie = NULL; + char *tmpcookie; + char *cookieend; - if (strlen(rawcookie) < 2) + if (strlen(rawcookie) < 2) return NULL; - tmpcookie = strdup(rawcookie+2); + tmpcookie = strdup(rawcookie + 2); cookieend = strchr(tmpcookie, ';'); - if(cookieend) + if (cookieend) *cookieend = '\0'; cookie = strdup(tmpcookie); @@ -876,18 +884,18 @@ static char * getcookie(char *rawcookie) return cookie; } -static char * getlcookie(char *cookie) +static char *getlcookie(char *cookie) { char *tmp; char *tmpend; char *login_cookie = NULL; tmpend = strstr(cookie, "n="); - if(tmpend) { - tmp = strdup(tmpend+2); + if (tmpend) { + tmp = strdup(tmpend + 2); tmpend = strchr(tmp, '&'); - if(tmpend) - *tmpend='\0'; + if (tmpend) + *tmpend = '\0'; login_cookie = strdup(tmp); FREE(tmp); } @@ -895,7 +903,8 @@ static char * getlcookie(char *cookie) return login_cookie; } -static void yahoo_process_notify(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_notify(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *msg = NULL; @@ -926,40 +935,42 @@ static void yahoo_process_notify(struct yahoo_input_data *yid, struct yahoo_pack if (!msg) return; - - if (!strncasecmp(msg, "TYPING", strlen("TYPING"))) - YAHOO_CALLBACK(ext_yahoo_typing_notify)(yd->client_id, to, from, stat); - else if (!strncasecmp(msg, "GAME", strlen("GAME"))) - YAHOO_CALLBACK(ext_yahoo_game_notify)(yd->client_id, to, from, stat); - else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) - { + + if (!strncasecmp(msg, "TYPING", strlen("TYPING"))) + YAHOO_CALLBACK(ext_yahoo_typing_notify) (yd->client_id, to, + from, stat); + else if (!strncasecmp(msg, "GAME", strlen("GAME"))) + YAHOO_CALLBACK(ext_yahoo_game_notify) (yd->client_id, to, from, + stat); + else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) { if (!strcmp(ind, " ")) { - YAHOO_CALLBACK(ext_yahoo_webcam_invite)(yd->client_id, to, from); + YAHOO_CALLBACK(ext_yahoo_webcam_invite) (yd->client_id, + to, from); } else { accept = atoi(ind); /* accept the invitation (-1 = deny 1 = accept) */ if (accept < 0) accept = 0; - YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply)(yd->client_id, to, from, accept); + YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply) (yd-> + client_id, to, from, accept); } - } - else + } else LOG(("Got unknown notification: %s", msg)); } static void yahoo_process_filetransfer(struct yahoo_input_data *yid, struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; - char *from=NULL; - char *to=NULL; - char *msg=NULL; - char *url=NULL; - long expires=0; + char *from = NULL; + char *to = NULL; + char *msg = NULL; + char *url = NULL; + long expires = 0; - char *service=NULL; + char *service = NULL; - char *filename=NULL; - unsigned long filesize=0L; + char *filename = NULL; + unsigned long filesize = 0L; YList *l; for (l = pkt->hash; l; l = l->next) { @@ -984,22 +995,22 @@ static void yahoo_process_filetransfer(struct yahoo_input_data *yid, struct yaho service = pair->value; } - if(pkt->service == YAHOO_SERVICE_P2PFILEXFER) { - if(strcmp("FILEXFER", service) != 0) { + if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) { + if (strcmp("FILEXFER", service) != 0) { WARNING(("unhandled service 0x%02x", pkt->service)); yahoo_dump_unhandled(pkt); return; } } - if(msg) { + if (msg) { char *tmp; tmp = strchr(msg, '\006'); - if(tmp) + if (tmp) *tmp = '\0'; } - if(url && from) - YAHOO_CALLBACK(ext_yahoo_got_file)(yd->client_id, to, from, url, expires, msg, filename, filesize); + if (url && from) + YAHOO_CALLBACK(ext_yahoo_got_file) (yd->client_id, to, from, url, expires, msg, filename, filesize); } @@ -1011,98 +1022,103 @@ static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_ char *who = NULL; char *room = NULL; char *id = NULL; - int utf8 = 0; + int utf8 = 0; YList *members = NULL; YList *l; - + for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; if (pair->key == 50) host = pair->value; - - if (pair->key == 52) { /* invite */ + + if (pair->key == 52) { /* invite */ members = y_list_append(members, strdup(pair->value)); } - if (pair->key == 53) /* logon */ + if (pair->key == 53) /* logon */ who = pair->value; - if (pair->key == 54) /* decline */ + if (pair->key == 54) /* decline */ who = pair->value; - if (pair->key == 55) /* unavailable (status == 2) */ + if (pair->key == 55) /* unavailable (status == 2) */ who = pair->value; - if (pair->key == 56) /* logoff */ + if (pair->key == 56) /* logoff */ who = pair->value; if (pair->key == 57) room = pair->value; - if (pair->key == 58) /* join message */ + if (pair->key == 58) /* join message */ msg = pair->value; - if (pair->key == 14) /* decline/conf message */ + if (pair->key == 14) /* decline/conf message */ msg = pair->value; - if (pair->key == 13) - ; - if (pair->key == 16) /* error */ + if (pair->key == 13) ; + if (pair->key == 16) /* error */ msg = pair->value; - if (pair->key == 1) /* my id */ + if (pair->key == 1) /* my id */ id = pair->value; - if (pair->key == 3) /* message sender */ + if (pair->key == 3) /* message sender */ who = pair->value; if (pair->key == 97) utf8 = atoi(pair->value); } - if(!room) + if (!room) return; - if(host) { - for(l = members; l; l = l->next) { - char * w = l->data; - if(!strcmp(w, host)) + if (host) { + for (l = members; l; l = l->next) { + char *w = l->data; + if (!strcmp(w, host)) break; } - if(!l) + if (!l) members = y_list_append(members, strdup(host)); } /* invite, decline, join, left, message -> status == 1 */ - switch(pkt->service) { + switch (pkt->service) { case YAHOO_SERVICE_CONFINVITE: - if(pkt->status == 2) - ; - else if(members) - YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, id, host, room, msg, members); - else if(msg) - YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, msg, 0, E_CONFNOTAVAIL); + if (pkt->status == 2) ; + else if (members) + YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd-> + client_id, id, host, room, msg, members); + else if (msg) + YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, msg, 0, + E_CONFNOTAVAIL); break; case YAHOO_SERVICE_CONFADDINVITE: - if(pkt->status == 2) + if (pkt->status == 2) ; else - YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, id, host, room, msg, members); + YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd->client_id, id, host, room, msg, members); break; case YAHOO_SERVICE_CONFDECLINE: - if(who) - YAHOO_CALLBACK(ext_yahoo_conf_userdecline)(yd->client_id, id, who, room, msg); + if (who) + YAHOO_CALLBACK(ext_yahoo_conf_userdecline) (yd-> + client_id, id, who, room, msg); break; case YAHOO_SERVICE_CONFLOGON: - if(who) - YAHOO_CALLBACK(ext_yahoo_conf_userjoin)(yd->client_id, id, who, room); + if (who) + YAHOO_CALLBACK(ext_yahoo_conf_userjoin) (yd->client_id, + id, who, room); break; case YAHOO_SERVICE_CONFLOGOFF: - if(who) - YAHOO_CALLBACK(ext_yahoo_conf_userleave)(yd->client_id, id, who, room); + if (who) + YAHOO_CALLBACK(ext_yahoo_conf_userleave) (yd->client_id, + id, who, room); break; case YAHOO_SERVICE_CONFMSG: - if(who) - YAHOO_CALLBACK(ext_yahoo_conf_message)(yd->client_id, id, who, room, msg, utf8); + if (who) + YAHOO_CALLBACK(ext_yahoo_conf_message) (yd->client_id, + id, who, room, msg, utf8); break; } } -static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_chat(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { char *msg = NULL; char *id = NULL; @@ -1111,11 +1127,11 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet char *topic = NULL; YList *members = NULL; struct yahoo_chat_member *currentmember = NULL; - int msgtype = 1; - int utf8 = 0; - int firstjoin = 0; - int membercount = 0; - int chaterr=0; + int msgtype = 1; + int utf8 = 0; + int firstjoin = 0; + int membercount = 0; + int chaterr = 0; YList *l; yahoo_dump_unhandled(pkt); @@ -1147,7 +1163,8 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet who = pair->value; if (pkt->service == YAHOO_SERVICE_CHATJOIN) { - currentmember = y_new0(struct yahoo_chat_member, 1); + currentmember = + y_new0(struct yahoo_chat_member, 1); currentmember->id = strdup(pair->value); members = y_list_append(members, currentmember); } @@ -1177,7 +1194,6 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet currentmember->location = strdup(pair->value); } - if (pair->key == 130) { /* first join */ firstjoin = 1; @@ -1195,17 +1211,19 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet if (pair->key == 114) { /* message error not sure what all the pair values mean */ /* but -1 means no session in room */ - chaterr= atoi(pair->value); + chaterr = atoi(pair->value); } } - if(!room) { - if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */ - YAHOO_CALLBACK(ext_yahoo_chat_yahoologout)(yid->yd->client_id, id); - return ; + if (!room) { + if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */ + YAHOO_CALLBACK(ext_yahoo_chat_yahoologout) (yid->yd-> + client_id, id); + return; } - if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr) { - YAHOO_CALLBACK(ext_yahoo_chat_yahooerror)(yid->yd->client_id, id); + if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr) { + YAHOO_CALLBACK(ext_yahoo_chat_yahooerror) (yid->yd-> + client_id, id); return; } @@ -1213,64 +1231,67 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet return; } - switch(pkt->service) { + switch (pkt->service) { case YAHOO_SERVICE_CHATJOIN: - if(y_list_length(members) != membercount) { + if (y_list_length(members) != membercount) { WARNING(("Count of members doesn't match No. of members we got")); } - if(firstjoin && members) { - YAHOO_CALLBACK(ext_yahoo_chat_join)(yid->yd->client_id, id, room, topic, members, yid->fd); - } else if(who) { - if(y_list_length(members) != 1) { + if (firstjoin && members) { + YAHOO_CALLBACK(ext_yahoo_chat_join) (yid->yd->client_id, + id, room, topic, members, yid->fd); + } else if (who) { + if (y_list_length(members) != 1) { WARNING(("Got more than 1 member on a normal join")); } /* this should only ever have one, but just in case */ - while(members) { + while (members) { YList *n = members->next; currentmember = members->data; - YAHOO_CALLBACK(ext_yahoo_chat_userjoin)(yid->yd->client_id, id, room, currentmember); + YAHOO_CALLBACK(ext_yahoo_chat_userjoin) (yid-> + yd->client_id, id, room, currentmember); y_list_free_1(members); - members=n; + members = n; } } break; case YAHOO_SERVICE_CHATEXIT: - if(who) { - YAHOO_CALLBACK(ext_yahoo_chat_userleave)(yid->yd->client_id, id, room, who); + if (who) { + YAHOO_CALLBACK(ext_yahoo_chat_userleave) (yid->yd-> + client_id, id, room, who); } break; case YAHOO_SERVICE_COMMENT: - if(who) { - YAHOO_CALLBACK(ext_yahoo_chat_message)(yid->yd->client_id, id, who, room, msg, msgtype, utf8); + if (who) { + YAHOO_CALLBACK(ext_yahoo_chat_message) (yid->yd-> + client_id, id, who, room, msg, msgtype, utf8); } break; } } -static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_message(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; YList *l; - YList * messages = NULL; + YList *messages = NULL; struct m { - int i_31; - int i_32; + int i_31; + int i_32; char *to; char *from; long tm; char *msg; - int utf8; + int utf8; } *message = y_new0(struct m, 1); for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; - if (pair->key == 1 || pair->key == 4) - { - if(!message->from) + if (pair->key == 1 || pair->key == 4) { + if (!message->from) message->from = pair->value; - } - else if (pair->key == 5) + } else if (pair->key == 5) message->to = pair->value; else if (pair->key == 15) message->tm = strtol(pair->value, NULL, 10); @@ -1280,29 +1301,27 @@ static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_pac else if (pair->key == 14 || pair->key == 16) message->msg = pair->value; else if (pair->key == 31) { - if(message->i_31) { + if (message->i_31) { messages = y_list_append(messages, message); message = y_new0(struct m, 1); } message->i_31 = atoi(pair->value); - } - else if (pair->key == 32) + } else if (pair->key == 32) message->i_32 = atoi(pair->value); else - LOG(("yahoo_process_message: status: %d, key: %d, value: %s", - pkt->status, pair->key, pair->value)); + LOG(("yahoo_process_message: status: %d, key: %d, value: %s", pkt->status, pair->key, pair->value)); } messages = y_list_append(messages, message); - for (l = messages; l; l=l->next) { + for (l = messages; l; l = l->next) { message = l->data; if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) { - YAHOO_CALLBACK(ext_yahoo_system_message)(yd->client_id, message->msg); + YAHOO_CALLBACK(ext_yahoo_system_message) (yd->client_id, message->msg); } else if (pkt->status <= 2 || pkt->status == 5) { - YAHOO_CALLBACK(ext_yahoo_got_im)(yd->client_id, message->to, message->from, message->msg, message->tm, pkt->status, message->utf8); + YAHOO_CALLBACK(ext_yahoo_got_im) (yd->client_id, message->to, message->from, message->msg, message->tm, pkt->status, message->utf8); } else if (pkt->status == 0xffffffff) { - YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, message->msg, 0, E_SYSTEM); + YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, message->msg, 0, E_SYSTEM); } free(message); } @@ -1326,10 +1345,12 @@ static void yahoo_process_status(struct yahoo_input_data *yid, return; } - /* Status updates may be spread accross multiple packets and not - even on buddy boundaries, so keeping some state is important. - So, continue where we left off, and only add a user entry to - the list once it's complete (301-315 End buddy). */ + /* + * Status updates may be spread accross multiple packets and not + * even on buddy boundaries, so keeping some state is important. + * So, continue where we left off, and only add a user entry to + * the list once it's complete (301-315 End buddy). + */ u = yd->half_user; for (l = pkt->hash; l; l = l->next) { @@ -1593,17 +1614,20 @@ static void yahoo_process_list(struct yahoo_input_data *yid, YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id); } -static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_verify(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; - if(pkt->status != 0x01) { + if (pkt->status != 0x01) { DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status)); - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_LOCK, ""); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_LOCK, ""); return; } - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT, + yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); yahoo_send_packet(yid, pkt, 0); @@ -1612,7 +1636,8 @@ static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_pack } -static void yahoo_process_picture_checksum( struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_picture_checksum(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *from = NULL; @@ -1620,30 +1645,30 @@ static void yahoo_process_picture_checksum( struct yahoo_input_data *yid, struct int checksum = 0; YList *l; - for(l = pkt->hash; l; l = l->next) - { + for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; - switch(pair->key) - { - case 1: - case 4: - from = pair->value; - case 5: - to = pair->value; - break; - case 212: - break; - case 192: - checksum = atoi( pair->value ); - break; + switch (pair->key) { + case 1: + case 4: + from = pair->value; + case 5: + to = pair->value; + break; + case 212: + break; + case 192: + checksum = atoi(pair->value); + break; } } - YAHOO_CALLBACK(ext_yahoo_got_buddyicon_checksum)(yd->client_id,to,from,checksum); + YAHOO_CALLBACK(ext_yahoo_got_buddyicon_checksum) (yd->client_id, to, + from, checksum); } -static void yahoo_process_picture(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_picture(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *url = NULL; @@ -1652,44 +1677,44 @@ static void yahoo_process_picture(struct yahoo_input_data *yid, struct yahoo_pac int status = 0; int checksum = 0; YList *l; - - for(l = pkt->hash; l; l = l->next) - { + + for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; - switch(pair->key) - { + switch (pair->key) { case 1: - case 4: /* sender */ + case 4: /* sender */ from = pair->value; break; - case 5: /* we */ + case 5: /* we */ to = pair->value; break; - case 13: /* request / sending */ - status = atoi( pair->value ); + case 13: /* request / sending */ + status = atoi(pair->value); break; - case 20: /* url */ + case 20: /* url */ url = pair->value; break; case 192: /*checksum */ - checksum = atoi( pair->value ); + checksum = atoi(pair->value); break; } } - switch( status ) - { - case 1: /* this is a request, ignore for now */ - YAHOO_CALLBACK(ext_yahoo_got_buddyicon_request)(yd->client_id, to, from); - break; - case 2: /* this is cool - we get a picture :) */ - YAHOO_CALLBACK(ext_yahoo_got_buddyicon)(yd->client_id,to, from, url, checksum); - break; + switch (status) { + case 1: /* this is a request, ignore for now */ + YAHOO_CALLBACK(ext_yahoo_got_buddyicon_request) (yd->client_id, + to, from); + break; + case 2: /* this is cool - we get a picture :) */ + YAHOO_CALLBACK(ext_yahoo_got_buddyicon) (yd->client_id, to, + from, url, checksum); + break; } } -static void yahoo_process_picture_upload(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_picture_upload(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; YList *l; @@ -1698,7 +1723,7 @@ static void yahoo_process_picture_upload(struct yahoo_input_data *yid, struct ya if ( pkt->status != 1 ) return; /* something went wrong */ - for(l = pkt->hash; l; l = l->next) + for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; @@ -1716,7 +1741,7 @@ static void yahoo_process_picture_upload(struct yahoo_input_data *yid, struct ya } } - YAHOO_CALLBACK(ext_yahoo_buddyicon_uploaded)(yd->client_id, url); + YAHOO_CALLBACK(ext_yahoo_buddyicon_uploaded) (yd->client_id, url); } static void yahoo_process_auth_pre_0x0b(struct yahoo_input_data *yid, @@ -1872,8 +1897,8 @@ static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *se unsigned char magic_key_char[4]; const unsigned char *magic_ptr; - unsigned int magic[64]; - unsigned int magic_work=0; + unsigned int magic[64]; + unsigned int magic_work = 0; char comparison_src[20]; @@ -1990,8 +2015,8 @@ static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *se x = 0; do { - unsigned int bl = 0; - unsigned int cl = magic[magic_cnt++]; + unsigned int bl = 0; + unsigned int cl = magic[magic_cnt++]; if (magic_cnt >= magic_len) break; @@ -2017,15 +2042,15 @@ static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *se /* Dump magic key into a char for SHA1 action. */ - for(x = 0; x < 4; x++) + for (x = 0; x < 4; x++) magic_key_char[x] = comparison_src[x]; /* Compute values for recursive function table! */ memcpy( chal, magic_key_char, 4 ); x = 1; - for( i = 0; i < 0xFFFF && x; i++ ) + for ( i = 0; i < 0xFFFF && x; i++ ) { - for( j = 0; j < 5 && x; j++ ) + for ( j = 0; j < 5 && x; j++ ) { chal[4] = i; chal[5] = i >> 8; @@ -2033,7 +2058,7 @@ static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *se md5_init( &ctx ); md5_append( &ctx, chal, 7 ); md5_finish( &ctx, result ); - if( memcmp( comparison_src + 4, result, 16 ) == 0 ) + if ( memcmp( comparison_src + 4, result, 16 ) == 0 ) { depth = i; table = j; @@ -2109,9 +2134,9 @@ static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *se * our first authentication response. */ for (x = 0; x < 20; x += 2) { - unsigned int val = 0; - unsigned int lookup = 0; - char byte[6]; + unsigned int val = 0; + unsigned int lookup = 0; + char byte[6]; memset(&byte, 0, 6); @@ -2352,17 +2377,17 @@ static void yahoo_https_auth_token_finish(struct http_request *req) yd = yid->yd; if (req->status_code != 200) { - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 2000 + req->status_code, NULL); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL); goto fail; } if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) { - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, yahoo_https_status_parse(st), NULL); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL); goto fail; } if ((had->token = yahoo_ha_find_key(req->reply_body, "ymsgr")) == NULL) { - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 3001, NULL); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3001, NULL); goto fail; } @@ -2408,19 +2433,19 @@ static void yahoo_https_auth_finish(struct http_request *req) unsigned char yhash[32]; if (req->status_code != 200) { - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 2000 + req->status_code, NULL); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL); goto fail; } if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) { - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, yahoo_https_status_parse(st), NULL); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL); goto fail; } if ((yd->cookie_y = yahoo_ha_find_key(req->reply_body, "Y")) == NULL || (yd->cookie_t = yahoo_ha_find_key(req->reply_body, "T")) == NULL || (crumb = yahoo_ha_find_key(req->reply_body, "crumb")) == NULL) { - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 3002, NULL); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3002, NULL); goto fail; } @@ -2497,8 +2522,8 @@ static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_p struct yahoo_data *yd = yid->yd; char *login_id; char *handle; - char *url=NULL; - int login_status=0; + char *url = NULL; + int login_status = 0; YList *l; @@ -2514,13 +2539,14 @@ static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_p login_status = atoi(pair->value); } - if(pkt->status == 0xffffffff) { - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, login_status, url); + if (pkt->status == 0xffffffff) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, login_status, url); /* yahoo_logoff(yd->client_id);*/ } } -static void yahoo_process_mail(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_mail(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *who = NULL; @@ -2546,12 +2572,15 @@ static void yahoo_process_mail(struct yahoo_input_data *yid, struct yahoo_packet if (who && email && subj) { char from[1024]; snprintf(from, sizeof(from), "%s (%s)", who, email); - YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, from, subj, count); - } else if(count > 0) - YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, NULL, NULL, count); + YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, from, + subj, count); + } else if (count > 0) + YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, NULL, + NULL, count); } -static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_contact(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *id = NULL; @@ -2560,7 +2589,7 @@ static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_pac char *name = NULL; long tm = 0L; int state = YAHOO_STATUS_AVAILABLE; - int online = FALSE; + int online = 0; int away = 0; int idle = 0; int mobile = 0; @@ -2589,18 +2618,21 @@ static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_pac idle = strtol(pair->value, NULL, 10); else if (pair->key == 60) mobile = strtol(pair->value, NULL, 10); - + } if (id) - YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, id, who, msg); + YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, id, who, + msg); else if (name) - YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away, idle, mobile); - else if(pkt->status == 0x07) - YAHOO_CALLBACK(ext_yahoo_rejected)(yd->client_id, who, msg); + YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, name, + state, msg, away, idle, mobile); + else if (pkt->status == 0x07) + YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg); } -static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_buddyadd(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *who = NULL; @@ -2608,7 +2640,7 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa int status = 0; char *me = NULL; - struct yahoo_buddy *bud=NULL; + struct yahoo_buddy *bud = NULL; YList *l; for (l = pkt->hash; l; l = l->next) { @@ -2623,15 +2655,13 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa status = strtol(pair->value, NULL, 10); } - yahoo_dump_unhandled(pkt); - - if(!who) + if (!who) return; - if(!where) + if (!where) where = "Unknown"; /* status: 0 == Successful, 1 == Error (does not exist), 2 == Already in list */ - if( status == 0 ) { + if ( status == 0 ) { bud = y_new0(struct yahoo_buddy, 1); bud->id = strdup(who); bud->group = strdup(where); @@ -2641,17 +2671,17 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa /* Possibly called already, but at least the call above doesn't seem to happen every time (not anytime I tried). */ - YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, me, who, NULL); + YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, me, who, NULL); } -/* YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */ +/* YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */ } static void yahoo_process_contact_ymsg13(struct yahoo_input_data *yid, struct yahoo_packet *pkt) { - char* who=NULL; - char* me=NULL; - char* msg=NULL; + char* who = NULL; + char* me = NULL; + char* msg = NULL; YList *l; for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; @@ -2663,11 +2693,12 @@ static void yahoo_process_contact_ymsg13(struct yahoo_input_data *yid, struct ya DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value)); } - if(pkt->status==3) - YAHOO_CALLBACK(ext_yahoo_contact_auth_request)(yid->yd->client_id, me, who, msg); + if (pkt->status==3) + YAHOO_CALLBACK(ext_yahoo_contact_auth_request) (yid->yd->client_id, me, who, msg); } -static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_buddydel(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *who = NULL; @@ -2690,12 +2721,13 @@ static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_pa else if (pair->key == 66) unk_66 = strtol(pair->value, NULL, 10); else - DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value)); + DEBUG_MSG(("unknown key: %d = %s", pair->key, + pair->value)); } - if(!who || !where) + if (!who || !where) return; - + bud = y_new0(struct yahoo_buddy, 1); bud->id = strdup(who); bud->group = strdup(where); @@ -2706,7 +2738,7 @@ static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_pa FREE(bud->group); FREE(bud); - if(buddy) { + if (buddy) { bud = buddy->data; yd->buddies = y_list_remove_link(yd->buddies, buddy); y_list_free_1(buddy); @@ -2716,16 +2748,17 @@ static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_pa FREE(bud->real_name); FREE(bud); - bud=NULL; + bud = NULL; } } -static void yahoo_process_ignore(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_ignore(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { char *who = NULL; - int status = 0; + int status = 0; char *me = NULL; - int un_ignore = 0; + int un_ignore = 0; YList *l; for (l = pkt->hash; l; l = l->next) { @@ -2734,27 +2767,27 @@ static void yahoo_process_ignore(struct yahoo_input_data *yid, struct yahoo_pack who = pair->value; if (pair->key == 1) me = pair->value; - if (pair->key == 13) /* 1 == ignore, 2 == unignore */ + if (pair->key == 13) /* 1 == ignore, 2 == unignore */ un_ignore = strtol(pair->value, NULL, 10); - if (pair->key == 66) + if (pair->key == 66) status = strtol(pair->value, NULL, 10); } - /* * status - * 0 - ok - * 2 - already in ignore list, could not add - * 3 - not in ignore list, could not delete - * 12 - is a buddy, could not add + * 0 - ok + * 2 - already in ignore list, could not add + * 3 - not in ignore list, could not delete + * 12 - is a buddy, could not add */ /* if(status) - YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, who, 0, status); -*/ + YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, who, 0, status); +*/ } -static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_voicechat(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { char *who = NULL; char *me = NULL; @@ -2769,12 +2802,13 @@ static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_p if (pair->key == 5) me = pair->value; if (pair->key == 13) - voice_room=pair->value; - if (pair->key == 57) - room=pair->value; + voice_room = pair->value; + if (pair->key == 57) + room = pair->value; } - NOTICE(("got voice chat invite from %s in %s to identity %s", who, room, me)); + NOTICE(("got voice chat invite from %s in %s to identity %s", who, room, + me)); /* * send: s:0 1:me 5:who 57:room 13:1 * ???? s:4 5:who 10:99 19:-1615114531 @@ -2786,19 +2820,20 @@ static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_p */ } -static void yahoo_process_ping(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_ping(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { char *errormsg = NULL; - + YList *l; for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; if (pair->key == 16) errormsg = pair->value; } - + NOTICE(("got ping packet")); - YAHOO_CALLBACK(ext_yahoo_got_ping)(yid->yd->client_id, errormsg); + YAHOO_CALLBACK(ext_yahoo_got_ping) (yid->yd->client_id, errormsg); } static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) @@ -2807,12 +2842,12 @@ static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) char *who = yid->wcm->user; char *data = NULL; char *packet = NULL; - unsigned char magic_nr[] = {0, 1, 0}; + unsigned char magic_nr[] = { 0, 1, 0 }; unsigned char header_len = 8; unsigned int len = 0; unsigned int pos = 0; - if(error || fd <= 0) { + if (error || fd <= 0) { FREE(who); FREE(yid); return; @@ -2820,7 +2855,7 @@ static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) yid->fd = fd; inputs = y_list_prepend(inputs, yid); - + /* send initial packet */ if (who) data = strdup(""); @@ -2830,8 +2865,7 @@ static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) FREE(data); /* send data */ - if (who) - { + if (who) { data = strdup("g="); data = y_string_append(data, who); data = y_string_append(data, "\r\n"); @@ -2850,10 +2884,13 @@ static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) FREE(packet); FREE(data); - yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid); + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, + YAHOO_INPUT_READ, yid); } -static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, char *key) +static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, + char *key) { struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); struct yahoo_server_settings *yss = y->yd->server_settings; @@ -2861,34 +2898,36 @@ static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, char yid->type = YAHOO_CONNECTION_WEBCAM_MASTER; yid->yd = y->yd; yid->wcm = y_new0(struct yahoo_webcam, 1); - yid->wcm->user = who?strdup(who):NULL; - yid->wcm->direction = who?YAHOO_WEBCAM_DOWNLOAD:YAHOO_WEBCAM_UPLOAD; + yid->wcm->user = who ? strdup(who) : NULL; + yid->wcm->direction = who ? YAHOO_WEBCAM_DOWNLOAD : YAHOO_WEBCAM_UPLOAD; yid->wcm->key = strdup(key); - YAHOO_CALLBACK(ext_yahoo_connect_async)(yid->yd->client_id, yss->webcam_host, yss->webcam_port, - _yahoo_webcam_get_server_connected, yid); + YAHOO_CALLBACK(ext_yahoo_connect_async) (yid->yd->client_id, + yss->webcam_host, yss->webcam_port, + _yahoo_webcam_get_server_connected, yid); } -static YList *webcam_queue=NULL; -static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static YList *webcam_queue = NULL; +static void yahoo_process_webcam_key(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { char *me = NULL; char *key = NULL; char *who = NULL; YList *l; - // yahoo_dump_unhandled(pkt); + yahoo_dump_unhandled(pkt); for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; if (pair->key == 5) me = pair->value; - if (pair->key == 61) - key=pair->value; + if (pair->key == 61) + key = pair->value; } l = webcam_queue; - if(!l) + if (!l) return; who = l->data; webcam_queue = y_list_remove_link(webcam_queue, webcam_queue); @@ -2897,12 +2936,11 @@ static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_ FREE(who); } -static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_packet_process(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service)); - yahoo_dump_unhandled(pkt); - switch (pkt->service) - { + switch (pkt->service) { case YAHOO_SERVICE_USERSTAT: case YAHOO_SERVICE_LOGON: case YAHOO_SERVICE_LOGOFF: @@ -3021,14 +3059,14 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack } } -static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid) +static struct yahoo_packet *yahoo_getdata(struct yahoo_input_data *yid) { struct yahoo_packet *pkt; struct yahoo_data *yd = yid->yd; int pos = 0; int pktlen; - if(!yd) + if (!yd) return NULL; DEBUG_MSG(("rxlen is %d", yid->rxlen)); @@ -3037,13 +3075,13 @@ static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid) return NULL; } - pos += 4; /* YMSG */ + pos += 4; /* YMSG */ pos += 2; pos += 2; - pktlen = yahoo_get16(yid->rxqueue + pos); pos += 2; - DEBUG_MSG(("%d bytes to read, rxlen is %d", - pktlen, yid->rxlen)); + pktlen = yahoo_get16(yid->rxqueue + pos); + pos += 2; + DEBUG_MSG(("%d bytes to read, rxlen is %d", pktlen, yid->rxlen)); if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) { DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen")); @@ -3055,11 +3093,14 @@ static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid) pkt = yahoo_packet_new(0, 0, 0); - pkt->service = yahoo_get16(yid->rxqueue + pos); pos += 2; - pkt->status = yahoo_get32(yid->rxqueue + pos); pos += 4; + pkt->service = yahoo_get16(yid->rxqueue + pos); + pos += 2; + pkt->status = yahoo_get32(yid->rxqueue + pos); + pos += 4; DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service, - pkt->status)); - pkt->id = yahoo_get32(yid->rxqueue + pos); pos += 4; + pkt->status)); + pkt->id = yahoo_get32(yid->rxqueue + pos); + pos += 4; yd->session_id = pkt->id; @@ -3067,12 +3108,13 @@ static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid) yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen; DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); - if (yid->rxlen>0) { - unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN - + pktlen, yid->rxlen); + if (yid->rxlen > 0) { + unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN + + pktlen, yid->rxlen); FREE(yid->rxqueue); yid->rxqueue = tmp; - DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); } else { DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); FREE(yid->rxqueue); @@ -3081,136 +3123,166 @@ static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid) return pkt; } -static void yahoo_yab_read(struct yab *yab, unsigned char *d, int len) +static struct yab *yahoo_yab_read(unsigned char *d, int len) { char *st, *en; char *data = (char *)d; - data[len]='\0'; + struct yab *yab = NULL; + + data[len] = '\0'; DEBUG_MSG(("Got yab: %s", data)); - st = en = strstr(data, "userid=\""); - if(st) { - st += strlen("userid=\""); - en = strchr(st, '"'); *en++ = '\0'; - yab->id = yahoo_xmldecode(st); + st = en = strstr(data, "e0=\""); + if (st) { + yab = y_new0(struct yab, 1); + + st += strlen("e0=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->email = yahoo_xmldecode(st); + } + + if (!en) + return NULL; + + st = strstr(en, "id=\""); + if (st) { + st += strlen("id=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->yid = atoi(yahoo_xmldecode(st)); } - st = strstr(en, "fname=\""); - if(st) { - st += strlen("fname=\""); - en = strchr(st, '"'); *en++ = '\0'; + st = strstr(en, "fn=\""); + if (st) { + st += strlen("fn=\""); + en = strchr(st, '"'); + *en++ = '\0'; yab->fname = yahoo_xmldecode(st); } - st = strstr(en, "lname=\""); - if(st) { - st += strlen("lname=\""); - en = strchr(st, '"'); *en++ = '\0'; + st = strstr(en, "ln=\""); + if (st) { + st += strlen("ln=\""); + en = strchr(st, '"'); + *en++ = '\0'; yab->lname = yahoo_xmldecode(st); } - st = strstr(en, "nname=\""); - if(st) { - st += strlen("nname=\""); - en = strchr(st, '"'); *en++ = '\0'; + st = strstr(en, "nn=\""); + if (st) { + st += strlen("nn=\""); + en = strchr(st, '"'); + *en++ = '\0'; yab->nname = yahoo_xmldecode(st); } - st = strstr(en, "email=\""); - if(st) { - st += strlen("email=\""); - en = strchr(st, '"'); *en++ = '\0'; - yab->email = yahoo_xmldecode(st); + st = strstr(en, "yi=\""); + if (st) { + st += strlen("yi=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->id = yahoo_xmldecode(st); } st = strstr(en, "hphone=\""); - if(st) { + if (st) { st += strlen("hphone=\""); - en = strchr(st, '"'); *en++ = '\0'; + en = strchr(st, '"'); + *en++ = '\0'; yab->hphone = yahoo_xmldecode(st); } st = strstr(en, "wphone=\""); - if(st) { + if (st) { st += strlen("wphone=\""); - en = strchr(st, '"'); *en++ = '\0'; + en = strchr(st, '"'); + *en++ = '\0'; yab->wphone = yahoo_xmldecode(st); } st = strstr(en, "mphone=\""); - if(st) { + if (st) { st += strlen("mphone=\""); - en = strchr(st, '"'); *en++ = '\0'; + en = strchr(st, '"'); + *en++ = '\0'; yab->mphone = yahoo_xmldecode(st); } st = strstr(en, "dbid=\""); - if(st) { + if (st) { st += strlen("dbid=\""); - en = strchr(st, '"'); *en++ = '\0'; + en = strchr(st, '"'); + *en++ = '\0'; yab->dbid = atoi(st); } + + return yab; } -static struct yab * yahoo_getyab(struct yahoo_input_data *yid) +static struct yab *yahoo_getyab(struct yahoo_input_data *yid) { struct yab *yab = NULL; - int pos = 0, end=0; + int pos = 0, end = 0; struct yahoo_data *yd = yid->yd; - if(!yd) + if (!yd) return NULL; - DEBUG_MSG(("rxlen is %d", yid->rxlen)); - - if(yid->rxlen <= strlen("rxlen-strlen("rxqueue + pos, "= yid->rxlen-1) - return NULL; - - end = pos+2; - /* end with /> */ - while(end < yid->rxlen-strlen("/>")+1 && memcmp(yid->rxqueue + end, "/>", strlen("/>"))) - end++; - - if(end >= yid->rxlen-1) - return NULL; - - yab = y_new0(struct yab, 1); - yahoo_yab_read(yab, yid->rxqueue + pos, end+2-pos); - - - yid->rxlen -= end+1; - DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); - if (yid->rxlen>0) { - unsigned char *tmp = y_memdup(yid->rxqueue + end + 1, yid->rxlen); - FREE(yid->rxqueue); - yid->rxqueue = tmp; - DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); - } else { - DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); - FREE(yid->rxqueue); - } + do { + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + + if (yid->rxlen <= strlen("rxlen - strlen("rxqueue + pos, "= yid->rxlen - 1) + return NULL; + + end = pos + 2; + /* end with > */ + while (end < yid->rxlen - strlen(">") + && memcmp(yid->rxqueue + end, ">", strlen(">"))) + end++; + + if (end >= yid->rxlen - 1) + return NULL; + + yab = yahoo_yab_read(yid->rxqueue + pos, end + 2 - pos); + + yid->rxlen -= end + 1; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); + if (yid->rxlen > 0) { + unsigned char *tmp = + y_memdup(yid->rxqueue + end + 1, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + } while (!yab && end < yid->rxlen - 1); return yab; } -static char * yahoo_getwebcam_master(struct yahoo_input_data *yid) +static char *yahoo_getwebcam_master(struct yahoo_input_data *yid) { - unsigned int pos=0; - unsigned int len=0; - unsigned int status=0; - char *server=NULL; + unsigned int pos = 0; + unsigned int len = 0; + unsigned int status = 0; + char *server = NULL; struct yahoo_data *yd = yid->yd; - if(!yid || !yd) + if (!yid || !yd) return NULL; DEBUG_MSG(("rxlen is %d", yid->rxlen)); @@ -3222,14 +3294,11 @@ static char * yahoo_getwebcam_master(struct yahoo_input_data *yid) /* extract status (0 = ok, 6 = webcam not online) */ status = yid->rxqueue[pos++]; - if (status == 0) - { - pos += 2; /* skip next 2 bytes */ - server = y_memdup(yid->rxqueue+pos, 16); + if (status == 0) { + pos += 2; /* skip next 2 bytes */ + server = y_memdup(yid->rxqueue + pos, 16); pos += 16; - } - else if (status == 6) - { + } else if (status == 6) { YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id, yid->wcm->user, 4); } @@ -3238,11 +3307,12 @@ static char * yahoo_getwebcam_master(struct yahoo_input_data *yid) yid->rxlen -= len; DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); - if (yid->rxlen>0) { + if (yid->rxlen > 0) { unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen); FREE(yid->rxqueue); yid->rxqueue = tmp; - DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); } else { DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); FREE(yid->rxqueue); @@ -3253,35 +3323,33 @@ static char * yahoo_getwebcam_master(struct yahoo_input_data *yid) static int yahoo_get_webcam_data(struct yahoo_input_data *yid) { - unsigned char reason=0; - unsigned int pos=0; - unsigned int begin=0; - unsigned int end=0; - unsigned int closed=0; - unsigned char header_len=0; + unsigned char reason = 0; + unsigned int pos = 0; + unsigned int begin = 0; + unsigned int end = 0; + unsigned int closed = 0; + unsigned char header_len = 0; char *who; - int connect=0; + int connect = 0; struct yahoo_data *yd = yid->yd; - if(!yd) + if (!yd) return -1; - if(!yid->wcm || !yid->wcd || !yid->rxlen) + if (!yid->wcm || !yid->wcd || !yid->rxlen) return -1; DEBUG_MSG(("rxlen is %d", yid->rxlen)); /* if we are not reading part of image then read header */ - if (!yid->wcd->to_read) - { - header_len=yid->rxqueue[pos++]; - yid->wcd->packet_type=0; + if (!yid->wcd->to_read) { + header_len = yid->rxqueue[pos++]; + yid->wcd->packet_type = 0; if (yid->rxlen < header_len) return 0; - if (header_len >= 8) - { + if (header_len >= 8) { reason = yid->rxqueue[pos++]; /* next 2 bytes should always be 05 00 */ pos += 2; @@ -3289,8 +3357,7 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) pos += 4; yid->wcd->to_read = yid->wcd->data_size; } - if (header_len >= 13) - { + if (header_len >= 13) { yid->wcd->packet_type = yid->rxqueue[pos++]; yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos); pos += 4; @@ -3302,7 +3369,8 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) begin = pos; pos += yid->wcd->to_read; - if (pos > yid->rxlen) pos = yid->rxlen; + if (pos > yid->rxlen) + pos = yid->rxlen; /* if it is not an image then make sure we have the whole packet */ if (yid->wcd->packet_type != 0x02) { @@ -3315,7 +3383,7 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) } DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type, - yid->wcd->data_size)); + yid->wcd->data_size)); /* find out what kind of packet we got */ switch (yid->wcd->packet_type) @@ -3331,7 +3399,7 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) { who = y_memdup(yid->rxqueue + begin, end - begin); who[end - begin - 1] = 0; - YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who + 2, 2); + YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id, who + 2, 2); FREE(who); } } @@ -3341,7 +3409,7 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) /* 0 = declined viewing permission */ /* 1 = accepted viewing permission */ if (yid->wcd->timestamp == 0) { - YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, 3); + YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id, yid->wcm->user, 3); } } break; @@ -3350,14 +3418,14 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) /* 00 00 00 01 = we have data?? */ break; case 0x02: /* image data */ - YAHOO_CALLBACK(ext_yahoo_got_webcam_image)(yd->client_id, + YAHOO_CALLBACK(ext_yahoo_got_webcam_image) (yd->client_id, yid->wcm->user, yid->rxqueue + begin, yid->wcd->data_size, pos - begin, yid->wcd->timestamp); break; case 0x05: /* response packets when uploading */ if (!yid->wcd->data_size) { - YAHOO_CALLBACK(ext_yahoo_webcam_data_request)(yd->client_id, yid->wcd->timestamp); + YAHOO_CALLBACK(ext_yahoo_webcam_data_request) (yd->client_id, yid->wcd->timestamp); } break; case 0x07: /* connection is closing */ @@ -3370,7 +3438,7 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) closed = 2; break; } - YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, closed); + YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id, yid->wcm->user, closed); break; case 0x0C: /* user connected */ case 0x0D: /* user disconnected */ @@ -3378,16 +3446,16 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) who = y_memdup(yid->rxqueue + begin, pos - begin + 1); who[pos - begin] = 0; if (yid->wcd->packet_type == 0x0C) - connect=1; + connect = 1; else - connect=0; - YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who, connect); + connect = 0; + YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id, who, connect); FREE(who); } break; case 0x13: /* user data */ - /* i=user_ip (ip of the user we are viewing) */ - /* j=user_ext_ip (external ip of the user we */ + /* i = user_ip (ip of the user we are viewing) */ + /* j = user_ext_ip (external ip of the user we */ /* are viewing) */ break; case 0x17: /* ?? */ @@ -3421,21 +3489,21 @@ int yahoo_write_ready(int id, int fd, void *data) struct data_queue *tx; LOG(("write callback: id=%d fd=%d data=%p", id, fd, data)); - if(!yid || !yid->txqueues || !find_conn_by_id(id)) + if (!yid || !yid->txqueues || !find_conn_by_id(id)) return -2; tx = yid->txqueues->data; LOG(("writing %d bytes", tx->len)); len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len)); - if(len == -1 && errno == EAGAIN) + if (len == -1 && errno == EAGAIN) return 1; - if(len <= 0) { + if (len <= 0) { int e = errno; DEBUG_MSG(("len == %d (<= 0)", len)); - while(yid->txqueues) { - YList *l=yid->txqueues; + while (yid->txqueues) { + YList *l = yid->txqueues; tx = l->data; free(tx->queue); free(tx); @@ -3443,31 +3511,31 @@ int yahoo_write_ready(int id, int fd, void *data) y_list_free_1(l); } LOG(("yahoo_write_ready(%d, %d) len < 0", id, fd)); - YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag); yid->write_tag = 0; - errno=e; + errno = e; return 0; } tx->len -= len; - if(tx->len > 0) { + if (tx->len > 0) { unsigned char *tmp = y_memdup(tx->queue + len, tx->len); FREE(tx->queue); tx->queue = tmp; } else { - YList *l=yid->txqueues; + YList *l = yid->txqueues; free(tx->queue); free(tx); yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues); y_list_free_1(l); /* - if(!yid->txqueues) + if (!yid->txqueues) LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); */ - if(!yid->txqueues) { + if (!yid->txqueues) { LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); - YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag); yid->write_tag = 0; } } @@ -3481,7 +3549,7 @@ static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int ove struct yahoo_data *yd = yid->yd; int id = yd->client_id; - if(over) + if (over) return; while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER) @@ -3499,11 +3567,11 @@ static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over) static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, int over) { - if(over) + if (over) return; if (strstr((char*)yid->rxqueue+(yid->rxlen-20), "")) { - YAHOO_CALLBACK(ext_yahoo_chat_cat_xml)(yid->yd->client_id, (char*)yid->rxqueue); + YAHOO_CALLBACK(ext_yahoo_chat_cat_xml) (yid->yd->client_id, (char*)yid->rxqueue); } } @@ -3512,31 +3580,31 @@ static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over) struct yahoo_data *yd = yid->yd; struct yab *yab; YList *buds; - int changed=0; + int changed = 0; int id = yd->client_id; - if(over) + if (over) return; - while(find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) + while (find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) && (yab = yahoo_getyab(yid)) != NULL) { - if(!yab->id) + if (!yab->id) continue; - changed=1; - for(buds = yd->buddies; buds; buds=buds->next) { - struct yahoo_buddy * bud = buds->data; - if(!strcmp(bud->id, yab->id)) { + changed = 1; + for (buds = yd->buddies; buds; buds = buds->next) { + struct yahoo_buddy *bud = buds->data; + if (!strcmp(bud->id, yab->id)) { bud->yab_entry = yab; - if(yab->nname) { + if (yab->nname) { bud->real_name = strdup(yab->nname); - } else if(yab->fname && yab->lname) { + } else if (yab->fname && yab->lname) { bud->real_name = y_new0(char, strlen(yab->fname)+ strlen(yab->lname)+2 ); sprintf(bud->real_name, "%s %s", yab->fname, yab->lname); - } else if(yab->fname) { + } else if (yab->fname) { bud->real_name = strdup(yab->fname); } break; /* for */ @@ -3544,26 +3612,26 @@ static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over) } } - if(changed) - YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies); + if (changed) + YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies); } static void yahoo_process_search_connection(struct yahoo_input_data *yid, int over) { - struct yahoo_found_contact *yct=NULL; + struct yahoo_found_contact *yct = NULL; char *p = (char *)yid->rxqueue, *np, *cp; int k, n; - int start=0, found=0, total=0; - YList *contacts=NULL; + int start = 0, found=0, total=0; + YList *contacts = NULL; struct yahoo_input_data *pyid = find_input_by_id_and_type(yid->yd->client_id, YAHOO_CONNECTION_PAGER); - if(!over || !pyid) + if (!over || !pyid) return; - if(p && (p=strstr(p, "\r\n\r\n"))) { + if (p && (p = strstr(p, "\r\n\r\n"))) { p += 4; - for(k = 0; (p = strchr(p, 4)) && (k < 4); k++) { + for (k = 0; (p = strchr(p, 4)) && (k < 4); k++) { p++; n = atoi(p); switch(k) { @@ -3573,22 +3641,22 @@ static void yahoo_process_search_connection(struct yahoo_input_data *yid, int ov } } - if(p) + if (p) p++; - k=0; - while(p && *p) { + k = 0; + while (p && *p) { cp = p; np = strchr(p, 4); - if(!np) + if (!np) break; *np = 0; p = np+1; switch(k++) { case 1: - if(strlen(cp) > 2 && y_list_length(contacts) < total) { + if (strlen(cp) > 2 && y_list_length(contacts) < total) { yct = y_new0(struct yahoo_found_contact, 1); contacts = y_list_append(contacts, yct); yct->id = cp+2; @@ -3606,7 +3674,7 @@ static void yahoo_process_search_connection(struct yahoo_input_data *yid, int ov yct->age = atoi(cp); break; case 5: - if(strcmp(cp, "5") != 0) + if (strcmp(cp, "5") != 0) yct->location = cp; k = 0; break; @@ -3614,9 +3682,9 @@ static void yahoo_process_search_connection(struct yahoo_input_data *yid, int ov } } - YAHOO_CALLBACK(ext_yahoo_got_search_result)(yid->yd->client_id, found, start, total, contacts); + YAHOO_CALLBACK(ext_yahoo_got_search_result) (yid->yd->client_id, found, start, total, contacts); - while(contacts) { + while (contacts) { YList *node = contacts; contacts = y_list_remove_link(contacts, node); free(node->data); @@ -3630,14 +3698,14 @@ static void _yahoo_webcam_connected(int fd, int error, void *d) struct yahoo_webcam *wcm = yid->wcm; struct yahoo_data *yd = yid->yd; char conn_type[100]; - char *data=NULL; - char *packet=NULL; + char *data = NULL; + char *packet = NULL; unsigned char magic_nr[] = {1, 0, 0, 0, 1}; - unsigned header_len=0; - unsigned int len=0; - unsigned int pos=0; + unsigned header_len = 0; + unsigned int len = 0; + unsigned int pos = 0; - if(error || fd <= 0) { + if (error || fd <= 0) { FREE(yid); return; } @@ -3723,7 +3791,7 @@ static void _yahoo_webcam_connected(int fd, int error, void *d) FREE(packet); FREE(data); - yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); + yid->read_tag = YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); } static void yahoo_webcam_connect(struct yahoo_input_data *y) @@ -3748,7 +3816,7 @@ static void yahoo_webcam_connect(struct yahoo_input_data *y) yid->wcd = y_new0(struct yahoo_webcam_data, 1); LOG(("Connecting to: %s:%d", wcm->server, wcm->port)); - YAHOO_CALLBACK(ext_yahoo_connect_async)(y->yd->client_id, wcm->server, wcm->port, + YAHOO_CALLBACK(ext_yahoo_connect_async) (y->yd->client_id, wcm->server, wcm->port, _yahoo_webcam_connected, yid); } @@ -3758,7 +3826,7 @@ static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, char* server; struct yahoo_server_settings *yss; - if(over) + if (over) return; server = yahoo_getwebcam_master(yid); @@ -3782,7 +3850,7 @@ static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, int ov int id = yid->yd->client_id; int fd = yid->fd; - if(over) + if (over) return; /* as long as we still have packets available keep processing them */ @@ -3807,33 +3875,33 @@ int yahoo_read_ready(int id, int fd, void *data) int len; LOG(("read callback: id=%d fd=%d data=%p", id, fd, data)); - if(!yid) + if (!yid) return -2; do { len = read(fd, buf, sizeof(buf)); - } while(len == -1 && errno == EINTR); + } while (len == -1 && errno == EINTR); - if(len == -1 && (errno == EAGAIN||errno == EINTR)) /* we'll try again later */ + if (len == -1 && (errno == EAGAIN||errno == EINTR)) /* we'll try again later */ return 1; if (len <= 0) { int e = errno; DEBUG_MSG(("len == %d (<= 0)", len)); - if(yid->type == YAHOO_CONNECTION_PAGER) { - YAHOO_CALLBACK(ext_yahoo_error)(yid->yd->client_id, "Connection closed by server", 1, E_CONNECTION); + if (yid->type == YAHOO_CONNECTION_PAGER) { + YAHOO_CALLBACK(ext_yahoo_error) (yid->yd->client_id, "Connection closed by server", 1, E_CONNECTION); } yahoo_process_connection[yid->type](yid, 1); yahoo_input_close(yid); /* no need to return an error, because we've already fixed it */ - if(len == 0) + if (len == 0) return 1; - errno=e; + errno = e; LOG(("read error: %s", strerror(errno))); return -1; } @@ -3854,7 +3922,7 @@ int yahoo_init_with_attributes(const char *username, const char *password, ...) yd = y_new0(struct yahoo_data, 1); - if(!yd) + if (!yd) return 0; yd->user = strdup(username); @@ -3893,18 +3961,18 @@ static void yahoo_connected(int fd, int error, void *data) struct yahoo_input_data *yid; struct yahoo_server_settings *yss = yd->server_settings; - if(error) { - if(fallback_ports[ccd->i]) { + if (error) { + if (fallback_ports[ccd->i]) { int tag; yss->pager_port = fallback_ports[ccd->i++]; - tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, + tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, yss->pager_host, yss->pager_port, yahoo_connected, ccd); - if(tag > 0) - ccd->tag=tag; + if (tag > 0) + ccd->tag = tag; } else { FREE(ccd); - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, YAHOO_LOGIN_SOCK, NULL); } return; } @@ -3912,7 +3980,7 @@ static void yahoo_connected(int fd, int error, void *data) FREE(ccd); /* fd < 0 && error == 0 means connect was cancelled */ - if(fd < 0) + if (fd < 0) return; pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id); @@ -3929,7 +3997,7 @@ static void yahoo_connected(int fd, int error, void *data) yahoo_packet_free(pkt); - yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); + yid->read_tag = YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); } void yahoo_login(int id, int initial) @@ -3939,7 +4007,7 @@ void yahoo_login(int id, int initial) struct yahoo_server_settings *yss; int tag; - if(!yd) + if (!yd) return; yss = yd->server_settings; @@ -3948,24 +4016,24 @@ void yahoo_login(int id, int initial) ccd = y_new0(struct connect_callback_data, 1); ccd->yd = yd; - tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, yss->pager_port, + tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, yss->pager_host, yss->pager_port, yahoo_connected, ccd); /* * if tag <= 0, then callback has already been called * so ccd will have been freed */ - if(tag > 0) + if (tag > 0) ccd->tag = tag; - else if(tag < 0) - YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL); + else if (tag < 0) + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, YAHOO_LOGIN_SOCK, NULL); } int yahoo_get_fd(int id) { struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); - if(!yid) + if (!yid) return 0; else return yid->fd; @@ -3978,7 +4046,7 @@ void yahoo_send_im(int id, const char *from, const char *who, const char *what, struct yahoo_data *yd; char pic_str[10]; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -3987,13 +4055,13 @@ void yahoo_send_im(int id, const char *from, const char *who, const char *what, snprintf(pic_str, sizeof(pic_str), "%d", picture); - if(from && strcmp(from, yd->user)) + if (from && strcmp(from, yd->user)) yahoo_packet_hash(pkt, 0, yd->user); yahoo_packet_hash(pkt, 1, from?from:yd->user); yahoo_packet_hash(pkt, 5, who); yahoo_packet_hash(pkt, 14, what); - if(utf8) + if (utf8) yahoo_packet_hash(pkt, 97, "1"); yahoo_packet_hash(pkt, 63, ";0"); /* imvironment name; or ;0 */ @@ -4011,7 +4079,7 @@ void yahoo_send_typing(int id, const char *from, const char *who, int typ) struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4036,7 +4104,7 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) int old_status; char s[4]; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4061,7 +4129,7 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); - if(old_status == YAHOO_STATUS_INVISIBLE) { + if (old_status == YAHOO_STATUS_INVISIBLE) { pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0); yahoo_packet_hash(pkt, 13, "1"); yahoo_send_packet(yid, pkt, 0); @@ -4075,13 +4143,13 @@ void yahoo_logoff(int id) struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; - if(!yid) + if (!yid) return; yd = yid->yd; LOG(("yahoo_logoff: current status: %d", yd->current_status)); - if(yd->current_status != -1 && 0) { + if (yd->current_status != -1 && 0) { /* Meh. Don't send this. The event handlers are not going to get to do this so it'll just leak memory. And the TCP connection reset will hopefully be clear enough. */ @@ -4096,7 +4164,7 @@ void yahoo_logoff(int id) do { yahoo_input_close(yid); - } while((yid = find_input_by_id(id))); + } while ((yid = find_input_by_id(id))); } void yahoo_get_list(int id) @@ -4105,7 +4173,7 @@ void yahoo_get_list(int id) struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4120,14 +4188,14 @@ void yahoo_get_list(int id) static void _yahoo_http_connected(int id, int fd, int error, void *data) { struct yahoo_input_data *yid = data; - if(fd <= 0) { + if (fd <= 0) { inputs = y_list_remove(inputs, yid); FREE(yid); return; } yid->fd = fd; - yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid); + yid->read_tag = YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, YAHOO_INPUT_READ, yid); } void yahoo_get_yab(int id) @@ -4137,7 +4205,7 @@ void yahoo_get_yab(int id) char url[1024]; char buff[1024]; - if(!yd) + if (!yd) return; yid = y_new0(struct yahoo_input_data, 1); @@ -4155,7 +4223,7 @@ void yahoo_get_yab(int id) _yahoo_http_connected, yid); } -void yahoo_set_yab(int id, struct yab * yab) +void yahoo_set_yab(int id, struct yab *yab) { struct yahoo_data *yd = find_conn_by_id(id); struct yahoo_input_data *yid; @@ -4164,7 +4232,7 @@ void yahoo_set_yab(int id, struct yab * yab) char *temp; int size = sizeof(url)-1; - if(!yd) + if (!yd) return; yid = y_new0(struct yahoo_input_data, 1); @@ -4173,7 +4241,7 @@ void yahoo_set_yab(int id, struct yab * yab) strncpy(url, "http://insider.msg.yahoo.com/ycontent/?addab2=0", size); - if(yab->dbid) { + if (yab->dbid) { /* change existing yab */ char tmp[32]; strncat(url, "&ee=1&ow=1&id=", size - strlen(url)); @@ -4181,13 +4249,13 @@ void yahoo_set_yab(int id, struct yab * yab) strncat(url, tmp, size - strlen(url)); } - if(yab->fname) { + if (yab->fname) { strncat(url, "&fn=", size - strlen(url)); temp = yahoo_urlencode(yab->fname); strncat(url, temp, size - strlen(url)); free(temp); } - if(yab->lname) { + if (yab->lname) { strncat(url, "&ln=", size - strlen(url)); temp = yahoo_urlencode(yab->lname); strncat(url, temp, size - strlen(url)); @@ -4197,31 +4265,31 @@ void yahoo_set_yab(int id, struct yab * yab) temp = yahoo_urlencode(yab->id); strncat(url, temp, size - strlen(url)); free(temp); - if(yab->nname) { + if (yab->nname) { strncat(url, "&nn=", size - strlen(url)); temp = yahoo_urlencode(yab->nname); strncat(url, temp, size - strlen(url)); free(temp); } - if(yab->email) { + if (yab->email) { strncat(url, "&e=", size - strlen(url)); temp = yahoo_urlencode(yab->email); strncat(url, temp, size - strlen(url)); free(temp); } - if(yab->hphone) { + if (yab->hphone) { strncat(url, "&hp=", size - strlen(url)); temp = yahoo_urlencode(yab->hphone); strncat(url, temp, size - strlen(url)); free(temp); } - if(yab->wphone) { + if (yab->wphone) { strncat(url, "&wp=", size - strlen(url)); temp = yahoo_urlencode(yab->wphone); strncat(url, temp, size - strlen(url)); free(temp); } - if(yab->mphone) { + if (yab->mphone) { strncat(url, "&mp=", size - strlen(url)); temp = yahoo_urlencode(yab->mphone); strncat(url, temp, size - strlen(url)); @@ -4238,13 +4306,13 @@ void yahoo_set_yab(int id, struct yab * yab) _yahoo_http_connected, yid); } -void yahoo_set_identity_status(int id, const char * identity, int active) +void yahoo_set_identity_status(int id, const char *identity, int active) { struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4263,7 +4331,7 @@ void yahoo_refresh(int id) struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4278,8 +4346,8 @@ void yahoo_keepalive(int id) { struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; - struct yahoo_packet *pkt=NULL; - if(!yid) + struct yahoo_packet *pkt = NULL; + if (!yid) return; yd = yid->yd; @@ -4310,7 +4378,7 @@ void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4345,7 +4413,7 @@ void yahoo_remove_buddy(int id, const char *who, const char *group) struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4362,11 +4430,11 @@ void yahoo_accept_buddy_ymsg13(int id,const char* me,const char* who){ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; - if(!yid) + if (!yid) return; yd = yid->yd; - struct yahoo_packet* pkt=NULL; + struct yahoo_packet* pkt = NULL; pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0); yahoo_packet_hash(pkt,1,me ?: yd->user); @@ -4381,11 +4449,11 @@ void yahoo_reject_buddy_ymsg13(int id,const char* me,const char* who,const char* struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; - if(!yid) + if (!yid) return; yd = yid->yd; - struct yahoo_packet* pkt=NULL; + struct yahoo_packet* pkt = NULL; pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0); yahoo_packet_hash(pkt,1,me ?: yd->user); @@ -4407,7 +4475,7 @@ void yahoo_reject_buddy(int id, const char *who, const char *msg) struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4428,7 +4496,7 @@ void yahoo_ignore_buddy(int id, const char *who, int unignore) struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4449,7 +4517,7 @@ void yahoo_stealth_buddy(int id, const char *who, int unstealth) struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4471,7 +4539,7 @@ void yahoo_change_buddy_group(int id, const char *who, const char *old_group, co struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4498,7 +4566,7 @@ void yahoo_group_rename(int id, const char *old_group, const char *new_group) struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4511,13 +4579,13 @@ void yahoo_group_rename(int id, const char *old_group, const char *new_group) yahoo_packet_free(pkt); } -void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg) +void yahoo_conference_addinvite(int id, const char *from, const char *who, const char *room, const YList *members, const char *msg) { struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4528,7 +4596,7 @@ void yahoo_conference_addinvite(int id, const char * from, const char *who, cons yahoo_packet_hash(pkt, 57, room); yahoo_packet_hash(pkt, 58, msg); yahoo_packet_hash(pkt, 13, "0"); - for(; members; members = members->next) { + for (; members; members = members->next) { yahoo_packet_hash(pkt, 52, (char *)members->data); yahoo_packet_hash(pkt, 53, (char *)members->data); } @@ -4539,13 +4607,13 @@ void yahoo_conference_addinvite(int id, const char * from, const char *who, cons yahoo_packet_free(pkt); } -void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg) +void yahoo_conference_invite(int id, const char *from, YList *who, const char *room, const char *msg) { struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4553,7 +4621,7 @@ void yahoo_conference_invite(int id, const char * from, YList *who, const char * yahoo_packet_hash(pkt, 1, (from?from:yd->user)); yahoo_packet_hash(pkt, 50, yd->user); - for(; who; who = who->next) { + for (; who; who = who->next) { yahoo_packet_hash(pkt, 52, (char *)who->data); } yahoo_packet_hash(pkt, 57, room); @@ -4571,14 +4639,14 @@ void yahoo_conference_logon(int id, const char *from, YList *who, const char *ro struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, 1, (from?from:yd->user)); - for(; who; who = who->next) { + for (; who; who = who->next) { yahoo_packet_hash(pkt, 3, (char *)who->data); } yahoo_packet_hash(pkt, 57, room); @@ -4588,20 +4656,20 @@ void yahoo_conference_logon(int id, const char *from, YList *who, const char *ro yahoo_packet_free(pkt); } -void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg) +void yahoo_conference_decline(int id, const char *from, YList *who, const char *room, const char *msg) { struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, 1, (from?from:yd->user)); - for(; who; who = who->next) { + for (; who; who = who->next) { yahoo_packet_hash(pkt, 3, (char *)who->data); } yahoo_packet_hash(pkt, 57, room); @@ -4612,20 +4680,20 @@ void yahoo_conference_decline(int id, const char * from, YList *who, const char yahoo_packet_free(pkt); } -void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room) +void yahoo_conference_logoff(int id, const char *from, YList *who, const char *room) { struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, 1, (from?from:yd->user)); - for(; who; who = who->next) { + for (; who; who = who->next) { yahoo_packet_hash(pkt, 3, (char *)who->data); } yahoo_packet_hash(pkt, 57, room); @@ -4635,26 +4703,26 @@ void yahoo_conference_logoff(int id, const char * from, YList *who, const char * yahoo_packet_free(pkt); } -void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8) +void yahoo_conference_message(int id, const char *from, YList *who, const char *room, const char *msg, int utf8) { struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, 1, (from?from:yd->user)); - for(; who; who = who->next) { + for (; who; who = who->next) { yahoo_packet_hash(pkt, 53, (char *)who->data); } yahoo_packet_hash(pkt, 57, room); yahoo_packet_hash(pkt, 14, msg); - if(utf8) + if (utf8) yahoo_packet_hash(pkt, 97, "1"); yahoo_send_packet(yid, pkt, 0); @@ -4669,7 +4737,7 @@ void yahoo_get_chatrooms(int id, int chatroomid) char url[1024]; char buff[1024]; - if(!yd) + if (!yd) return; yid = y_new0(struct yahoo_input_data, 1); @@ -4695,7 +4763,7 @@ void yahoo_chat_logon(int id, const char *from, const char *room, const char *ro struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4730,7 +4798,7 @@ void yahoo_chat_message(int id, const char *from, const char *room, const char struct yahoo_packet *pkt; char buf[2]; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4744,7 +4812,7 @@ void yahoo_chat_message(int id, const char *from, const char *room, const char snprintf(buf, sizeof(buf), "%d", msgtype); yahoo_packet_hash(pkt, 124, buf); - if(utf8) + if (utf8) yahoo_packet_hash(pkt, 97, "1"); yahoo_send_packet(yid, pkt, 0); @@ -4759,7 +4827,7 @@ void yahoo_chat_logoff(int id, const char *from) struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; yd = yid->yd; @@ -4779,7 +4847,7 @@ void yahoo_buddyicon_request(int id, const char *who) struct yahoo_data *yd; struct yahoo_packet *pkt; - if( !yid ) + if ( !yid ) return; yd = yid->yd; @@ -4800,7 +4868,7 @@ void yahoo_send_picture_info(int id, const char *who, const char *url, int check struct yahoo_packet *pkt; char checksum_str[10]; - if( !yid ) + if ( !yid ) return; yd = yid->yd; @@ -4826,7 +4894,7 @@ void yahoo_send_picture_update(int id, const char *who, int type) struct yahoo_packet *pkt; char type_str[10]; - if( !yid ) + if ( !yid ) return; yd = yid->yd; @@ -4849,7 +4917,7 @@ void yahoo_send_picture_checksum(int id, const char *who, int checksum) struct yahoo_packet *pkt; char checksum_str[10]; - if( !yid ) + if ( !yid ) return; yd = yid->yd; @@ -4858,7 +4926,7 @@ void yahoo_send_picture_checksum(int id, const char *who, int checksum) pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM, YAHOO_STATUS_AVAILABLE, 0); yahoo_packet_hash(pkt, 1, yd->user); - if( who != 0 ) + if ( who != 0 ) yahoo_packet_hash(pkt, 5, who); yahoo_packet_hash(pkt, 192, checksum_str); yahoo_packet_hash(pkt, 212, "1"); @@ -4871,7 +4939,7 @@ void yahoo_webcam_close_feed(int id, const char *who) { struct yahoo_input_data *yid = find_input_by_id_and_webcam_user(id, who); - if(yid) + if (yid) yahoo_input_close(yid); } @@ -4881,7 +4949,7 @@ void yahoo_webcam_get_feed(int id, const char *who) struct yahoo_data *yd; struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; /* @@ -4967,7 +5035,7 @@ void yahoo_webcam_invite(int id, const char *who) struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_packet *pkt; - if(!yid) + if (!yid) return; pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yid->yd->session_id); @@ -4990,7 +5058,7 @@ static void yahoo_search_internal(int id, int t, const char *text, int g, int ar char buff[1024]; char *ctext, *p; - if(!yd) + if (!yd) return; yid = y_new0(struct yahoo_input_data, 1); @@ -5005,7 +5073,7 @@ static void yahoo_search_internal(int id, int t, const char *text, int g, int ar snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, startpos); ctext = strdup(text); - while((p = strchr(ctext, ' '))) + while ((p = strchr(ctext, ' '))) *p = '+'; snprintf(url, 1024, "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s", @@ -5026,10 +5094,10 @@ void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_search_state *yss; - if(!yid) + if (!yid) return; - if(!yid->ys) + if (!yid->ys) yid->ys = y_new0(struct yahoo_search_state, 1); yss = yid->ys; @@ -5050,12 +5118,12 @@ void yahoo_search_again(int id, int start) struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_search_state *yss; - if(!yid || !yid->ys) + if (!yid || !yid->ys) return; yss = yid->ys; - if(start == -1) + if (start == -1) start = yss->lsearch_nstart + yss->lsearch_nfound; yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text, @@ -5077,7 +5145,7 @@ static void _yahoo_send_picture_connected(int id, int fd, int error, void *data) struct yahoo_packet *pkt = sfd->pkt; unsigned char buff[1024]; - if(fd <= 0) { + if (fd <= 0) { sfd->callback(id, fd, error, sfd->user_data); FREE(sfd); yahoo_packet_free(pkt); @@ -5096,14 +5164,14 @@ static void _yahoo_send_picture_connected(int id, int fd, int error, void *data) write(yid->fd, buff, 4); - /* YAHOO_CALLBACK(ext_yahoo_add_handler)(nyd->fd, YAHOO_INPUT_READ); */ + /* YAHOO_CALLBACK(ext_yahoo_add_handler) (nyd->fd, YAHOO_INPUT_READ); */ sfd->callback(id, fd, error, sfd->user_data); FREE(sfd); inputs = y_list_remove(inputs, yid); /* - while(yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { - if(!strcmp(buff, "")) + while (yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { + if (!strcmp(buff, "")) break; } @@ -5120,12 +5188,12 @@ void yahoo_send_picture(int id, const char *name, unsigned long size, struct yahoo_packet *pkt = NULL; char size_str[10]; char expire_str[10]; - long content_length=0; + long content_length = 0; unsigned char buff[1024]; char url[255]; struct send_file_data *sfd; - if(!yd) + if (!yd) return; yss = yd->server_settings; @@ -5170,7 +5238,7 @@ static void _yahoo_send_file_connected(int id, int fd, int error, void *data) struct yahoo_packet *pkt = sfd->pkt; unsigned char buff[1024]; - if(fd <= 0) { + if (fd <= 0) { sfd->callback(id, fd, error, sfd->user_data); FREE(sfd); yahoo_packet_free(pkt); @@ -5189,14 +5257,14 @@ static void _yahoo_send_file_connected(int id, int fd, int error, void *data) write(yid->fd, buff, 4); -/* YAHOO_CALLBACK(ext_yahoo_add_handler)(nyd->fd, YAHOO_INPUT_READ); */ +/* YAHOO_CALLBACK(ext_yahoo_add_handler) (nyd->fd, YAHOO_INPUT_READ); */ sfd->callback(id, fd, error, sfd->user_data); FREE(sfd); inputs = y_list_remove(inputs, yid); /* - while(yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { - if(!strcmp(buff, "")) + while (yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { + if (!strcmp(buff, "")) break; } @@ -5213,12 +5281,12 @@ void yahoo_send_file(int id, const char *who, const char *msg, struct yahoo_server_settings *yss; struct yahoo_packet *pkt = NULL; char size_str[10]; - long content_length=0; + long content_length = 0; unsigned char buff[1024]; char url[255]; struct send_file_data *sfd; - if(!yd) + if (!yd) return; yss = yd->server_settings; @@ -5257,47 +5325,47 @@ void yahoo_send_file(int id, const char *who, const char *msg, enum yahoo_status yahoo_current_status(int id) { struct yahoo_data *yd = find_conn_by_id(id); - if(!yd) + if (!yd) return YAHOO_STATUS_OFFLINE; return yd->current_status; } -const YList * yahoo_get_buddylist(int id) +const YList *yahoo_get_buddylist(int id) { struct yahoo_data *yd = find_conn_by_id(id); - if(!yd) + if (!yd) return NULL; return yd->buddies; } -const YList * yahoo_get_ignorelist(int id) +const YList *yahoo_get_ignorelist(int id) { struct yahoo_data *yd = find_conn_by_id(id); - if(!yd) + if (!yd) return NULL; return yd->ignore; } -const YList * yahoo_get_identities(int id) +const YList *yahoo_get_identities(int id) { struct yahoo_data *yd = find_conn_by_id(id); - if(!yd) + if (!yd) return NULL; return yd->identities; } -const char * yahoo_get_cookie(int id, const char *which) +const char *yahoo_get_cookie(int id, const char *which) { struct yahoo_data *yd = find_conn_by_id(id); - if(!yd) + if (!yd) return NULL; - if(!strncasecmp(which, "y", 1)) + if (!strncasecmp(which, "y", 1)) return yd->cookie_y; - if(!strncasecmp(which, "t", 1)) + if (!strncasecmp(which, "t", 1)) return yd->cookie_t; - if(!strncasecmp(which, "c", 1)) + if (!strncasecmp(which, "c", 1)) return yd->cookie_c; - if(!strncasecmp(which, "login", 5)) + if (!strncasecmp(which, "login", 5)) return yd->login_cookie; return NULL; } @@ -5306,13 +5374,13 @@ void yahoo_get_url_handle(int id, const char *url, yahoo_get_url_handle_callback callback, void *data) { struct yahoo_data *yd = find_conn_by_id(id); - if(!yd) + if (!yd) return; yahoo_get_url_fd(id, url, yd, callback, data); } -const char * yahoo_get_profile_url( void ) +const char *yahoo_get_profile_url( void ) { return profile_url; } diff --git a/protocols/yahoo/yahoo2.h b/protocols/yahoo/yahoo2.h index 2184a321..ba42b39e 100644 --- a/protocols/yahoo/yahoo2.h +++ b/protocols/yahoo/yahoo2.h @@ -62,7 +62,6 @@ enum yahoo_log_level yahoo_get_log_level( void ); /* who always means the buddy you're acting on */ /* id is the successful value returned by yahoo_init */ - /* init returns a connection id used to identify the connection hereon */ /* or 0 on failure */ /* you must call init before calling any other function */ @@ -87,101 +86,129 @@ enum yahoo_log_level yahoo_get_log_level( void ); * * You should set at least local_host if you intend to use webcams */ -int yahoo_init_with_attributes(const char *username, const char *password, ...); + int yahoo_init_with_attributes(const char *username, + const char *password, ...); /* yahoo_init does the same as yahoo_init_with_attributes, assuming defaults * for all attributes */ -int yahoo_init(const char *username, const char *password); - - + int yahoo_init(const char *username, const char *password); /* release all resources held by this session */ /* you need to call yahoo_close for a session only if * yahoo_logoff is never called for it (ie, it was never logged in) */ -void yahoo_close(int id); + void yahoo_close(int id); /* login logs in to the server */ /* initial is of type enum yahoo_status. see yahoo2_types.h */ -void yahoo_login(int id, int initial); -void yahoo_logoff(int id); + void yahoo_login(int id, int initial); + void yahoo_logoff(int id); /* reloads status of all buddies */ -void yahoo_refresh(int id); + void yahoo_refresh(int id); /* activates/deactivates an identity */ -void yahoo_set_identity_status(int id, const char * identity, int active); + void yahoo_set_identity_status(int id, const char *identity, + int active); /* regets the entire buddy list from the server */ -void yahoo_get_list(int id); + void yahoo_get_list(int id); /* download buddy contact information from your yahoo addressbook */ -void yahoo_get_yab(int id); + void yahoo_get_yab(int id); /* add/modify an address book entry. if yab->dbid is set, it will */ /* modify that entry else it creates a new entry */ -void yahoo_set_yab(int id, struct yab * yab); -void yahoo_keepalive(int id); -void yahoo_chat_keepalive(int id); + void yahoo_set_yab(int id, struct yab *yab); + void yahoo_keepalive(int id); + void yahoo_chat_keepalive(int id); /* from is the identity you're sending from. if NULL, the default is used */ /* utf8 is whether msg is a utf8 string or not. */ -void yahoo_send_im(int id, const char *from, const char *who, const char *msg, int utf8, int picture); + void yahoo_send_im(int id, const char *from, const char *who, + const char *msg, int utf8, int picture); + void yahoo_send_buzz(int id, const char *from, const char *who); /* if type is true, send typing notice, else send stopped typing notice */ -void yahoo_send_typing(int id, const char *from, const char *who, int typ); + void yahoo_send_typing(int id, const char *from, const char *who, + int typ); /* used to set away/back status. */ /* away says whether the custom message is an away message or a sig */ -void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away); - -void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg); -void yahoo_remove_buddy(int id, const char *who, const char *group); -void yahoo_reject_buddy(int id, const char *who, const char *msg); -void yahoo_stealth_buddy(int id, const char *who, int unstealth); + void yahoo_set_away(int id, enum yahoo_status state, const char *msg, + int away); + + void yahoo_add_buddy(int id, const char *who, const char *group, + const char *msg); + void yahoo_remove_buddy(int id, const char *who, const char *group); + void yahoo_confirm_buddy(int id, const char *who, int reject, + const char *msg); + void yahoo_stealth_buddy(int id, const char *who, int unstealth); /* if unignore is true, unignore, else ignore */ -void yahoo_ignore_buddy(int id, const char *who, int unignore); -void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group); -void yahoo_group_rename(int id, const char *old_group, const char *new_group); - -void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg); -void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg); -void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg); -void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8); -void yahoo_conference_logon(int id, const char * from, YList *who, const char *room); -void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room); + void yahoo_ignore_buddy(int id, const char *who, int unignore); + void yahoo_change_buddy_group(int id, const char *who, + const char *old_group, const char *new_group); + void yahoo_group_rename(int id, const char *old_group, + const char *new_group); + + void yahoo_conference_invite(int id, const char *from, YList *who, + const char *room, const char *msg); + void yahoo_conference_addinvite(int id, const char *from, + const char *who, const char *room, const YList *members, + const char *msg); + void yahoo_conference_decline(int id, const char *from, YList *who, + const char *room, const char *msg); + void yahoo_conference_message(int id, const char *from, YList *who, + const char *room, const char *msg, int utf8); + void yahoo_conference_logon(int id, const char *from, YList *who, + const char *room); + void yahoo_conference_logoff(int id, const char *from, YList *who, + const char *room); /* Get a list of chatrooms */ -void yahoo_get_chatrooms(int id,int chatroomid); + void yahoo_get_chatrooms(int id, int chatroomid); /* join room with specified roomname and roomid */ -void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid); + void yahoo_chat_logon(int id, const char *from, const char *room, + const char *roomid); /* Send message "msg" to room with specified roomname, msgtype is 1-normal message or 2-/me mesage */ -void yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8); + void yahoo_chat_message(int id, const char *from, const char *room, + const char *msg, const int msgtype, const int utf8); /* Log off chat */ -void yahoo_chat_logoff(int id, const char *from); + void yahoo_chat_logoff(int id, const char *from); /* requests a webcam feed */ /* who is the person who's webcam you would like to view */ /* if who is null, then you're the broadcaster */ -void yahoo_webcam_get_feed(int id, const char *who); -void yahoo_webcam_close_feed(int id, const char *who); + void yahoo_webcam_get_feed(int id, const char *who); + void yahoo_webcam_close_feed(int id, const char *who); /* sends an image when uploading */ /* image points to a JPEG-2000 image, length is the length of the image */ /* in bytes. The timestamp is the time in milliseconds since we started the */ /* webcam. */ -void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp); + void yahoo_webcam_send_image(int id, unsigned char *image, + unsigned int length, unsigned int timestamp); /* this function should be called if we want to allow a user to watch the */ /* webcam. Who is the user we want to accept. */ /* Accept user (accept = 1), decline user (accept = 0) */ -void yahoo_webcam_accept_viewer(int id, const char* who, int accept); + void yahoo_webcam_accept_viewer(int id, const char *who, int accept); /* send an invitation to a user to view your webcam */ -void yahoo_webcam_invite(int id, const char *who); + void yahoo_webcam_invite(int id, const char *who); /* will set up a connection and initiate file transfer. * callback will be called with the fd that you should write * the file data to */ -void yahoo_send_file(int id, const char *who, const char *msg, const char *name, unsigned long size, + void yahoo_send_file(int id, const char *who, const char *msg, + const char *name, unsigned long size, yahoo_get_fd_callback callback, void *data); +/* + * Respond to a file transfer request. Be sure to provide the callback data + * since that is your only chance to recognize future callbacks + */ + void yahoo_send_file_transfer_response(int client_id, int response, + char *id, void *data); + + /* send a search request */ -void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar, + void yahoo_search(int id, enum yahoo_search_type t, const char *text, + enum yahoo_search_gender g, enum yahoo_search_agerange ar, int photo, int yahoo_only); /* continue last search @@ -189,11 +216,7 @@ void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo * * where the above three are passed to ext_yahoo_got_search_result */ -void yahoo_search_again(int id, int start); - -/* returns a socket fd to a url for downloading a file. */ -void yahoo_get_url_handle(int id, const char *url, - yahoo_get_url_handle_callback callback, void *data); + void yahoo_search_again(int id, int start); /* these should be called when input is available on a fd */ /* registered by ext_yahoo_add_handler */ @@ -202,27 +225,26 @@ int yahoo_read_ready(int id, int fd, void *data); int yahoo_write_ready(int id, int fd, void *data); /* utility functions. these do not hit the server */ -enum yahoo_status yahoo_current_status(int id); -const YList * yahoo_get_buddylist(int id); -const YList * yahoo_get_ignorelist(int id); -const YList * yahoo_get_identities(int id); + enum yahoo_status yahoo_current_status(int id); + const YList *yahoo_get_buddylist(int id); + const YList *yahoo_get_ignorelist(int id); + const YList *yahoo_get_identities(int id); /* 'which' could be y, t, c or login. This may change in later versions. */ -const char * yahoo_get_cookie(int id, const char *which); + const char *yahoo_get_cookie(int id, const char *which); /* returns the url used to get user profiles - you must append the user id */ /* as of now this is http://profiles.yahoo.com/ */ /* You'll have to do urlencoding yourself, but see yahoo_httplib.h first */ -const char * yahoo_get_profile_url( void ); + const char *yahoo_get_profile_url(void); -void yahoo_buddyicon_request(int id, const char *who); + void yahoo_buddyicon_request(int id, const char *who); -void yahoo_accept_buddy_ymsg13(int,const char*,const char*); -void yahoo_reject_buddy_ymsg13(int,const char*,const char*,const char*); + void yahoo_accept_buddy_ymsg13(int,const char*, const char*); + void yahoo_reject_buddy_ymsg13(int,const char*, const char*, const char*); #include "yahoo_httplib.h" #ifdef __cplusplus } #endif - #endif diff --git a/protocols/yahoo/yahoo2_callbacks.h b/protocols/yahoo/yahoo2_callbacks.h index e2c8ea42..19060a22 100644 --- a/protocols/yahoo/yahoo2_callbacks.h +++ b/protocols/yahoo/yahoo2_callbacks.h @@ -29,7 +29,6 @@ * declared in this file and defined in libyahoo2.c */ - #ifndef YAHOO2_CALLBACKS_H #define YAHOO2_CALLBACKS_H @@ -45,11 +44,11 @@ extern "C" { * Callback interface for libyahoo2 */ -typedef enum { - YAHOO_INPUT_READ = 1 << 0, - YAHOO_INPUT_WRITE = 1 << 1, - YAHOO_INPUT_EXCEPTION = 1 << 2 -} yahoo_input_condition; + typedef enum { + YAHOO_INPUT_READ = 1 << 0, + YAHOO_INPUT_WRITE = 1 << 1, + YAHOO_INPUT_EXCEPTION = 1 << 2 + } yahoo_input_condition; /* * A callback function called when an asynchronous connect completes. @@ -93,8 +92,8 @@ struct yahoo_callbacks { * succ - enum yahoo_login_status * url - url to reactivate account if locked */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response)(int id, int succ, const char *url); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response) (int id, int succ, + const char *url); /* * Name: ext_yahoo_got_buddies @@ -103,8 +102,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response)(int id, int succ, const char * id - the id that identifies the server connection * buds - the buddy list */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies)(int id, YList * buds); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies) (int id, YList *buds); /* * Name: ext_yahoo_got_ignore @@ -113,8 +111,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies)(int id, YList * buds); * id - the id that identifies the server connection * igns - the ignore list */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore)(int id, YList * igns); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore) (int id, YList *igns); /* * Name: ext_yahoo_got_identities @@ -123,8 +120,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore)(int id, YList * igns); * id - the id that identifies the server connection * ids - the identity list */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities)(int id, YList * ids); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities) (int id, YList *ids); /* * Name: ext_yahoo_got_cookies @@ -132,8 +128,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities)(int id, YList * ids); * Params: * id - the id that identifies the server connection */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies)(int id); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies) (int id); /* * Name: ext_yahoo_got_ping @@ -142,8 +137,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies)(int id); * id - the id that identifies the server connection * errormsg - optional error message */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ping)(int id, const char *errormsg); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ping) (int id, + const char *errormsg); /* * Name: ext_yahoo_status_changed @@ -158,8 +153,21 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ping)(int id, const char *errormsg); * mobile - this is set for mobile users/buddies * TODO: add support for pager, chat, and game states */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed)(int id, const char *who, int stat, const char *msg, int away, int idle, int mobile); + void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed) (int id, + const char *who, int stat, const char *msg, int away, int idle, + int mobile); +/* + * Name: ext_yahoo_got_buzz + * Called when remote user sends you a buzz. + * Params: + * id - the id that identifies the server connection + * me - the identity the message was sent to + * who - the handle of the remote user + * tm - timestamp of message if offline + */ + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buzz) (int id, const char *me, + const char *who, long tm); /* * Name: ext_yahoo_got_im @@ -176,8 +184,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed)(int id, const char *who, int * 5 * utf8 - whether the message is encoded as utf8 or not */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im)(int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im) (int id, const char *me, + const char *who, const char *msg, long tm, int stat, int utf8); /* * Name: ext_yahoo_got_conf_invite @@ -190,8 +198,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im)(int id, const char *me, const char *w * msg - the message * members - the initial members of the conference (null terminated list) */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite)(int id, const char *me, const char *who, const char *room, const char *msg, YList *members); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite) (int id, + const char *me, const char *who, const char *room, + const char *msg, YList *members); /* * Name: ext_yahoo_conf_userdecline @@ -203,8 +212,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite)(int id, const char *me, cons * room - the room * msg - the declining message */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline)(int id, const char *me, const char *who, const char *room, const char *msg); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline) (int id, + const char *me, const char *who, const char *room, + const char *msg); /* * Name: ext_yahoo_conf_userjoin @@ -215,8 +225,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline)(int id, const char *me, con * who - the user who has joined * room - the room joined */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin)(int id, const char *me, const char *who, const char *room); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin) (int id, + const char *me, const char *who, const char *room); /* * Name: ext_yahoo_conf_userleave @@ -227,8 +237,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin)(int id, const char *me, const * who - the user who has left * room - the room left */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave)(int id, const char *me, const char *who, const char *room); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave) (int id, + const char *me, const char *who, const char *room); /* * Name: ext_yahoo_chat_cat_xml @@ -237,8 +247,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave)(int id, const char *me, const * id - the id that identifies the server connection * xml - ? */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml)(int id, const char *xml); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml) (int id, + const char *xml); /* * Name: ext_yahoo_chat_join @@ -253,8 +263,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml)(int id, const char *xml); * of yahoo_chat_member's) Must be freed by the client * fd - the socket where the connection is coming from (for tracking) */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join)(int id, const char *me, const char *room, const char *topic, YList *members, int fd); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join) (int id, const char *me, + const char *room, const char *topic, YList *members, int fd); /* * Name: ext_yahoo_chat_userjoin @@ -265,8 +275,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join)(int id, const char *me, const char * room - the room joined * who - the user who has joined, Must be freed by the client */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin)(int id, const char *me, const char *room, struct yahoo_chat_member *who); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin) (int id, + const char *me, const char *room, + struct yahoo_chat_member *who); /* * Name: ext_yahoo_chat_userleave @@ -277,8 +288,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin)(int id, const char *me, const * room - the room left * who - the user who has left (Just the User ID) */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave)(int id, const char *me, const char *room, const char *who); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave) (int id, + const char *me, const char *room, const char *who); /* * Name: ext_yahoo_chat_message @@ -293,8 +304,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave)(int id, const char *me, const * 2 = /me type message * utf8 - whether the message is utf8 encoded or not */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message)(int id, const char *me, const char *who, const char *room, const char *msg, int msgtype, int utf8); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message) (int id, + const char *me, const char *who, const char *room, + const char *msg, int msgtype, int utf8); /* * @@ -309,8 +321,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message)(int id, const char *me, const c * Returns: * nothing. */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout)(int id, const char *me); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout) (int id, + const char *me); /* * @@ -326,8 +338,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout)(int id, const char *me); * Returns: * nothing. */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror)(int id, const char *me); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror) (int id, + const char *me); /* * Name: ext_yahoo_conf_message @@ -340,8 +352,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror)(int id, const char *me); * msg - the message * utf8 - whether the message is utf8 encoded or not */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_message)(int id, const char *me, const char *who, const char *room, const char *msg, int utf8); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_message) (int id, + const char *me, const char *who, const char *room, + const char *msg, int utf8); /* * Name: ext_yahoo_got_file @@ -380,8 +393,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_auth_request)(int id, const char *myi * who - who was added * msg - any message sent */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added)(int id, const char *myid, const char *who, const char *msg); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added) (int id, + const char *myid, const char *who, const char *msg); /* * Name: ext_yahoo_rejected @@ -391,8 +404,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added)(int id, const char *myid, cons * who - who rejected you * msg - any message sent */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected)(int id, const char *who, const char *msg); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected) (int id, const char *who, + const char *msg); /* * Name: ext_yahoo_typing_notify @@ -403,8 +416,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected)(int id, const char *who, const char * who - the handle of the remote user * stat - 1 if typing, 0 if stopped typing */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_typing_notify)(int id, const char *me, const char *who, int stat); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_typing_notify) (int id, + const char *me, const char *who, int stat); /* * Name: ext_yahoo_game_notify @@ -427,8 +440,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify)(int id, const char *me, const ch * subj - the subject of the mail - NULL if only mail count * cnt - mail count - 0 if new mail notification */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_mail_notify)(int id, const char *from, const char *subj, int cnt); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_mail_notify) (int id, + const char *from, const char *subj, int cnt); /* * Name: ext_yahoo_system_message @@ -449,7 +462,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, const char *msg); * url - the url to use to load the icon * checksum - the checksum of the icon content */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon)(int id, const char *me, const char *who, const char *url, int checksum); + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon) (int id, + const char *me, const char *who, const char *url, int checksum); /* * Name: ext_yahoo_got_buddyicon_checksum @@ -460,7 +474,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon)(int id, const char *me, const * who - the yahoo id of the buddy icon checksum is for * checksum - the checksum of the icon content */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_checksum)(int id, const char *me,const char *who, int checksum); + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_checksum) (int id, + const char *me, const char *who, int checksum); /* * Name: ext_yahoo_got_buddyicon_request @@ -470,7 +485,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_checksum)(int id, const char *m * me - the handle of the identity the notification is sent to * who - the yahoo id of the buddy that requested the buddy icon */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_request)(int id, const char *me, const char *who); + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_request) (int id, + const char *me, const char *who); /* * Name: ext_yahoo_got_buddyicon_request @@ -479,7 +495,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_request)(int id, const char *me * id - the id that identifies the server connection * url - remote url, the uploaded buddy icon can be fetched from */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_buddyicon_uploaded)(int id, const char *url); + void YAHOO_CALLBACK_TYPE(ext_yahoo_buddyicon_uploaded) (int id, + const char *url); /* * Name: ext_yahoo_got_webcam_image @@ -504,11 +521,11 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_buddyicon_uploaded)(int id, const char *url); * to transport then others. When image_size is 0 we can still receive * a timestamp to stay in sync */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image)(int id, const char * who, - const unsigned char *image, unsigned int image_size, unsigned int real_size, + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image) (int id, + const char *who, const unsigned char *image, + unsigned int image_size, unsigned int real_size, unsigned int timestamp); - /* * Name: ext_yahoo_webcam_invite * Called when you get a webcam invitation @@ -517,8 +534,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image)(int id, const char * who, * me - identity the invitation is to * from - who the invitation is from */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite)(int id, const char *me, const char *from); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite) (int id, + const char *me, const char *from); /* * Name: ext_yahoo_webcam_invite_reply @@ -529,8 +546,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite)(int id, const char *me, const * from - who the invitation response is from * accept - 0 (decline), 1 (accept) */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply)(int id, const char *me, const char *from, int accept); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply) (int id, + const char *me, const char *from, int accept); /* * Name: ext_yahoo_webcam_closed @@ -544,8 +561,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply)(int id, const char *me, * 3 = user declines permission * 4 = user does not have webcam online */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed)(int id, const char *who, int reason); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed) (int id, + const char *who, int reason); /* * Name: ext_yahoo_got_search_result @@ -559,8 +576,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed)(int id, const char *who, int r * these will be freed after this function returns, so * if you need to use the information, make a copy */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result)(int id, int found, int start, int total, YList *contacts); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result) (int id, + int found, int start, int total, YList *contacts); /* * Name: ext_yahoo_error @@ -571,8 +588,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result)(int id, int found, int sta * fatal- whether this error is fatal to the connection or not * num - Which error is this */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_error)(int id, const char *err, int fatal, int num); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_error) (int id, const char *err, + int fatal, int num); /* * Name: ext_yahoo_webcam_viewer @@ -582,8 +599,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_error)(int id, const char *err, int fatal, in * who - the viewer * connect - 0=disconnect 1=connect 2=request */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer)(int id, const char *who, int connect); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer) (int id, + const char *who, int connect); /* * Name: ext_yahoo_webcam_data_request @@ -592,8 +609,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer)(int id, const char *who, int c * id - the id that identifies the server connection * send - whether to send images or not */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request)(int id, int send); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request) (int id, + int send); /* * Name: ext_yahoo_log @@ -603,8 +620,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request)(int id, int send); * Returns: * 0 */ -int YAHOO_CALLBACK_TYPE(ext_yahoo_log)(const char *fmt, ...); - + int YAHOO_CALLBACK_TYPE(ext_yahoo_log) (const char *fmt, ...); /* * Name: ext_yahoo_add_handler @@ -619,8 +635,8 @@ int YAHOO_CALLBACK_TYPE(ext_yahoo_log)(const char *fmt, ...); * * Returns: a tag to be used when removing the handler */ -int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler)(int id, int fd, yahoo_input_condition cond, void *data); - + int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler) (int id, int fd, + yahoo_input_condition cond, void *data); /* * Name: ext_yahoo_remove_handler @@ -629,8 +645,7 @@ int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler)(int id, int fd, yahoo_input_condi * id - the id that identifies the connection * tag - the handler tag to remove */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler)(int id, int tag); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler) (int id, int tag); /* * Name: ext_yahoo_connect @@ -641,12 +656,11 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler)(int id, int tag); * Returns: * a unix file descriptor to the socket */ -int YAHOO_CALLBACK_TYPE(ext_yahoo_connect)(const char *host, int port); - + int YAHOO_CALLBACK_TYPE(ext_yahoo_connect) (const char *host, int port); /* * Name: ext_yahoo_connect_async - * Connect to a host:port asynchronously. This function should return + * Connect to a host:port asynchronously. This function should return * immediately returing a tag used to identify the connection handler, * or a pre-connect error (eg: host name lookup failure). * Once the connect completes (successfully or unsuccessfully), callback @@ -662,8 +676,9 @@ int YAHOO_CALLBACK_TYPE(ext_yahoo_connect)(const char *host, int port); * Returns: * a unix file descriptor to the socket */ -int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async)(int id, const char *host, int port, - yahoo_connect_callback callback, void *callback_data); + int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async) (int id, + const char *host, int port, yahoo_connect_callback callback, + void *callback_data); #ifdef USE_STRUCT_CALLBACKS }; @@ -672,7 +687,7 @@ int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async)(int id, const char *host, int p * if using a callback structure, call yahoo_register_callbacks * before doing anything else */ -void yahoo_register_callbacks(struct yahoo_callbacks * tyc); +void yahoo_register_callbacks(struct yahoo_callbacks *tyc); #undef YAHOO_CALLBACK_TYPE diff --git a/protocols/yahoo/yahoo2_types.h b/protocols/yahoo/yahoo2_types.h index f05acb3c..0f6d7aab 100644 --- a/protocols/yahoo/yahoo2_types.h +++ b/protocols/yahoo/yahoo2_types.h @@ -200,6 +200,7 @@ struct yahoo_data { }; struct yab { + int yid; char *id; char *fname; char *lname; diff --git a/protocols/yahoo/yahoo_fn.h b/protocols/yahoo/yahoo_fn.h index 3f79f524..5400e5d0 100644 --- a/protocols/yahoo/yahoo_fn.h +++ b/protocols/yahoo/yahoo_fn.h @@ -18,16 +18,15 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define IDENT 1 /* identify function */ -#define XOR 2 /* xor with arg1 */ -#define MULADD 3 /* multipy by arg1 then add arg2 */ -#define LOOKUP 4 /* lookup each byte in the table pointed to by arg1 */ -#define BITFLD 5 /* reorder bits according to table pointed to by arg1 */ +#define IDENT 1 /* identify function */ +#define XOR 2 /* xor with arg1 */ +#define MULADD 3 /* multipy by arg1 then add arg2 */ +#define LOOKUP 4 /* lookup each byte in the table pointed to by arg1 */ +#define BITFLD 5 /* reorder bits according to table pointed to by arg1 */ -struct yahoo_fn -{ - int type; +struct yahoo_fn { + int type; long arg1, arg2; }; -int yahoo_xfrm( int table, int depth, int seed ); +int yahoo_xfrm(int table, int depth, int seed); diff --git a/protocols/yahoo/yahoo_util.c b/protocols/yahoo/yahoo_util.c index 5375205f..33a12674 100644 --- a/protocols/yahoo/yahoo_util.c +++ b/protocols/yahoo/yahoo_util.c @@ -35,12 +35,12 @@ char *strchr (), *strrchr (); #include "yahoo_util.h" -char * y_string_append(char * string, char * append) +char *y_string_append(char *string, char *append) { int size = strlen(string) + strlen(append) + 1; - char * new_string = y_renew(char, string, size); + char *new_string = y_renew(char, string, size); - if(new_string == NULL) { + if (new_string == NULL) { new_string = y_new(char, size); strcpy(new_string, string); FREE(string); diff --git a/protocols/yahoo/yahoo_util.h b/protocols/yahoo/yahoo_util.h index 0046fe16..8cb721c1 100644 --- a/protocols/yahoo/yahoo_util.h +++ b/protocols/yahoo/yahoo_util.h @@ -60,17 +60,19 @@ # define y_new0(type, n) (type *)calloc((n), sizeof(type)) # define y_renew(type, mem, n) (type *)realloc(mem, n) -void * y_memdup(const void * addr, int n); -char ** y_strsplit(char * str, char * sep, int nelem); -void y_strfreev(char ** vector); +void *y_memdup(const void *addr, int n); +char **y_strsplit(char *str, char *sep, int nelem); +void y_strfreev(char **vector); -int strncasecmp(const char * s1, const char * s2, size_t n); -int strcasecmp(const char * s1, const char * s2); +#ifndef _WIN32 +int strncasecmp(const char *s1, const char *s2, size_t n); +int strcasecmp(const char *s1, const char *s2); -char * strdup(const char *s); +char *strdup(const char *s); int snprintf(char *str, size_t size, const char *format, ...); int vsnprintf(char *str, size_t size, const char *format, va_list ap); +#endif #endif @@ -94,9 +96,9 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap); * The following three functions return newly allocated memory. * You must free it yourself */ -char * y_string_append(char * str, char * append); -char * y_str_to_utf8(const char * in); -char * y_utf8_to_str(const char * in); +char *y_string_append(char *str, char *append); +char *y_str_to_utf8(const char *in); +char *y_utf8_to_str(const char *in); #endif -- cgit v1.2.3 From 9034ba002b8945aee0f905b928bb0f60da9afe9f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 12:57:08 +0200 Subject: Merge complete. It still logs in... --- protocols/yahoo/libyahoo2.c | 3141 ++++++++++++++++++------------------ protocols/yahoo/yahoo.c | 111 +- protocols/yahoo/yahoo2.h | 15 +- protocols/yahoo/yahoo2_callbacks.h | 128 +- protocols/yahoo/yahoo2_types.h | 539 ++++--- protocols/yahoo/yahoo_httplib.c | 337 ++-- protocols/yahoo/yahoo_httplib.h | 26 +- protocols/yahoo/yahoo_list.h | 2 +- 8 files changed, 2242 insertions(+), 2057 deletions(-) diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index c376a2de..bd111654 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -107,7 +107,9 @@ void yahoo_register_callbacks(struct yahoo_callbacks *tyc) #define YAHOO_CALLBACK(x) x #endif -static int yahoo_send_data(int fd, void *data, int len); +static int yahoo_send_data(void *fd, void *data, int len); +static void _yahoo_http_connected(int id, void *fd, int error, void *data); +static void yahoo_connected(void *fd, int error, void *data); int yahoo_log_message(char *fmt, ...) { @@ -139,9 +141,15 @@ int yahoo_set_log_level(enum yahoo_log_level level) } /* default values for servers */ -static char pager_host[] = "scs.msg.yahoo.com"; +static char *default_pager_hosts[] = { "scs.msg.yahoo.com", + "scsa.msg.yahoo.com", + "scsb.msg.yahoo.com", + "scsc.msg.yahoo.com", + NULL}; + static int pager_port = 5050; static int fallback_ports[] = { 23, 25, 80, 20, 119, 8001, 8002, 5050, 0 }; + static char filetransfer_host[] = "filetransfer.msg.yahoo.com"; static int filetransfer_port = 80; static char webcam_host[] = "webcam.yahoo.com"; @@ -152,84 +160,11 @@ static int conn_type = Y_WCM_DSL; static char profile_url[] = "http://profiles.yahoo.com/"; -enum yahoo_service { /* these are easier to see in hex */ - YAHOO_SERVICE_LOGON = 1, - YAHOO_SERVICE_LOGOFF, - YAHOO_SERVICE_ISAWAY, - YAHOO_SERVICE_ISBACK, - YAHOO_SERVICE_IDLE, /* 5 (placemarker) */ - YAHOO_SERVICE_MESSAGE, - YAHOO_SERVICE_IDACT, - YAHOO_SERVICE_IDDEACT, - YAHOO_SERVICE_MAILSTAT, - YAHOO_SERVICE_USERSTAT, /* 0xa */ - YAHOO_SERVICE_NEWMAIL, - YAHOO_SERVICE_CHATINVITE, - YAHOO_SERVICE_CALENDAR, - YAHOO_SERVICE_NEWPERSONALMAIL, - YAHOO_SERVICE_NEWCONTACT, - YAHOO_SERVICE_ADDIDENT, /* 0x10 */ - YAHOO_SERVICE_ADDIGNORE, - YAHOO_SERVICE_PING, - YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */ - YAHOO_SERVICE_SYSMESSAGE = 0x14, - YAHOO_SERVICE_SKINNAME = 0x15, - YAHOO_SERVICE_PASSTHROUGH2 = 0x16, - YAHOO_SERVICE_CONFINVITE = 0x18, - YAHOO_SERVICE_CONFLOGON, - YAHOO_SERVICE_CONFDECLINE, - YAHOO_SERVICE_CONFLOGOFF, - YAHOO_SERVICE_CONFADDINVITE, - YAHOO_SERVICE_CONFMSG, - YAHOO_SERVICE_CHATLOGON, - YAHOO_SERVICE_CHATLOGOFF, - YAHOO_SERVICE_CHATMSG = 0x20, - YAHOO_SERVICE_GAMELOGON = 0x28, - YAHOO_SERVICE_GAMELOGOFF, - YAHOO_SERVICE_GAMEMSG = 0x2a, - YAHOO_SERVICE_FILETRANSFER = 0x46, - YAHOO_SERVICE_VOICECHAT = 0x4A, - YAHOO_SERVICE_NOTIFY, - YAHOO_SERVICE_VERIFY, - YAHOO_SERVICE_P2PFILEXFER, - YAHOO_SERVICE_PEERTOPEER = 0x4F, /* Checks if P2P possible */ - YAHOO_SERVICE_WEBCAM, - YAHOO_SERVICE_AUTHRESP = 0x54, - YAHOO_SERVICE_LIST, - YAHOO_SERVICE_AUTH = 0x57, - YAHOO_SERVICE_AUTHBUDDY = 0x6d, - YAHOO_SERVICE_ADDBUDDY = 0x83, - YAHOO_SERVICE_REMBUDDY, - YAHOO_SERVICE_IGNORECONTACT, /* > 1, 7, 13 < 1, 66, 13, 0*/ - YAHOO_SERVICE_REJECTCONTACT, - YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */ - YAHOO_SERVICE_Y7_PING = 0x8A, /* 0 - id and that's it?? */ - YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/ - YAHOO_SERVICE_CHATGOTO, - YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */ - YAHOO_SERVICE_CHATLEAVE, - YAHOO_SERVICE_CHATEXIT = 0x9b, - YAHOO_SERVICE_CHATADDINVITE = 0x9d, - YAHOO_SERVICE_CHATLOGOUT = 0xa0, - YAHOO_SERVICE_CHATPING, - YAHOO_SERVICE_COMMENT = 0xa8, - YAHOO_SERVICE_STEALTH = 0xb9, - YAHOO_SERVICE_PICTURE_CHECKSUM = 0xbd, - YAHOO_SERVICE_PICTURE = 0xbe, - YAHOO_SERVICE_PICTURE_UPDATE = 0xc1, - YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2, - YAHOO_SERVICE_Y6_VISIBILITY = 0xc5, - YAHOO_SERVICE_Y6_STATUS_UPDATE = 0xc6, - YAHOO_PHOTOSHARE_INIT = 0xd2, - YAHOO_SERVICE_CONTACT_YMSG13 = 0xd6, - YAHOO_PHOTOSHARE_PREV = 0xd7, - YAHOO_PHOTOSHARE_KEY = 0xd8, - YAHOO_PHOTOSHARE_TRANS = 0xda, - YAHOO_FILE_TRANSFER_INIT_YMSG13 = 0xdc, - YAHOO_FILE_TRANSFER_GET_YMSG13 = 0xdd, - YAHOO_FILE_TRANSFER_PUT_YMSG13 = 0xde, - YAHOO_SERVICE_YMSG15_STATUS = 0xf0, - YAHOO_SERVICE_YMSG15_BUDDY_LIST = 0xf1, +struct connect_callback_data { + struct yahoo_data *yd; + int tag; + int i; + int server_i; }; struct yahoo_pair { @@ -267,9 +202,9 @@ struct yahoo_input_data { struct yahoo_webcam_data *wcd; struct yahoo_search_state *ys; - int fd; + void *fd; enum yahoo_connection_type type; - + unsigned char *rxqueue; int rxlen; int read_tag; @@ -288,13 +223,31 @@ struct yahoo_server_settings { char *webcam_description; char *local_host; int conn_type; + char **pager_host_list; }; +static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over); + +static void yahoo_process_filetransfer(struct yahoo_input_data *yid, + struct yahoo_packet *pkt); +static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid, + struct yahoo_packet *pkt); +static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid, + struct yahoo_packet *pkt); + +static void yahoo_https_auth(struct yahoo_input_data *yid, const char *seed, const char *sn); + static void *_yahoo_default_server_settings() { - struct yahoo_server_settings *yss = y_new0(struct yahoo_server_settings, 1); + struct yahoo_server_settings *yss = + y_new0(struct yahoo_server_settings, 1); + + /* Give preference to the default host list + * Make sure that only one of the two is set at any time + */ + yss->pager_host = NULL; + yss->pager_host_list = default_pager_hosts; - yss->pager_host = strdup(pager_host); yss->pager_port = pager_port; yss->filetransfer_host = strdup(filetransfer_host); yss->filetransfer_port = filetransfer_port; @@ -313,6 +266,7 @@ static void *_yahoo_assign_server_settings(va_list ap) char *key; char *svalue; int nvalue; + char **pvalue; while (1) { key = va_arg(ap, char *); @@ -323,6 +277,12 @@ static void *_yahoo_assign_server_settings(va_list ap) svalue = va_arg(ap, char *); free(yss->pager_host); yss->pager_host = strdup(svalue); + yss->pager_host_list = NULL; + } else if (!strcmp(key, "pager_host_list")) { + pvalue = va_arg(ap, char **); + yss->pager_host_list = pvalue; + free(yss->pager_host); + yss->pager_host = NULL; } else if (!strcmp(key, "pager_port")) { nvalue = va_arg(ap, int); yss->pager_port = nvalue; @@ -383,6 +343,7 @@ static void add_to_list(struct yahoo_data *yd) { conns = y_list_prepend(conns, yd); } + static struct yahoo_data *find_conn_by_id(int id) { YList *l; @@ -393,13 +354,15 @@ static struct yahoo_data *find_conn_by_id(int id) } return NULL; } + static void del_from_list(struct yahoo_data *yd) { conns = y_list_remove(conns, yd); } /* call repeatedly to get the next one */ -static struct yahoo_input_data *find_input_by_id(int id) +/* +static struct yahoo_input_data * find_input_by_id(int id) { YList *l; for(l = inputs; l; l = y_list_next(l)) { @@ -409,23 +372,27 @@ static struct yahoo_input_data *find_input_by_id(int id) } return NULL; } +*/ -static struct yahoo_input_data *find_input_by_id_and_webcam_user(int id, const char *who) +static struct yahoo_input_data *find_input_by_id_and_webcam_user(int id, + const char *who) { YList *l; LOG(("find_input_by_id_and_webcam_user")); for (l = inputs; l; l = y_list_next(l)) { struct yahoo_input_data *yid = l->data; - if (yid->type == YAHOO_CONNECTION_WEBCAM && yid->yd->client_id == id - && yid->wcm && - ((who && yid->wcm->user && !strcmp(who, yid->wcm->user)) || - !(yid->wcm->user && !who))) + if (yid->type == YAHOO_CONNECTION_WEBCAM + && yid->yd->client_id == id && yid->wcm && ((who + && yid->wcm->user + && !strcmp(who, yid->wcm->user)) + || !(yid->wcm->user && !who))) return yid; } return NULL; } -static struct yahoo_input_data *find_input_by_id_and_type(int id, enum yahoo_connection_type type) +static struct yahoo_input_data *find_input_by_id_and_type(int id, + enum yahoo_connection_type type) { YList *l; LOG(("find_input_by_id_and_type")); @@ -437,7 +404,7 @@ static struct yahoo_input_data *find_input_by_id_and_type(int id, enum yahoo_con return NULL; } -static struct yahoo_input_data *find_input_by_id_and_fd(int id, int fd) +static struct yahoo_input_data *find_input_by_id_and_fd(int id, void *fd) { YList *l; LOG(("find_input_by_id_and_fd")); @@ -463,7 +430,6 @@ static int count_inputs_with_id(int id) return c; } - extern char *yahoo_crypt(char *, char *); /* Free a buddy list */ @@ -527,6 +493,7 @@ static void yahoo_free_data(struct yahoo_data *yd) FREE(yd->password); FREE(yd->cookie_y); FREE(yd->cookie_t); + FREE(yd->cookie_b); FREE(yd->cookie_c); FREE(yd->login_cookie); FREE(yd->login_id); @@ -542,8 +509,8 @@ static void yahoo_free_data(struct yahoo_data *yd) #define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4) -static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, - enum yahoo_status status, int id) +static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, + enum ypacket_status status, int id) { struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1); @@ -554,7 +521,8 @@ static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, return pkt; } -static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, const char *value) +static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, + const char *value) { struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1); pair->key = key; @@ -599,7 +567,8 @@ static int yahoo_packet_length(struct yahoo_packet *pkt) (((*((buf)+2))&0xff)<< 8) + \ (((*((buf)+3))&0xff))) -static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, int len) +static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, + int len) { int pos = 0; @@ -652,7 +621,8 @@ static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, int pair->value = strdup(value); FREE(value); pkt->hash = y_list_append(pkt->hash, pair); - DEBUG_MSG(("Key: %d \tValue: %s", pair->key, pair->value)); + DEBUG_MSG(("Key: %d \tValue: %s", pair->key, + pair->value)); } else { FREE(pair); } @@ -787,18 +757,18 @@ static void yahoo_packet_free(struct yahoo_packet *pkt) FREE(pkt); } -static int yahoo_send_data(int fd, void *data, int len) +static int yahoo_send_data(void *fd, void *data, int len) { int ret; int e; - if (fd < 0) + if (fd == NULL) return -1; yahoo_packet_dump(data, len); do { - ret = write(fd, data, len); + ret = YAHOO_CALLBACK(ext_yahoo_write) (fd, data, len); } while (ret == -1 && errno == EINTR); e = errno; @@ -837,7 +807,7 @@ static void yahoo_input_close(struct yahoo_input_data *yid) yid->write_tag); yid->read_tag = yid->write_tag = 0; if (yid->fd) - close(yid->fd); + YAHOO_CALLBACK(ext_yahoo_close) (yid->fd); yid->fd = 0; FREE(yid->rxqueue); if (count_inputs_with_id(yid->yd->client_id) == 0) { @@ -941,7 +911,7 @@ static void yahoo_process_notify(struct yahoo_input_data *yid, from, stat); else if (!strncasecmp(msg, "GAME", strlen("GAME"))) YAHOO_CALLBACK(ext_yahoo_game_notify) (yd->client_id, to, from, - stat); + stat, ind); else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) { if (!strcmp(ind, " ")) { YAHOO_CALLBACK(ext_yahoo_webcam_invite) (yd->client_id, @@ -958,63 +928,8 @@ static void yahoo_process_notify(struct yahoo_input_data *yid, LOG(("Got unknown notification: %s", msg)); } -static void yahoo_process_filetransfer(struct yahoo_input_data *yid, struct yahoo_packet *pkt) -{ - struct yahoo_data *yd = yid->yd; - char *from = NULL; - char *to = NULL; - char *msg = NULL; - char *url = NULL; - long expires = 0; - - char *service = NULL; - - char *filename = NULL; - unsigned long filesize = 0L; - - YList *l; - for (l = pkt->hash; l; l = l->next) { - struct yahoo_pair *pair = l->data; - if (pair->key == 4) - from = pair->value; - if (pair->key == 5) - to = pair->value; - if (pair->key == 14) - msg = pair->value; - if (pair->key == 20) - url = pair->value; - if (pair->key == 38) - expires = atol(pair->value); - - if (pair->key == 27) - filename = pair->value; - if (pair->key == 28) - filesize = atol(pair->value); - - if (pair->key == 49) - service = pair->value; - } - - if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) { - if (strcmp("FILEXFER", service) != 0) { - WARNING(("unhandled service 0x%02x", pkt->service)); - yahoo_dump_unhandled(pkt); - return; - } - } - - if (msg) { - char *tmp; - tmp = strchr(msg, '\006'); - if (tmp) - *tmp = '\0'; - } - if (url && from) - YAHOO_CALLBACK(ext_yahoo_got_file) (yd->client_id, to, from, url, expires, msg, filename, filesize); - -} - -static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_conference(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *msg = NULL; @@ -1089,10 +1004,9 @@ static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_ E_CONFNOTAVAIL); break; case YAHOO_SERVICE_CONFADDINVITE: - if (pkt->status == 2) - ; - else - YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd->client_id, id, host, room, msg, members); + if (pkt->status == 1) + YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd-> + client_id, id, host, room, msg, members); break; case YAHOO_SERVICE_CONFDECLINE: if (who) @@ -1133,7 +1047,7 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, int membercount = 0; int chaterr = 0; YList *l; - + yahoo_dump_unhandled(pkt); for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; @@ -1284,6 +1198,7 @@ static void yahoo_process_message(struct yahoo_input_data *yid, long tm; char *msg; int utf8; + char *gunk; } *message = y_new0(struct m, 1); for (l = pkt->hash; l; l = l->next) { @@ -1297,7 +1212,10 @@ static void yahoo_process_message(struct yahoo_input_data *yid, message->tm = strtol(pair->value, NULL, 10); else if (pair->key == 97) message->utf8 = atoi(pair->value); - /* user message */ /* sys message */ + /* This comes when the official client sends us a message */ + else if (pair->key == 429) + message->gunk = pair->value; + /* user message *//* sys message */ else if (pair->key == 14 || pair->key == 16) message->msg = pair->value; else if (pair->key == 31) { @@ -1317,18 +1235,66 @@ static void yahoo_process_message(struct yahoo_input_data *yid, for (l = messages; l; l = l->next) { message = l->data; if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) { - YAHOO_CALLBACK(ext_yahoo_system_message) (yd->client_id, message->msg); + YAHOO_CALLBACK(ext_yahoo_system_message) (yd->client_id, + message->to, message->from, message->msg); } else if (pkt->status <= 2 || pkt->status == 5) { - YAHOO_CALLBACK(ext_yahoo_got_im) (yd->client_id, message->to, message->from, message->msg, message->tm, pkt->status, message->utf8); + /* Confirm message receipt if we got the gunk */ + if(message->gunk) { + struct yahoo_packet *outpkt; + + outpkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_CONFIRM, + YPACKET_STATUS_DEFAULT, 0); + yahoo_packet_hash(outpkt, 1, yd->user); + yahoo_packet_hash(outpkt, 5, message->from); + yahoo_packet_hash(outpkt, 302, "430"); + yahoo_packet_hash(outpkt, 430, message->gunk); + yahoo_packet_hash(outpkt, 303, "430"); + yahoo_packet_hash(outpkt, 450, "0"); + yahoo_send_packet(yid, outpkt, 0); + + yahoo_packet_free(outpkt); + } + + if (!strcmp(message->msg, "")) + YAHOO_CALLBACK(ext_yahoo_got_buzz) (yd->client_id, + message->to, message->from, message->tm); + else + YAHOO_CALLBACK(ext_yahoo_got_im) (yd->client_id, + message->to, message->from, message->msg, + message->tm, pkt->status, message->utf8); } else if (pkt->status == 0xffffffff) { - YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, message->msg, 0, E_SYSTEM); + YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, + message->msg, 0, E_SYSTEM); } - free(message); + FREE(message); } y_list_free(messages); } +/* + * Here's what multi-level packets look like. Data in brackets is the value. + * + * 3 level: + * ======= + * + * 302 (318) - Beginning level 1 + * 300 (318) - Begin level 2 + * 302 (319) - End level 2 header + * 300 (319) - Begin level 3 + * 301 (319) - End level 3 + * 303 (319) - End level 2 + * 303 (318) - End level 1 + * + * 2 level: + * ======= + * + * 302 (315) - Beginning level 1 + * 300 (315) - Begin level 2 + * 301 (315) - End level 2 + * 303 (315) - End level 1 + * + */ static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt) { @@ -1720,648 +1686,149 @@ static void yahoo_process_picture_upload(struct yahoo_input_data *yid, YList *l; char *url = NULL; - if ( pkt->status != 1 ) + if (pkt->status != 1) return; /* something went wrong */ - - for (l = pkt->hash; l; l = l->next) - { + + for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; - switch(pair->key) - { - case 5: /* we */ - break; - case 20: /* url */ - url = pair->value; - break; - case 27: /* local filename */ - break; - case 38: /* time */ - break; + switch (pair->key) { + case 5: /* we */ + break; + case 20: /* url */ + url = pair->value; + break; + case 27: /* local filename */ + break; + case 38: /* time */ + break; } } YAHOO_CALLBACK(ext_yahoo_buddyicon_uploaded) (yd->client_id, url); } -static void yahoo_process_auth_pre_0x0b(struct yahoo_input_data *yid, - const char *seed, const char *sn) +void yahoo_login(int id, int initial) { - struct yahoo_data *yd = yid->yd; - - /* So, Yahoo has stopped supporting its older clients in India, and - * undoubtedly will soon do so in the rest of the world. - * - * The new clients use this authentication method. I warn you in - * advance, it's bizzare, convoluted, inordinately complicated. - * It's also no more secure than crypt() was. The only purpose this - * scheme could serve is to prevent third part clients from connecting - * to their servers. - * - * Sorry, Yahoo. - */ - - struct yahoo_packet *pack; - - md5_byte_t result[16]; - md5_state_t ctx; - char *crypt_result; - unsigned char *password_hash = malloc(25); - unsigned char *crypt_hash = malloc(25); - unsigned char *hash_string_p = malloc(50 + strlen(sn)); - unsigned char *hash_string_c = malloc(50 + strlen(sn)); - - char checksum; - - int sv; - - unsigned char *result6 = malloc(25); - unsigned char *result96 = malloc(25); - - sv = seed[15]; - sv = (sv % 8) % 5; + struct yahoo_data *yd = find_conn_by_id(id); + struct connect_callback_data *ccd; + struct yahoo_server_settings *yss; + int tag; - md5_init(&ctx); - md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password)); - md5_finish(&ctx, result); - to_y64(password_hash, result, 16); - - md5_init(&ctx); - crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$"); - md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result)); - md5_finish(&ctx, result); - to_y64(crypt_hash, result, 16); - free(crypt_result); - - switch (sv) { - case 0: - checksum = seed[seed[7] % 16]; - snprintf((char *)hash_string_p, strlen(sn) + 50, - "%c%s%s%s", checksum, password_hash, yd->user, seed); - snprintf((char *)hash_string_c, strlen(sn) + 50, - "%c%s%s%s", checksum, crypt_hash, yd->user, seed); - break; - case 1: - checksum = seed[seed[9] % 16]; - snprintf((char *)hash_string_p, strlen(sn) + 50, - "%c%s%s%s", checksum, yd->user, seed, password_hash); - snprintf((char *)hash_string_c, strlen(sn) + 50, - "%c%s%s%s", checksum, yd->user, seed, crypt_hash); - break; - case 2: - checksum = seed[seed[15] % 16]; - snprintf((char *)hash_string_p, strlen(sn) + 50, - "%c%s%s%s", checksum, seed, password_hash, yd->user); - snprintf((char *)hash_string_c, strlen(sn) + 50, - "%c%s%s%s", checksum, seed, crypt_hash, yd->user); - break; - case 3: - checksum = seed[seed[1] % 16]; - snprintf((char *)hash_string_p, strlen(sn) + 50, - "%c%s%s%s", checksum, yd->user, password_hash, seed); - snprintf((char *)hash_string_c, strlen(sn) + 50, - "%c%s%s%s", checksum, yd->user, crypt_hash, seed); - break; - case 4: - checksum = seed[seed[3] % 16]; - snprintf((char *)hash_string_p, strlen(sn) + 50, - "%c%s%s%s", checksum, password_hash, seed, yd->user); - snprintf((char *)hash_string_c, strlen(sn) + 50, - "%c%s%s%s", checksum, crypt_hash, seed, yd->user); - break; - } - - md5_init(&ctx); - md5_append(&ctx, (md5_byte_t *)hash_string_p, strlen((char *)hash_string_p)); - md5_finish(&ctx, result); - to_y64(result6, result, 16); + char *host; - md5_init(&ctx); - md5_append(&ctx, (md5_byte_t *)hash_string_c, strlen((char *)hash_string_c)); - md5_finish(&ctx, result); - to_y64(result96, result, 16); + struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_PAGER; + inputs = y_list_prepend(inputs, yid); - pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id); - yahoo_packet_hash(pack, 0, yd->user); - yahoo_packet_hash(pack, 6, (char *)result6); - yahoo_packet_hash(pack, 96, (char *)result96); - yahoo_packet_hash(pack, 1, yd->user); - - yahoo_send_packet(yid, pack, 0); - - FREE(result6); - FREE(result96); - FREE(password_hash); - FREE(crypt_hash); - FREE(hash_string_p); - FREE(hash_string_c); + yd->initial_status = initial; + yss = yd->server_settings; - yahoo_packet_free(pack); + ccd = y_new0(struct connect_callback_data, 1); + ccd->yd = yd; -} + host = yss->pager_host; -/* - * New auth protocol cracked by Cerulean Studios and sent in to Gaim - */ -static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *seed, const char *sn) -{ - struct yahoo_packet *pack = NULL; - struct yahoo_data *yd = yid->yd; + if (!host) + host = yss->pager_host_list[0]; - md5_byte_t result[16]; - md5_state_t ctx; - - sha1_state_t ctx1; - sha1_state_t ctx2; - - char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ"; - char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop"; - - char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5"; - char *operand_lookup = "+|&%/*^-"; - char *delimit_lookup = ",;"; - - unsigned char *password_hash = malloc(25); - unsigned char *crypt_hash = malloc(25); - char *crypt_result = NULL; - unsigned char pass_hash_xor1[64]; - unsigned char pass_hash_xor2[64]; - unsigned char crypt_hash_xor1[64]; - unsigned char crypt_hash_xor2[64]; - unsigned char chal[7]; - char resp_6[100]; - char resp_96[100]; - - unsigned char digest1[20]; - unsigned char digest2[20]; - unsigned char magic_key_char[4]; - const unsigned char *magic_ptr; - - unsigned int magic[64]; - unsigned int magic_work = 0; - - char comparison_src[20]; - - int x, j, i; - int cnt = 0; - int magic_cnt = 0; - int magic_len; - int depth =0, table =0; - - memset(&pass_hash_xor1, 0, 64); - memset(&pass_hash_xor2, 0, 64); - memset(&crypt_hash_xor1, 0, 64); - memset(&crypt_hash_xor2, 0, 64); - memset(&digest1, 0, 20); - memset(&digest2, 0, 20); - memset(&magic, 0, 64); - memset(&resp_6, 0, 100); - memset(&resp_96, 0, 100); - memset(&magic_key_char, 0, 4); + tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, + host, yss->pager_port, yahoo_connected, ccd, 0); - /* - * Magic: Phase 1. Generate what seems to be a 30 - * byte value (could change if base64 - * ends up differently? I don't remember and I'm - * tired, so use a 64 byte buffer. + /* + * if tag <= 0, then callback has already been called + * so ccd will have been freed */ + if (tag > 0) + ccd->tag = tag; + else if (tag < 0) + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_SOCK, NULL); +} - magic_ptr = (unsigned char *)seed; - - while (*magic_ptr != 0) { - char *loc; - - /* Ignore parentheses. */ - - if (*magic_ptr == '(' || *magic_ptr == ')') { - magic_ptr++; - continue; - } - - /* Characters and digits verify against - the challenge lookup. - */ - - if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) { - loc = strchr(challenge_lookup, *magic_ptr); - if (!loc) { - /* This isn't good */ - continue; - } - - /* Get offset into lookup table and lsh 3. */ - - magic_work = loc - challenge_lookup; - magic_work <<= 3; - - magic_ptr++; - continue; - } else { - unsigned int local_store; - - loc = strchr(operand_lookup, *magic_ptr); - if (!loc) { - /* Also not good. */ - continue; - } - - local_store = loc - operand_lookup; - - /* Oops; how did this happen? */ - if (magic_cnt >= 64) - break; - - magic[magic_cnt++] = magic_work | local_store; - magic_ptr++; - continue; - } - } - - magic_len = magic_cnt; - magic_cnt = 0; - - /* Magic: Phase 2. Take generated magic value and - * sprinkle fairy dust on the values. */ +struct yahoo_https_auth_data +{ + struct yahoo_input_data *yid; + char *token; + char *chal; +}; - for (magic_cnt = magic_len-2; magic_cnt >= 0; magic_cnt--) { - unsigned char byte1; - unsigned char byte2; +static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had); +static void yahoo_https_auth_token_finish(struct http_request *req); +static void yahoo_https_auth_init(struct yahoo_https_auth_data *had); +static void yahoo_https_auth_finish(struct http_request *req); - /* Bad. Abort. - */ - if (magic_cnt >= magic_len) { - WARNING(("magic_cnt(%d) magic_len(%d)", magic_cnt, magic_len)) - break; +/* Extract a value from a login.yahoo.com response. Assume CRLF-linebreaks + and FAIL miserably if they're not there... */ +static char *yahoo_ha_find_key(char *response, char *key) +{ + char *s, *end; + int len = strlen(key); + + s = response; + do { + if (strncmp(s, key, len) == 0 && s[len] == '=') { + s += len + 1; + if ((end = strchr(s, '\r'))) + return g_strndup(s, end - s); + else + return g_strdup(s); } + + if ((s = strchr(s, '\n'))) + s ++; + } while (s && *s); + + return NULL; +} - byte1 = magic[magic_cnt]; - byte2 = magic[magic_cnt+1]; - - byte1 *= 0xcd; - byte1 ^= byte2; - - magic[magic_cnt+1] = byte1; +static enum yahoo_status yahoo_https_status_parse(int code) +{ + switch (code) + { + case 1212: return YAHOO_LOGIN_PASSWD; + case 1213: return YAHOO_LOGIN_LOCK; + case 1235: return YAHOO_LOGIN_UNAME; + default: return (enum yahoo_status) code; } +} - /* Magic: Phase 3. This computes 20 bytes. The first 4 bytes are used as our magic - * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key - * plus 3 bytes. The 3 bytes are found by looping, and they represent the offsets - * into particular functions we'll later call to potentially alter the magic key. - * - * %-) - */ +static void yahoo_https_auth(struct yahoo_input_data *yid, const char *seed, const char *sn) +{ + struct yahoo_https_auth_data *had = g_new0(struct yahoo_https_auth_data, 1); - magic_cnt = 1; - x = 0; + had->yid = yid; + had->chal = g_strdup(seed); - do { - unsigned int bl = 0; - unsigned int cl = magic[magic_cnt++]; - - if (magic_cnt >= magic_len) - break; - - if (cl > 0x7F) { - if (cl < 0xe0) - bl = cl = (cl & 0x1f) << 6; - else { - bl = magic[magic_cnt++]; - cl = (cl & 0x0f) << 6; - bl = ((bl & 0x3f) + cl) << 6; - } - - cl = magic[magic_cnt++]; - bl = (cl & 0x3f) + bl; - } else - bl = cl; - - comparison_src[x++] = (bl & 0xff00) >> 8; - comparison_src[x++] = bl & 0xff; - } while (x < 20); + yahoo_https_auth_token_init(had); +} - /* Dump magic key into a char for SHA1 action. */ +static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had) +{ + struct yahoo_input_data *yid = had->yid; + struct yahoo_data *yd = yid->yd; + struct http_request *req; + char *login, *passwd, *chal; + char *url; - - for (x = 0; x < 4; x++) - magic_key_char[x] = comparison_src[x]; - - /* Compute values for recursive function table! */ - memcpy( chal, magic_key_char, 4 ); - x = 1; - for ( i = 0; i < 0xFFFF && x; i++ ) - { - for ( j = 0; j < 5 && x; j++ ) - { - chal[4] = i; - chal[5] = i >> 8; - chal[6] = j; - md5_init( &ctx ); - md5_append( &ctx, chal, 7 ); - md5_finish( &ctx, result ); - if ( memcmp( comparison_src + 4, result, 16 ) == 0 ) - { - depth = i; - table = j; - x = 0; - } - } - } - - /* Transform magic_key_char using transform table */ - x = magic_key_char[3] << 24 | magic_key_char[2] << 16 - | magic_key_char[1] << 8 | magic_key_char[0]; - x = yahoo_xfrm( table, depth, x ); - x = yahoo_xfrm( table, depth, x ); - magic_key_char[0] = x & 0xFF; - magic_key_char[1] = x >> 8 & 0xFF; - magic_key_char[2] = x >> 16 & 0xFF; - magic_key_char[3] = x >> 24 & 0xFF; - - /* Get password and crypt hashes as per usual. */ - md5_init(&ctx); - md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password)); - md5_finish(&ctx, result); - to_y64(password_hash, result, 16); - - md5_init(&ctx); - crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$"); - md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result)); - md5_finish(&ctx, result); - to_y64(crypt_hash, result, 16); - free(crypt_result); - - /* Our first authentication response is based off - * of the password hash. */ - - for (x = 0; x < (int)strlen((char *)password_hash); x++) - pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36; - - if (cnt < 64) - memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt); - - cnt = 0; - - for (x = 0; x < (int)strlen((char *)password_hash); x++) - pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c; - - if (cnt < 64) - memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt); - - sha1_init(&ctx1); - sha1_init(&ctx2); - - /* The first context gets the password hash XORed - * with 0x36 plus a magic value - * which we previously extrapolated from our - * challenge. */ - - sha1_append(&ctx1, pass_hash_xor1, 64); - if (j >= 3 ) - ctx1.Length_Low = 0x1ff; - sha1_append(&ctx1, magic_key_char, 4); - sha1_finish(&ctx1, digest1); - - /* The second context gets the password hash XORed - * with 0x5c plus the SHA-1 digest - * of the first context. */ - - sha1_append(&ctx2, pass_hash_xor2, 64); - sha1_append(&ctx2, digest1, 20); - sha1_finish(&ctx2, digest2); - - /* Now that we have digest2, use it to fetch - * characters from an alphabet to construct - * our first authentication response. */ - - for (x = 0; x < 20; x += 2) { - unsigned int val = 0; - unsigned int lookup = 0; - char byte[6]; - - memset(&byte, 0, 6); - - /* First two bytes of digest stuffed - * together. - */ - - val = digest2[x]; - val <<= 8; - val += digest2[x+1]; - - lookup = (val >> 0x0b); - lookup &= 0x1f; - if (lookup >= strlen(alphabet1)) - break; - sprintf(byte, "%c", alphabet1[lookup]); - strcat(resp_6, byte); - strcat(resp_6, "="); - - lookup = (val >> 0x06); - lookup &= 0x1f; - if (lookup >= strlen(alphabet2)) - break; - sprintf(byte, "%c", alphabet2[lookup]); - strcat(resp_6, byte); - - lookup = (val >> 0x01); - lookup &= 0x1f; - if (lookup >= strlen(alphabet2)) - break; - sprintf(byte, "%c", alphabet2[lookup]); - strcat(resp_6, byte); - - lookup = (val & 0x01); - if (lookup >= strlen(delimit_lookup)) - break; - sprintf(byte, "%c", delimit_lookup[lookup]); - strcat(resp_6, byte); - } - - /* Our second authentication response is based off - * of the crypto hash. */ - - cnt = 0; - memset(&digest1, 0, 20); - memset(&digest2, 0, 20); - - for (x = 0; x < (int)strlen((char *)crypt_hash); x++) - crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36; - - if (cnt < 64) - memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt); - - cnt = 0; - - for (x = 0; x < (int)strlen((char *)crypt_hash); x++) - crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c; - - if (cnt < 64) - memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt); - - sha1_init(&ctx1); - sha1_init(&ctx2); - - /* The first context gets the password hash XORed - * with 0x36 plus a magic value - * which we previously extrapolated from our - * challenge. */ - - sha1_append(&ctx1, crypt_hash_xor1, 64); - if (j >= 3 ) - ctx1.Length_Low = 0x1ff; - sha1_append(&ctx1, magic_key_char, 4); - sha1_finish(&ctx1, digest1); - - /* The second context gets the password hash XORed - * with 0x5c plus the SHA-1 digest - * of the first context. */ - - sha1_append(&ctx2, crypt_hash_xor2, 64); - sha1_append(&ctx2, digest1, 20); - sha1_finish(&ctx2, digest2); - - /* Now that we have digest2, use it to fetch - * characters from an alphabet to construct - * our first authentication response. */ - - for (x = 0; x < 20; x += 2) { - unsigned int val = 0; - unsigned int lookup = 0; - - char byte[6]; - - memset(&byte, 0, 6); - - /* First two bytes of digest stuffed - * together. */ - - val = digest2[x]; - val <<= 8; - val += digest2[x+1]; - - lookup = (val >> 0x0b); - lookup &= 0x1f; - if (lookup >= strlen(alphabet1)) - break; - sprintf(byte, "%c", alphabet1[lookup]); - strcat(resp_96, byte); - strcat(resp_96, "="); - - lookup = (val >> 0x06); - lookup &= 0x1f; - if (lookup >= strlen(alphabet2)) - break; - sprintf(byte, "%c", alphabet2[lookup]); - strcat(resp_96, byte); - - lookup = (val >> 0x01); - lookup &= 0x1f; - if (lookup >= strlen(alphabet2)) - break; - sprintf(byte, "%c", alphabet2[lookup]); - strcat(resp_96, byte); - - lookup = (val & 0x01); - if (lookup >= strlen(delimit_lookup)) - break; - sprintf(byte, "%c", delimit_lookup[lookup]); - strcat(resp_96, byte); - } - - pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id); - yahoo_packet_hash(pack, 0, sn); - yahoo_packet_hash(pack, 6, resp_6); - yahoo_packet_hash(pack, 96, resp_96); - yahoo_packet_hash(pack, 1, sn); - yahoo_send_packet(yid, pack, 0); - yahoo_packet_free(pack); - - free(password_hash); - free(crypt_hash); -} - -struct yahoo_https_auth_data -{ - struct yahoo_input_data *yid; - char *token; - char *chal; -}; - -static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had); -static void yahoo_https_auth_token_finish(struct http_request *req); -static void yahoo_https_auth_init(struct yahoo_https_auth_data *had); -static void yahoo_https_auth_finish(struct http_request *req); - -/* Extract a value from a login.yahoo.com response. Assume CRLF-linebreaks - and FAIL miserably if they're not there... */ -static char *yahoo_ha_find_key(char *response, char *key) -{ - char *s, *end; - int len = strlen(key); - - s = response; - do { - if (strncmp(s, key, len) == 0 && s[len] == '=') { - s += len + 1; - if ((end = strchr(s, '\r'))) - return g_strndup(s, end - s); - else - return g_strdup(s); - } - - if ((s = strchr(s, '\n'))) - s ++; - } while (s && *s); - - return NULL; -} - -static enum yahoo_status yahoo_https_status_parse(int code) -{ - switch (code) - { - case 1212: return YAHOO_LOGIN_PASSWD; - case 1213: return YAHOO_LOGIN_LOCK; - case 1235: return YAHOO_LOGIN_UNAME; - default: return (enum yahoo_status) code; - } -} - -static void yahoo_process_auth_0x10(struct yahoo_input_data *yid, const char *seed, const char *sn) -{ - struct yahoo_https_auth_data *had = g_new0(struct yahoo_https_auth_data, 1); - - had->yid = yid; - had->chal = g_strdup(seed); - - yahoo_https_auth_token_init(had); -} - -static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had) -{ - struct yahoo_input_data *yid = had->yid; - struct yahoo_data *yd = yid->yd; - struct http_request *req; - char *login, *passwd, *chal; - char *url; - - login = g_strndup(yd->user, 3 * strlen(yd->user)); - http_encode(login); - passwd = g_strndup(yd->password, 3 * strlen(yd->password)); - http_encode(passwd); - chal = g_strndup(had->chal, 3 * strlen(had->chal)); - http_encode(chal); - - url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=%d&login=%s&passwd=%s&chal=%s", - (int) time(NULL), login, passwd, chal); - - req = http_dorequest_url(url, yahoo_https_auth_token_finish, had); - - g_free(url); - g_free(chal); - g_free(passwd); - g_free(login); -} + login = g_strndup(yd->user, 3 * strlen(yd->user)); + http_encode(login); + passwd = g_strndup(yd->password, 3 * strlen(yd->password)); + http_encode(passwd); + chal = g_strndup(had->chal, 3 * strlen(had->chal)); + http_encode(chal); + + url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=%d&login=%s&passwd=%s&chal=%s", + (int) time(NULL), login, passwd, chal); + + req = http_dorequest_url(url, yahoo_https_auth_token_finish, had); + + g_free(url); + g_free(chal); + g_free(passwd); + g_free(login); +} static void yahoo_https_auth_token_finish(struct http_request *req) { @@ -2478,52 +1945,53 @@ fail: g_free(had); } -static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_auth(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { char *seed = NULL; - char *sn = NULL; + char *sn = NULL; YList *l = pkt->hash; int m = 0; + struct yahoo_data *yd = yid->yd; while (l) { struct yahoo_pair *pair = l->data; - if (pair->key == 94) + + switch (pair->key) { + case 94: seed = pair->value; - if (pair->key == 1) + break; + case 1: sn = pair->value; - if (pair->key == 13) + break; + case 13: m = atoi(pair->value); + break; + } l = l->next; } - if (!seed) + if (!seed) return; - switch (m) { - case 0: - yahoo_process_auth_pre_0x0b(yid, seed, sn); - break; - case 1: - yahoo_process_auth_0x0b(yid, seed, sn); - break; - case 2: - yahoo_process_auth_0x10(yid, seed, sn); - break; - default: - /* call error */ - WARNING(("unknown auth type %d", m)); - yahoo_process_auth_0x0b(yid, seed, sn); - break; + if (m==2) + yahoo_https_auth(yid, seed, sn); + else { + /* call error */ + WARNING(("unknown auth type %d", m)); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_UNKNOWN, NULL); } } -static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_auth_resp(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { struct yahoo_data *yd = yid->yd; char *login_id; char *handle; char *url = NULL; - int login_status = 0; + int login_status = -1; YList *l; @@ -2539,9 +2007,10 @@ static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_p login_status = atoi(pair->value); } - if (pkt->status == 0xffffffff) { - YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, login_status, url); - /* yahoo_logoff(yd->client_id);*/ + if (pkt->status == YPACKET_STATUS_DISCONNECTED) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + login_status, url); + /* yahoo_logoff(yd->client_id); */ } } @@ -2579,6 +2048,37 @@ static void yahoo_process_mail(struct yahoo_input_data *yid, NULL, count); } +static void yahoo_process_new_contact(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *me = NULL; + char *who = NULL; + char *msg = NULL; + int online = -1; + + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) + who = pair->value; + else if (pair->key == 5) + me = pair->value; + else if (pair->key == 14) + msg = pair->value; + else if (pair->key == 13) + online = strtol(pair->value, NULL, 10); + } + + if (who && online < 0) + YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, me, who, + msg); + else if (online == 2) + YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg); +} + +/* UNUSED? */ static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_packet *pkt) { @@ -2660,41 +2160,21 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, if (!where) where = "Unknown"; - /* status: 0 == Successful, 1 == Error (does not exist), 2 == Already in list */ - if ( status == 0 ) { - bud = y_new0(struct yahoo_buddy, 1); - bud->id = strdup(who); - bud->group = strdup(where); - bud->real_name = NULL; - - yd->buddies = y_list_append(yd->buddies, bud); - - /* Possibly called already, but at least the call above doesn't - seem to happen every time (not anytime I tried). */ - YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, me, who, NULL); - } + bud = y_new0(struct yahoo_buddy, 1); + bud->id = strdup(who); + bud->group = strdup(where); + bud->real_name = NULL; -/* YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */ -} + yd->buddies = y_list_append(yd->buddies, bud); -static void yahoo_process_contact_ymsg13(struct yahoo_input_data *yid, struct yahoo_packet *pkt) -{ - char* who = NULL; - char* me = NULL; - char* msg = NULL; - YList *l; - for (l = pkt->hash; l; l = l->next) { - struct yahoo_pair *pair = l->data; - if (pair->key == 4) - who = pair->value; - else if (pair->key == 5) - me = pair->value; - else - DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value)); + /* A non-zero status (i've seen 2) seems to mean the buddy is already + * added and is online */ + if (status) { + LOG(("Setting online see packet for info")); + yahoo_dump_unhandled(pkt); + YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, who, + YAHOO_STATUS_AVAILABLE, NULL, 0, 0, 0); } - - if (pkt->status==3) - YAHOO_CALLBACK(ext_yahoo_contact_auth_request) (yid->yd->client_id, me, who, msg); } static void yahoo_process_buddydel(struct yahoo_input_data *yid, @@ -2782,7 +2262,7 @@ static void yahoo_process_ignore(struct yahoo_input_data *yid, */ /* if(status) - YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, who, 0, status); + YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, who, 0, status); */ } @@ -2836,7 +2316,32 @@ static void yahoo_process_ping(struct yahoo_input_data *yid, YAHOO_CALLBACK(ext_yahoo_got_ping) (yid->yd->client_id, errormsg); } -static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) +static void yahoo_process_buddy_change_group(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + YList *l; + char *me = NULL; + char *who = NULL; + char *old_group = NULL; + char *new_group = NULL; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + me = pair->value; + if (pair->key == 7) + who = pair->value; + if (pair->key == 224) + old_group = pair->value; + if (pair->key == 264) + new_group = pair->value; + } + + YAHOO_CALLBACK(ext_yahoo_got_buddy_change_group) (yid->yd->client_id, + me, who, old_group, new_group); +} + +static void _yahoo_webcam_get_server_connected(void *fd, int error, void *d) { struct yahoo_input_data *yid = d; char *who = yid->wcm->user; @@ -2847,7 +2352,7 @@ static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) unsigned int len = 0; unsigned int pos = 0; - if (error || fd <= 0) { + if (error || !fd) { FREE(who); FREE(yid); return; @@ -2904,7 +2409,7 @@ static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, YAHOO_CALLBACK(ext_yahoo_connect_async) (yid->yd->client_id, yss->webcam_host, yss->webcam_port, - _yahoo_webcam_get_server_connected, yid); + _yahoo_webcam_get_server_connected, yid, 0); } @@ -2951,7 +2456,7 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, case YAHOO_SERVICE_IDACT: case YAHOO_SERVICE_IDDEACT: case YAHOO_SERVICE_Y6_STATUS_UPDATE: - case YAHOO_SERVICE_YMSG15_STATUS: + case YAHOO_SERVICE_Y8_STATUS: yahoo_process_status(yid, pkt); break; case YAHOO_SERVICE_NOTIFY: @@ -2965,7 +2470,9 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, case YAHOO_SERVICE_NEWMAIL: yahoo_process_mail(yid, pkt); break; - case YAHOO_SERVICE_REJECTCONTACT: + case YAHOO_SERVICE_Y7_AUTHORIZATION: + yahoo_process_new_contact(yid, pkt); + break; case YAHOO_SERVICE_NEWCONTACT: yahoo_process_contact(yid, pkt); break; @@ -3000,15 +2507,18 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, yahoo_process_chat(yid, pkt); break; case YAHOO_SERVICE_P2PFILEXFER: - case YAHOO_SERVICE_FILETRANSFER: + case YAHOO_SERVICE_Y7_FILETRANSFER: yahoo_process_filetransfer(yid, pkt); break; + case YAHOO_SERVICE_Y7_FILETRANSFERINFO: + yahoo_process_filetransferinfo(yid, pkt); + break; + case YAHOO_SERVICE_Y7_FILETRANSFERACCEPT: + yahoo_process_filetransferaccept(yid, pkt); + break; case YAHOO_SERVICE_ADDBUDDY: yahoo_process_buddyadd(yid, pkt); break; - case YAHOO_SERVICE_CONTACT_YMSG13: - yahoo_process_contact_ymsg13(yid,pkt); - break; case YAHOO_SERVICE_REMBUDDY: yahoo_process_buddydel(yid, pkt); break; @@ -3024,6 +2534,9 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, case YAHOO_SERVICE_PING: yahoo_process_ping(yid, pkt); break; + case YAHOO_SERVICE_Y7_CHANGE_GROUP: + yahoo_process_buddy_change_group(yid, pkt); + break; case YAHOO_SERVICE_IDLE: case YAHOO_SERVICE_MAILSTAT: case YAHOO_SERVICE_CHATINVITE: @@ -3037,6 +2550,7 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, case YAHOO_SERVICE_CHATLOGON: case YAHOO_SERVICE_CHATLOGOFF: case YAHOO_SERVICE_CHATMSG: + case YAHOO_SERVICE_REJECTCONTACT: case YAHOO_SERVICE_PEERTOPEER: WARNING(("unhandled service 0x%02x", pkt->service)); yahoo_dump_unhandled(pkt); @@ -3049,9 +2563,10 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, break; case YAHOO_SERVICE_PICTURE_UPLOAD: yahoo_process_picture_upload(yid, pkt); - break; - case YAHOO_SERVICE_YMSG15_BUDDY_LIST: /* Buddy List */ + break; + case YAHOO_SERVICE_Y8_LIST: /* Buddy List */ yahoo_process_buddy_list(yid, pkt); + break; default: WARNING(("unknown service 0x%02x", pkt->service)); yahoo_dump_unhandled(pkt); @@ -3386,90 +2901,92 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) yid->wcd->data_size)); /* find out what kind of packet we got */ - switch (yid->wcd->packet_type) - { - case 0x00: - /* user requests to view webcam (uploading) */ - if (yid->wcd->data_size && - yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) { - end = begin; - while (end <= yid->rxlen && - yid->rxqueue[end++] != 13); - if (end > begin) - { - who = y_memdup(yid->rxqueue + begin, end - begin); - who[end - begin - 1] = 0; - YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id, who + 2, 2); - FREE(who); - } + switch (yid->wcd->packet_type) { + case 0x00: + /* user requests to view webcam (uploading) */ + if (yid->wcd->data_size && + yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) { + end = begin; + while (end <= yid->rxlen && yid->rxqueue[end++] != 13) ; + if (end > begin) { + who = y_memdup(yid->rxqueue + begin, + end - begin); + who[end - begin - 1] = 0; + YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd-> + client_id, who + 2, 2); + FREE(who); } + } - if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) { - /* timestamp/status field */ - /* 0 = declined viewing permission */ - /* 1 = accepted viewing permission */ - if (yid->wcd->timestamp == 0) { - YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id, yid->wcm->user, 3); - } - } - break; - case 0x01: /* status packets?? */ - /* timestamp contains status info */ - /* 00 00 00 01 = we have data?? */ - break; - case 0x02: /* image data */ - YAHOO_CALLBACK(ext_yahoo_got_webcam_image) (yd->client_id, - yid->wcm->user, yid->rxqueue + begin, - yid->wcd->data_size, pos - begin, - yid->wcd->timestamp); - break; - case 0x05: /* response packets when uploading */ - if (!yid->wcd->data_size) { - YAHOO_CALLBACK(ext_yahoo_webcam_data_request) (yd->client_id, yid->wcd->timestamp); - } - break; - case 0x07: /* connection is closing */ - switch(reason) - { - case 0x01: /* user closed connection */ - closed = 1; - break; - case 0x0F: /* user cancelled permission */ - closed = 2; - break; + if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) { + /* timestamp/status field */ + /* 0 = declined viewing permission */ + /* 1 = accepted viewing permission */ + if (yid->wcd->timestamp == 0) { + YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd-> + client_id, yid->wcm->user, 3); } - YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id, yid->wcm->user, closed); + } + break; + case 0x01: /* status packets?? */ + /* timestamp contains status info */ + /* 00 00 00 01 = we have data?? */ + break; + case 0x02: /* image data */ + YAHOO_CALLBACK(ext_yahoo_got_webcam_image) (yd->client_id, + yid->wcm->user, yid->rxqueue + begin, + yid->wcd->data_size, pos - begin, yid->wcd->timestamp); + break; + case 0x05: /* response packets when uploading */ + if (!yid->wcd->data_size) { + YAHOO_CALLBACK(ext_yahoo_webcam_data_request) (yd-> + client_id, yid->wcd->timestamp); + } + break; + case 0x07: /* connection is closing */ + switch (reason) { + case 0x01: /* user closed connection */ + closed = 1; break; - case 0x0C: /* user connected */ - case 0x0D: /* user disconnected */ - if (yid->wcd->data_size) { - who = y_memdup(yid->rxqueue + begin, pos - begin + 1); - who[pos - begin] = 0; - if (yid->wcd->packet_type == 0x0C) - connect = 1; - else - connect = 0; - YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id, who, connect); - FREE(who); - } - break; - case 0x13: /* user data */ - /* i = user_ip (ip of the user we are viewing) */ - /* j = user_ext_ip (external ip of the user we */ - /* are viewing) */ - break; - case 0x17: /* ?? */ + case 0x0F: /* user cancelled permission */ + closed = 2; break; + } + YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id, + yid->wcm->user, closed); + break; + case 0x0C: /* user connected */ + case 0x0D: /* user disconnected */ + if (yid->wcd->data_size) { + who = y_memdup(yid->rxqueue + begin, pos - begin + 1); + who[pos - begin] = 0; + if (yid->wcd->packet_type == 0x0C) + connect = 1; + else + connect = 0; + YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id, + who, connect); + FREE(who); + } + break; + case 0x13: /* user data */ + /* i=user_ip (ip of the user we are viewing) */ + /* j=user_ext_ip (external ip of the user we */ + /* are viewing) */ + break; + case 0x17: /* ?? */ + break; } yid->wcd->to_read -= pos - begin; yid->rxlen -= pos; DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); - if (yid->rxlen>0) { + if (yid->rxlen > 0) { unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen); FREE(yid->rxqueue); yid->rxqueue = tmp; - DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); } else { DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); FREE(yid->rxqueue); @@ -3482,16 +2999,16 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid) return 0; } -int yahoo_write_ready(int id, int fd, void *data) +int yahoo_write_ready(int id, void *fd, void *data) { struct yahoo_input_data *yid = data; int len; struct data_queue *tx; - LOG(("write callback: id=%d fd=%d data=%p", id, fd, data)); - if (!yid || !yid->txqueues || !find_conn_by_id(id)) + LOG(("write callback: id=%d fd=%p data=%p", id, fd, data)); + if (!yid || !yid->txqueues) return -2; - + tx = yid->txqueues->data; LOG(("writing %d bytes", tx->len)); len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len)); @@ -3507,10 +3024,12 @@ int yahoo_write_ready(int id, int fd, void *data) tx = l->data; free(tx->queue); free(tx); - yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues); + yid->txqueues = + y_list_remove_link(yid->txqueues, + yid->txqueues); y_list_free_1(l); } - LOG(("yahoo_write_ready(%d, %d) len < 0", id, fd)); + LOG(("yahoo_write_ready(%d, %p) len < 0", id, fd)); YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag); yid->write_tag = 0; errno = e; @@ -3527,15 +3046,17 @@ int yahoo_write_ready(int id, int fd, void *data) YList *l = yid->txqueues; free(tx->queue); free(tx); - yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues); + yid->txqueues = + y_list_remove_link(yid->txqueues, yid->txqueues); y_list_free_1(l); /* - if (!yid->txqueues) - LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); - */ + if(!yid->txqueues) + LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); + */ if (!yid->txqueues) { - LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); - YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag); + LOG(("yahoo_write_ready(%d, %p) !txqueues", id, fd)); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, + yid->write_tag); yid->write_tag = 0; } } @@ -3543,7 +3064,8 @@ int yahoo_write_ready(int id, int fd, void *data) return 1; } -static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_pager_connection(struct yahoo_input_data *yid, + int over) { struct yahoo_packet *pkt; struct yahoo_data *yd = yid->yd; @@ -3552,8 +3074,8 @@ static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int ove if (over) return; - while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER) - && (pkt = yahoo_getdata(yid)) != NULL) { + while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER) + && (pkt = yahoo_getdata(yid)) != NULL) { yahoo_packet_process(yid, pkt); @@ -3561,17 +3083,15 @@ static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int ove } } -static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over) -{ -} - -static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, + int over) { if (over) return; - if (strstr((char*)yid->rxqueue+(yid->rxlen-20), "")) { - YAHOO_CALLBACK(ext_yahoo_chat_cat_xml) (yid->yd->client_id, (char*)yid->rxqueue); + if (strstr((char *)yid->rxqueue + (yid->rxlen - 20), "")) { + YAHOO_CALLBACK(ext_yahoo_chat_cat_xml) (yid->yd->client_id, + (char *)yid->rxqueue); } } @@ -3582,48 +3102,70 @@ static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over) YList *buds; int changed = 0; int id = yd->client_id; + int yab_used = 0; + + LOG(("Got data for YAB")); if (over) return; - while (find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) - && (yab = yahoo_getyab(yid)) != NULL) { + while (find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) + && (yab = yahoo_getyab(yid)) != NULL) { if (!yab->id) continue; + changed = 1; + yab_used = 0; for (buds = yd->buddies; buds; buds = buds->next) { struct yahoo_buddy *bud = buds->data; if (!strcmp(bud->id, yab->id)) { + yab_used = 1; bud->yab_entry = yab; if (yab->nname) { bud->real_name = strdup(yab->nname); } else if (yab->fname && yab->lname) { - bud->real_name = y_new0(char, - strlen(yab->fname)+ - strlen(yab->lname)+2 - ); + bud->real_name = y_new0(char, + strlen(yab->fname) + + strlen(yab->lname) + 2); sprintf(bud->real_name, "%s %s", - yab->fname, yab->lname); + yab->fname, yab->lname); } else if (yab->fname) { bud->real_name = strdup(yab->fname); } - break; /* for */ + break; /* for */ } } + + if (!yab_used) { + FREE(yab->fname); + FREE(yab->lname); + FREE(yab->nname); + FREE(yab->id); + FREE(yab->email); + FREE(yab->hphone); + FREE(yab->wphone); + FREE(yab->mphone); + FREE(yab); + } + } if (changed) - YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies); + YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, + yd->buddies); } -static void yahoo_process_search_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_search_connection(struct yahoo_input_data *yid, + int over) { struct yahoo_found_contact *yct = NULL; char *p = (char *)yid->rxqueue, *np, *cp; int k, n; - int start = 0, found=0, total=0; + int start = 0, found = 0, total = 0; YList *contacts = NULL; - struct yahoo_input_data *pyid = find_input_by_id_and_type(yid->yd->client_id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *pyid = + find_input_by_id_and_type(yid->yd->client_id, + YAHOO_CONNECTION_PAGER); if (!over || !pyid) return; @@ -3634,10 +3176,16 @@ static void yahoo_process_search_connection(struct yahoo_input_data *yid, int ov for (k = 0; (p = strchr(p, 4)) && (k < 4); k++) { p++; n = atoi(p); - switch(k) { - case 0: found = pyid->ys->lsearch_nfound = n; break; - case 2: start = pyid->ys->lsearch_nstart = n; break; - case 3: total = pyid->ys->lsearch_ntotal = n; break; + switch (k) { + case 0: + found = pyid->ys->lsearch_nfound = n; + break; + case 2: + start = pyid->ys->lsearch_nstart = n; + break; + case 3: + total = pyid->ys->lsearch_ntotal = n; + break; } } @@ -3652,37 +3200,41 @@ static void yahoo_process_search_connection(struct yahoo_input_data *yid, int ov if (!np) break; *np = 0; - p = np+1; - - switch(k++) { - case 1: - if (strlen(cp) > 2 && y_list_length(contacts) < total) { - yct = y_new0(struct yahoo_found_contact, 1); - contacts = y_list_append(contacts, yct); - yct->id = cp+2; - } else { - *p = 0; - } - break; - case 2: - yct->online = !strcmp(cp, "2") ? 1 : 0; - break; - case 3: - yct->gender = cp; - break; - case 4: - yct->age = atoi(cp); - break; - case 5: - if (strcmp(cp, "5") != 0) - yct->location = cp; - k = 0; - break; + p = np + 1; + + switch (k++) { + case 1: + if (strlen(cp) > 2 + && y_list_length(contacts) < total) { + yct = y_new0(struct yahoo_found_contact, + 1); + contacts = y_list_append(contacts, yct); + yct->id = cp + 2; + } else { + *p = 0; + } + break; + case 2: + yct->online = !strcmp(cp, "2") ? 1 : 0; + break; + case 3: + yct->gender = cp; + break; + case 4: + yct->age = atoi(cp); + break; + case 5: + /* not worth the context switch for strcmp */ + if (cp[0] != '\005' || cp[1] != '\000') + yct->location = cp; + k = 0; + break; } } } - YAHOO_CALLBACK(ext_yahoo_got_search_result) (yid->yd->client_id, found, start, total, contacts); + YAHOO_CALLBACK(ext_yahoo_got_search_result) (yid->yd->client_id, found, + start, total, contacts); while (contacts) { YList *node = contacts; @@ -3692,7 +3244,7 @@ static void yahoo_process_search_connection(struct yahoo_input_data *yid, int ov } } -static void _yahoo_webcam_connected(int fd, int error, void *d) +static void _yahoo_webcam_connected(void *fd, int error, void *d) { struct yahoo_input_data *yid = d; struct yahoo_webcam *wcm = yid->wcm; @@ -3700,12 +3252,12 @@ static void _yahoo_webcam_connected(int fd, int error, void *d) char conn_type[100]; char *data = NULL; char *packet = NULL; - unsigned char magic_nr[] = {1, 0, 0, 0, 1}; + unsigned char magic_nr[] = { 1, 0, 0, 0, 1 }; unsigned header_len = 0; unsigned int len = 0; unsigned int pos = 0; - if (error || fd <= 0) { + if (error || !fd) { FREE(yid); return; } @@ -3715,74 +3267,70 @@ static void _yahoo_webcam_connected(int fd, int error, void *d) LOG(("Connected")); /* send initial packet */ - switch (wcm->direction) - { - case YAHOO_WEBCAM_DOWNLOAD: - data = strdup(""); - break; - case YAHOO_WEBCAM_UPLOAD: - data = strdup(""); - break; - default: - return; + switch (wcm->direction) { + case YAHOO_WEBCAM_DOWNLOAD: + data = strdup(""); + break; + case YAHOO_WEBCAM_UPLOAD: + data = strdup(""); + break; + default: + return; } yahoo_add_to_send_queue(yid, data, strlen(data)); FREE(data); /* send data */ - switch (wcm->direction) - { - case YAHOO_WEBCAM_DOWNLOAD: - header_len = 8; - data = strdup("a=2\r\nc=us\r\ne=21\r\nu="); - data = y_string_append(data, yd->user); - data = y_string_append(data, "\r\nt="); - data = y_string_append(data, wcm->key); - data = y_string_append(data, "\r\ni="); - data = y_string_append(data, wcm->my_ip); - data = y_string_append(data, "\r\ng="); - data = y_string_append(data, wcm->user); - data = y_string_append(data, "\r\no=w-2-5-1\r\np="); - snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); - data = y_string_append(data, conn_type); - data = y_string_append(data, "\r\n"); - break; - case YAHOO_WEBCAM_UPLOAD: - header_len = 13; - data = strdup("a=2\r\nc=us\r\nu="); - data = y_string_append(data, yd->user); - data = y_string_append(data, "\r\nt="); - data = y_string_append(data, wcm->key); - data = y_string_append(data, "\r\ni="); - data = y_string_append(data, wcm->my_ip); - data = y_string_append(data, "\r\no=w-2-5-1\r\np="); - snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); - data = y_string_append(data, conn_type); - data = y_string_append(data, "\r\nb="); - data = y_string_append(data, wcm->description); - data = y_string_append(data, "\r\n"); - break; + switch (wcm->direction) { + case YAHOO_WEBCAM_DOWNLOAD: + header_len = 8; + data = strdup("a=2\r\nc=us\r\ne=21\r\nu="); + data = y_string_append(data, yd->user); + data = y_string_append(data, "\r\nt="); + data = y_string_append(data, wcm->key); + data = y_string_append(data, "\r\ni="); + data = y_string_append(data, wcm->my_ip); + data = y_string_append(data, "\r\ng="); + data = y_string_append(data, wcm->user); + data = y_string_append(data, "\r\no=w-2-5-1\r\np="); + snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); + data = y_string_append(data, conn_type); + data = y_string_append(data, "\r\n"); + break; + case YAHOO_WEBCAM_UPLOAD: + header_len = 13; + data = strdup("a=2\r\nc=us\r\nu="); + data = y_string_append(data, yd->user); + data = y_string_append(data, "\r\nt="); + data = y_string_append(data, wcm->key); + data = y_string_append(data, "\r\ni="); + data = y_string_append(data, wcm->my_ip); + data = y_string_append(data, "\r\no=w-2-5-1\r\np="); + snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); + data = y_string_append(data, conn_type); + data = y_string_append(data, "\r\nb="); + data = y_string_append(data, wcm->description); + data = y_string_append(data, "\r\n"); + break; } len = strlen(data); packet = y_new0(char, header_len + len); packet[pos++] = header_len; packet[pos++] = 0; - switch (wcm->direction) - { - case YAHOO_WEBCAM_DOWNLOAD: - packet[pos++] = 1; - packet[pos++] = 0; - break; - case YAHOO_WEBCAM_UPLOAD: - packet[pos++] = 5; - packet[pos++] = 0; - break; + switch (wcm->direction) { + case YAHOO_WEBCAM_DOWNLOAD: + packet[pos++] = 1; + packet[pos++] = 0; + break; + case YAHOO_WEBCAM_UPLOAD: + packet[pos++] = 5; + packet[pos++] = 0; + break; } pos += yahoo_put32(packet + pos, len); - if (wcm->direction == YAHOO_WEBCAM_UPLOAD) - { + if (wcm->direction == YAHOO_WEBCAM_UPLOAD) { memcpy(packet + pos, magic_nr, sizeof(magic_nr)); pos += sizeof(magic_nr); } @@ -3791,7 +3339,9 @@ static void _yahoo_webcam_connected(int fd, int error, void *d) FREE(packet); FREE(data); - yid->read_tag = YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, + yid->fd, YAHOO_INPUT_READ, yid); } static void yahoo_webcam_connect(struct yahoo_input_data *y) @@ -3816,14 +3366,15 @@ static void yahoo_webcam_connect(struct yahoo_input_data *y) yid->wcd = y_new0(struct yahoo_webcam_data, 1); LOG(("Connecting to: %s:%d", wcm->server, wcm->port)); - YAHOO_CALLBACK(ext_yahoo_connect_async) (y->yd->client_id, wcm->server, wcm->port, - _yahoo_webcam_connected, yid); + YAHOO_CALLBACK(ext_yahoo_connect_async) (y->yd->client_id, wcm->server, + wcm->port, _yahoo_webcam_connected, yid, 0); } -static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, + int over) { - char* server; + char *server; struct yahoo_server_settings *yss; if (over) @@ -3831,8 +3382,7 @@ static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, server = yahoo_getwebcam_master(yid); - if (server) - { + if (server) { yss = yid->yd->server_settings; yid->wcm->server = strdup(server); yid->wcm->port = yss->webcam_port; @@ -3845,45 +3395,44 @@ static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, } } -static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, + int over) { int id = yid->yd->client_id; - int fd = yid->fd; + void *fd = yid->fd; if (over) return; /* as long as we still have packets available keep processing them */ - while (find_input_by_id_and_fd(id, fd) - && yahoo_get_webcam_data(yid) == 1); -} - -static void (*yahoo_process_connection[])(struct yahoo_input_data *, int over) = { - yahoo_process_pager_connection, - yahoo_process_ft_connection, - yahoo_process_yab_connection, - yahoo_process_webcam_master_connection, - yahoo_process_webcam_connection, - yahoo_process_chatcat_connection, - yahoo_process_search_connection, -}; + while (find_input_by_id_and_fd(id, fd) + && yahoo_get_webcam_data(yid) == 1) ; +} + +static void (*yahoo_process_connection[]) (struct yahoo_input_data *, + int over) = { +yahoo_process_pager_connection, yahoo_process_ft_connection, + yahoo_process_yab_connection, + yahoo_process_webcam_master_connection, + yahoo_process_webcam_connection, + yahoo_process_chatcat_connection, + yahoo_process_search_connection}; -int yahoo_read_ready(int id, int fd, void *data) +int yahoo_read_ready(int id, void *fd, void *data) { struct yahoo_input_data *yid = data; char buf[1024]; int len; - LOG(("read callback: id=%d fd=%d data=%p", id, fd, data)); + LOG(("read callback: id=%d fd=%p data=%p", id, fd, data)); if (!yid) return -2; - do { - len = read(fd, buf, sizeof(buf)); + len = YAHOO_CALLBACK(ext_yahoo_read) (fd, buf, sizeof(buf)); } while (len == -1 && errno == EINTR); - if (len == -1 && (errno == EAGAIN||errno == EINTR)) /* we'll try again later */ + if (len == -1 && (errno == EAGAIN || errno == EINTR)) /* we'll try again later */ return 1; if (len <= 0) { @@ -3891,10 +3440,11 @@ int yahoo_read_ready(int id, int fd, void *data) DEBUG_MSG(("len == %d (<= 0)", len)); if (yid->type == YAHOO_CONNECTION_PAGER) { - YAHOO_CALLBACK(ext_yahoo_error) (yid->yd->client_id, "Connection closed by server", 1, E_CONNECTION); + YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd-> + client_id, YAHOO_LOGIN_SOCK, NULL); } - yahoo_process_connection[yid->type](yid, 1); + yahoo_process_connection[yid->type] (yid, 1); yahoo_input_close(yid); /* no need to return an error, because we've already fixed it */ @@ -3906,11 +3456,13 @@ int yahoo_read_ready(int id, int fd, void *data) return -1; } - yid->rxqueue = y_renew(unsigned char, yid->rxqueue, len + yid->rxlen); + yid->rxqueue = + y_renew(unsigned char, yid->rxqueue, len + yid->rxlen + 1); memcpy(yid->rxqueue + yid->rxlen, buf, len); yid->rxlen += len; + yid->rxqueue[yid->rxlen] = 0; - yahoo_process_connection[yid->type](yid, 0); + yahoo_process_connection[yid->type] (yid, 0); return len; } @@ -3947,13 +3499,7 @@ int yahoo_init(const char *username, const char *password) return yahoo_init_with_attributes(username, password, NULL); } -struct connect_callback_data { - struct yahoo_data *yd; - int tag; - int i; -}; - -static void yahoo_connected(int fd, int error, void *data) +static void yahoo_connected(void *fd, int error, void *data) { struct connect_callback_data *ccd = data; struct yahoo_data *yd = ccd->yd; @@ -3962,86 +3508,84 @@ static void yahoo_connected(int fd, int error, void *data) struct yahoo_server_settings *yss = yd->server_settings; if (error) { + int tag; if (fallback_ports[ccd->i]) { - int tag; + char *host = yss->pager_host; + + if (!host) + host = yss->pager_host_list[ccd->server_i]; + yss->pager_port = fallback_ports[ccd->i++]; - tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, yss->pager_host, - yss->pager_port, yahoo_connected, ccd); + tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd-> + client_id, host, yss->pager_port, + yahoo_connected, ccd, 0); if (tag > 0) ccd->tag = tag; + } else if (yss->pager_host_list + && yss->pager_host_list[ccd->server_i]) { + + /* Get back to the default port */ + yss->pager_port = pager_port; + ccd->server_i++; + LOG(("Fallback: Connecting to %s:%d", yss->pager_host_list[ccd->server_i], yss->pager_port)); + + ccd->i = 0; + tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, + yss->pager_host_list[ccd->server_i], yss->pager_port, + yahoo_connected, ccd, 0); } else { FREE(ccd); - YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, YAHOO_LOGIN_SOCK, NULL); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_SOCK, NULL); } return; } FREE(ccd); - /* fd < 0 && error == 0 means connect was cancelled */ - if (fd < 0) + /* fd == NULL && error == 0 means connect was cancelled */ + if (!fd) return; - pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT, + yd->session_id); NOTICE(("Sending initial packet")); yahoo_packet_hash(pkt, 1, yd->user); - yid = y_new0(struct yahoo_input_data, 1); - yid->yd = yd; + yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER); yid->fd = fd; - inputs = y_list_prepend(inputs, yid); yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); - yid->read_tag = YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); -} - -void yahoo_login(int id, int initial) -{ - struct yahoo_data *yd = find_conn_by_id(id); - struct connect_callback_data *ccd; - struct yahoo_server_settings *yss; - int tag; - - if (!yd) - return; - - yss = yd->server_settings; - - yd->initial_status = initial; - - ccd = y_new0(struct connect_callback_data, 1); - ccd->yd = yd; - tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, yss->pager_host, yss->pager_port, - yahoo_connected, ccd); - - /* - * if tag <= 0, then callback has already been called - * so ccd will have been freed - */ - if (tag > 0) - ccd->tag = tag; - else if (tag < 0) - YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, YAHOO_LOGIN_SOCK, NULL); + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, + yid->fd, YAHOO_INPUT_READ, yid); } - -int yahoo_get_fd(int id) +void *yahoo_get_fd(int id) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); if (!yid) return 0; else return yid->fd; } -void yahoo_send_im(int id, const char *from, const char *who, const char *what, int utf8, int picture) +void yahoo_send_buzz(int id, const char *from, const char *who) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + yahoo_send_im(id, from, who, "", 1, 0); +} + +void yahoo_send_im(int id, const char *from, const char *who, const char *what, + int utf8, int picture) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_packet *pkt = NULL; struct yahoo_data *yd; char pic_str[10]; @@ -4051,13 +3595,14 @@ void yahoo_send_im(int id, const char *from, const char *who, const char *what, yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, + yd->session_id); snprintf(pic_str, sizeof(pic_str), "%d", picture); - + if (from && strcmp(from, yd->user)) yahoo_packet_hash(pkt, 0, yd->user); - yahoo_packet_hash(pkt, 1, from?from:yd->user); + yahoo_packet_hash(pkt, 1, from ? from : yd->user); yahoo_packet_hash(pkt, 5, who); yahoo_packet_hash(pkt, 14, what); @@ -4068,7 +3613,6 @@ void yahoo_send_im(int id, const char *from, const char *who, const char *what, yahoo_packet_hash(pkt, 64, "0"); yahoo_packet_hash(pkt, 206, pic_str); - yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); @@ -4076,17 +3620,19 @@ void yahoo_send_im(int id, const char *from, const char *who, const char *what, void yahoo_send_typing(int id, const char *from, const char *who, int typ) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY, + yd->session_id); yahoo_packet_hash(pkt, 5, who); - yahoo_packet_hash(pkt, 1, from?from:yd->user); + yahoo_packet_hash(pkt, 1, from ? from : yd->user); yahoo_packet_hash(pkt, 14, " "); yahoo_packet_hash(pkt, 13, typ ? "1" : "0"); yahoo_packet_hash(pkt, 49, "TYPING"); @@ -4098,7 +3644,8 @@ void yahoo_send_typing(int id, const char *from, const char *who, int typ) void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; int old_status; @@ -4108,12 +3655,14 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) return; yd = yid->yd; + old_status = yd->current_status; yd->current_status = state; /* Thank you libpurple :) */ if (yd->current_status == YAHOO_STATUS_INVISIBLE) { - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0); + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, + YAHOO_STATUS_AVAILABLE, 0); yahoo_packet_hash(pkt, 13, "2"); yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); @@ -4121,7 +3670,8 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) return; } - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, yd->current_status, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, + yd->current_status, yd->session_id); snprintf(s, sizeof(s), "%d", yd->current_status); yahoo_packet_hash(pkt, 10, s); yahoo_packet_hash(pkt, 19, msg && state == YAHOO_STATUS_CUSTOM ? msg : ""); @@ -4130,7 +3680,8 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) yahoo_packet_free(pkt); if (old_status == YAHOO_STATUS_INVISIBLE) { - pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0); + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, + YAHOO_STATUS_AVAILABLE, 0); yahoo_packet_hash(pkt, 13, "1"); yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); @@ -4139,7 +3690,8 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) void yahoo_logoff(int id) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; @@ -4153,7 +3705,8 @@ void yahoo_logoff(int id) /* Meh. Don't send this. The event handlers are not going to get to do this so it'll just leak memory. And the TCP connection reset will hopefully be clear enough. */ - pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, + YPACKET_STATUS_DEFAULT, yd->session_id); yd->current_status = -1; if (pkt) { @@ -4162,14 +3715,16 @@ void yahoo_logoff(int id) } } - do { +/* do { yahoo_input_close(yid); - } while ((yid = find_input_by_id(id))); + } while((yid = find_input_by_id(id)));*/ + } void yahoo_get_list(int id) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; @@ -4177,7 +3732,8 @@ void yahoo_get_list(int id) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YPACKET_STATUS_DEFAULT, + yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); if (pkt) { yahoo_send_packet(yid, pkt, 0); @@ -4185,25 +3741,28 @@ void yahoo_get_list(int id) } } -static void _yahoo_http_connected(int id, int fd, int error, void *data) +static void _yahoo_http_connected(int id, void *fd, int error, void *data) { struct yahoo_input_data *yid = data; - if (fd <= 0) { + if (fd == NULL || error) { inputs = y_list_remove(inputs, yid); FREE(yid); return; } yid->fd = fd; - yid->read_tag = YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, YAHOO_INPUT_READ, yid); + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, + YAHOO_INPUT_READ, yid); } +/* FIXME Get address book from address.yahoo.com instead */ void yahoo_get_yab(int id) { struct yahoo_data *yd = find_conn_by_id(id); struct yahoo_input_data *yid; char url[1024]; - char buff[1024]; + char buff[2048]; if (!yd) return; @@ -4212,25 +3771,58 @@ void yahoo_get_yab(int id) yid->yd = yd; yid->type = YAHOO_CONNECTION_YAB; - snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?ab2=0"); + LOG(("Sending request for Address Book")); - snprintf(buff, sizeof(buff), "Y=%s; T=%s", - yd->cookie_y, yd->cookie_t); + snprintf(url, 1024, + "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us" + "&diffs=1&t=0&tags=short&rt=0&prog-ver=8.1.0.249&useutf8=1&legenc=codepage-1252"); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); inputs = y_list_prepend(inputs, yid); - yahoo_http_get(yid->yd->client_id, url, buff, - _yahoo_http_connected, yid); + yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, + _yahoo_http_connected, yid); +} + +struct yahoo_post_data { + struct yahoo_input_data *yid; + char *data; +}; + +static void _yahoo_http_post_connected(int id, void *fd, int error, void *data) +{ + struct yahoo_post_data *yad = data; + struct yahoo_input_data *yid = yad->yid; + char *buff = yad->data; + + if (!fd) { + inputs = y_list_remove(inputs, yid); + FREE(yid); + return; + } + + YAHOO_CALLBACK(ext_yahoo_write) (fd, buff, strlen(buff)); + + yid->fd = fd; + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, + YAHOO_INPUT_READ, yid); + + FREE(buff); + FREE(yad); } +/* FIXME This is also likely affected */ void yahoo_set_yab(int id, struct yab *yab) { + struct yahoo_post_data *yad = y_new0(struct yahoo_post_data, 1); struct yahoo_data *yd = find_conn_by_id(id); struct yahoo_input_data *yid; char url[1024]; char buff[1024]; - char *temp; - int size = sizeof(url)-1; + char post[1024]; + int size = 0; if (!yd) return; @@ -4239,76 +3831,37 @@ void yahoo_set_yab(int id, struct yab *yab) yid->type = YAHOO_CONNECTION_YAB; yid->yd = yd; - strncpy(url, "http://insider.msg.yahoo.com/ycontent/?addab2=0", size); - - if (yab->dbid) { - /* change existing yab */ - char tmp[32]; - strncat(url, "&ee=1&ow=1&id=", size - strlen(url)); - snprintf(tmp, sizeof(tmp), "%d", yab->dbid); - strncat(url, tmp, size - strlen(url)); - } - - if (yab->fname) { - strncat(url, "&fn=", size - strlen(url)); - temp = yahoo_urlencode(yab->fname); - strncat(url, temp, size - strlen(url)); - free(temp); - } - if (yab->lname) { - strncat(url, "&ln=", size - strlen(url)); - temp = yahoo_urlencode(yab->lname); - strncat(url, temp, size - strlen(url)); - free(temp); - } - strncat(url, "&yid=", size - strlen(url)); - temp = yahoo_urlencode(yab->id); - strncat(url, temp, size - strlen(url)); - free(temp); - if (yab->nname) { - strncat(url, "&nn=", size - strlen(url)); - temp = yahoo_urlencode(yab->nname); - strncat(url, temp, size - strlen(url)); - free(temp); - } - if (yab->email) { - strncat(url, "&e=", size - strlen(url)); - temp = yahoo_urlencode(yab->email); - strncat(url, temp, size - strlen(url)); - free(temp); - } - if (yab->hphone) { - strncat(url, "&hp=", size - strlen(url)); - temp = yahoo_urlencode(yab->hphone); - strncat(url, temp, size - strlen(url)); - free(temp); - } - if (yab->wphone) { - strncat(url, "&wp=", size - strlen(url)); - temp = yahoo_urlencode(yab->wphone); - strncat(url, temp, size - strlen(url)); - free(temp); - } - if (yab->mphone) { - strncat(url, "&mp=", size - strlen(url)); - temp = yahoo_urlencode(yab->mphone); - strncat(url, temp, size - strlen(url)); - free(temp); - } - strncat(url, "&pp=0", size - strlen(url)); - - snprintf(buff, sizeof(buff), "Y=%s; T=%s", - yd->cookie_y, yd->cookie_t); + if(yab->yid) + size = snprintf(post, sizeof(post), "" + "" + "" + "", yd->user, 9, yab->yid, /* Don't know why */ + yab->id, yab->nname?yab->nname:""); + else + size = snprintf(post, sizeof(post), "" + "" + "" + "", yd->user, 1, /* Don't know why */ + yab->id, yab->nname?yab->nname:""); + + yad->yid = yid; + yad->data = strdup(post); + + strcpy(url, "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us" + "&sync=1&tags=short&noclear=1&useutf8=1&legenc=codepage-1252"); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); inputs = y_list_prepend(inputs, yid); - yahoo_http_get(yid->yd->client_id, url, buff, - _yahoo_http_connected, yid); + yahoo_http_post(yid->yd->client_id, url, buff, size, + _yahoo_http_post_connected, yad); } void yahoo_set_identity_status(int id, const char *identity, int active) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; @@ -4316,8 +3869,8 @@ void yahoo_set_identity_status(int id, const char *identity, int active) return; yd = yid->yd; - pkt = yahoo_packet_new(active?YAHOO_SERVICE_IDACT:YAHOO_SERVICE_IDDEACT, - YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(active ? YAHOO_SERVICE_IDACT : + YAHOO_SERVICE_IDDEACT, YPACKET_STATUS_DEFAULT, yd->session_id); yahoo_packet_hash(pkt, 3, identity); if (pkt) { yahoo_send_packet(yid, pkt, 0); @@ -4327,7 +3880,8 @@ void yahoo_set_identity_status(int id, const char *identity, int active) void yahoo_refresh(int id) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; @@ -4335,7 +3889,8 @@ void yahoo_refresh(int id) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YPACKET_STATUS_DEFAULT, + yd->session_id); if (pkt) { yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); @@ -4344,37 +3899,43 @@ void yahoo_refresh(int id) void yahoo_keepalive(int id) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YPACKET_STATUS_DEFAULT, + yd->session_id); yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); } -void yahoo_chat_keepalive (int id) +void yahoo_chat_keepalive(int id) { - struct yahoo_input_data *yid = find_input_by_id_and_type (id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; if (!yid) - return; + return; yd = yid->yd; - pkt = yahoo_packet_new (YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_send_packet (yid, pkt, 0); - yahoo_packet_free (pkt); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YPACKET_STATUS_DEFAULT, + yd->session_id); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); } -void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg) +void yahoo_add_buddy(int id, const char *who, const char *group, + const char *msg) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; @@ -4385,13 +3946,12 @@ void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg if (!yd->logged_in) return; - pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT, yd->session_id); - - if (msg != NULL) /* add message/request "it's me add me" */ + pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT, + yd->session_id); + if (msg != NULL) /* add message/request "it's me add me" */ yahoo_packet_hash(pkt, 14, msg); else - yahoo_packet_hash(pkt,14,""); - + yahoo_packet_hash(pkt, 14, ""); yahoo_packet_hash(pkt, 65, group); yahoo_packet_hash(pkt, 97, "1"); yahoo_packet_hash(pkt, 1, yd->user); @@ -4401,15 +3961,14 @@ void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg yahoo_packet_hash(pkt, 334, "0"); yahoo_packet_hash(pkt, 301, "319"); yahoo_packet_hash(pkt, 303, "319"); - - yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); } void yahoo_remove_buddy(int id, const char *who, const char *group) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; @@ -4417,7 +3976,8 @@ void yahoo_remove_buddy(int id, const char *who, const char *group) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YPACKET_STATUS_DEFAULT, + yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); yahoo_packet_hash(pkt, 7, who); @@ -4426,52 +3986,10 @@ void yahoo_remove_buddy(int id, const char *who, const char *group) yahoo_packet_free(pkt); } -void yahoo_accept_buddy_ymsg13(int id,const char* me,const char* who){ - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); - struct yahoo_data *yd; - - if (!yid) - return; - yd = yid->yd; - - struct yahoo_packet* pkt = NULL; - pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0); - - yahoo_packet_hash(pkt,1,me ?: yd->user); - yahoo_packet_hash(pkt,5,who); - yahoo_packet_hash(pkt,13,"1"); - yahoo_packet_hash(pkt,334,"0"); - yahoo_send_packet(yid, pkt, 0); - yahoo_packet_free(pkt); -} - -void yahoo_reject_buddy_ymsg13(int id,const char* me,const char* who,const char* msg){ - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); - struct yahoo_data *yd; - - if (!yid) - return; - yd = yid->yd; - - struct yahoo_packet* pkt = NULL; - pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0); - - yahoo_packet_hash(pkt,1,me ?: yd->user); - yahoo_packet_hash(pkt,5,who); -// yahoo_packet_hash(pkt,241,YAHOO_PROTO_VER); - yahoo_packet_hash(pkt,13,"2"); - yahoo_packet_hash(pkt,334,"0"); - yahoo_packet_hash(pkt,97,"1"); - yahoo_packet_hash(pkt,14,msg?:""); - - yahoo_send_packet(yid, pkt, 0); - yahoo_packet_free(pkt); - -} - -void yahoo_reject_buddy(int id, const char *who, const char *msg) +void yahoo_confirm_buddy(int id, const char *who, int reject, const char *msg) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; @@ -4482,17 +4000,32 @@ void yahoo_reject_buddy(int id, const char *who, const char *msg) if (!yd->logged_in) return; - pkt = yahoo_packet_new(YAHOO_SERVICE_REJECTCONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_AUTHORIZATION, + YPACKET_STATUS_DEFAULT, yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); - yahoo_packet_hash(pkt, 7, who); - yahoo_packet_hash(pkt, 14, msg); + yahoo_packet_hash(pkt, 5, who); + if (reject) + yahoo_packet_hash(pkt, 13, "2"); + else { + yahoo_packet_hash(pkt, 241, "0"); + yahoo_packet_hash(pkt, 13, "1"); + } + + yahoo_packet_hash(pkt, 334, "0"); + + if (reject) { + yahoo_packet_hash(pkt, 14, msg ? msg : ""); + yahoo_packet_hash(pkt, 97, "1"); + } + yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); } void yahoo_ignore_buddy(int id, const char *who, int unignore) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; @@ -4503,17 +4036,19 @@ void yahoo_ignore_buddy(int id, const char *who, int unignore) if (!yd->logged_in) return; - pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, + YPACKET_STATUS_DEFAULT, yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); yahoo_packet_hash(pkt, 7, who); - yahoo_packet_hash(pkt, 13, unignore?"2":"1"); + yahoo_packet_hash(pkt, 13, unignore ? "2" : "1"); yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); } void yahoo_stealth_buddy(int id, const char *who, int unstealth) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; @@ -4524,18 +4059,21 @@ void yahoo_stealth_buddy(int id, const char *who, int unstealth) if (!yd->logged_in) return; - pkt = yahoo_packet_new(YAHOO_SERVICE_STEALTH, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_STEALTH_PERM, + YPACKET_STATUS_DEFAULT, yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); yahoo_packet_hash(pkt, 7, who); - yahoo_packet_hash(pkt, 31, unstealth?"2":"1"); + yahoo_packet_hash(pkt, 31, unstealth ? "2" : "1"); yahoo_packet_hash(pkt, 13, "2"); yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); } -void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group) +void yahoo_change_buddy_group(int id, const char *who, const char *old_group, + const char *new_group) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; @@ -4543,26 +4081,25 @@ void yahoo_change_buddy_group(int id, const char *who, const char *old_group, co return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_CHANGE_GROUP, + YPACKET_STATUS_DEFAULT, yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 302, "240"); + yahoo_packet_hash(pkt, 300, "240"); yahoo_packet_hash(pkt, 7, who); - yahoo_packet_hash(pkt, 65, new_group); - yahoo_packet_hash(pkt, 14, " "); - - yahoo_send_packet(yid, pkt, 0); - yahoo_packet_free(pkt); + yahoo_packet_hash(pkt, 224, old_group); + yahoo_packet_hash(pkt, 264, new_group); + yahoo_packet_hash(pkt, 301, "240"); + yahoo_packet_hash(pkt, 303, "240"); - pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt, 1, yd->user); - yahoo_packet_hash(pkt, 7, who); - yahoo_packet_hash(pkt, 65, old_group); yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); } void yahoo_group_rename(int id, const char *old_group, const char *new_group) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt = NULL; @@ -4570,7 +4107,8 @@ void yahoo_group_rename(int id, const char *old_group, const char *new_group) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, + YPACKET_STATUS_DEFAULT, yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); yahoo_packet_hash(pkt, 65, old_group); yahoo_packet_hash(pkt, 67, new_group); @@ -4579,19 +4117,22 @@ void yahoo_group_rename(int id, const char *old_group, const char *new_group) yahoo_packet_free(pkt); } -void yahoo_conference_addinvite(int id, const char *from, const char *who, const char *room, const YList *members, const char *msg) +void yahoo_conference_addinvite(int id, const char *from, const char *who, + const char *room, const YList *members, const char *msg) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, + YPACKET_STATUS_DEFAULT, yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); yahoo_packet_hash(pkt, 51, who); yahoo_packet_hash(pkt, 57, room); yahoo_packet_hash(pkt, 58, msg); @@ -4607,19 +4148,22 @@ void yahoo_conference_addinvite(int id, const char *from, const char *who, const yahoo_packet_free(pkt); } -void yahoo_conference_invite(int id, const char *from, YList *who, const char *room, const char *msg) +void yahoo_conference_invite(int id, const char *from, YList *who, + const char *room, const char *msg) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YPACKET_STATUS_DEFAULT, + yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); yahoo_packet_hash(pkt, 50, yd->user); for (; who; who = who->next) { yahoo_packet_hash(pkt, 52, (char *)who->data); @@ -4633,45 +4177,51 @@ void yahoo_conference_invite(int id, const char *from, YList *who, const char *r yahoo_packet_free(pkt); } -void yahoo_conference_logon(int id, const char *from, YList *who, const char *room) +void yahoo_conference_logon(int id, const char *from, YList *who, + const char *room) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YPACKET_STATUS_DEFAULT, + yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); - for (; who; who = who->next) { - yahoo_packet_hash(pkt, 3, (char *)who->data); - } + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 3, (from ? from : yd->user)); yahoo_packet_hash(pkt, 57, room); + for (; who; who = who->next) + yahoo_packet_hash(pkt, 3, (char *)who->data); yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); } -void yahoo_conference_decline(int id, const char *from, YList *who, const char *room, const char *msg) +void yahoo_conference_decline(int id, const char *from, YList *who, + const char *room, const char *msg) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, + YPACKET_STATUS_DEFAULT, yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); - for (; who; who = who->next) { + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 3, (from ? from : yd->user)); + for (; who; who = who->next) yahoo_packet_hash(pkt, 3, (char *)who->data); - } yahoo_packet_hash(pkt, 57, room); yahoo_packet_hash(pkt, 14, msg); @@ -4680,22 +4230,26 @@ void yahoo_conference_decline(int id, const char *from, YList *who, const char * yahoo_packet_free(pkt); } -void yahoo_conference_logoff(int id, const char *from, YList *who, const char *room) +void yahoo_conference_logoff(int id, const char *from, YList *who, + const char *room) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YPACKET_STATUS_DEFAULT, + yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); - for (; who; who = who->next) { + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 3, (from ? from : yd->user)); + for (; who; who = who->next) yahoo_packet_hash(pkt, 3, (char *)who->data); - } + yahoo_packet_hash(pkt, 57, room); yahoo_send_packet(yid, pkt, 0); @@ -4703,22 +4257,26 @@ void yahoo_conference_logoff(int id, const char *from, YList *who, const char *r yahoo_packet_free(pkt); } -void yahoo_conference_message(int id, const char *from, YList *who, const char *room, const char *msg, int utf8) +void yahoo_conference_message(int id, const char *from, YList *who, + const char *room, const char *msg, int utf8) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YPACKET_STATUS_DEFAULT, + yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); - for (; who; who = who->next) { + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 53, (from ? from : yd->user)); + for (; who; who = who->next) yahoo_packet_hash(pkt, 53, (char *)who->data); - } + yahoo_packet_hash(pkt, 57, room); yahoo_packet_hash(pkt, 14, msg); @@ -4745,32 +4303,39 @@ void yahoo_get_chatrooms(int id, int chatroomid) yid->type = YAHOO_CONNECTION_CHATCAT; if (chatroomid == 0) { - snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatcat=0"); + snprintf(url, 1024, + "http://insider.msg.yahoo.com/ycontent/?chatcat=0"); } else { - snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",chatroomid); + snprintf(url, 1024, + "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0", + chatroomid); } snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); inputs = y_list_prepend(inputs, yid); - yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid); + yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, + _yahoo_http_connected, yid); } -void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid) +void yahoo_chat_logon(int id, const char *from, const char *room, + const char *roomid) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YPACKET_STATUS_DEFAULT, + yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); yahoo_packet_hash(pkt, 109, yd->user); yahoo_packet_hash(pkt, 6, "abcde"); @@ -4778,37 +4343,40 @@ void yahoo_chat_logon(int id, const char *from, const char *room, const char *ro yahoo_packet_free(pkt); - pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YPACKET_STATUS_DEFAULT, + yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); yahoo_packet_hash(pkt, 104, room); yahoo_packet_hash(pkt, 129, roomid); - yahoo_packet_hash(pkt, 62, "2"); /* ??? */ + yahoo_packet_hash(pkt, 62, "2"); /* ??? */ yahoo_send_packet(yid, pkt, 0); yahoo_packet_free(pkt); } - -void yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8) +void yahoo_chat_message(int id, const char *from, const char *room, + const char *msg, const int msgtype, const int utf8) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; char buf[2]; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YPACKET_STATUS_DEFAULT, + yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); yahoo_packet_hash(pkt, 104, room); yahoo_packet_hash(pkt, 117, msg); - + snprintf(buf, sizeof(buf), "%d", msgtype); yahoo_packet_hash(pkt, 124, buf); @@ -4820,21 +4388,22 @@ void yahoo_chat_message(int id, const char *from, const char *room, const char yahoo_packet_free(pkt); } - void yahoo_chat_logoff(int id, const char *from) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YPACKET_STATUS_DEFAULT, + yd->session_id); - yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); yahoo_send_packet(yid, pkt, 0); @@ -4843,16 +4412,18 @@ void yahoo_chat_logoff(int id, const char *from) void yahoo_buddyicon_request(int id, const char *who) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - if ( !yid ) + if (!yid) return; yd = yid->yd; - - pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0); + + pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT, + 0); yahoo_packet_hash(pkt, 4, yd->user); yahoo_packet_hash(pkt, 5, who); yahoo_packet_hash(pkt, 13, "1"); @@ -4861,21 +4432,24 @@ void yahoo_buddyicon_request(int id, const char *who) yahoo_packet_free(pkt); } -void yahoo_send_picture_info(int id, const char *who, const char *url, int checksum) +void yahoo_send_picture_info(int id, const char *who, const char *url, + int checksum) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; char checksum_str[10]; - if ( !yid ) + if (!yid) return; yd = yid->yd; snprintf(checksum_str, sizeof(checksum_str), "%d", checksum); - pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0); + pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT, + 0); yahoo_packet_hash(pkt, 1, yd->user); yahoo_packet_hash(pkt, 4, yd->user); yahoo_packet_hash(pkt, 5, who); @@ -4889,19 +4463,21 @@ void yahoo_send_picture_info(int id, const char *who, const char *url, int check void yahoo_send_picture_update(int id, const char *who, int type) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; char type_str[10]; - if ( !yid ) + if (!yid) return; yd = yid->yd; snprintf(type_str, sizeof(type_str), "%d", type); - pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE, YAHOO_STATUS_AVAILABLE, 0); + pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE, + YPACKET_STATUS_DEFAULT, 0); yahoo_packet_hash(pkt, 1, yd->user); yahoo_packet_hash(pkt, 5, who); yahoo_packet_hash(pkt, 206, type_str); @@ -4912,21 +4488,23 @@ void yahoo_send_picture_update(int id, const char *who, int type) void yahoo_send_picture_checksum(int id, const char *who, int checksum) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; char checksum_str[10]; - if ( !yid ) + if (!yid) return; yd = yid->yd; - + snprintf(checksum_str, sizeof(checksum_str), "%d", checksum); - pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM, YAHOO_STATUS_AVAILABLE, 0); + pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM, + YPACKET_STATUS_DEFAULT, 0); yahoo_packet_hash(pkt, 1, yd->user); - if ( who != 0 ) + if (who != 0) yahoo_packet_hash(pkt, 5, who); yahoo_packet_hash(pkt, 192, checksum_str); yahoo_packet_hash(pkt, 212, "1"); @@ -4937,7 +4515,8 @@ void yahoo_send_picture_checksum(int id, const char *who, int checksum) void yahoo_webcam_close_feed(int id, const char *who) { - struct yahoo_input_data *yid = find_input_by_id_and_webcam_user(id, who); + struct yahoo_input_data *yid = + find_input_by_id_and_webcam_user(id, who); if (yid) yahoo_input_close(yid); @@ -4945,10 +4524,11 @@ void yahoo_webcam_close_feed(int id, const char *who) void yahoo_webcam_get_feed(int id, const char *who) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_data *yd; struct yahoo_packet *pkt; - + if (!yid) return; @@ -4959,11 +4539,12 @@ void yahoo_webcam_get_feed(int id, const char *who) * order that we request them. * The queue is popped in yahoo_process_webcam_key */ - webcam_queue = y_list_append(webcam_queue, who?strdup(who):NULL); + webcam_queue = y_list_append(webcam_queue, who ? strdup(who) : NULL); yd = yid->yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YPACKET_STATUS_DEFAULT, + yd->session_id); yahoo_packet_hash(pkt, 1, yd->user); if (who != NULL) @@ -4973,9 +4554,11 @@ void yahoo_webcam_get_feed(int id, const char *who) yahoo_packet_free(pkt); } -void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp) +void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, + unsigned int timestamp) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); unsigned char *packet; unsigned char header_len = 13; unsigned int pos = 0; @@ -4987,10 +4570,10 @@ void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, packet[pos++] = header_len; packet[pos++] = 0; - packet[pos++] = 5; /* version byte?? */ + packet[pos++] = 5; /* version byte?? */ packet[pos++] = 0; pos += yahoo_put32(packet + pos, length); - packet[pos++] = 2; /* packet type, image */ + packet[pos++] = 2; /* packet type, image */ pos += yahoo_put32(packet + pos, timestamp); yahoo_add_to_send_queue(yid, packet, header_len); FREE(packet); @@ -4999,9 +4582,10 @@ void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, yahoo_add_to_send_queue(yid, image, length); } -void yahoo_webcam_accept_viewer(int id, const char* who, int accept) +void yahoo_webcam_accept_viewer(int id, const char *who, int accept) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); char *packet = NULL; char *data = NULL; unsigned char header_len = 13; @@ -5012,17 +4596,17 @@ void yahoo_webcam_accept_viewer(int id, const char* who, int accept) return; data = strdup("u="); - data = y_string_append(data, (char*)who); + data = y_string_append(data, (char *)who); data = y_string_append(data, "\r\n"); len = strlen(data); packet = y_new0(char, header_len + len); packet[pos++] = header_len; packet[pos++] = 0; - packet[pos++] = 5; /* version byte?? */ + packet[pos++] = 5; /* version byte?? */ packet[pos++] = 0; pos += yahoo_put32(packet + pos, len); - packet[pos++] = 0; /* packet type */ + packet[pos++] = 0; /* packet type */ pos += yahoo_put32(packet + pos, accept); memcpy(packet + pos, data, len); FREE(data); @@ -5032,13 +4616,15 @@ void yahoo_webcam_accept_viewer(int id, const char* who, int accept) void yahoo_webcam_invite(int id, const char *who) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_packet *pkt; - + if (!yid) return; - pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yid->yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY, + yid->yd->session_id); yahoo_packet_hash(pkt, 49, "WEBCAMINVITE"); yahoo_packet_hash(pkt, 14, " "); @@ -5050,7 +4636,8 @@ void yahoo_webcam_invite(int id, const char *who) yahoo_packet_free(pkt); } -static void yahoo_search_internal(int id, int t, const char *text, int g, int ar, int photo, int yahoo_only, int startpos, int total) +static void yahoo_search_internal(int id, int t, const char *text, int g, + int ar, int photo, int yahoo_only, int startpos, int total) { struct yahoo_data *yd = find_conn_by_id(id); struct yahoo_input_data *yid; @@ -5066,32 +4653,37 @@ static void yahoo_search_internal(int id, int t, const char *text, int g, int ar yid->type = YAHOO_CONNECTION_SEARCH; /* - age range - .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+ - */ + age range + .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+ + */ - snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, startpos); + snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, + startpos); ctext = strdup(text); while ((p = strchr(ctext, ' '))) *p = '+'; - snprintf(url, 1024, "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s", - ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "", - startpos ? buff : ""); + snprintf(url, 1024, + "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s", + ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "", + startpos ? buff : ""); FREE(ctext); snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); inputs = y_list_prepend(inputs, yid); - yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid); + yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, + _yahoo_http_connected, yid); } -void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar, - int photo, int yahoo_only) +void yahoo_search(int id, enum yahoo_search_type t, const char *text, + enum yahoo_search_gender g, enum yahoo_search_agerange ar, int photo, + int yahoo_only) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_search_state *yss; if (!yid) @@ -5115,7 +4707,8 @@ void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo void yahoo_search_again(int id, int start) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); struct yahoo_search_state *yss; if (!yid || !yid->ys) @@ -5126,201 +4719,644 @@ void yahoo_search_again(int id, int start) if (start == -1) start = yss->lsearch_nstart + yss->lsearch_nfound; - yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text, - yss->lsearch_gender, yss->lsearch_agerange, - yss->lsearch_photo, yss->lsearch_yahoo_only, - start, yss->lsearch_ntotal); + yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text, + yss->lsearch_gender, yss->lsearch_agerange, + yss->lsearch_photo, yss->lsearch_yahoo_only, + start, yss->lsearch_ntotal); } +void yahoo_send_picture(int id, const char *name, unsigned long size, + yahoo_get_fd_callback callback, void *data) +{ + /* Not Implemented */ +} + +/* File Transfer */ +static YList *active_file_transfers = NULL; + +enum { + FT_STATE_HEAD = 1, + FT_STATE_RECV, + FT_STATE_RECV_START, + FT_STATE_SEND +}; + struct send_file_data { - struct yahoo_packet *pkt; + int client_id; + char *id; + char *who; + char *filename; + char *ip_addr; + char *token; + int size; + + struct yahoo_input_data *yid; + int state; + yahoo_get_fd_callback callback; - void *user_data; + void *data; }; -static void _yahoo_send_picture_connected(int id, int fd, int error, void *data) +static char *yahoo_get_random(void) +{ + int i = 0; + int r = 0; + int c = 0; + char out[25]; + + out[24] = '\0'; + out[23] = '$'; + out[22] = '$'; + + for (i = 0; i < 22; i++) { + if(r == 0) + r = rand(); + + c = r%61; + + if(c<26) + out[i] = c + 'a'; + else if (c<52) + out[i] = c - 26 + 'A'; + else + out[i] = c - 52 + '0'; + + r /= 61; + } + + return strdup(out); +} + +static int _are_same_id(const void *sfd1, const void *id) +{ + return strcmp(((struct send_file_data *)sfd1)->id, (char *)id); +} + +static int _are_same_yid(const void *sfd1, const void *yid) +{ + if(((struct send_file_data *)sfd1)->yid == yid) + return 0; + else + return 1; +} + +static struct send_file_data *yahoo_get_active_transfer(char *id) +{ + YList *l = y_list_find_custom(active_file_transfers, id, + _are_same_id); + + if(l) + return (struct send_file_data *)l->data; + + return NULL; +} + +static struct send_file_data *yahoo_get_active_transfer_with_yid(void *yid) +{ + YList *l = y_list_find_custom(active_file_transfers, yid, + _are_same_yid); + + if(l) + return (struct send_file_data *)l->data; + + return NULL; +} + +static void yahoo_add_active_transfer(struct send_file_data *sfd) +{ + active_file_transfers = y_list_prepend(active_file_transfers, sfd); +} + +static void yahoo_remove_active_transfer(struct send_file_data *sfd) +{ + active_file_transfers = y_list_remove(active_file_transfers, sfd); + free(sfd->id); + free(sfd->who); + free(sfd->filename); + free(sfd->ip_addr); + FREE(sfd); +} + +static void _yahoo_ft_upload_connected(int id, void *fd, int error, void *data) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_FT); struct send_file_data *sfd = data; - struct yahoo_packet *pkt = sfd->pkt; - unsigned char buff[1024]; + struct yahoo_input_data *yid = sfd->yid; - if (fd <= 0) { - sfd->callback(id, fd, error, sfd->user_data); - FREE(sfd); - yahoo_packet_free(pkt); + if (!fd) { inputs = y_list_remove(inputs, yid); FREE(yid); return; } + sfd->callback(id, fd, error, sfd->data); + yid->fd = fd; - yahoo_send_packet(yid, pkt, 8); - yahoo_packet_free(pkt); + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, + YAHOO_INPUT_READ, yid); +} - snprintf((char *)buff, sizeof(buff), "29"); - buff[2] = 0xc0; - buff[3] = 0x80; - - write(yid->fd, buff, 4); +static void yahoo_file_transfer_upload(struct yahoo_data *yd, + struct send_file_data *sfd) +{ + char url[256]; + char buff[4096]; + char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; - /* YAHOO_CALLBACK(ext_yahoo_add_handler) (nyd->fd, YAHOO_INPUT_READ); */ + struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); - sfd->callback(id, fd, error, sfd->user_data); - FREE(sfd); - inputs = y_list_remove(inputs, yid); - /* - while (yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { - if (!strcmp(buff, "")) - break; + yid->yd = yd; + yid->type = YAHOO_CONNECTION_FT; + + inputs = y_list_prepend(inputs, yid); + sfd->yid = yid; + sfd->state = FT_STATE_SEND; + + token_enc = yahoo_urlencode(sfd->token); + sender_enc = yahoo_urlencode(yd->user); + recv_enc = yahoo_urlencode(sfd->who); + + snprintf(url, sizeof(url), + "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, + token_enc, sender_enc, recv_enc); + + snprintf(buff, sizeof(buff), "T=%s; Y=%s", yd->cookie_t, yd->cookie_y); + + yahoo_http_post(yd->client_id, url, buff, sfd->size, + _yahoo_ft_upload_connected, sfd); + + FREE(token_enc); + FREE(sender_enc); + FREE(recv_enc); +} + +static void yahoo_init_ft_recv(struct yahoo_data *yd, + struct send_file_data *sfd) +{ + char url[256]; + char buff[1024]; + char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; + + struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); + + yid->yd = yd; + yid->type = YAHOO_CONNECTION_FT; + + inputs = y_list_prepend(inputs, yid); + sfd->yid = yid; + sfd->state = FT_STATE_HEAD; + + token_enc = yahoo_urlencode(sfd->token); + sender_enc = yahoo_urlencode(sfd->who); + recv_enc = yahoo_urlencode(yd->user); + + snprintf(url, sizeof(url), + "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, + token_enc, sender_enc, recv_enc); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + + yahoo_http_head(yid->yd->client_id, url, buff, 0, NULL, + _yahoo_http_connected, yid); + + FREE(token_enc); + FREE(sender_enc); + FREE(recv_enc); } - */ - yahoo_input_close(yid); +static void yahoo_file_transfer_accept(struct yahoo_input_data *yid, + struct send_file_data *sfd) +{ + struct yahoo_packet *pkt; + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERACCEPT, + YPACKET_STATUS_DEFAULT, yid->yd->session_id); + + yahoo_packet_hash(pkt, 1, yid->yd->user); + yahoo_packet_hash(pkt, 5, sfd->who); + yahoo_packet_hash(pkt, 265, sfd->id); + yahoo_packet_hash(pkt, 27, sfd->filename); + yahoo_packet_hash(pkt, 249, "3"); + yahoo_packet_hash(pkt, 251, sfd->token); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + + yahoo_init_ft_recv(yid->yd, sfd); } -void yahoo_send_picture(int id, const char *name, unsigned long size, - yahoo_get_fd_callback callback, void *data) +static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) { - struct yahoo_data *yd = find_conn_by_id(id); - struct yahoo_input_data *yid; - struct yahoo_server_settings *yss; - struct yahoo_packet *pkt = NULL; - char size_str[10]; - char expire_str[10]; - long content_length = 0; - unsigned char buff[1024]; - char url[255]; + YList *l; struct send_file_data *sfd; + char *who = NULL; + char *filename = NULL; + char *id = NULL; + char *token = NULL; - if (!yd) - return; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + switch (pair->key) { + case 4: + who = pair->value; + break; + case 5: + /* Me... don't care */ + break; + case 249: + break; + case 265: + id = pair->value; + break; + case 251: + token = pair->value; + break; + case 27: + filename = pair->value; + break; + } + } - yss = yd->server_settings; + sfd = yahoo_get_active_transfer(id); - yid = y_new0(struct yahoo_input_data, 1); - yid->yd = yd; - yid->type = YAHOO_CONNECTION_FT; + if (sfd) { + sfd->token = strdup(token); - pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPLOAD, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_file_transfer_upload(yid->yd, sfd); + } + else { + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN, + sfd->data); - snprintf(size_str, sizeof(size_str), "%ld", size); - snprintf(expire_str, sizeof(expire_str), "%ld", (long)604800); + yahoo_remove_active_transfer(sfd); + } +} - yahoo_packet_hash(pkt, 0, yd->user); - yahoo_packet_hash(pkt, 1, yd->user); - yahoo_packet_hash(pkt, 14, ""); - yahoo_packet_hash(pkt, 27, name); - yahoo_packet_hash(pkt, 28, size_str); - yahoo_packet_hash(pkt, 38, expire_str); - +static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + YList *l; + char *who = NULL; + char *filename = NULL; + char *id = NULL; + char *token = NULL; + char *ip_addr = NULL; - content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); + struct send_file_data *sfd; - snprintf(url, sizeof(url), "http://%s:%d/notifyft", - yss->filetransfer_host, yss->filetransfer_port); - snprintf((char *)buff, sizeof(buff), "Y=%s; T=%s", - yd->cookie_y, yd->cookie_t); - inputs = y_list_prepend(inputs, yid); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + switch (pair->key) { + case 1: + case 4: + who = pair->value; + break; + case 5: + /* Me... don't care */ + break; + case 249: + break; + case 265: + id = pair->value; + break; + case 250: + ip_addr = pair->value; + break; + case 251: + token = pair->value; + break; + case 27: + filename = pair->value; + break; + } + } - sfd = y_new0(struct send_file_data, 1); - sfd->pkt = pkt; - sfd->callback = callback; - sfd->user_data = data; - yahoo_http_post(yid->yd->client_id, url, (char *)buff, content_length+4+size, - _yahoo_send_picture_connected, sfd); + sfd = yahoo_get_active_transfer(id); + + if (sfd) { + sfd->token = strdup(token); + sfd->ip_addr = strdup(ip_addr); + + yahoo_file_transfer_accept(yid, sfd); + } + else { + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN, + sfd->data); + + yahoo_remove_active_transfer(sfd); + } } -static void _yahoo_send_file_connected(int id, int fd, int error, void *data) +static void yahoo_send_filetransferinfo(struct yahoo_data *yd, + struct send_file_data *sfd) { - struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_FT); - struct send_file_data *sfd = data; - struct yahoo_packet *pkt = sfd->pkt; - unsigned char buff[1024]; + struct yahoo_input_data *yid; + struct yahoo_packet *pkt; + + yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER); + sfd->ip_addr = YAHOO_CALLBACK(ext_yahoo_get_ip_addr)("relay.yahoo.com"); + + if (!sfd->ip_addr) { + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yd->client_id, YAHOO_FILE_TRANSFER_RELAY, sfd->data); + + yahoo_remove_active_transfer(sfd); - if (fd <= 0) { - sfd->callback(id, fd, error, sfd->user_data); - FREE(sfd); - yahoo_packet_free(pkt); - inputs = y_list_remove(inputs, yid); - FREE(yid); return; } - yid->fd = fd; - yahoo_send_packet(yid, pkt, 8); + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERINFO, + YPACKET_STATUS_DEFAULT, yd->session_id); + + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 5, sfd->who); + yahoo_packet_hash(pkt, 265, sfd->id); + yahoo_packet_hash(pkt, 27, sfd->filename); + yahoo_packet_hash(pkt, 249, "3"); + yahoo_packet_hash(pkt, 250, sfd->ip_addr); + + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} - snprintf((char *)buff, sizeof(buff), "29"); - buff[2] = 0xc0; - buff[3] = 0x80; - - write(yid->fd, buff, 4); +static void yahoo_process_filetransfer(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + YList *l; + char *who = NULL; + char *filename = NULL; + char *msg = NULL; + char *id = NULL; + int action = 0; + int size = 0; + struct yahoo_data *yd = yid->yd; -/* YAHOO_CALLBACK(ext_yahoo_add_handler) (nyd->fd, YAHOO_INPUT_READ); */ + struct send_file_data *sfd; - sfd->callback(id, fd, error, sfd->user_data); - FREE(sfd); - inputs = y_list_remove(inputs, yid); - /* - while (yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { - if (!strcmp(buff, "")) + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + switch (pair->key) { + case 4: + who = pair->value; + break; + case 5: + /* Me... don't care */ + break; + case 222: + action = atoi(pair->value); + break; + case 265: + id = pair->value; + break; + case 266: /* Don't know */ + break; + case 302: /* Start Data? */ + break; + case 300: break; + case 27: + filename = pair->value; + break; + case 28: + size = atoi(pair->value); + break; + case 14: + msg = pair->value; + case 301: /* End Data? */ + break; + case 303: + break; + + } + } + + if (action == YAHOO_FILE_TRANSFER_INIT) { + /* Received a FT request from buddy */ + sfd = y_new0(struct send_file_data, 1); + + sfd->client_id = yd->client_id; + sfd->id = strdup(id); + sfd->who = strdup(who); + sfd->filename = strdup(filename); + sfd->size = size; + + yahoo_add_active_transfer(sfd); + + YAHOO_CALLBACK(ext_yahoo_got_file) (yd->client_id, yd->user, + who, msg, filename, size, sfd->id); } + else { + /* Response to our request */ + sfd = yahoo_get_active_transfer(id); - */ - yahoo_input_close(yid); + if (sfd && action == YAHOO_FILE_TRANSFER_ACCEPT) { + yahoo_send_filetransferinfo(yd, sfd); + } + else if (!sfd || action == YAHOO_FILE_TRANSFER_REJECT) { + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yd->client_id, YAHOO_FILE_TRANSFER_REJECT, + sfd->data); + + yahoo_remove_active_transfer(sfd); + } + } } -void yahoo_send_file(int id, const char *who, const char *msg, - const char *name, unsigned long size, - yahoo_get_fd_callback callback, void *data) +void yahoo_send_file(int id, const char *who, const char *msg, + const char *name, unsigned long size, + yahoo_get_fd_callback callback, void *data) { - struct yahoo_data *yd = find_conn_by_id(id); - struct yahoo_input_data *yid; - struct yahoo_server_settings *yss; struct yahoo_packet *pkt = NULL; char size_str[10]; - long content_length = 0; - unsigned char buff[1024]; - char url[255]; + struct yahoo_input_data *yid; + struct yahoo_data *yd; struct send_file_data *sfd; + + yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + yd = find_conn_by_id(id); + sfd = y_new0(struct send_file_data, 1); - if (!yd) - return; + sfd->client_id = id; + sfd->id = yahoo_get_random(); + sfd->who = strdup(who); + sfd->filename = strdup(name); + sfd->size = size; + sfd->callback = callback; + sfd->data = data; - yss = yd->server_settings; + yahoo_add_active_transfer(sfd); - yid = y_new0(struct yahoo_input_data, 1); - yid->yd = yd; - yid->type = YAHOO_CONNECTION_FT; + if (!yd) + return; - pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, + YPACKET_STATUS_DEFAULT, yd->session_id); snprintf(size_str, sizeof(size_str), "%ld", size); - yahoo_packet_hash(pkt, 0, yd->user); + yahoo_packet_hash(pkt, 1, yd->user); yahoo_packet_hash(pkt, 5, who); - yahoo_packet_hash(pkt, 14, msg); + yahoo_packet_hash(pkt, 265, sfd->id); + yahoo_packet_hash(pkt, 222, "1"); + yahoo_packet_hash(pkt, 266, "1"); + yahoo_packet_hash(pkt, 302, "268"); + yahoo_packet_hash(pkt, 300, "268"); yahoo_packet_hash(pkt, 27, name); yahoo_packet_hash(pkt, 28, size_str); + yahoo_packet_hash(pkt, 301, "268"); + yahoo_packet_hash(pkt, 303, "268"); - content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); + yahoo_send_packet(yid, pkt, 0); - snprintf(url, sizeof(url), "http://%s:%d/notifyft", - yss->filetransfer_host, yss->filetransfer_port); - snprintf((char *)buff, sizeof(buff), "Y=%s; T=%s", - yd->cookie_y, yd->cookie_t); - inputs = y_list_prepend(inputs, yid); + yahoo_packet_free(pkt); +} - sfd = y_new0(struct send_file_data, 1); - sfd->pkt = pkt; - sfd->callback = callback; - sfd->user_data = data; - yahoo_http_post(yid->yd->client_id, url, (char *)buff, content_length+4+size, - _yahoo_send_file_connected, sfd); +void yahoo_send_file_transfer_response(int client_id, int response, char *id, void *data) +{ + struct yahoo_packet *pkt = NULL; + char resp[2]; + struct yahoo_input_data *yid; + + struct send_file_data *sfd = yahoo_get_active_transfer(id); + + sfd->data = data; + + yid = find_input_by_id_and_type(client_id, YAHOO_CONNECTION_PAGER); + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, + YPACKET_STATUS_DEFAULT, yid->yd->session_id); + + snprintf(resp, sizeof(resp), "%d", response); + + yahoo_packet_hash(pkt, 1, yid->yd->user); + yahoo_packet_hash(pkt, 5, sfd->who); + yahoo_packet_hash(pkt, 265, sfd->id); + yahoo_packet_hash(pkt, 222, resp); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + + if(response == YAHOO_FILE_TRANSFER_REJECT) + yahoo_remove_active_transfer(sfd); } +static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over) +{ + struct send_file_data *sfd; + struct yahoo_data *yd = yid->yd; + + sfd = yahoo_get_active_transfer_with_yid(yid); + + if (!sfd) { + LOG(("Something funny happened. yid %p has no sfd.\n", yid)); + return; + } + + /* + * We want to handle only the complete data with HEAD since we don't + * want a situation where both the GET and HEAD are active. + * With SEND, we really can't do much with partial response + */ + if ((sfd->state == FT_STATE_HEAD || sfd->state == FT_STATE_SEND) + && !over) + return; + + if (sfd->state == FT_STATE_HEAD) { + /* Do a GET */ + char url[256]; + char buff[1024]; + char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; + + struct yahoo_input_data *yid_ft = + y_new0(struct yahoo_input_data, 1); + + yid_ft->yd = yid->yd; + yid_ft->type = YAHOO_CONNECTION_FT; + + inputs = y_list_prepend(inputs, yid_ft); + sfd->yid = yid_ft; + sfd->state = FT_STATE_RECV; + + token_enc = yahoo_urlencode(sfd->token); + sender_enc = yahoo_urlencode(sfd->who); + recv_enc = yahoo_urlencode(yd->user); + + snprintf(url, sizeof(url), + "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, + token_enc, sender_enc, recv_enc); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, + yd->cookie_t); + + + yahoo_http_get(yd->client_id, url, buff, 1, 1, + _yahoo_http_connected, yid_ft); + + FREE(token_enc); + FREE(sender_enc); + FREE(recv_enc); + } + else if (sfd->state == FT_STATE_RECV || + sfd->state == FT_STATE_RECV_START) { + + unsigned char *data_begin = NULL; + + if (yid->rxlen == 0) + yahoo_remove_active_transfer(sfd); + + if (sfd->state != FT_STATE_RECV_START && + (data_begin = + (unsigned char *)strstr((char *)yid->rxqueue, + "\r\n\r\n"))) { + + sfd->state = FT_STATE_RECV_START; + + yid->rxlen -= 4+(data_begin-yid->rxqueue)/sizeof(char); + data_begin += 4; + + if (yid->rxlen > 0) + YAHOO_CALLBACK(ext_yahoo_got_ft_data) + (yd->client_id, data_begin, + yid->rxlen, sfd->data); + } + else if (sfd->state == FT_STATE_RECV_START) + YAHOO_CALLBACK(ext_yahoo_got_ft_data) (yd->client_id, + yid->rxqueue, yid->rxlen, sfd->data); + + FREE(yid->rxqueue); + yid->rxqueue = NULL; + yid->rxlen = 0; + } + else if (sfd->state == FT_STATE_SEND) { + /* Sent file completed */ + int len = 0; + char *off = strstr((char *)yid->rxqueue, "Content-Length: "); + + if (off) { + off += 16; + len = atoi(off); + } + + if (len < sfd->size) + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yd->client_id, + YAHOO_FILE_TRANSFER_FAILED, sfd->data); + else + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yd->client_id, + YAHOO_FILE_TRANSFER_DONE, sfd->data); + + yahoo_remove_active_transfer(sfd); + } +} + +/* End File Transfer */ enum yahoo_status yahoo_current_status(int id) { @@ -5361,6 +5397,8 @@ const char *yahoo_get_cookie(int id, const char *which) return NULL; if (!strncasecmp(which, "y", 1)) return yd->cookie_y; + if (!strncasecmp(which, "b", 1)) + return yd->cookie_b; if (!strncasecmp(which, "t", 1)) return yd->cookie_t; if (!strncasecmp(which, "c", 1)) @@ -5370,18 +5408,7 @@ const char *yahoo_get_cookie(int id, const char *which) return NULL; } -void yahoo_get_url_handle(int id, const char *url, - yahoo_get_url_handle_callback callback, void *data) -{ - struct yahoo_data *yd = find_conn_by_id(id); - if (!yd) - return; - - yahoo_get_url_fd(id, url, yd, callback, data); -} - -const char *yahoo_get_profile_url( void ) +const char *yahoo_get_profile_url(void) { return profile_url; } - diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index bf577496..7a856254 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -1,7 +1,7 @@ /* * libyahoo2 wrapper to BitlBee * - * Mostly Copyright 2004 Wilmer van der Gaast + * Mostly Copyright 2004-2010 Wilmer van der Gaast * * 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 @@ -338,16 +338,20 @@ static struct groupchat *byahoo_chat_with( struct im_connection *ic, char *who ) static void byahoo_auth_allow( struct im_connection *ic, const char *who ) { + /* struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; yahoo_accept_buddy_ymsg13( yd->y2_id, NULL, who ); + */ } static void byahoo_auth_deny( struct im_connection *ic, const char *who ) { + /* struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; yahoo_reject_buddy_ymsg13( yd->y2_id, NULL, who, NULL ); + */ } void byahoo_initmodule( ) @@ -420,7 +424,7 @@ void byahoo_connect_callback( gpointer data, gint source, b_input_condition cond return; } - d->callback( d->fd, 0, d->data ); + d->callback( NULL + d->fd, 0, d->data ); g_free( d ); } @@ -440,7 +444,7 @@ gboolean byahoo_read_ready_callback( gpointer data, gint source, b_input_conditi /* WTF doesn't libyahoo clean this up? */ return FALSE; - yahoo_read_ready( d->id, d->fd, d->data ); + yahoo_read_ready( d->id, NULL + d->fd, d->data ); return TRUE; } @@ -457,7 +461,7 @@ gboolean byahoo_write_ready_callback( gpointer data, gint source, b_input_condit { struct byahoo_write_ready_data *d = data; - return yahoo_write_ready( d->id, d->fd, d->data ); + return yahoo_write_ready( d->id, NULL + d->fd, d->data ); } void ext_yahoo_login_response( int id, int succ, const char *url ) @@ -605,9 +609,6 @@ void ext_yahoo_status_changed( int id, const char *who, int stat, const char *ms state_string = "Offline"; flags = 0; break; - case YAHOO_STATUS_NOTIFY: - state_string = "Notify"; - break; } imcb_buddy_status( ic, who, flags, state_string, msg ); @@ -616,6 +617,10 @@ void ext_yahoo_status_changed( int id, const char *who, int stat, const char *ms imcb_buddy_times( ic, who, 0, idle ); } +void ext_yahoo_got_buzz( int id, const char *me, const char *who, long tm ) +{ +} + void ext_yahoo_got_im( int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8 ) { struct im_connection *ic = byahoo_get_ic_by_id( id ); @@ -629,15 +634,22 @@ void ext_yahoo_got_im( int id, const char *me, const char *who, const char *msg, } } -void ext_yahoo_got_file( int id, - const char *ignored, - const char *who, const char *url, long expires, const char *msg, const char *fname, unsigned long fesize ) +void ext_yahoo_got_file( int id, const char *ignored, const char *who, const char *msg, + const char *fname, unsigned long fesize, char *trid ) { struct im_connection *ic = byahoo_get_ic_by_id( id ); imcb_log( ic, "Got a file transfer (file = %s) from %s. Ignoring for now due to lack of support.", fname, who ); } +void ext_yahoo_got_ft_data( int id, const unsigned char *in, int len, void *data ) +{ +} + +void ext_yahoo_file_transfer_done( int id, int result, void *data ) +{ +} + void ext_yahoo_typing_notify( int id, const char *ignored, const char *who, int stat ) { struct im_connection *ic = byahoo_get_ic_by_id( id ); @@ -648,7 +660,7 @@ void ext_yahoo_typing_notify( int id, const char *ignored, const char *who, int imcb_buddy_typing( ic, (char*) who, 0 ); } -void ext_yahoo_system_message( int id, const char *msg ) +void ext_yahoo_system_message( int id, const char *me, const char *who, const char *msg ) { struct im_connection *ic = byahoo_get_ic_by_id( id ); @@ -670,9 +682,10 @@ void ext_yahoo_error( int id, const char *err, int fatal, int num ) } /* TODO: Clear up the mess of inp and d structures */ -int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *data ) +int ext_yahoo_add_handler( int id, void *fd_, yahoo_input_condition cond, void *data ) { struct byahoo_input_data *inp = g_new0( struct byahoo_input_data, 1 ); + int fd = (int) fd_; if( cond == YAHOO_INPUT_READ ) { @@ -699,12 +712,12 @@ int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *dat else { g_free( inp ); - return( -1 ); + return -1; /* Panic... */ } byahoo_inputs = g_slist_append( byahoo_inputs, inp ); - return( inp->h ); + return inp->h; } void ext_yahoo_remove_handler( int id, int tag ) @@ -728,7 +741,7 @@ void ext_yahoo_remove_handler( int id, int tag ) b_event_remove( tag ); } -int ext_yahoo_connect_async( int id, const char *host, int port, yahoo_connect_callback callback, void *data ) +int ext_yahoo_connect_async( int id, const char *host, int port, yahoo_connect_callback callback, void *data, int use_ssl ) { struct byahoo_connect_callback_data *d; int fd; @@ -744,48 +757,38 @@ int ext_yahoo_connect_async( int id, const char *host, int port, yahoo_connect_c d->data = data; d->id = id; - return( fd ); + return fd; } -/* Because we don't want asynchronous connects in BitlBee, and because - libyahoo doesn't seem to use this one anyway, this one is now defunct. */ -int ext_yahoo_connect(const char *host, int port) +char *ext_yahoo_get_ip_addr( const char *domain ) { -#if 0 - struct sockaddr_in serv_addr; - static struct hostent *server; - static char last_host[256]; - int servfd; - char **p; + return NULL; +} - if(last_host[0] || g_strcasecmp(last_host, host)!=0) { - if(!(server = gethostbyname(host))) { - return -1; - } - strncpy(last_host, host, 255); - } +int ext_yahoo_write( void *fd, char *buf, int len ) +{ + return write( (int) fd, buf, len ); +} - if((servfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - return -1; - } +int ext_yahoo_read( void *fd, char *buf, int len ) +{ + return read( (int) fd, buf, len ); +} - for (p = server->h_addr_list; *p; p++) - { - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - memcpy(&serv_addr.sin_addr.s_addr, *p, server->h_length); - serv_addr.sin_port = htons(port); - - if(connect(servfd, (struct sockaddr *) &serv_addr, - sizeof(serv_addr)) == -1) { - return -1; - } else { - return servfd; - } - } +void ext_yahoo_close( void *fd ) +{ + close( (int) fd ); +} + +void ext_yahoo_got_buddy_change_group( int id, const char *me, const char *who, + const char *old_group, const char *new_group ) +{ +} - closesocket(servfd); -#endif +/* Because we don't want asynchronous connects in BitlBee, and because + libyahoo doesn't seem to use this one anyway, this one is now defunct. */ +int ext_yahoo_connect(const char *host, int port) +{ return -1; } @@ -897,7 +900,7 @@ void ext_yahoo_chat_cat_xml( int id, const char *xml ) { } -void ext_yahoo_chat_join( int id, const char *who, const char *room, const char *topic, YList *members, int fd ) +void ext_yahoo_chat_join( int id, const char *who, const char *room, const char *topic, YList *members, void *fd ) { } @@ -927,9 +930,7 @@ void ext_yahoo_chat_yahooerror( int id, const char *me ) void ext_yahoo_contact_auth_request( int id, const char *myid, const char *who, const char *msg ) { - struct im_connection *ic = byahoo_get_ic_by_id( id ); - - imcb_ask_auth( ic, who, NULL ); + /* Apparently no longer implemented.. */ } void ext_yahoo_contact_added( int id, const char *myid, const char *who, const char *msg ) @@ -943,7 +944,7 @@ void ext_yahoo_rejected( int id, const char *who, const char *msg ) { } -void ext_yahoo_game_notify( int id, const char *me, const char *who, int stat ) +void ext_yahoo_game_notify( int id, const char *me, const char *who, int stat, const char *msg ) { } diff --git a/protocols/yahoo/yahoo2.h b/protocols/yahoo/yahoo2.h index ba42b39e..589aaa5a 100644 --- a/protocols/yahoo/yahoo2.h +++ b/protocols/yahoo/yahoo2.h @@ -50,13 +50,13 @@ extern "C" { #include "yahoo2_types.h" -/* returns the socket descriptor for a given pager connection. shouldn't be needed */ -int yahoo_get_fd(int id); +/* returns the socket descriptor object for a given pager connection. shouldn't be needed */ + void *yahoo_get_fd(int id); /* says how much logging to do */ /* see yahoo2_types.h for the different values */ -int yahoo_set_log_level(enum yahoo_log_level level); -enum yahoo_log_level yahoo_get_log_level( void ); + int yahoo_set_log_level(enum yahoo_log_level level); + enum yahoo_log_level yahoo_get_log_level(void); /* these functions should be self explanatory */ /* who always means the buddy you're acting on */ @@ -221,8 +221,8 @@ enum yahoo_log_level yahoo_get_log_level( void ); /* these should be called when input is available on a fd */ /* registered by ext_yahoo_add_handler */ /* if these return negative values, errno may be set */ -int yahoo_read_ready(int id, int fd, void *data); -int yahoo_write_ready(int id, int fd, void *data); + int yahoo_read_ready(int id, void *fd, void *data); + int yahoo_write_ready(int id, void *fd, void *data); /* utility functions. these do not hit the server */ enum yahoo_status yahoo_current_status(int id); @@ -239,9 +239,6 @@ int yahoo_write_ready(int id, int fd, void *data); void yahoo_buddyicon_request(int id, const char *who); - void yahoo_accept_buddy_ymsg13(int,const char*, const char*); - void yahoo_reject_buddy_ymsg13(int,const char*, const char*, const char*); - #include "yahoo_httplib.h" #ifdef __cplusplus diff --git a/protocols/yahoo/yahoo2_callbacks.h b/protocols/yahoo/yahoo2_callbacks.h index 19060a22..0dccf188 100644 --- a/protocols/yahoo/yahoo2_callbacks.h +++ b/protocols/yahoo/yahoo2_callbacks.h @@ -54,15 +54,16 @@ extern "C" { * A callback function called when an asynchronous connect completes. * * Params: - * fd - The file descriptor that has been connected, or -1 on error + * fd - The file descriptor object that has been connected, or NULL on + * error * error - The value of errno set by the call to connect or 0 if no error * Set both fd and error to 0 if the connect was cancelled by the * user * callback_data - the callback_data passed to the ext_yahoo_connect_async * function */ -typedef void (*yahoo_connect_callback)(int fd, int error, void *callback_data); - + typedef void (*yahoo_connect_callback) (void *fd, int error, + void *callback_data); /* * The following functions need to be implemented in the client @@ -261,10 +262,10 @@ struct yahoo_callbacks { * topic - the topic of the room, freed by library after call * members - the initial members of the chatroom (null terminated YList * of yahoo_chat_member's) Must be freed by the client - * fd - the socket where the connection is coming from (for tracking) + * fd - the object where the connection is coming from (for tracking) */ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join) (int id, const char *me, - const char *room, const char *topic, YList *members, int fd); + const char *room, const char *topic, YList *members, void *fd); /* * Name: ext_yahoo_chat_userjoin @@ -363,26 +364,42 @@ struct yahoo_callbacks { * id - the id that identifies the server connection * me - the identity the file was sent to * who - the user who sent the file - * url - the file url - * expires - the expiry date of the file on the server (timestamp) * msg - the message * fname- the file name if direct transfer * fsize- the file size if direct transfer + * trid - transfer id. Unique for this transfer + * + * NOTE: Subsequent callbacks for file transfer do not send all of this + * information again since it is wasteful. Implementations are expected to + * save this information and supply it as callback data when the file or + * confirmation is sent */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file)(int id, const char *me, const char *who, const char *url, long expires, const char *msg, const char *fname, unsigned long fesize); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file) (int id, const char *me, + const char *who, const char *msg, const char *fname, + unsigned long fesize, char *trid); /* - * Name: ext_yahoo_contact_auth_request - * Called when a contact wants to add you to his/her contact list + * Name: ext_yahoo_got_ft_data + * Called multiple times when parts of the file are received * Params: * id - the id that identifies the server connection - * myid - the identity s/he added - * who - who did it - * msg - any message sent + * in - The data + * len - Length of the data + * data - callback data */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_auth_request)(int id, const char *myid, const char *who, const char *msg); + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ft_data) (int id, + const unsigned char *in, int len, void *data); +/* + * Name: ext_yahoo_file_transfer_done + * File transfer is done + * Params: + * id - the id that identifies the server connection + * result - To notify if it finished successfully or with a failure + * data - callback data + */ + void YAHOO_CALLBACK_TYPE(ext_yahoo_file_transfer_done) (int id, + int result, void *data); /* * Name: ext_yahoo_contact_added @@ -427,9 +444,10 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_auth_request)(int id, const char *myi * me - the handle of the identity the notification is sent to * who - the handle of the remote user * stat - 1 if game, 0 if stopped gaming + * msg - game description and/or other text */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify)(int id, const char *me, const char *who, int stat); - + void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify) (int id, const char *me, + const char *who, int stat, const char *msg); /* * Name: ext_yahoo_mail_notify @@ -448,9 +466,12 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify)(int id, const char *me, const ch * System message * Params: * id - the id that identifies the server connection + * me - the handle of the identity the notification is sent to + * who - the source of the system message (there are different types) * msg - the message */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, const char *msg); + void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message) (int id, + const char *me, const char *who, const char *msg); /* * Name: ext_yahoo_got_buddyicon @@ -629,13 +650,13 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, const char *msg); * when a YAHOO_INPUT_WRITE fd is ready. * Params: * id - the id that identifies the server connection - * fd - the fd on which to listen + * fd - the fd object on which to listen * cond - the condition on which to call the callback * data - callback data to pass to yahoo_*_ready * * Returns: a tag to be used when removing the handler */ - int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler) (int id, int fd, + int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler) (int id, void *fd, yahoo_input_condition cond, void *data); /* @@ -673,12 +694,74 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, const char *msg); * port - the port to connect on * callback - function to call when connect completes * callback_data - data to pass to the callback function + * use_ssl - Whether we need an SSL connection * Returns: - * a unix file descriptor to the socket + * a tag signifying the connection attempt */ int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async) (int id, const char *host, int port, yahoo_connect_callback callback, - void *callback_data); + void *callback_data, int use_ssl); + +/* + * Name: ext_yahoo_get_ip_addr + * get IP Address for a domain name + * Params: + * domain - Domain name + * Returns: + * Newly allocated string containing the IP Address in IPv4 notation + */ + char *YAHOO_CALLBACK_TYPE(ext_yahoo_get_ip_addr) (const char *domain); + +/* + * Name: ext_yahoo_write + * Write data from the buffer into the socket for the specified connection + * Params: + * fd - the file descriptor object that identifies this connection + * buf - Buffer to write the data from + * len - Length of the data + * Returns: + * Number of bytes written or -1 for error + */ + int YAHOO_CALLBACK_TYPE(ext_yahoo_write) (void *fd, char *buf, int len); + +/* + * Name: ext_yahoo_read + * Read data into a buffer from socket for the specified connection + * Params: + * fd - the file descriptor object that identifies this connection + * buf - Buffer to read the data into + * len - Max length to read + * Returns: + * Number of bytes read or -1 for error + */ + int YAHOO_CALLBACK_TYPE(ext_yahoo_read) (void *fd, char *buf, int len); + +/* + * Name: ext_yahoo_close + * Close the file descriptor object and free its resources. Libyahoo2 will not + * use this object again. + * Params: + * fd - the file descriptor object that identifies this connection + * Returns: + * Nothing + */ + void YAHOO_CALLBACK_TYPE(ext_yahoo_close) (void *fd); + +/* + * Name: ext_yahoo_got_buddy_change_group + * Acknowledgement of buddy changing group + * Params: + * id: client id + * me: The user + * who: Buddy name + * old_group: Old group name + * new_group: New group name + * Returns: + * Nothing + */ + void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddy_change_group) (int id, + const char *me, const char *who, const char *old_group, + const char *new_group); #ifdef USE_STRUCT_CALLBACKS }; @@ -698,4 +781,3 @@ void yahoo_register_callbacks(struct yahoo_callbacks *tyc); #endif #endif - diff --git a/protocols/yahoo/yahoo2_types.h b/protocols/yahoo/yahoo2_types.h index 0f6d7aab..bbade5d8 100644 --- a/protocols/yahoo/yahoo2_types.h +++ b/protocols/yahoo/yahoo2_types.h @@ -28,74 +28,175 @@ extern "C" { #endif -enum yahoo_status { - YAHOO_STATUS_DISCONNECTED = -1, - YAHOO_STATUS_AVAILABLE = 0, - YAHOO_STATUS_BRB, - YAHOO_STATUS_BUSY, - YAHOO_STATUS_NOTATHOME, - YAHOO_STATUS_NOTATDESK, - YAHOO_STATUS_NOTINOFFICE, - YAHOO_STATUS_ONPHONE, - YAHOO_STATUS_ONVACATION, - YAHOO_STATUS_OUTTOLUNCH, - YAHOO_STATUS_STEPPEDOUT, - YAHOO_STATUS_INVISIBLE = 12, - YAHOO_STATUS_CUSTOM = 99, - YAHOO_STATUS_IDLE = 999, - YAHOO_STATUS_WEBLOGIN = 0x5a55aa55, - YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */ - YAHOO_STATUS_NOTIFY = 0x16 /* TYPING */ -}; -#define YAHOO_STATUS_GAME 0x2 /* Games don't fit into the regular status model */ - -enum yahoo_login_status { - YAHOO_LOGIN_OK = 0, - YAHOO_LOGIN_LOGOFF = 2, - YAHOO_LOGIN_UNAME = 3, - YAHOO_LOGIN_PASSWD = 13, - YAHOO_LOGIN_LOCK = 14, - YAHOO_LOGIN_DUPL = 99, - YAHOO_LOGIN_SOCK = -1, -}; - -enum ypacket_status { - YPACKET_STATUS_DISCONNECTED = -1, - YPACKET_STATUS_DEFAULT = 0, - YPACKET_STATUS_SERVERACK = 1, - YPACKET_STATUS_GAME = 0x2, - YPACKET_STATUS_AWAY = 0x4, - YPACKET_STATUS_CONTINUED = 0x5, - YPACKET_STATUS_INVISIBLE = 12, - YPACKET_STATUS_NOTIFY = 0x16, /* TYPING */ - YPACKET_STATUS_WEBLOGIN = 0x5a55aa55, - YPACKET_STATUS_OFFLINE = 0x5a55aa56 -}; - -enum yahoo_error { - E_UNKNOWN = -1, - E_CONNECTION = -2, - E_SYSTEM = -3, - E_CUSTOM = 0, - - /* responses from ignore buddy */ - E_IGNOREDUP = 2, - E_IGNORENONE = 3, - E_IGNORECONF = 12, - - /* conference */ - E_CONFNOTAVAIL = 20 -}; - -enum yahoo_log_level { - YAHOO_LOG_NONE = 0, - YAHOO_LOG_FATAL, - YAHOO_LOG_ERR, - YAHOO_LOG_WARNING, - YAHOO_LOG_NOTICE, - YAHOO_LOG_INFO, - YAHOO_LOG_DEBUG -}; + enum yahoo_service { /* these are easier to see in hex */ + YAHOO_SERVICE_LOGON = 1, + YAHOO_SERVICE_LOGOFF, + YAHOO_SERVICE_ISAWAY, + YAHOO_SERVICE_ISBACK, + YAHOO_SERVICE_IDLE, /* 5 (placemarker) */ + YAHOO_SERVICE_MESSAGE, + YAHOO_SERVICE_IDACT, + YAHOO_SERVICE_IDDEACT, + YAHOO_SERVICE_MAILSTAT, + YAHOO_SERVICE_USERSTAT, /* 0xa */ + YAHOO_SERVICE_NEWMAIL, + YAHOO_SERVICE_CHATINVITE, + YAHOO_SERVICE_CALENDAR, + YAHOO_SERVICE_NEWPERSONALMAIL, + YAHOO_SERVICE_NEWCONTACT, + YAHOO_SERVICE_ADDIDENT, /* 0x10 */ + YAHOO_SERVICE_ADDIGNORE, + YAHOO_SERVICE_PING, + YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */ + YAHOO_SERVICE_SYSMESSAGE = 0x14, + YAHOO_SERVICE_SKINNAME = 0x15, + YAHOO_SERVICE_PASSTHROUGH2 = 0x16, + YAHOO_SERVICE_CONFINVITE = 0x18, + YAHOO_SERVICE_CONFLOGON, + YAHOO_SERVICE_CONFDECLINE, + YAHOO_SERVICE_CONFLOGOFF, + YAHOO_SERVICE_CONFADDINVITE, + YAHOO_SERVICE_CONFMSG, + YAHOO_SERVICE_CHATLOGON, + YAHOO_SERVICE_CHATLOGOFF, + YAHOO_SERVICE_CHATMSG = 0x20, + YAHOO_SERVICE_GAMELOGON = 0x28, + YAHOO_SERVICE_GAMELOGOFF, + YAHOO_SERVICE_GAMEMSG = 0x2a, + YAHOO_SERVICE_FILETRANSFER = 0x46, + YAHOO_SERVICE_VOICECHAT = 0x4A, + YAHOO_SERVICE_NOTIFY, + YAHOO_SERVICE_VERIFY, + YAHOO_SERVICE_P2PFILEXFER, + YAHOO_SERVICE_PEERTOPEER = 0x4F, /* Checks if P2P possible */ + YAHOO_SERVICE_WEBCAM, + YAHOO_SERVICE_AUTHRESP = 0x54, + YAHOO_SERVICE_LIST, + YAHOO_SERVICE_AUTH = 0x57, + YAHOO_SERVICE_AUTHBUDDY = 0x6d, + YAHOO_SERVICE_ADDBUDDY = 0x83, + YAHOO_SERVICE_REMBUDDY, + YAHOO_SERVICE_IGNORECONTACT, /* > 1, 7, 13 < 1, 66, 13, 0 */ + YAHOO_SERVICE_REJECTCONTACT, + YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */ + YAHOO_SERVICE_Y7_PING = 0x8A, + YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1 */ + YAHOO_SERVICE_CHATGOTO, + YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */ + YAHOO_SERVICE_CHATLEAVE, + YAHOO_SERVICE_CHATEXIT = 0x9b, + YAHOO_SERVICE_CHATADDINVITE = 0x9d, + YAHOO_SERVICE_CHATLOGOUT = 0xa0, + YAHOO_SERVICE_CHATPING, + YAHOO_SERVICE_COMMENT = 0xa8, + YAHOO_SERVICE_GAME_INVITE = 0xb7, + YAHOO_SERVICE_STEALTH_PERM = 0xb9, + YAHOO_SERVICE_STEALTH_SESSION = 0xba, + YAHOO_SERVICE_AVATAR = 0xbc, + YAHOO_SERVICE_PICTURE_CHECKSUM = 0xbd, + YAHOO_SERVICE_PICTURE = 0xbe, + YAHOO_SERVICE_PICTURE_UPDATE = 0xc1, + YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2, + YAHOO_SERVICE_YAB_UPDATE = 0xc4, + YAHOO_SERVICE_Y6_VISIBLE_TOGGLE = 0xc5, /* YMSG13, key 13: 2 = invisible, 1 = visible */ + YAHOO_SERVICE_Y6_STATUS_UPDATE = 0xc6, /* YMSG13 */ + YAHOO_SERVICE_PICTURE_STATUS = 0xc7, /* YMSG13, key 213: 0 = none, 1 = avatar, 2 = picture */ + YAHOO_SERVICE_VERIFY_ID_EXISTS = 0xc8, + YAHOO_SERVICE_AUDIBLE = 0xd0, + YAHOO_SERVICE_Y7_PHOTO_SHARING = 0xd2, + YAHOO_SERVICE_Y7_CONTACT_DETAILS = 0xd3, /* YMSG13 */ + YAHOO_SERVICE_Y7_CHAT_SESSION = 0xd4, + YAHOO_SERVICE_Y7_AUTHORIZATION = 0xd6, /* YMSG13 */ + YAHOO_SERVICE_Y7_FILETRANSFER = 0xdc, /* YMSG13 */ + YAHOO_SERVICE_Y7_FILETRANSFERINFO, /* YMSG13 */ + YAHOO_SERVICE_Y7_FILETRANSFERACCEPT, /* YMSG13 */ + YAHOO_SERVICE_Y7_MINGLE = 0xe1, /* YMSG13 */ + YAHOO_SERVICE_Y7_CHANGE_GROUP = 0xe7, /* YMSG13 */ + YAHOO_SERVICE_MYSTERY = 0xef, /* Don't know what this is for */ + YAHOO_SERVICE_Y8_STATUS = 0xf0, /* YMSG15 */ + YAHOO_SERVICE_Y8_LIST = 0Xf1, /* YMSG15 */ + YAHOO_SERVICE_MESSAGE_CONFIRM = 0xfb, + YAHOO_SERVICE_WEBLOGIN = 0x0226, + YAHOO_SERVICE_SMS_MSG = 0x02ea + }; + + enum yahoo_status { + YAHOO_STATUS_AVAILABLE = 0, + YAHOO_STATUS_BRB, + YAHOO_STATUS_BUSY, + YAHOO_STATUS_NOTATHOME, + YAHOO_STATUS_NOTATDESK, + YAHOO_STATUS_NOTINOFFICE, + YAHOO_STATUS_ONPHONE, + YAHOO_STATUS_ONVACATION, + YAHOO_STATUS_OUTTOLUNCH, + YAHOO_STATUS_STEPPEDOUT, + YAHOO_STATUS_INVISIBLE = 12, + YAHOO_STATUS_CUSTOM = 99, + YAHOO_STATUS_IDLE = 999, + YAHOO_STATUS_OFFLINE = 0x5a55aa56 /* don't ask */ + }; + + enum ypacket_status { + YPACKET_STATUS_DISCONNECTED = -1, + YPACKET_STATUS_DEFAULT = 0, + YPACKET_STATUS_SERVERACK = 1, + YPACKET_STATUS_GAME = 0x2, + YPACKET_STATUS_AWAY = 0x4, + YPACKET_STATUS_CONTINUED = 0x5, + YPACKET_STATUS_INVISIBLE = 12, + YPACKET_STATUS_NOTIFY = 0x16, /* TYPING */ + YPACKET_STATUS_WEBLOGIN = 0x5a55aa55, + YPACKET_STATUS_OFFLINE = 0x5a55aa56 + }; + +#define YAHOO_STATUS_GAME 0x2 /* Games don't fit into the regular status model */ + + enum yahoo_login_status { + YAHOO_LOGIN_OK = 0, + YAHOO_LOGIN_LOGOFF = 1, + YAHOO_LOGIN_UNAME = 3, + YAHOO_LOGIN_PASSWD = 13, + YAHOO_LOGIN_LOCK = 14, + YAHOO_LOGIN_DUPL = 99, + YAHOO_LOGIN_SOCK = -1, + YAHOO_LOGIN_UNKNOWN = 999 + }; + + enum yahoo_error { + E_UNKNOWN = -1, + E_CONNECTION = -2, + E_SYSTEM = -3, + E_CUSTOM = 0, + + /* responses from ignore buddy */ + E_IGNOREDUP = 2, + E_IGNORENONE = 3, + E_IGNORECONF = 12, + + /* conference */ + E_CONFNOTAVAIL = 20 + }; + + enum yahoo_log_level { + YAHOO_LOG_NONE = 0, + YAHOO_LOG_FATAL, + YAHOO_LOG_ERR, + YAHOO_LOG_WARNING, + YAHOO_LOG_NOTICE, + YAHOO_LOG_INFO, + YAHOO_LOG_DEBUG + }; + + enum yahoo_file_transfer { + YAHOO_FILE_TRANSFER_INIT = 1, + YAHOO_FILE_TRANSFER_ACCEPT = 3, + YAHOO_FILE_TRANSFER_REJECT = 4, + YAHOO_FILE_TRANSFER_DONE = 5, + YAHOO_FILE_TRANSFER_RELAY, + YAHOO_FILE_TRANSFER_FAILED, + YAHOO_FILE_TRANSFER_UNKNOWN + }; #define YAHOO_PROTO_VER 0x0010 @@ -120,172 +221,176 @@ enum yahoo_log_level { #define YAHOO_STYLE_URLON "\033[lm" #define YAHOO_STYLE_URLOFF "\033[xlm" -enum yahoo_connection_type { - YAHOO_CONNECTION_PAGER=0, - YAHOO_CONNECTION_FT, - YAHOO_CONNECTION_YAB, - YAHOO_CONNECTION_WEBCAM_MASTER, - YAHOO_CONNECTION_WEBCAM, - YAHOO_CONNECTION_CHATCAT, - YAHOO_CONNECTION_SEARCH, - YAHOO_CONNECTION_AUTH, -}; - -enum yahoo_webcam_direction_type { - YAHOO_WEBCAM_DOWNLOAD=0, - YAHOO_WEBCAM_UPLOAD -}; - -enum yahoo_stealth_visibility_type { - YAHOO_STEALTH_DEFAULT = 0, - YAHOO_STEALTH_ONLINE, - YAHOO_STEALTH_PERM_OFFLINE -}; + enum yahoo_connection_type { + YAHOO_CONNECTION_PAGER = 0, + YAHOO_CONNECTION_FT, + YAHOO_CONNECTION_YAB, + YAHOO_CONNECTION_WEBCAM_MASTER, + YAHOO_CONNECTION_WEBCAM, + YAHOO_CONNECTION_CHATCAT, + YAHOO_CONNECTION_SEARCH, + YAHOO_CONNECTION_AUTH + }; + + enum yahoo_webcam_direction_type { + YAHOO_WEBCAM_DOWNLOAD = 0, + YAHOO_WEBCAM_UPLOAD + }; + + enum yahoo_stealth_visibility_type { + YAHOO_STEALTH_DEFAULT = 0, + YAHOO_STEALTH_ONLINE, + YAHOO_STEALTH_PERM_OFFLINE + }; /* chat member attribs */ #define YAHOO_CHAT_MALE 0x8000 #define YAHOO_CHAT_FEMALE 0x10000 +#define YAHOO_CHAT_FEMALE 0x10000 #define YAHOO_CHAT_DUNNO 0x400 #define YAHOO_CHAT_WEBCAM 0x10 -enum yahoo_webcam_conn_type { Y_WCM_DIALUP, Y_WCM_DSL, Y_WCM_T1 }; - -struct yahoo_webcam { - int direction; /* Uploading or downloading */ - int conn_type; /* 0=Dialup, 1=DSL/Cable, 2=T1/Lan */ - - char *user; /* user we are viewing */ - char *server; /* webcam server to connect to */ - int port; /* webcam port to connect on */ - char *key; /* key to connect to the server with */ - char *description; /* webcam description */ - char *my_ip; /* own ip number */ -}; - -struct yahoo_webcam_data { - unsigned int data_size; - unsigned int to_read; - unsigned int timestamp; - unsigned char packet_type; -}; - -struct yahoo_data { - char *user; - char *password; - - char *cookie_y; - char *cookie_t; - char *cookie_c; - char *login_cookie; - - YList *buddies; - YList *ignore; - YList *identities; - char *login_id; - - int current_status; - int initial_status; - int logged_in; - - int session_id; - - int client_id; - - char *rawbuddylist; - char *ignorelist; - - void *server_settings; - - struct yahoo_process_status_entry *half_user; -}; - -struct yab { - int yid; - char *id; - char *fname; - char *lname; - char *nname; - char *email; - char *hphone; - char *wphone; - char *mphone; - int dbid; -}; - -struct yahoo_buddy { - char *group; - char *id; - char *real_name; - struct yab *yab_entry; -}; - -enum yahoo_search_type { - YAHOO_SEARCH_KEYWORD = 0, - YAHOO_SEARCH_YID, - YAHOO_SEARCH_NAME -}; - -enum yahoo_search_gender { - YAHOO_GENDER_NONE = 0, - YAHOO_GENDER_MALE, - YAHOO_GENDER_FEMALE -}; - -enum yahoo_search_agerange { - YAHOO_AGERANGE_NONE = 0 -}; - -struct yahoo_found_contact { - char *id; - char *gender; - char *location; - int age; - int online; -}; + enum yahoo_webcam_conn_type { Y_WCM_DIALUP, Y_WCM_DSL, Y_WCM_T1 }; + + struct yahoo_webcam { + int direction; /* Uploading or downloading */ + int conn_type; /* 0=Dialup, 1=DSL/Cable, 2=T1/Lan */ + + char *user; /* user we are viewing */ + char *server; /* webcam server to connect to */ + int port; /* webcam port to connect on */ + char *key; /* key to connect to the server with */ + char *description; /* webcam description */ + char *my_ip; /* own ip number */ + }; + + struct yahoo_webcam_data { + unsigned int data_size; + unsigned int to_read; + unsigned int timestamp; + unsigned char packet_type; + }; + + struct yahoo_data { + char *user; + char *password; + + char *cookie_y; + char *cookie_t; + char *cookie_c; + char *cookie_b; + char *login_cookie; + char *crumb; + char *seed; + + YList *buddies; + YList *ignore; + YList *identities; + char *login_id; + + int current_status; + int initial_status; + int logged_in; + + int session_id; + + int client_id; + + char *rawbuddylist; + char *ignorelist; + + void *server_settings; + + struct yahoo_process_status_entry *half_user; + }; + + struct yab { + int yid; + char *id; + char *fname; + char *lname; + char *nname; + char *email; + char *hphone; + char *wphone; + char *mphone; + int dbid; + }; + + struct yahoo_buddy { + char *group; + char *id; + char *real_name; + struct yab *yab_entry; + }; + + enum yahoo_search_type { + YAHOO_SEARCH_KEYWORD = 0, + YAHOO_SEARCH_YID, + YAHOO_SEARCH_NAME + }; + + enum yahoo_search_gender { + YAHOO_GENDER_NONE = 0, + YAHOO_GENDER_MALE, + YAHOO_GENDER_FEMALE + }; + + enum yahoo_search_agerange { + YAHOO_AGERANGE_NONE = 0 + }; + + struct yahoo_found_contact { + char *id; + char *gender; + char *location; + int age; + int online; + }; /* * Function pointer to be passed to http get/post and send file */ -typedef void (*yahoo_get_fd_callback)(int id, int fd, int error, void *data); + typedef void (*yahoo_get_fd_callback) (int id, void *fd, int error, + void *data); /* * Function pointer to be passed to yahoo_get_url_handle */ -typedef void (*yahoo_get_url_handle_callback)(int id, int fd, int error, - const char *filename, unsigned long size, void *data); - - -struct yahoo_chat_member { - char *id; - int age; - int attribs; - char *alias; - char *location; -}; - -struct yahoo_process_status_entry { - char *name; /* 7 name */ - int state; /* 10 state */ - int flags; /* 13 flags, bit 0 = pager, bit 1 = chat, bit 2 = game */ - int mobile; /* 60 mobile */ - char *msg; /* 19 custom status message */ - int away; /* 47 away (or invisible) */ - int buddy_session; /* 11 state */ - int f17; /* 17 in chat? then what about flags? */ - int idle; /* 137 seconds idle */ - int f138; /* 138 state */ - char *f184; /* 184 state */ - int f192; /* 192 state */ - int f10001; /* 10001 state */ - int f10002; /* 10002 state */ - int f198; /* 198 state */ - char *f197; /* 197 state */ - char *f205; /* 205 state */ - int f213; /* 213 state */ -}; + typedef void (*yahoo_get_url_handle_callback) (int id, void *fd, + int error, const char *filename, unsigned long size, + void *data); + + struct yahoo_chat_member { + char *id; + int age; + int attribs; + char *alias; + char *location; + }; + + struct yahoo_process_status_entry { + char *name; /* 7 name */ + int state; /* 10 state */ + int flags; /* 13 flags, bit 0 = pager, bit 1 = chat, bit 2 = game */ + int mobile; /* 60 mobile */ + char *msg; /* 19 custom status message */ + int away; /* 47 away (or invisible) */ + int buddy_session; /* 11 state */ + int f17; /* 17 in chat? then what about flags? */ + int idle; /* 137 seconds idle */ + int f138; /* 138 state */ + char *f184; /* 184 state */ + int f192; /* 192 state */ + int f10001; /* 10001 state */ + int f10002; /* 10002 state */ + int f198; /* 198 state */ + char *f197; /* 197 state */ + char *f205; /* 205 state */ + int f213; /* 213 state */ + }; #ifdef __cplusplus } #endif - #endif diff --git a/protocols/yahoo/yahoo_httplib.c b/protocols/yahoo/yahoo_httplib.c index 1b084992..6bb8923d 100644 --- a/protocols/yahoo/yahoo_httplib.c +++ b/protocols/yahoo/yahoo_httplib.c @@ -29,14 +29,13 @@ # define strchr index # define strrchr rindex # endif -char *strchr (), *strrchr (); +char *strchr(), *strrchr(); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif - #include #ifndef _WIN32 #include @@ -62,7 +61,7 @@ extern struct yahoo_callbacks *yc; extern enum yahoo_log_level log_level; -int yahoo_tcp_readline(char *ptr, int maxlen, int fd) +int yahoo_tcp_readline(char *ptr, int maxlen, void *fd) { int n, rc; char c; @@ -70,11 +69,11 @@ int yahoo_tcp_readline(char *ptr, int maxlen, int fd) for (n = 1; n < maxlen; n++) { do { - rc = read(fd, &c, 1); - } while(rc == -1 && (errno == EINTR || errno == EAGAIN)); /* this is bad - it should be done asynchronously */ + rc = YAHOO_CALLBACK(ext_yahoo_read) (fd, &c, 1); + } while (rc == -1 && (errno == EINTR || errno == EAGAIN)); /* this is bad - it should be done asynchronously */ if (rc == 1) { - if(c == '\r') /* get rid of \r */ + if (c == '\r') /* get rid of \r */ continue; *ptr = c; if (c == '\n') @@ -82,9 +81,9 @@ int yahoo_tcp_readline(char *ptr, int maxlen, int fd) ptr++; } else if (rc == 0) { if (n == 1) - return (0); /* EOF, no data */ + return (0); /* EOF, no data */ else - break; /* EOF, w/ data */ + break; /* EOF, w/ data */ } else { return -1; } @@ -95,12 +94,12 @@ int yahoo_tcp_readline(char *ptr, int maxlen, int fd) } static int url_to_host_port_path(const char *url, - char *host, int *port, char *path) + char *host, int *port, char *path, int *ssl) { - char *urlcopy=NULL; - char *slash=NULL; - char *colon=NULL; - + char *urlcopy = NULL; + char *slash = NULL; + char *colon = NULL; + /* * http://hostname * http://hostname/ @@ -110,10 +109,14 @@ static int url_to_host_port_path(const char *url, * http://hostname:port/ * http://hostname:port/path * http://hostname:port/path:foo + * and https:// variants of the above */ - if(strstr(url, "http://") == url) { - urlcopy = strdup(url+7); + if (strstr(url, "http://") == url) { + urlcopy = strdup(url + 7); + } else if (strstr(url, "https://") == url) { + urlcopy = strdup(url + 8); + *ssl = 1; } else { WARNING(("Weird url - unknown protocol: %s", url)); return 0; @@ -122,14 +125,17 @@ static int url_to_host_port_path(const char *url, slash = strchr(urlcopy, '/'); colon = strchr(urlcopy, ':'); - if(!colon || (slash && slash < colon)) { - *port = 80; + if (!colon || (slash && slash < colon)) { + if (*ssl) + *port = 443; + else + *port = 80; } else { *colon = 0; - *port = atoi(colon+1); + *port = atoi(colon + 1); } - if(!slash) { + if (!slash) { strcpy(path, "/"); } else { strcpy(path, slash); @@ -137,7 +143,7 @@ static int url_to_host_port_path(const char *url, } strcpy(host, urlcopy); - + FREE(urlcopy); return 1; @@ -145,135 +151,135 @@ static int url_to_host_port_path(const char *url, static int isurlchar(unsigned char c) { - return (isalnum(c) || '-' == c || '_' == c); + return (isalnum(c)); } char *yahoo_urlencode(const char *instr) { - int ipos=0, bpos=0; + int ipos = 0, bpos = 0; char *str = NULL; int len = strlen(instr); - if(!(str = y_new(char, 3*len + 1) )) - return ""; + if (!(str = y_new(char, 3 *len + 1))) + return ""; - while(instr[ipos]) { - while(isurlchar(instr[ipos])) + while (instr[ipos]) { + while (isurlchar(instr[ipos])) str[bpos++] = instr[ipos++]; - if(!instr[ipos]) + if (!instr[ipos]) break; - - snprintf(&str[bpos], 4, "%%%.2x", instr[ipos]); - bpos+=3; + + snprintf(&str[bpos], 4, "%%%02x", instr[ipos] & 0xff); + bpos += 3; ipos++; } - str[bpos]='\0'; + str[bpos] = '\0'; /* free extra alloc'ed mem. */ len = strlen(str); - str = y_renew(char, str, len+1); + str = y_renew(char, str, len + 1); return (str); } char *yahoo_urldecode(const char *instr) { - int ipos=0, bpos=0; + int ipos = 0, bpos = 0; char *str = NULL; - char entity[3]={0,0,0}; + char entity[3] = { 0, 0, 0 }; unsigned dec; int len = strlen(instr); - if(!(str = y_new(char, len+1) )) - return ""; + if (!(str = y_new(char, len + 1))) + return ""; - while(instr[ipos]) { - while(instr[ipos] && instr[ipos]!='%') - if(instr[ipos]=='+') { - str[bpos++]=' '; + while (instr[ipos]) { + while (instr[ipos] && instr[ipos] != '%') + if (instr[ipos] == '+') { + str[bpos++] = ' '; ipos++; } else str[bpos++] = instr[ipos++]; - if(!instr[ipos]) + if (!instr[ipos]) break; - - if(instr[ipos+1] && instr[ipos+2]) { + + if (instr[ipos + 1] && instr[ipos + 2]) { ipos++; - entity[0]=instr[ipos++]; - entity[1]=instr[ipos++]; + entity[0] = instr[ipos++]; + entity[1] = instr[ipos++]; sscanf(entity, "%2x", &dec); str[bpos++] = (char)dec; } else { str[bpos++] = instr[ipos++]; } } - str[bpos]='\0'; + str[bpos] = '\0'; /* free extra alloc'ed mem. */ len = strlen(str); - str = y_renew(char, str, len+1); + str = y_renew(char, str, len + 1); return (str); } char *yahoo_xmldecode(const char *instr) { - int ipos=0, bpos=0, epos=0; + int ipos = 0, bpos = 0, epos = 0; char *str = NULL; - char entity[4]={0,0,0,0}; - char *entitymap[5][2]={ - {"amp;", "&"}, + char entity[4] = { 0, 0, 0, 0 }; + char *entitymap[5][2] = { + {"amp;", "&"}, {"quot;", "\""}, - {"lt;", "<"}, - {"gt;", "<"}, + {"lt;", "<"}, + {"gt;", "<"}, {"nbsp;", " "} }; unsigned dec; int len = strlen(instr); - if(!(str = y_new(char, len+1) )) - return ""; + if (!(str = y_new(char, len + 1))) + return ""; - while(instr[ipos]) { - while(instr[ipos] && instr[ipos]!='&') - if(instr[ipos]=='+') { - str[bpos++]=' '; + while (instr[ipos]) { + while (instr[ipos] && instr[ipos] != '&') + if (instr[ipos] == '+') { + str[bpos++] = ' '; ipos++; } else str[bpos++] = instr[ipos++]; - if(!instr[ipos] || !instr[ipos+1]) + if (!instr[ipos] || !instr[ipos + 1]) break; ipos++; - if(instr[ipos] == '#') { + if (instr[ipos] == '#') { ipos++; - epos=0; - while(instr[ipos] != ';') - entity[epos++]=instr[ipos++]; + epos = 0; + while (instr[ipos] != ';') + entity[epos++] = instr[ipos++]; sscanf(entity, "%u", &dec); str[bpos++] = (char)dec; ipos++; } else { int i; - for (i=0; i<5; i++) - if(!strncmp(instr+ipos, entitymap[i][0], - strlen(entitymap[i][0]))) { - str[bpos++] = entitymap[i][1][0]; + for (i = 0; i < 5; i++) + if (!strncmp(instr + ipos, entitymap[i][0], + strlen(entitymap[i][0]))) { + str[bpos++] = entitymap[i][1][0]; ipos += strlen(entitymap[i][0]); break; } } } - str[bpos]='\0'; + str[bpos] = '\0'; /* free extra alloc'ed mem. */ len = strlen(str); - str = y_renew(char, str, len+1); + str = y_renew(char, str, len + 1); return (str); } -typedef void (*http_connected)(int id, int fd, int error); +typedef void (*http_connected) (int id, void *fd, int error); struct callback_data { int id; @@ -282,150 +288,117 @@ struct callback_data { void *user_data; }; -static void connect_complete(int fd, int error, void *data) +static void connect_complete(void *fd, int error, void *data) { struct callback_data *ccd = data; - if(error == 0 && fd > 0) - write(fd, ccd->request, strlen(ccd->request)); - FREE(ccd->request); + if (error == 0) + YAHOO_CALLBACK(ext_yahoo_write) (fd, ccd->request, + strlen(ccd->request)); + free(ccd->request); ccd->callback(ccd->id, fd, error, ccd->user_data); FREE(ccd); } -static void yahoo_send_http_request(int id, char *host, int port, char *request, - yahoo_get_fd_callback callback, void *data) +static void yahoo_send_http_request(int id, char *host, int port, char *request, + yahoo_get_fd_callback callback, void *data, int use_ssl) { - struct callback_data *ccd=y_new0(struct callback_data, 1); + struct callback_data *ccd = y_new0(struct callback_data, 1); ccd->callback = callback; ccd->id = id; ccd->request = strdup(request); ccd->user_data = data; - - YAHOO_CALLBACK(ext_yahoo_connect_async)(id, host, port, connect_complete, ccd); + + YAHOO_CALLBACK(ext_yahoo_connect_async) (id, host, port, + connect_complete, ccd, use_ssl); } -void yahoo_http_post(int id, const char *url, const char *cookies, long content_length, - yahoo_get_fd_callback callback, void *data) +void yahoo_http_post(int id, const char *url, const char *cookies, + long content_length, yahoo_get_fd_callback callback, void *data) { char host[255]; int port = 80; char path[255]; char buff[1024]; - - if(!url_to_host_port_path(url, host, &port, path)) - return; + int ssl = 0; - snprintf(buff, sizeof(buff), - "POST %s HTTP/1.0\r\n" - "Content-length: %ld\r\n" - "User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" - "Host: %s:%d\r\n" - "Cookie: %s\r\n" - "\r\n", - path, content_length, - host, port, - cookies); + if (!url_to_host_port_path(url, host, &port, path, &ssl)) + return; - yahoo_send_http_request(id, host, port, buff, callback, data); + /* thanks to kopete dumpcap */ + snprintf(buff, sizeof(buff), + "POST %s HTTP/1.1\r\n" + "Cookie: %s\r\n" + "User-Agent: Mozilla/5.0\r\n" + "Host: %s\r\n" + "Content-Length: %ld\r\n" + "Cache-Control: no-cache\r\n" + "\r\n", path, cookies, host, content_length); + + yahoo_send_http_request(id, host, port, buff, callback, data, ssl); } -void yahoo_http_get(int id, const char *url, const char *cookies, - yahoo_get_fd_callback callback, void *data) +void yahoo_http_get(int id, const char *url, const char *cookies, int http11, + int keepalive, yahoo_get_fd_callback callback, void *data) { char host[255]; int port = 80; char path[255]; - char buff[1024]; - - if(!url_to_host_port_path(url, host, &port, path)) - return; + char buff[2048]; + char cookiebuff[1024]; + int ssl = 0; - snprintf(buff, sizeof(buff), - "GET %s HTTP/1.0\r\n" - "Host: %s:%d\r\n" - "User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" - "Cookie: %s\r\n" - "\r\n", - path, host, port, cookies); + if (!url_to_host_port_path(url, host, &port, path, &ssl)) + return; - yahoo_send_http_request(id, host, port, buff, callback, data); + /* Allow cases when we don't need to send a cookie */ + if (cookies) + snprintf(cookiebuff, sizeof(cookiebuff), "Cookie: %s\r\n", + cookies); + else + cookiebuff[0] = '\0'; + + snprintf(buff, sizeof(buff), + "GET %s HTTP/1.%s\r\n" + "%sHost: %s\r\n" + "User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" + "Accept: */*\r\n" + "%s" "\r\n", path, http11?"1":"0", cookiebuff, host, + keepalive? "Connection: Keep-Alive\r\n":"Connection: close\r\n"); + + yahoo_send_http_request(id, host, port, buff, callback, data, ssl); } -struct url_data { - yahoo_get_url_handle_callback callback; - void *user_data; -}; - -static void yahoo_got_url_fd(int id, int fd, int error, void *data) +void yahoo_http_head(int id, const char *url, const char *cookies, int len, + char *payload, yahoo_get_fd_callback callback, void *data) { - char *tmp=NULL; - char buff[1024]; - unsigned long filesize=0; - char *filename=NULL; - int n; - - struct url_data *ud = data; + char host[255]; + int port = 80; + char path[255]; + char buff[2048]; + char cookiebuff[1024]; + int ssl = 0; - if(error || fd < 0) { - ud->callback(id, fd, error, filename, filesize, ud->user_data); - FREE(ud); + if (!url_to_host_port_path(url, host, &port, path, &ssl)) return; - } - - while((n=yahoo_tcp_readline(buff, sizeof(buff), fd)) > 0) { - LOG(("Read:%s:\n", buff)); - if(!strcmp(buff, "")) - break; - - if( !strncasecmp(buff, "Content-length:", - strlen("Content-length:")) ) { - tmp = strrchr(buff, ' '); - if(tmp) - filesize = atol(tmp); - } - - if( !strncasecmp(buff, "Content-disposition:", - strlen("Content-disposition:")) ) { - tmp = strstr(buff, "name="); - if(tmp) { - tmp+=strlen("name="); - if(tmp[0] == '"') { - char *tmp2; - tmp++; - tmp2 = strchr(tmp, '"'); - if(tmp2) - *tmp2 = '\0'; - } else { - char *tmp2; - tmp2 = strchr(tmp, ';'); - if(!tmp2) - tmp2 = strchr(tmp, '\r'); - if(!tmp2) - tmp2 = strchr(tmp, '\n'); - if(tmp2) - *tmp2 = '\0'; - } - filename = strdup(tmp); - } - } - } - - LOG(("n == %d\n", n)); - LOG(("Calling callback, filename:%s, size: %ld\n", filename, filesize)); - ud->callback(id, fd, error, filename, filesize, ud->user_data); - FREE(ud); - FREE(filename); -} - -void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd, - yahoo_get_url_handle_callback callback, void *data) -{ - char buff[1024]; - struct url_data *ud = y_new0(struct url_data, 1); - snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); - ud->callback = callback; - ud->user_data = data; - yahoo_http_get(id, url, buff, yahoo_got_url_fd, ud); + /* Allow cases when we don't need to send a cookie */ + if (cookies) + snprintf(cookiebuff, sizeof(cookiebuff), "Cookie: %s\r\n", + cookies); + else + cookiebuff[0] = '\0'; + + snprintf(buff, sizeof(buff), + "HEAD %s HTTP/1.0\r\n" + "Accept: */*\r\n" + "Host: %s:%d\r\n" + "User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" + "%s" + "Content-Length: %d\r\n" + "Cache-Control: no-cache\r\n" + "\r\n%s", path, host, port, cookiebuff, len, + payload?payload:""); + + yahoo_send_http_request(id, host, port, buff, callback, data, ssl); } diff --git a/protocols/yahoo/yahoo_httplib.h b/protocols/yahoo/yahoo_httplib.h index fd28ad48..ab699b20 100644 --- a/protocols/yahoo/yahoo_httplib.h +++ b/protocols/yahoo/yahoo_httplib.h @@ -28,21 +28,21 @@ extern "C" { #include "yahoo2_types.h" -char *yahoo_urlencode(const char *instr); -char *yahoo_urldecode(const char *instr); -char *yahoo_xmldecode(const char *instr); - -int yahoo_tcp_readline(char *ptr, int maxlen, int fd); -void yahoo_http_post(int id, const char *url, const char *cookies, long size, - yahoo_get_fd_callback callback, void *data); -void yahoo_http_get(int id, const char *url, const char *cookies, - yahoo_get_fd_callback callback, void *data); -void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd, - yahoo_get_url_handle_callback callback, void *data); - + char *yahoo_urlencode(const char *instr); + char *yahoo_urldecode(const char *instr); + char *yahoo_xmldecode(const char *instr); + + int yahoo_tcp_readline(char *ptr, int maxlen, void *fd); + void yahoo_http_post(int id, const char *url, const char *cookies, + long size, yahoo_get_fd_callback callback, void *data); + void yahoo_http_get(int id, const char *url, const char *cookies, + int http11, int keepalive, yahoo_get_fd_callback callback, + void *data); + void yahoo_http_head(int id, const char *url, const char *cookies, + int size, char *payload, yahoo_get_fd_callback callback, + void *data); #ifdef __cplusplus } #endif - #endif diff --git a/protocols/yahoo/yahoo_list.h b/protocols/yahoo/yahoo_list.h index 0d335acd..c2e5ad18 100644 --- a/protocols/yahoo/yahoo_list.h +++ b/protocols/yahoo/yahoo_list.h @@ -23,7 +23,7 @@ #ifndef __YLIST_H__ #define __YLIST_H__ -/* GLib has linked list already, so I don't see why libyahoo2 has to copy this... */ +/* BitlBee already uses GLib so use it. */ typedef GList YList; -- cgit v1.2.3 From 78e2eb7bdaf0ab00321f47b93b37b603ef32bdb0 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 13:15:43 +0200 Subject: New handling of authorization requests. The old one wasn't fully broken actually, but it needed a reconnect to see new contacts. --- protocols/yahoo/Makefile | 2 +- protocols/yahoo/yahoo.c | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile index 20ecce71..e5374538 100644 --- a/protocols/yahoo/Makefile +++ b/protocols/yahoo/Makefile @@ -14,7 +14,7 @@ endif # [SH] Program variables objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_util.o -CFLAGS += -Wall -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STRCHR -DHAVE_MEMCPY -DHAVE_GLIB +CFLAGS += -Wall -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STRCHR -DHAVE_MEMCPY -DHAVE_GLIB -Wno-pointer-to-int-cast LFLAGS += -r # [SH] Phony targets diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 7a856254..1ace6020 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -338,20 +338,16 @@ static struct groupchat *byahoo_chat_with( struct im_connection *ic, char *who ) static void byahoo_auth_allow( struct im_connection *ic, const char *who ) { - /* struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; - yahoo_accept_buddy_ymsg13( yd->y2_id, NULL, who ); - */ + yahoo_confirm_buddy( yd->y2_id, who, 0, "" ); } static void byahoo_auth_deny( struct im_connection *ic, const char *who ) { - /* struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; - yahoo_reject_buddy_ymsg13( yd->y2_id, NULL, who, NULL ); - */ + yahoo_confirm_buddy( yd->y2_id, who, 1, "" ); } void byahoo_initmodule( ) @@ -928,16 +924,11 @@ void ext_yahoo_chat_yahooerror( int id, const char *me ) { } -void ext_yahoo_contact_auth_request( int id, const char *myid, const char *who, const char *msg ) -{ - /* Apparently no longer implemented.. */ -} - void ext_yahoo_contact_added( int id, const char *myid, const char *who, const char *msg ) { struct im_connection *ic = byahoo_get_ic_by_id( id ); - imcb_add_buddy( ic, (char*) who, NULL ); + imcb_ask_auth( ic, who, msg ); } void ext_yahoo_rejected( int id, const char *who, const char *msg ) -- cgit v1.2.3 From ccc595b4b445bd8eab7ea454c6eebc3ff2e5a521 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 15:04:00 +0200 Subject: Support buddy groups on Yahoo! --- protocols/yahoo/yahoo.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 1ace6020..fd0b0bc3 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -270,8 +270,32 @@ static void byahoo_keepalive( struct im_connection *ic ) static void byahoo_add_buddy( struct im_connection *ic, char *who, char *group ) { struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; + bee_user_t *bu; - yahoo_add_buddy( yd->y2_id, who, group ? group : BYAHOO_DEFAULT_GROUP, NULL ); + if( group && ( bu = bee_user_by_handle( ic->bee, ic, who ) ) && bu->group ) + { + GSList *bgl; + + /* If the person is in our list already, this is a group change. */ + yahoo_change_buddy_group( yd->y2_id, who, bu->group->name, group ); + + /* No idea how often people have people in multiple groups and + BitlBee doesn't currently support this anyway .. but keep + this struct up-to-date for now. */ + for( bgl = yd->buddygroups; bgl; bgl = bgl->next ) + { + struct byahoo_buddygroups *bg = bgl->data; + + if( g_strcasecmp( bg->buddy, who ) == 0 && + g_strcasecmp( bg->group, bu->group->name ) == 0 ) + { + g_free( bg->group ); + bg->group = g_strdup( group ); + } + } + } + else + yahoo_add_buddy( yd->y2_id, who, group ? group : BYAHOO_DEFAULT_GROUP, NULL ); } static void byahoo_remove_buddy( struct im_connection *ic, char *who, char *group ) @@ -779,7 +803,10 @@ void ext_yahoo_close( void *fd ) void ext_yahoo_got_buddy_change_group( int id, const char *me, const char *who, const char *old_group, const char *new_group ) { -} + struct im_connection *ic = byahoo_get_ic_by_id( id ); + + imcb_add_buddy( ic, who, new_group ); +} /* Because we don't want asynchronous connects in BitlBee, and because libyahoo doesn't seem to use this one anyway, this one is now defunct. */ @@ -980,7 +1007,7 @@ void ext_yahoo_got_webcam_image( int id, const char * who, const unsigned char * { } -void ext_yahoo_got_ping( int id, const char *msg) +void ext_yahoo_got_ping( int id, const char *msg ) { } -- cgit v1.2.3 From c495217e7c02c908d831645b033cf115ccdc3d6d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 15:28:36 +0200 Subject: Inverting allow_reconnect logic on login failures. Automatic reconnects were getting im.bitlbee.org IP-banned sometimes. This fix keeps it happy for some time already. --- protocols/yahoo/yahoo.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index fd0b0bc3..dfd2e70f 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -510,7 +510,7 @@ void ext_yahoo_login_response( int id, int succ, const char *url ) else { char *errstr; - int allow_reconnect = TRUE; + int allow_reconnect = FALSE; yd->logged_in = FALSE; @@ -520,13 +520,15 @@ void ext_yahoo_login_response( int id, int succ, const char *url ) errstr = "Incorrect Yahoo! password"; else if( succ == YAHOO_LOGIN_LOCK ) errstr = "Yahoo! account locked"; + else if( succ == 1236 ) + errstr = "Yahoo! account locked or machine temporarily banned"; else if( succ == YAHOO_LOGIN_DUPL ) - { errstr = "Logged in on a different machine or device"; - allow_reconnect = FALSE; - } else if( succ == YAHOO_LOGIN_SOCK ) + { errstr = "Socket problem"; + allow_reconnect = TRUE; + } else errstr = "Unknown error"; -- cgit v1.2.3 From 03f38289e15c27b93f8fdecf22a03353e4d01096 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 16:06:22 +0200 Subject: Adding protocol-specific chatroom settings. First one to use this: AIM chatrooms to use exchange numbers other than 4. --- irc_im.c | 11 +++++++++-- protocols/jabber/jabber.c | 2 +- protocols/nogaim.h | 15 ++++++++++++++- protocols/oscar/oscar.c | 29 ++++++++++++++++++++++++----- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/irc_im.c b/irc_im.c index 8a10cede..4dbcddce 100644 --- a/irc_im.c +++ b/irc_im.c @@ -692,7 +692,7 @@ static gboolean bee_irc_channel_chat_join( irc_channel_t *ic ) nick = ic->irc->user->nick; ic->flags |= IRC_CHANNEL_CHAT_PICKME; - acc->prpl->chat_join( acc->ic, room, nick, NULL ); + acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set ); ic->flags &= ~IRC_CHANNEL_CHAT_PICKME; return FALSE; @@ -787,7 +787,7 @@ static gboolean bee_irc_channel_init( irc_channel_t *ic ) static char *set_eval_room_account( set_t *set, char *value ) { struct irc_channel *ic = set->data; - account_t *acc; + account_t *acc, *oa; if( !( acc = account_get( ic->irc->b, value ) ) ) return SET_INVALID; @@ -797,6 +797,13 @@ static char *set_eval_room_account( set_t *set, char *value ) return SET_INVALID; } + if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) && + oa->prpl->chat_free_settings ) + oa->prpl->chat_free_settings( oa, &ic->set ); + + if( acc->prpl->chat_add_settings ) + acc->prpl->chat_add_settings( acc, &ic->set ); + return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user ); } diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 7147a628..01353f8e 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -440,7 +440,7 @@ static void jabber_remove_buddy( struct im_connection *ic, char *who, char *grou presence_send_request( ic, who, "unsubscribe" ); } -static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password ) +static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password, set_t **sets ) { if( strchr( room, '@' ) == NULL ) imcb_error( ic, "Invalid room name: %s", room ); diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 1d9ac71e..46f6535a 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -211,13 +211,19 @@ struct prpl { * your protocol does not support publicly named group chats, then do * not implement this. */ struct groupchat * - (* chat_join) (struct im_connection *, const char *room, const char *nick, const char *password); + (* chat_join) (struct im_connection *, const char *room, + const char *nick, const char *password, set_t **sets); /* Change the topic, if supported. Note that BitlBee expects the IM server to confirm the topic change with a regular topic change event. If it doesn't do that, you have to fake it to make it visible to the user. */ void (* chat_topic) (struct groupchat *, char *topic); + /* If your protocol module needs any special info for joining chatrooms + other than a roomname + nickname, add them here. */ + void (* chat_add_settings) (account_t *acc, set_t **head); + void (* chat_free_settings) (account_t *acc, set_t **head); + /* You can tell what away states your protocol supports, so that * BitlBee will try to map the IRC away reasons to them. If your * protocol doesn't have any, just return one generic "Away". */ @@ -233,6 +239,13 @@ struct prpl { /* Incoming transfer request */ void (* transfer_request) (struct im_connection *, file_transfer_t *ft, char *handle ); + + /* Some placeholders so eventually older plugins may cooperate with newer BitlBees. */ + void *resv1; + void *resv2; + void *resv3; + void *resv4; + void *resv5; }; /* im_api core stuff. */ diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 11539852..fdf0d82c 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -2505,7 +2505,8 @@ void oscar_chat_leave(struct groupchat *c) oscar_chat_kill(c->ic, c->data); } -struct groupchat *oscar_chat_join(struct im_connection * ic, const char * room, const char * nick, const char * password ) +struct groupchat *oscar_chat_join_internal(struct im_connection *ic, const char *room, + const char *nick, const char *password, int exchange_number) { struct oscar_data * od = (struct oscar_data *)ic->proto_data; aim_conn_t * cur; @@ -2513,13 +2514,13 @@ struct groupchat *oscar_chat_join(struct im_connection * ic, const char * room, if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) { int st; - st = aim_chatnav_createroom(od->sess, cur, room, 4); + st = aim_chatnav_createroom(od->sess, cur, room, exchange_number); return NULL; } else { struct create_room * cr = g_new0(struct create_room, 1); - cr->exchange = 4; + cr->exchange = exchange_number; cr->name = g_strdup(room); od->create_rooms = g_slist_append(od->create_rooms, cr); aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV); @@ -2528,6 +2529,12 @@ struct groupchat *oscar_chat_join(struct im_connection * ic, const char * room, } } +struct groupchat *oscar_chat_join(struct im_connection *ic, const char *room, + const char *nick, const char *password, set_t **sets) +{ + return oscar_chat_join_internal(ic, room, nick, password, set_getint(sets, "exchange_number")); +} + struct groupchat *oscar_chat_with(struct im_connection * ic, char *who) { struct oscar_data * od = (struct oscar_data *)ic->proto_data; @@ -2544,7 +2551,7 @@ struct groupchat *oscar_chat_with(struct im_connection * ic, char *who) *s = '0'; c = imcb_chat_new(ic, chatname); - ret = oscar_chat_join(ic, chatname, NULL, NULL); + ret = oscar_chat_join_internal(ic, chatname, NULL, NULL, 4); aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0); g_free(chatname); @@ -2556,7 +2563,7 @@ void oscar_accept_chat(void *data) { struct aim_chat_invitation * inv = data; - oscar_chat_join(inv->ic, inv->name, NULL, NULL); + oscar_chat_join_internal(inv->ic, inv->name, NULL, NULL, 4); g_free(inv->name); g_free(inv); } @@ -2569,6 +2576,16 @@ void oscar_reject_chat(void *data) g_free(inv); } +void oscar_chat_add_settings(account_t *acc, set_t **head) +{ + set_add(head, "exchange_number", "4", set_eval_int, NULL); +} + +void oscar_chat_free_settings(account_t *acc, set_t **head) +{ + set_del(head, "exchange_number"); +} + void oscar_initmodule() { struct prpl *ret = g_new0(struct prpl, 1); @@ -2589,6 +2606,8 @@ void oscar_initmodule() ret->chat_leave = oscar_chat_leave; ret->chat_with = oscar_chat_with; ret->chat_join = oscar_chat_join; + ret->chat_add_settings = oscar_chat_add_settings; + ret->chat_free_settings = oscar_chat_free_settings; ret->add_permit = oscar_add_permit; ret->add_deny = oscar_add_deny; ret->rem_permit = oscar_rem_permit; -- cgit v1.2.3 From 7989d40d5b9c8f05b38e7b07ab7e91335e717309 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 16:19:44 +0200 Subject: Fixing chat_join() for OSCAR to return a struct groupchat* right away, without this we end up creating a #chat_000. --- protocols/oscar/oscar.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index fdf0d82c..0310a27e 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -2509,6 +2509,7 @@ struct groupchat *oscar_chat_join_internal(struct im_connection *ic, const char const char *nick, const char *password, int exchange_number) { struct oscar_data * od = (struct oscar_data *)ic->proto_data; + struct groupchat *ret = imcb_chat_new(ic, room); aim_conn_t * cur; if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) { @@ -2516,7 +2517,7 @@ struct groupchat *oscar_chat_join_internal(struct im_connection *ic, const char st = aim_chatnav_createroom(od->sess, cur, room, exchange_number); - return NULL; + return ret; } else { struct create_room * cr = g_new0(struct create_room, 1); @@ -2525,7 +2526,7 @@ struct groupchat *oscar_chat_join_internal(struct im_connection *ic, const char od->create_rooms = g_slist_append(od->create_rooms, cr); aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV); - return NULL; + return ret; } } -- cgit v1.2.3 From 40e6dac45f29a4c2cc64ce55eecef03d0b185bd5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 17:46:59 +0200 Subject: Adding account tags as a way to 100% uniquely identify an account. protocol(screenname) doesn't do this and is a little bit long. These will be used for nick_format and XML storage. --- protocols/account.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- protocols/account.h | 4 +++- root_commands.c | 2 +- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/protocols/account.c b/protocols/account.c index cf9cbe71..6a762e8b 100644 --- a/protocols/account.c +++ b/protocols/account.c @@ -33,6 +33,7 @@ account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) { account_t *a; set_t *s; + char tag[strlen(prpl->name)+10]; if( bee->accounts ) { @@ -64,10 +65,29 @@ account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) s = set_add( &a->set, "password", NULL, set_eval_account, a ); s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; + s = set_add( &a->set, "tag", NULL, set_eval_account, a ); + s = set_add( &a->set, "username", NULL, set_eval_account, a ); s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; set_setstr( &a->set, "username", user ); + if( account_by_tag( bee, prpl->name ) ) + { + int i; + + for( i = 2; i < 10000; i ++ ) + { + sprintf( tag, "%s%d", prpl->name, i ); + if( !account_by_tag( bee, tag ) ) + break; + } + } + else + { + strcpy( tag, prpl->name ); + } + set_setstr( &a->set, "tag", tag ); + a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); /* This function adds some more settings (and might want to do more @@ -130,6 +150,18 @@ char *set_eval_account( set_t *set, char *value ) return SET_INVALID; } } + else if( strcmp( set->key, "tag" ) == 0 ) + { + account_t *oa; + + /* Enforce uniqueness. */ + if( ( oa = account_by_tag( acc->bee, value ) ) && oa != acc ) + return SET_INVALID; + + g_free( acc->tag ); + acc->tag = g_strdup( value ); + return value; + } else if( strcmp( set->key, "auto_connect" ) == 0 ) { if( !is_bool( value ) ) @@ -172,12 +204,16 @@ static char *set_eval_nick_source( set_t *set, char *value ) return value; } -account_t *account_get( bee_t *bee, char *id ) +account_t *account_get( bee_t *bee, const char *id ) { account_t *a, *ret = NULL; char *handle, *s; int nr; + /* Tags get priority above anything else. */ + if( ( a = account_by_tag( bee, id ) ) ) + return a; + /* This checks if the id string ends with (...) */ if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 ) { @@ -233,6 +269,17 @@ account_t *account_get( bee_t *bee, char *id ) return( ret ); } +account_t *account_by_tag( bee_t *bee, const char *tag ) +{ + account_t *a; + + for( a = bee->accounts; a; a = a->next ) + if( a->tag && g_strcasecmp( tag, a->tag ) == 0 ) + return a; + + return NULL; +} + void account_del( bee_t *bee, account_t *acc ) { account_t *a, *l = NULL; @@ -263,6 +310,7 @@ void account_del( bee_t *bee, account_t *acc ) g_hash_table_destroy( a->nicks ); + g_free( a->tag ); g_free( a->user ); g_free( a->pass ); g_free( a->server ); diff --git a/protocols/account.h b/protocols/account.h index be27542e..a39be2e2 100644 --- a/protocols/account.h +++ b/protocols/account.h @@ -32,6 +32,7 @@ typedef struct account char *user; char *pass; char *server; + char *tag; int auto_connect; int auto_reconnect_delay; @@ -47,7 +48,8 @@ typedef struct account } account_t; account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ); -account_t *account_get( bee_t *bee, char *id ); +account_t *account_get( bee_t *bee, const char *id ); +account_t *account_by_tag( bee_t *bee, const char *tag ); void account_del( bee_t *bee, account_t *acc ); void account_on( bee_t *bee, account_t *a ); void account_off( bee_t *bee, account_t *a ); diff --git a/root_commands.c b/root_commands.c index ddef9cde..cdddf7f2 100644 --- a/root_commands.c +++ b/root_commands.c @@ -412,7 +412,7 @@ static void cmd_account( irc_t *irc, char **cmd ) else con = ""; - irc_usermsg( irc, "%2d. %s, %s%s", i, a->prpl->name, a->user, con ); + irc_usermsg( irc, "%2d (%s): %s, %s%s", i, a->tag, a->prpl->name, a->user, con ); i ++; } -- cgit v1.2.3 From e135cd0997fb88ae644e63b6b7457ba08a60661a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 17:58:27 +0200 Subject: Use the account tag in a few places and store it in the XML file as an attribute, not as a setting (since all accounts have it anyway). --- irc_channel.c | 2 +- irc_im.c | 2 +- protocols/account.c | 1 + storage_xml.c | 18 ++++++++---------- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/irc_channel.c b/irc_channel.c index 536b2de1..5d504f66 100644 --- a/irc_channel.c +++ b/irc_channel.c @@ -578,7 +578,7 @@ static char *set_eval_by_account( set_t *set, char *value ) if( icc->type == IRC_CC_TYPE_ACCOUNT ) bee_irc_channel_update( ic->irc, ic, NULL ); - return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user ); + return g_strdup( acc->tag ); } static char *set_eval_fill_by( set_t *set, char *value ) diff --git a/irc_im.c b/irc_im.c index 4dbcddce..2033b1a8 100644 --- a/irc_im.c +++ b/irc_im.c @@ -804,7 +804,7 @@ static char *set_eval_room_account( set_t *set, char *value ) if( acc->prpl->chat_add_settings ) acc->prpl->chat_add_settings( acc, &ic->set ); - return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user ); + return g_strdup( acc->tag ); } static char *set_eval_chat_type( set_t *set, char *value ) diff --git a/protocols/account.c b/protocols/account.c index 6a762e8b..7fceae91 100644 --- a/protocols/account.c +++ b/protocols/account.c @@ -66,6 +66,7 @@ account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; s = set_add( &a->set, "tag", NULL, set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE; s = set_add( &a->set, "username", NULL, set_eval_account, a ); s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; diff --git a/storage_xml.c b/storage_xml.c index db72025d..1d6757ae 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -126,7 +126,7 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na } else if( g_strcasecmp( element_name, "account" ) == 0 ) { - char *protocol, *handle, *server, *password = NULL, *autoconnect; + char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag; char *pass_b64 = NULL; unsigned char *pass_cr = NULL; int pass_len; @@ -136,6 +136,7 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na pass_b64 = xml_attr( attr_names, attr_values, "password" ); server = xml_attr( attr_names, attr_values, "server" ); autoconnect = xml_attr( attr_names, attr_values, "autoconnect" ); + tag = xml_attr( attr_names, attr_values, "tag" ); protocol = xml_attr( attr_names, attr_values, "protocol" ); if( protocol ) @@ -155,6 +156,8 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na set_setstr( &xd->current_account->set, "server", server ); if( autoconnect ) set_setstr( &xd->current_account->set, "auto_connect", autoconnect ); + if( tag ) + set_setstr( &xd->current_account->set, "tag", tag ); } else { @@ -243,16 +246,11 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na if( xd->current_account && handle && channel ) { irc_channel_t *ic; - char *acc; - - acc = g_strdup_printf( "%s(%s)", - xd->current_account->prpl->name, - xd->current_account->user ); if( ( ic = irc_channel_new( irc, channel ) ) && set_setstr( &ic->set, "type", "chat" ) && set_setstr( &ic->set, "chat_type", "room" ) && - set_setstr( &ic->set, "account", acc ) && + set_setstr( &ic->set, "account", xd->current_account->tag ) && set_setstr( &ic->set, "room", handle ) ) { /* Try to pick up some settings where possible. */ @@ -260,8 +258,6 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na } else if( ic ) irc_channel_free( ic ); - - g_free( acc ); } else { @@ -501,7 +497,9 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) pass_b64 = base64_encode( pass_cr, pass_len ); g_free( pass_cr ); - if( !xml_printf( fd, 1, "prpl->name, acc->user, pass_b64, acc->auto_connect ) ) + if( !xml_printf( fd, 1, "prpl->name, acc->user, + pass_b64, acc->auto_connect, acc->tag ) ) { g_free( pass_b64 ); goto write_error; -- cgit v1.2.3 From 0f28785760c8e625874d35f79c27eaa19785b809 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 18:10:27 +0200 Subject: Account tag documentation update. --- doc/user-guide/commands.xml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 4413ce49..982e122e 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -107,7 +107,7 @@ - The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. + The account ID can be a number/tag (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. @@ -121,7 +121,7 @@ - The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. + The account ID can be a number/tag (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. @@ -136,7 +136,7 @@ - The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. + The account ID can be a number/tag (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. @@ -167,7 +167,7 @@ - The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. + The account ID can be a number/tag (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. @@ -1140,6 +1140,18 @@ + + + + For every account you have, you can set a tag you can use to uniquely identify that account. This tag can be used instead of the account number (or protocol name, or part of the screenname) when using commands like account, add, etc. You can't have two accounts with one and the same account tag. + + + + By default, it will be set to the name of the IM protocol. Once you add a second account on an IM network, a numeric suffix will be added, starting with 2. + + + + local local, utc, gmt, timezone-spec -- cgit v1.2.3 From 593971d9ff9f246cec5af5583f29e45fee62edfe Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 22:59:41 +0200 Subject: Warn when adding an account twice. People are doing this a lot actually, and it's unlikely to be intentional. --- root_commands.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/root_commands.c b/root_commands.c index cdddf7f2..69aa3e98 100644 --- a/root_commands.c +++ b/root_commands.c @@ -379,7 +379,13 @@ static void cmd_account( irc_t *irc, char **cmd ) irc_usermsg( irc, "Unknown protocol" ); return; } - + + for( a = irc->b->accounts; a; a = a->next ) + if( a->prpl == prpl && prpl->handle_cmp( a->user, cmd[3] ) == 0 ) + irc_usermsg( irc, "Warning: You already have an account with " + "protocol `%s' and username `%s'. Are you accidentally " + "trying to add it twice?", prpl->name, cmd[3] ); + a = account_add( irc->b, prpl, cmd[3], cmd[4] ); if( cmd[5] ) { -- cgit v1.2.3