Changes in irc.c [839189b:1c8e5f7]
Legend:
- Unmodified
- Added
- Removed
-
irc.c
r839189b r1c8e5f7 5 5 \********************************************************************/ 6 6 7 /* The big hairy IRCd part of the project*/7 /* The IRC-based UI (for now the only one) */ 8 8 9 9 /* … … 24 24 */ 25 25 26 #define BITLBEE_CORE27 26 #include "bitlbee.h" 28 #include "sock.h"29 #include "crypting.h"30 27 #include "ipc.h" 31 32 static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); 33 34 GSList *irc_connection_list = NULL; 35 36 static char *set_eval_password( set_t *set, char *value ) 37 { 38 irc_t *irc = set->data; 39 40 if( irc->status & USTATUS_IDENTIFIED && value ) 41 { 42 irc_setpass( irc, value ); 43 return NULL; 44 } 45 else 46 { 47 return SET_INVALID; 48 } 49 } 50 51 static char *set_eval_charset( set_t *set, char *value ) 52 { 53 irc_t *irc = set->data; 54 char *test; 55 gsize test_bytes = 0; 56 GIConv ic, oc; 57 58 if( g_strcasecmp( value, "none" ) == 0 ) 59 value = g_strdup( "utf-8" ); 60 61 if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) 62 { 63 return NULL; 64 } 65 66 /* Do a test iconv to see if the user picked an IRC-compatible 67 charset (for example utf-16 goes *horribly* wrong). */ 68 if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL || 69 test_bytes > 1 ) 70 { 71 g_free( test ); 72 g_iconv_close( oc ); 73 irc_usermsg( irc, "Unsupported character set: The IRC protocol " 74 "only supports 8-bit character sets." ); 75 return NULL; 76 } 77 g_free( test ); 78 79 if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) 80 { 81 g_iconv_close( oc ); 82 return NULL; 83 } 84 85 if( irc->iconv != (GIConv) -1 ) 86 g_iconv_close( irc->iconv ); 87 if( irc->oconv != (GIConv) -1 ) 88 g_iconv_close( irc->oconv ); 89 90 irc->iconv = ic; 91 irc->oconv = oc; 92 93 return value; 94 } 95 96 static char *set_eval_away_status( set_t *set, char *value ) 97 { 98 irc_t *irc = set->data; 99 account_t *a; 100 101 g_free( set->value ); 102 set->value = g_strdup( value ); 103 104 for( a = irc->accounts; a; a = a->next ) 105 { 106 struct im_connection *ic = a->ic; 107 108 if( ic && ic->flags & OPT_LOGGED_IN ) 109 imc_away_send_update( ic ); 110 } 111 112 return value; 113 } 28 #include "dcc.h" 29 30 GSList *irc_connection_list; 31 32 static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ); 33 static char *set_eval_charset( set_t *set, char *value ); 34 static char *set_eval_password( set_t *set, char *value ); 114 35 115 36 irc_t *irc_new( int fd ) … … 118 39 struct sockaddr_storage sock; 119 40 socklen_t socklen = sizeof( sock ); 41 char *host = NULL, *myhost = NULL; 42 irc_user_t *iu; 120 43 set_t *s; 44 bee_t *b; 121 45 122 46 irc = g_new0( irc_t, 1 ); … … 125 49 sock_make_nonblocking( irc->fd ); 126 50 127 irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );51 irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); 128 52 129 53 irc->status = USTATUS_OFFLINE; 130 54 irc->last_pong = gettime(); 131 55 132 irc-> userhash = g_hash_table_new( g_str_hash, g_str_equal );56 irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal ); 133 57 irc->watches = g_hash_table_new( g_str_hash, g_str_equal ); 134 135 strcpy( irc->umode, UMODE );136 irc->mynick = g_strdup( ROOT_NICK );137 irc->channel = g_strdup( ROOT_CHAN );138 58 139 59 irc->iconv = (GIConv) -1; … … 142 62 if( global.conf->hostname ) 143 63 { 144 irc->myhost = g_strdup( global.conf->hostname );64 myhost = g_strdup( global.conf->hostname ); 145 65 } 146 66 else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) … … 151 71 NI_MAXHOST, NULL, 0, 0 ) == 0 ) 152 72 { 153 irc->myhost = g_strdup( ipv6_unwrap( buf ) );73 myhost = g_strdup( ipv6_unwrap( buf ) ); 154 74 } 155 75 } … … 162 82 NI_MAXHOST, NULL, 0, 0 ) == 0 ) 163 83 { 164 irc->host = g_strdup( ipv6_unwrap( buf ) );165 } 166 } 167 168 if( irc->host == NULL )169 irc->host = g_strdup( "localhost.localdomain" );170 if( irc->myhost == NULL )171 irc->myhost = g_strdup( "localhost.localdomain" );84 host = g_strdup( ipv6_unwrap( buf ) ); 85 } 86 } 87 88 if( host == NULL ) 89 host = g_strdup( "localhost.localdomain" ); 90 if( myhost == NULL ) 91 myhost = g_strdup( "localhost.localdomain" ); 172 92 173 93 if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) 174 94 irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc ); 175 176 irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );177 95 178 96 irc_connection_list = g_slist_append( irc_connection_list, irc ); 179 97 180 s = set_add( &irc->set, "away", NULL, set_eval_away_status, irc ); 98 b = irc->b = bee_new(); 99 b->ui_data = irc; 100 b->ui = &irc_ui_funcs; 101 102 s = set_add( &b->set, "away_devoice", "true", set_eval_away_devoice, irc ); 103 s = set_add( &b->set, "away_reply_timeout", "3600", set_eval_int, irc ); 104 s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); 105 s = set_add( &b->set, "default_target", "root", NULL, irc ); 106 s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc ); 107 s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc ); 108 s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc ); 109 s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); 110 s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc ); 111 s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc ); 112 s->old_key = g_strdup( "buddy_sendbuffer" ); 113 s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc ); 114 s->old_key = g_strdup( "buddy_sendbuffer_delay" ); 115 s = set_add( &b->set, "password", NULL, set_eval_password, irc ); 181 116 s->flags |= SET_NULL_OK; 182 s = set_add( &irc->set, "away_devoice", "true", set_eval_away_devoice, irc ); 183 s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc ); 184 s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc ); 185 s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc ); 186 s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); 187 s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); 188 s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc ); 189 s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc ); 190 s = set_add( &irc->set, "debug", "false", set_eval_bool, irc ); 191 s = set_add( &irc->set, "default_target", "root", NULL, irc ); 192 s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); 193 s = set_add( &irc->set, "display_timestamps", "true", set_eval_bool, irc ); 194 s = set_add( &irc->set, "handle_unknown", "root", NULL, irc ); 195 s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); 196 s = set_add( &irc->set, "ops", "both", set_eval_ops, irc ); 197 s = set_add( &irc->set, "password", NULL, set_eval_password, irc ); 198 s->flags |= SET_NULL_OK; 199 s = set_add( &irc->set, "private", "true", set_eval_bool, irc ); 200 s = set_add( &irc->set, "query_order", "lifo", NULL, irc ); 201 s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc ); 202 s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc ); 203 s = set_add( &irc->set, "show_offline", "false", set_eval_bool, irc ); 204 s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc ); 205 s = set_add( &irc->set, "status", NULL, set_eval_away_status, irc ); 206 s->flags |= SET_NULL_OK; 207 s = set_add( &irc->set, "strip_html", "true", NULL, irc ); 208 s = set_add( &irc->set, "timezone", "local", set_eval_timezone, irc ); 209 s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc ); 210 s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); 117 s = set_add( &b->set, "private", "true", set_eval_bool, irc ); 118 s = set_add( &b->set, "query_order", "lifo", NULL, irc ); 119 s = set_add( &b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc ); 120 s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc ); 121 s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc ); 122 s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc ); 123 s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc ); 124 125 irc->root = iu = irc_user_new( irc, ROOT_NICK ); 126 iu->host = g_strdup( myhost ); 127 iu->fullname = g_strdup( ROOT_FN ); 128 iu->f = &irc_user_root_funcs; 129 130 iu = irc_user_new( irc, NS_NICK ); 131 iu->host = g_strdup( myhost ); 132 iu->fullname = g_strdup( ROOT_FN ); 133 iu->f = &irc_user_root_funcs; 134 135 irc->user = g_new0( irc_user_t, 1 ); 136 irc->user->host = g_strdup( host ); 211 137 212 138 conf_loaddefaults( irc ); 213 139 214 140 /* Evaluator sets the iconv/oconv structures. */ 215 set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) ); 216 217 return( irc ); 141 set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) ); 142 143 irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" ); 144 145 g_free( myhost ); 146 g_free( host ); 147 148 nogaim_init(); 149 150 return irc; 218 151 } 219 152 … … 236 169 237 170 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", 238 irc-> nick ? irc->nick : "(NONE)", irc->host, reason );171 irc->user->nick ? irc->user->nick : "(NONE)", irc->user->host, reason ); 239 172 240 173 g_free( reason ); … … 246 179 247 180 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", 248 irc-> nick ? irc->nick : "(NONE)", irc->host, "No reason given" );181 irc->user->nick ? irc->user->nick : "(NONE)", irc->user->host, "No reason given" ); 249 182 } 250 183 … … 267 200 } 268 201 269 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) 270 { 271 g_free( key ); 272 273 return( TRUE ); 274 } 275 276 /* Because we have no garbage collection, this is quite annoying */ 202 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ); 203 277 204 void irc_free( irc_t * irc ) 278 205 { 279 user_t *user, *usertmp;280 281 206 log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); 282 207 283 if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc-> set, "save_on_quit" ) )208 if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) ) 284 209 if( storage_save( irc, NULL, TRUE ) != STORAGE_OK ) 285 irc_usermsg( irc, "Error while saving settings!");210 log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick ); 286 211 287 212 irc_connection_list = g_slist_remove( irc_connection_list, irc ); 288 289 while( irc->accounts )290 {291 if( irc->accounts->ic )292 imc_logout( irc->accounts->ic, FALSE );293 else if( irc->accounts->reconnect )294 cancel_auto_reconnect( irc->accounts );295 296 if( irc->accounts->ic == NULL )297 account_del( irc, irc->accounts );298 else299 /* Nasty hack, but account_del() doesn't work in this300 case and we don't want infinite loops, do we? ;-) */301 irc->accounts = irc->accounts->next;302 }303 213 304 214 while( irc->queries != NULL ) 305 215 query_del( irc, irc->queries ); 306 216 307 while( irc->set ) 308 set_del( &irc->set, irc->set->key ); 309 310 if (irc->users != NULL) 311 { 312 user = irc->users; 313 while( user != NULL ) 314 { 315 g_free( user->nick ); 316 g_free( user->away ); 317 g_free( user->handle ); 318 if( user->user != user->nick ) g_free( user->user ); 319 if( user->host != user->nick ) g_free( user->host ); 320 if( user->realname != user->nick ) g_free( user->realname ); 321 b_event_remove( user->sendbuf_timer ); 322 323 usertmp = user; 324 user = user->next; 325 g_free( usertmp ); 326 } 327 } 217 /* This is a little bit messy: bee_free() frees all b->users which 218 calls us back to free the corresponding irc->users. So do this 219 before we clear the remaining ones ourselves. */ 220 bee_free( irc->b ); 221 222 while( irc->users ) 223 irc_user_free( irc, (irc_user_t *) irc->users->data ); 224 225 while( irc->channels ) 226 irc_channel_free( irc->channels->data ); 328 227 329 228 if( irc->ping_source_id > 0 ) … … 337 236 irc->fd = -1; 338 237 339 g_hash_table_foreach_remove( irc-> userhash, irc_free_hashkey, NULL );340 g_hash_table_destroy( irc-> userhash );238 g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL ); 239 g_hash_table_destroy( irc->nick_user_hash ); 341 240 342 241 g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL ); … … 350 249 g_free( irc->sendbuffer ); 351 250 g_free( irc->readbuffer ); 352 353 g_free( irc->nick );354 g_free( irc->user );355 g_free( irc->host );356 g_free( irc->realname );357 251 g_free( irc->password ); 358 359 g_free( irc->myhost ); 360 g_free( irc->mynick ); 361 362 g_free( irc->channel ); 363 364 g_free( irc->last_target ); 252 g_free( irc->last_root_cmd ); 365 253 366 254 g_free( irc ); … … 374 262 } 375 263 264 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) 265 { 266 g_free( key ); 267 268 return( TRUE ); 269 } 270 376 271 /* USE WITH CAUTION! 377 272 Sets pass without checking */ 378 void irc_setpass (irc_t *irc, const char *pass) 273 void irc_setpass (irc_t *irc, const char *pass) 379 274 { 380 275 g_free (irc->password); … … 387 282 } 388 283 284 static char *set_eval_password( set_t *set, char *value ) 285 { 286 irc_t *irc = set->data; 287 288 if( irc->status & USTATUS_IDENTIFIED && value ) 289 { 290 irc_setpass( irc, value ); 291 return NULL; 292 } 293 else 294 { 295 return SET_INVALID; 296 } 297 } 298 299 static char **irc_splitlines( char *buffer ); 300 389 301 void irc_process( irc_t *irc ) 390 302 { … … 394 306 if( irc->readbuffer != NULL ) 395 307 { 396 lines = irc_ tokenize( irc->readbuffer );308 lines = irc_splitlines( irc->readbuffer ); 397 309 398 310 for( i = 0; *lines[i] != '\0'; i ++ ) … … 431 343 "`help set charset' for more information. Your " 432 344 "message was ignored.", 433 set_getstr( &irc-> set, "charset" ) );345 set_getstr( &irc->b->set, "charset" ) ); 434 346 435 347 g_free( conv ); … … 438 350 else 439 351 { 440 irc_write( irc, ":%s NOTICE AUTH :%s", irc-> myhost,352 irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, 441 353 "Warning: invalid characters received at login time." ); 442 354 … … 476 388 } 477 389 478 /* Splits a long string into separate lines. The array is NULL-terminated and, unless the string 479 contains an incomplete line at the end, ends with an empty string. */ 480 char **irc_tokenize( char *buffer ) 390 /* Splits a long string into separate lines. The array is NULL-terminated 391 and, unless the string contains an incomplete line at the end, ends with 392 an empty string. Could use g_strsplit() but this one does it in-place. 393 (So yes, it's destructive.) */ 394 static char **irc_splitlines( char *buffer ) 481 395 { 482 396 int i, j, n = 3; … … 609 523 } 610 524 611 void irc_reply( irc_t *irc, int code, char *format, ... )612 {613 char text[IRC_MAX_LINE];614 va_list params;615 616 va_start( params, format );617 g_vsnprintf( text, IRC_MAX_LINE, format, params );618 va_end( params );619 irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );620 621 return;622 }623 624 int irc_usermsg( irc_t *irc, char *format, ... )625 {626 char text[1024];627 va_list params;628 char is_private = 0;629 user_t *u;630 631 u = user_find( irc, irc->mynick );632 is_private = u->is_private;633 634 va_start( params, format );635 g_vsnprintf( text, sizeof( text ), format, params );636 va_end( params );637 638 return( irc_msgfrom( irc, u->nick, text ) );639 }640 641 525 void irc_write( irc_t *irc, char *format, ... ) 642 526 { … … 649 533 return; 650 534 } 535 536 void irc_write_all( int now, char *format, ... ) 537 { 538 va_list params; 539 GSList *temp; 540 541 va_start( params, format ); 542 543 temp = irc_connection_list; 544 while( temp != NULL ) 545 { 546 irc_t *irc = temp->data; 547 548 if( now ) 549 { 550 g_free( irc->sendbuffer ); 551 irc->sendbuffer = g_strdup( "\r\n" ); 552 } 553 irc_vawrite( temp->data, format, params ); 554 if( now ) 555 { 556 bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ); 557 } 558 temp = temp->next; 559 } 560 561 va_end( params ); 562 return; 563 } 651 564 652 565 void irc_vawrite( irc_t *irc, char *format, va_list params ) … … 696 609 in the event queue. */ 697 610 /* Really can't be done as long as the code doesn't do error checking very well: 698 if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */611 if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */ 699 612 700 613 /* So just always do it via the event handler. */ 701 irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );614 irc->w_watch_source_id = b_input_add( irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc ); 702 615 } 703 616 … … 705 618 } 706 619 707 void irc_write_all( int now, char *format, ... ) 708 { 709 va_list params; 710 GSList *temp; 711 712 va_start( params, format ); 713 714 temp = irc_connection_list; 715 while( temp != NULL ) 716 { 717 irc_t *irc = temp->data; 718 719 if( now ) 720 { 721 g_free( irc->sendbuffer ); 722 irc->sendbuffer = g_strdup( "\r\n" ); 723 } 724 irc_vawrite( temp->data, format, params ); 725 if( now ) 726 { 727 bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ); 728 } 729 temp = temp->next; 730 } 731 732 va_end( params ); 733 return; 734 } 735 736 void irc_names( irc_t *irc, char *channel ) 737 { 738 user_t *u; 739 char namelist[385] = ""; 740 struct groupchat *c = NULL; 741 char *ops = set_getstr( &irc->set, "ops" ); 742 743 /* RFCs say there is no error reply allowed on NAMES, so when the 744 channel is invalid, just give an empty reply. */ 745 746 if( g_strcasecmp( channel, irc->channel ) == 0 ) 747 { 748 for( u = irc->users; u; u = u->next ) if( u->online ) 749 { 750 if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 ) 620 int irc_check_login( irc_t *irc ) 621 { 622 if( irc->user->user && irc->user->nick ) 623 { 624 if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) ) 625 { 626 irc_send_num( irc, 464, ":This server is password-protected." ); 627 return 0; 628 } 629 else 630 { 631 irc_channel_t *ic; 632 irc_user_t *iu = irc->user; 633 634 irc->user = irc_user_new( irc, iu->nick ); 635 irc->user->user = iu->user; 636 irc->user->host = iu->host; 637 irc->user->fullname = iu->fullname; 638 irc->user->f = &irc_user_self_funcs; 639 g_free( iu->nick ); 640 g_free( iu ); 641 642 if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) 643 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); 644 645 irc->status |= USTATUS_LOGGED_IN; 646 647 irc_send_login( irc ); 648 649 irc->umode[0] = '\0'; 650 irc_umode_set( irc, "+" UMODE, TRUE ); 651 652 ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN ); 653 irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); 654 irc_channel_add_user( ic, irc->user ); 655 656 irc->last_root_cmd = g_strdup( ROOT_CHAN ); 657 658 irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN, 659 "Welcome to the BitlBee gateway!\n\n" 660 "If you've never used BitlBee before, please do read the help " 661 "information using the \x02help\x02 command. Lots of FAQs are " 662 "answered there.\n" 663 "If you already have an account on this server, just use the " 664 "\x02identify\x02 command to identify yourself.", NULL ); 665 666 /* This is for bug #209 (use PASS to identify to NickServ). */ 667 if( irc->password != NULL ) 751 668 { 752 irc_reply( irc, 353, "= %s :%s", channel, namelist ); 753 *namelist = 0; 669 char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; 670 671 irc_setpass( irc, NULL ); 672 root_command( irc, send_cmd ); 673 g_free( send_cmd[1] ); 754 674 } 755 675 756 if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )757 strcat( namelist, "+" );758 else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||759 ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )760 strcat( namelist, "@" );761 762 strcat( namelist, u->nick );763 strcat( namelist, " " );764 }765 }766 else if( ( c = irc_chat_by_channel( irc, channel ) ) )767 {768 GList *l;769 770 /* root and the user aren't in the channel userlist but should771 show up in /NAMES, so list them first: */772 sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick,773 strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick );774 775 for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )776 {777 if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )778 {779 irc_reply( irc, 353, "= %s :%s", channel, namelist );780 *namelist = 0;781 }782 783 strcat( namelist, u->nick );784 strcat( namelist, " " );785 }786 }787 788 if( *namelist )789 irc_reply( irc, 353, "= %s :%s", channel, namelist );790 791 irc_reply( irc, 366, "%s :End of /NAMES list", channel );792 }793 794 int irc_check_login( irc_t *irc )795 {796 if( irc->user && irc->nick )797 {798 if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )799 {800 irc_reply( irc, 464, ":This server is password-protected." );801 return 0;802 }803 else804 {805 irc_login( irc );806 676 return 1; 807 677 } … … 814 684 } 815 685 816 void irc_login( irc_t *irc ) 817 { 818 user_t *u; 819 820 irc_reply( irc, 1, ":Welcome to the BitlBee gateway, %s", irc->nick ); 821 irc_reply( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost ); 822 irc_reply( irc, 3, ":%s", IRCD_INFO ); 823 irc_reply( irc, 4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES ); 824 irc_reply( irc, 5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee " 825 "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", 826 CTYPES, CMODES, MAX_NICK_LENGTH - 1 ); 827 irc_motd( irc ); 828 irc->umode[0] = '\0'; 829 irc_umode_set( irc, "+" UMODE, 1 ); 830 831 u = user_add( irc, irc->mynick ); 832 u->host = g_strdup( irc->myhost ); 833 u->realname = g_strdup( ROOT_FN ); 834 u->online = 1; 835 u->send_handler = root_command_string; 836 u->is_private = 0; /* [SH] The channel is root's personal playground. */ 837 irc_spawn( irc, u ); 838 839 u = user_add( irc, NS_NICK ); 840 u->host = g_strdup( irc->myhost ); 841 u->realname = g_strdup( ROOT_FN ); 842 u->online = 0; 843 u->send_handler = root_command_string; 844 u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */ 845 846 u = user_add( irc, irc->nick ); 847 u->user = g_strdup( irc->user ); 848 u->host = g_strdup( irc->host ); 849 u->realname = g_strdup( irc->realname ); 850 u->online = 1; 851 irc_spawn( irc, u ); 852 853 irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n" 854 "If you've never used BitlBee before, please do read the help " 855 "information using the \x02help\x02 command. Lots of FAQs are " 856 "answered there.\n" 857 "If you already have an account on this server, just use the " 858 "\x02identify\x02 command to identify yourself." ); 859 860 if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) 861 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname ); 862 863 irc->status |= USTATUS_LOGGED_IN; 864 865 /* This is for bug #209 (use PASS to identify to NickServ). */ 866 if( irc->password != NULL ) 867 { 868 char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; 869 870 irc_setpass( irc, NULL ); 871 root_command( irc, send_cmd ); 872 g_free( send_cmd[1] ); 873 } 874 } 875 876 void irc_motd( irc_t *irc ) 877 { 878 int fd; 879 880 fd = open( global.conf->motdfile, O_RDONLY ); 881 if( fd == -1 ) 882 { 883 irc_reply( irc, 422, ":We don't need MOTDs." ); 884 } 885 else 886 { 887 char linebuf[80]; /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */ 888 char *add, max; 889 int len; 890 891 linebuf[79] = len = 0; 892 max = sizeof( linebuf ) - 1; 893 894 irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost ); 895 while( read( fd, linebuf + len, 1 ) == 1 ) 896 { 897 if( linebuf[len] == '\n' || len == max ) 898 { 899 linebuf[len] = 0; 900 irc_reply( irc, 372, ":- %s", linebuf ); 901 len = 0; 902 } 903 else if( linebuf[len] == '%' ) 904 { 905 read( fd, linebuf + len, 1 ); 906 if( linebuf[len] == 'h' ) 907 add = irc->myhost; 908 else if( linebuf[len] == 'v' ) 909 add = BITLBEE_VERSION; 910 else if( linebuf[len] == 'n' ) 911 add = irc->nick; 912 else 913 add = "%"; 914 915 strncpy( linebuf + len, add, max - len ); 916 while( linebuf[++len] ); 917 } 918 else if( len < max ) 919 { 920 len ++; 921 } 922 } 923 irc_reply( irc, 376, ":End of MOTD" ); 924 close( fd ); 925 } 926 } 927 928 void irc_topic( irc_t *irc, char *channel ) 929 { 930 struct groupchat *c = irc_chat_by_channel( irc, channel ); 931 932 if( c && c->topic ) 933 irc_reply( irc, 332, "%s :%s", channel, c->topic ); 934 else if( g_strcasecmp( channel, irc->channel ) == 0 ) 935 irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC ); 936 else 937 irc_reply( irc, 331, "%s :No topic for this channel", channel ); 938 } 939 940 void irc_umode_set( irc_t *irc, char *s, int allow_priv ) 686 void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv ) 941 687 { 942 688 /* allow_priv: Set to 0 if s contains user input, 1 if you want 943 689 to set a "privileged" mode (+o, +R, etc). */ 944 char m[256], st = 1, *t; 690 char m[128], st = 1; 691 const char *t; 945 692 int i; 946 693 char changes[512], *p, st2 = 2; … … 950 697 951 698 for( t = irc->umode; *t; t ++ ) 952 m[(int)*t] = 1; 953 699 if( *t < sizeof( m ) ) 700 m[(int)*t] = 1; 701 954 702 p = changes; 955 703 for( t = s; *t; t ++ ) … … 957 705 if( *t == '+' || *t == '-' ) 958 706 st = *t == '+'; 959 else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) ) 707 else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) || 708 ( st == 1 && strchr( UMODES, *t ) ) || 709 ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) ) 960 710 { 961 711 if( m[(int)*t] != st) … … 974 724 memset( irc->umode, 0, sizeof( irc->umode ) ); 975 725 976 for( i = 0; i < 256&& strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )726 for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ ) 977 727 if( m[i] ) 978 728 irc->umode[strlen(irc->umode)] = i; 979 729 980 730 if( badflag ) 981 irc_reply( irc, 501, ":Unknown MODE flag" ); 982 /* Deliberately no !user@host on the prefix here */ 731 irc_send_num( irc, 501, ":Unknown MODE flag" ); 983 732 if( *changes ) 984 irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes ); 985 } 986 987 void irc_spawn( irc_t *irc, user_t *u ) 988 { 989 irc_join( irc, u, irc->channel ); 990 } 991 992 void irc_join( irc_t *irc, user_t *u, char *channel ) 993 { 994 char *nick; 995 996 if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) ) 997 irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel ); 998 999 if( nick_cmp( u->nick, irc->nick ) == 0 ) 1000 { 1001 irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE ); 1002 irc_names( irc, channel ); 1003 irc_topic( irc, channel ); 1004 } 1005 1006 nick = g_strdup( u->nick ); 1007 nick_lc( nick ); 1008 if( g_hash_table_lookup( irc->watches, nick ) ) 1009 { 1010 irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" ); 1011 } 1012 g_free( nick ); 1013 } 1014 1015 void irc_part( irc_t *irc, user_t *u, char *channel ) 1016 { 1017 irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" ); 1018 } 1019 1020 void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker ) 1021 { 1022 irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" ); 1023 } 1024 1025 void irc_kill( irc_t *irc, user_t *u ) 1026 { 1027 char *nick, *s; 1028 char reason[128]; 1029 1030 if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) ) 1031 { 1032 if( u->ic->acc->server ) 1033 g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, 1034 u->ic->acc->server ); 1035 else if( ( s = strchr( u->ic->acc->user, '@' ) ) ) 1036 g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, 1037 s + 1 ); 1038 else 1039 g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost, 1040 u->ic->acc->prpl->name, irc->myhost ); 1041 1042 /* proto_opt might contain garbage after the : */ 1043 if( ( s = strchr( reason, ':' ) ) ) 1044 *s = 0; 1045 } 1046 else 1047 { 1048 strcpy( reason, "Leaving..." ); 1049 } 1050 1051 irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason ); 1052 1053 nick = g_strdup( u->nick ); 1054 nick_lc( nick ); 1055 if( g_hash_table_lookup( irc->watches, nick ) ) 1056 { 1057 irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" ); 1058 } 1059 g_free( nick ); 1060 } 1061 1062 int irc_send( irc_t *irc, char *nick, char *s, int flags ) 1063 { 1064 struct groupchat *c = NULL; 1065 user_t *u = NULL; 1066 1067 if( strchr( CTYPES, *nick ) ) 1068 { 1069 if( !( c = irc_chat_by_channel( irc, nick ) ) ) 1070 { 1071 irc_reply( irc, 403, "%s :Channel does not exist", nick ); 1072 return( 0 ); 1073 } 1074 } 1075 else 1076 { 1077 u = user_find( irc, nick ); 1078 1079 if( !u ) 1080 { 1081 if( irc->is_private ) 1082 irc_reply( irc, 401, "%s :Nick does not exist", nick ); 1083 else 1084 irc_usermsg( irc, "Nick `%s' does not exist!", nick ); 1085 return( 0 ); 1086 } 1087 } 1088 1089 if( *s == 1 && s[strlen(s)-1] == 1 ) 1090 { 1091 if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 ) 1092 { 1093 if( s[7] == ' ' ) s ++; 1094 s += 3; 1095 *(s++) = '/'; 1096 *(s++) = 'm'; 1097 *(s++) = 'e'; 1098 *(s++) = ' '; 1099 s -= 4; 1100 s[strlen(s)-1] = 0; 1101 } 1102 else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 ) 1103 { 1104 u = user_find( irc, irc->mynick ); 1105 irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" ); 1106 return( 1 ); 1107 } 1108 else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 ) 1109 { 1110 u = user_find( irc, irc->mynick ); 1111 irc_privmsg( irc, u, "NOTICE", irc->nick, "", s ); 1112 return( 1 ); 1113 } 1114 else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 ) 1115 { 1116 if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 ) 1117 { 1118 time_t current_typing_notice = time( NULL ); 1119 1120 if( current_typing_notice - u->last_typing_notice >= 5 ) 1121 { 1122 u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 ); 1123 u->last_typing_notice = current_typing_notice; 1124 } 1125 } 1126 return( 1 ); 1127 } 1128 else 1129 { 1130 irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" ); 1131 return( 0 ); 1132 } 1133 } 1134 1135 if( u ) 1136 { 1137 /* For the next message, we probably do have to send new notices... */ 1138 u->last_typing_notice = 0; 1139 u->is_private = irc->is_private; 1140 1141 if( u->is_private ) 1142 { 1143 if( !u->online ) 1144 irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" ); 1145 else if( u->away ) 1146 irc_reply( irc, 301, "%s :%s", u->nick, u->away ); 1147 } 1148 1149 if( u->send_handler ) 1150 { 1151 u->send_handler( irc, u, s, flags ); 1152 return 1; 1153 } 1154 } 1155 else if( c && c->ic && c->ic->acc && c->ic->acc->prpl ) 1156 { 1157 return( imc_chat_msg( c, s, 0 ) ); 1158 } 1159 1160 return( 0 ); 1161 } 1162 1163 static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond ) 1164 { 1165 user_t *u = data; 1166 1167 /* Shouldn't happen, but just to be sure. */ 1168 if( u->sendbuf_len < 2 ) 1169 return FALSE; 1170 1171 u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */ 1172 imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags ); 1173 1174 g_free( u->sendbuf ); 1175 u->sendbuf = NULL; 1176 u->sendbuf_len = 0; 1177 u->sendbuf_timer = 0; 1178 u->sendbuf_flags = 0; 1179 1180 return FALSE; 1181 } 1182 1183 void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) 1184 { 1185 if( !u || !u->ic ) return; 1186 1187 if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 ) 1188 { 1189 int delay; 1190 1191 if( u->sendbuf_len > 0 && u->sendbuf_flags != flags) 1192 { 1193 /* Flush the buffer */ 1194 b_event_remove( u->sendbuf_timer ); 1195 buddy_send_handler_delayed( u, -1, 0 ); 1196 } 1197 1198 if( u->sendbuf_len == 0 ) 1199 { 1200 u->sendbuf_len = strlen( msg ) + 2; 1201 u->sendbuf = g_new( char, u->sendbuf_len ); 1202 u->sendbuf[0] = 0; 1203 u->sendbuf_flags = flags; 1204 } 1205 else 1206 { 1207 u->sendbuf_len += strlen( msg ) + 1; 1208 u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len ); 1209 } 1210 1211 strcat( u->sendbuf, msg ); 1212 strcat( u->sendbuf, "\n" ); 1213 1214 delay = set_getint( &irc->set, "buddy_sendbuffer_delay" ); 1215 if( delay <= 5 ) 1216 delay *= 1000; 1217 1218 if( u->sendbuf_timer > 0 ) 1219 b_event_remove( u->sendbuf_timer ); 1220 u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u ); 1221 } 1222 else 1223 { 1224 imc_buddy_msg( u->ic, u->handle, msg, flags ); 1225 } 1226 } 1227 1228 int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ) 1229 { 1230 char last = 0; 1231 char *s = msg, *line = msg; 1232 1233 /* The almighty linesplitter .. woohoo!! */ 1234 while( !last ) 1235 { 1236 if( *s == '\r' && *(s+1) == '\n' ) 1237 *(s++) = 0; 1238 if( *s == '\n' ) 1239 { 1240 last = s[1] == 0; 1241 *s = 0; 1242 } 1243 else 1244 { 1245 last = s[0] == 0; 1246 } 1247 if( *s == 0 ) 1248 { 1249 if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 ) 1250 { 1251 irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host, 1252 type, to, line + 4 ); 1253 } 1254 else 1255 { 1256 irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host, 1257 type, to, prefix ? prefix : "", line ); 1258 } 1259 line = s + 1; 1260 } 1261 s ++; 1262 } 1263 1264 return( 1 ); 1265 } 1266 1267 int irc_msgfrom( irc_t *irc, char *nick, char *msg ) 1268 { 1269 user_t *u = user_find( irc, nick ); 1270 static char *prefix = NULL; 1271 1272 if( !u ) return( 0 ); 1273 if( prefix && *prefix ) g_free( prefix ); 1274 1275 if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 ) 1276 { 1277 int len = strlen( irc->nick) + 3; 1278 prefix = g_new (char, len ); 1279 g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) ); 1280 prefix[len-1] = 0; 1281 } 1282 else 1283 { 1284 prefix = ""; 1285 } 1286 1287 return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) ); 1288 } 1289 1290 int irc_noticefrom( irc_t *irc, char *nick, char *msg ) 1291 { 1292 user_t *u = user_find( irc, nick ); 1293 1294 if( u ) 1295 return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) ); 1296 else 1297 return( 0 ); 733 irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick, 734 irc->user->user, irc->user->host, irc->user->nick, 735 changes ); 1298 736 } 1299 737 … … 1334 772 } 1335 773 1336 struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel ) 1337 { 1338 struct groupchat *c; 1339 account_t *a; 1340 1341 /* This finds the connection which has a conversation which belongs to this channel */ 1342 for( a = irc->accounts; a; a = a->next ) 1343 { 1344 if( a->ic == NULL ) 1345 continue; 1346 1347 c = a->ic->groupchats; 1348 while( c ) 1349 { 1350 if( c->channel && g_strcasecmp( c->channel, channel ) == 0 ) 1351 return c; 1352 1353 c = c->next; 1354 } 1355 } 1356 1357 return NULL; 1358 } 774 static char *set_eval_charset( set_t *set, char *value ) 775 { 776 irc_t *irc = (irc_t*) set->data; 777 char *test; 778 gsize test_bytes = 0; 779 GIConv ic, oc; 780 781 if( g_strcasecmp( value, "none" ) == 0 ) 782 value = g_strdup( "utf-8" ); 783 784 if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) 785 { 786 return NULL; 787 } 788 789 /* Do a test iconv to see if the user picked an IRC-compatible 790 charset (for example utf-16 goes *horribly* wrong). */ 791 if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL || 792 test_bytes > 1 ) 793 { 794 g_free( test ); 795 g_iconv_close( oc ); 796 irc_usermsg( irc, "Unsupported character set: The IRC protocol " 797 "only supports 8-bit character sets." ); 798 return NULL; 799 } 800 g_free( test ); 801 802 if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) 803 { 804 g_iconv_close( oc ); 805 return NULL; 806 } 807 808 if( irc->iconv != (GIConv) -1 ) 809 g_iconv_close( irc->iconv ); 810 if( irc->oconv != (GIConv) -1 ) 811 g_iconv_close( irc->oconv ); 812 813 irc->iconv = ic; 814 irc->oconv = oc; 815 816 return value; 817 } 818 819 char *set_eval_away_devoice( set_t *set, char *value ) 820 { 821 irc_t *irc = set->data; 822 823 if( !is_bool( value ) ) 824 return SET_INVALID; 825 826 /* The usual problem: The setting isn't actually changed at this 827 point and we need it to be, so do it by hand. */ 828 g_free( set->value ); 829 set->value = g_strdup( value ); 830 831 bee_irc_channel_update( irc, NULL, NULL ); 832 833 return value; 834 }
Note: See TracChangeset
for help on using the changeset viewer.