Changes in irc.c [839189b:0a6e5d1]
Legend:
- Unmodified
- Added
- Removed
-
irc.c
r839189b r0a6e5d1 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, "charset", "utf-8", set_eval_charset, irc ); 104 s = set_add( &b->set, "default_target", "root", NULL, irc ); 105 s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc ); 106 s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc ); 107 s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc ); 108 s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); 109 s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc ); 110 s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc ); 111 s->old_key = g_strdup( "buddy_sendbuffer" ); 112 s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc ); 113 s->old_key = g_strdup( "buddy_sendbuffer_delay" ); 114 s = set_add( &b->set, "password", NULL, set_eval_password, irc ); 181 115 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 ); 116 s = set_add( &b->set, "private", "true", set_eval_bool, irc ); 117 s = set_add( &b->set, "query_order", "lifo", NULL, irc ); 118 s = set_add( &b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc ); 119 s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc ); 120 s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc ); 121 s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc ); 122 s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc ); 123 124 irc->root = iu = irc_user_new( irc, ROOT_NICK ); 125 iu->host = g_strdup( myhost ); 126 iu->fullname = g_strdup( ROOT_FN ); 127 iu->f = &irc_user_root_funcs; 128 129 iu = irc_user_new( irc, NS_NICK ); 130 iu->host = g_strdup( myhost ); 131 iu->fullname = g_strdup( ROOT_FN ); 132 iu->f = &irc_user_root_funcs; 133 134 irc->user = g_new0( irc_user_t, 1 ); 135 irc->user->host = g_strdup( host ); 211 136 212 137 conf_loaddefaults( irc ); 213 138 214 139 /* Evaluator sets the iconv/oconv structures. */ 215 set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) ); 216 217 return( irc ); 140 set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) ); 141 142 irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" ); 143 144 g_free( myhost ); 145 g_free( host ); 146 147 nogaim_init(); 148 149 return irc; 218 150 } 219 151 … … 236 168 237 169 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", 238 irc-> nick ? irc->nick : "(NONE)", irc->host, reason );170 irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, reason ); 239 171 240 172 g_free( reason ); … … 246 178 247 179 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", 248 irc-> nick ? irc->nick : "(NONE)", irc->host, "No reason given" );180 irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, "No reason given" ); 249 181 } 250 182 … … 267 199 } 268 200 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 */ 201 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ); 202 277 203 void irc_free( irc_t * irc ) 278 204 { 279 user_t *user, *usertmp;280 281 205 log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); 282 206 283 if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc-> set, "save_on_quit" ) )207 if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) ) 284 208 if( storage_save( irc, NULL, TRUE ) != STORAGE_OK ) 285 irc_usermsg( irc, "Error while saving settings!");209 log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick ); 286 210 287 211 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 212 304 213 while( irc->queries != NULL ) 305 214 query_del( irc, irc->queries ); 306 215 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 } 216 /* This is a little bit messy: bee_free() frees all b->users which 217 calls us back to free the corresponding irc->users. So do this 218 before we clear the remaining ones ourselves. */ 219 bee_free( irc->b ); 220 221 while( irc->users ) 222 irc_user_free( irc, (irc_user_t *) irc->users->data ); 223 224 while( irc->channels ) 225 irc_channel_free( irc->channels->data ); 328 226 329 227 if( irc->ping_source_id > 0 ) … … 337 235 irc->fd = -1; 338 236 339 g_hash_table_foreach_remove( irc-> userhash, irc_free_hashkey, NULL );340 g_hash_table_destroy( irc-> userhash );237 g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL ); 238 g_hash_table_destroy( irc->nick_user_hash ); 341 239 342 240 g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL ); … … 350 248 g_free( irc->sendbuffer ); 351 249 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 250 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 ); 251 g_free( irc->last_root_cmd ); 365 252 366 253 g_free( irc ); … … 374 261 } 375 262 263 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) 264 { 265 g_free( key ); 266 267 return( TRUE ); 268 } 269 376 270 /* USE WITH CAUTION! 377 271 Sets pass without checking */ 378 void irc_setpass (irc_t *irc, const char *pass) 272 void irc_setpass (irc_t *irc, const char *pass) 379 273 { 380 274 g_free (irc->password); … … 387 281 } 388 282 283 static char *set_eval_password( set_t *set, char *value ) 284 { 285 irc_t *irc = set->data; 286 287 if( irc->status & USTATUS_IDENTIFIED && value ) 288 { 289 irc_setpass( irc, value ); 290 return NULL; 291 } 292 else 293 { 294 return SET_INVALID; 295 } 296 } 297 298 static char **irc_splitlines( char *buffer ); 299 389 300 void irc_process( irc_t *irc ) 390 301 { … … 394 305 if( irc->readbuffer != NULL ) 395 306 { 396 lines = irc_ tokenize( irc->readbuffer );307 lines = irc_splitlines( irc->readbuffer ); 397 308 398 309 for( i = 0; *lines[i] != '\0'; i ++ ) … … 431 342 "`help set charset' for more information. Your " 432 343 "message was ignored.", 433 set_getstr( &irc-> set, "charset" ) );344 set_getstr( &irc->b->set, "charset" ) ); 434 345 435 346 g_free( conv ); … … 438 349 else 439 350 { 440 irc_write( irc, ":%s NOTICE AUTH :%s", irc-> myhost,351 irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, 441 352 "Warning: invalid characters received at login time." ); 442 353 … … 476 387 } 477 388 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 ) 389 /* Splits a long string into separate lines. The array is NULL-terminated 390 and, unless the string contains an incomplete line at the end, ends with 391 an empty string. Could use g_strsplit() but this one does it in-place. 392 (So yes, it's destructive.) */ 393 static char **irc_splitlines( char *buffer ) 481 394 { 482 395 int i, j, n = 3; … … 609 522 } 610 523 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 524 void irc_write( irc_t *irc, char *format, ... ) 642 525 { … … 649 532 return; 650 533 } 534 535 void irc_write_all( int now, char *format, ... ) 536 { 537 va_list params; 538 GSList *temp; 539 540 va_start( params, format ); 541 542 temp = irc_connection_list; 543 while( temp != NULL ) 544 { 545 irc_t *irc = temp->data; 546 547 if( now ) 548 { 549 g_free( irc->sendbuffer ); 550 irc->sendbuffer = g_strdup( "\r\n" ); 551 } 552 irc_vawrite( temp->data, format, params ); 553 if( now ) 554 { 555 bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ); 556 } 557 temp = temp->next; 558 } 559 560 va_end( params ); 561 return; 562 } 651 563 652 564 void irc_vawrite( irc_t *irc, char *format, va_list params ) … … 696 608 in the event queue. */ 697 609 /* 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 ) ) */610 if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */ 699 611 700 612 /* 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 );613 irc->w_watch_source_id = b_input_add( irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc ); 702 614 } 703 615 … … 705 617 } 706 618 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 ) 619 int irc_check_login( irc_t *irc ) 620 { 621 if( irc->user->user && irc->user->nick ) 622 { 623 if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) ) 624 { 625 irc_send_num( irc, 464, ":This server is password-protected." ); 626 return 0; 627 } 628 else 629 { 630 irc_channel_t *ic; 631 irc_user_t *iu = irc->user; 632 633 irc->user = irc_user_new( irc, iu->nick ); 634 irc->user->user = iu->user; 635 irc->user->host = iu->host; 636 irc->user->fullname = iu->fullname; 637 irc->user->f = &irc_user_self_funcs; 638 g_free( iu->nick ); 639 g_free( iu ); 640 641 if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) 642 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); 643 644 irc->status |= USTATUS_LOGGED_IN; 645 646 irc_send_login( irc ); 647 648 irc->umode[0] = '\0'; 649 irc_umode_set( irc, "+" UMODE, TRUE ); 650 651 ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN ); 652 irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); 653 irc_channel_add_user( ic, irc->user ); 654 655 irc->last_root_cmd = g_strdup( ROOT_CHAN ); 656 657 irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN, 658 "Welcome to the BitlBee gateway!\n\n" 659 "If you've never used BitlBee before, please do read the help " 660 "information using the \x02help\x02 command. Lots of FAQs are " 661 "answered there.\n" 662 "If you already have an account on this server, just use the " 663 "\x02identify\x02 command to identify yourself.", NULL ); 664 665 /* This is for bug #209 (use PASS to identify to NickServ). */ 666 if( irc->password != NULL ) 751 667 { 752 irc_reply( irc, 353, "= %s :%s", channel, namelist ); 753 *namelist = 0; 668 char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; 669 670 irc_setpass( irc, NULL ); 671 root_command( irc, send_cmd ); 672 g_free( send_cmd[1] ); 754 673 } 755 674 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 675 return 1; 807 676 } … … 814 683 } 815 684 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 ) 685 void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv ) 941 686 { 942 687 /* allow_priv: Set to 0 if s contains user input, 1 if you want 943 688 to set a "privileged" mode (+o, +R, etc). */ 944 char m[256], st = 1, *t; 689 char m[128], st = 1; 690 const char *t; 945 691 int i; 946 692 char changes[512], *p, st2 = 2; … … 950 696 951 697 for( t = irc->umode; *t; t ++ ) 952 m[(int)*t] = 1; 953 698 if( *t < sizeof( m ) ) 699 m[(int)*t] = 1; 700 954 701 p = changes; 955 702 for( t = s; *t; t ++ ) … … 957 704 if( *t == '+' || *t == '-' ) 958 705 st = *t == '+'; 959 else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) ) 706 else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) || 707 ( st == 1 && strchr( UMODES, *t ) ) || 708 ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) ) 960 709 { 961 710 if( m[(int)*t] != st) … … 974 723 memset( irc->umode, 0, sizeof( irc->umode ) ); 975 724 976 for( i = 0; i < 256&& strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )725 for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ ) 977 726 if( m[i] ) 978 727 irc->umode[strlen(irc->umode)] = i; 979 728 980 729 if( badflag ) 981 irc_reply( irc, 501, ":Unknown MODE flag" ); 982 /* Deliberately no !user@host on the prefix here */ 730 irc_send_num( irc, 501, ":Unknown MODE flag" ); 983 731 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 ); 732 irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick, 733 irc->user->user, irc->user->host, irc->user->nick, 734 changes ); 1298 735 } 1299 736 … … 1334 771 } 1335 772 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 } 773 static char *set_eval_charset( set_t *set, char *value ) 774 { 775 irc_t *irc = (irc_t*) set->data; 776 char *test; 777 gsize test_bytes = 0; 778 GIConv ic, oc; 779 780 if( g_strcasecmp( value, "none" ) == 0 ) 781 value = g_strdup( "utf-8" ); 782 783 if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) 784 { 785 return NULL; 786 } 787 788 /* Do a test iconv to see if the user picked an IRC-compatible 789 charset (for example utf-16 goes *horribly* wrong). */ 790 if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL || 791 test_bytes > 1 ) 792 { 793 g_free( test ); 794 g_iconv_close( oc ); 795 irc_usermsg( irc, "Unsupported character set: The IRC protocol " 796 "only supports 8-bit character sets." ); 797 return NULL; 798 } 799 g_free( test ); 800 801 if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) 802 { 803 g_iconv_close( oc ); 804 return NULL; 805 } 806 807 if( irc->iconv != (GIConv) -1 ) 808 g_iconv_close( irc->iconv ); 809 if( irc->oconv != (GIConv) -1 ) 810 g_iconv_close( irc->oconv ); 811 812 irc->iconv = ic; 813 irc->oconv = oc; 814 815 return value; 816 } 817 818 char *set_eval_away_devoice( set_t *set, char *value ) 819 { 820 irc_t *irc = set->data; 821 822 if( !is_bool( value ) ) 823 return SET_INVALID; 824 825 /* The usual problem: The setting isn't actually changed at this 826 point and we need it to be, so do it by hand. */ 827 g_free( set->value ); 828 set->value = g_strdup( value ); 829 830 bee_irc_channel_update( irc, NULL, NULL ); 831 832 return value; 833 }
Note: See TracChangeset
for help on using the changeset viewer.