Changes in irc.c [6a9d068:839189b]
Legend:
- Unmodified
- Added
- Removed
-
irc.c
r6a9d068 r839189b 5 5 \********************************************************************/ 6 6 7 /* The IRC-based UI (for now the only one)*/7 /* The big hairy IRCd part of the project */ 8 8 9 9 /* … … 24 24 */ 25 25 26 #define BITLBEE_CORE 26 27 #include "bitlbee.h" 28 #include "sock.h" 29 #include "crypting.h" 27 30 #include "ipc.h" 28 31 29 GSList *irc_connection_list; 30 31 static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ); 32 static char *set_eval_charset( set_t *set, char *value ); 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 } 33 114 34 115 irc_t *irc_new( int fd ) … … 37 118 struct sockaddr_storage sock; 38 119 socklen_t socklen = sizeof( sock ); 39 char *host = NULL, *myhost = NULL;40 irc_user_t *iu;41 120 set_t *s; 42 bee_t *b;43 121 44 122 irc = g_new0( irc_t, 1 ); … … 52 130 irc->last_pong = gettime(); 53 131 54 irc-> nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal );132 irc->userhash = g_hash_table_new( g_str_hash, g_str_equal ); 55 133 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 ); 56 138 57 139 irc->iconv = (GIConv) -1; … … 60 142 if( global.conf->hostname ) 61 143 { 62 myhost = g_strdup( global.conf->hostname );144 irc->myhost = g_strdup( global.conf->hostname ); 63 145 } 64 146 else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) … … 69 151 NI_MAXHOST, NULL, 0, 0 ) == 0 ) 70 152 { 71 myhost = g_strdup( ipv6_unwrap( buf ) );153 irc->myhost = g_strdup( ipv6_unwrap( buf ) ); 72 154 } 73 155 } … … 80 162 NI_MAXHOST, NULL, 0, 0 ) == 0 ) 81 163 { 82 host = g_strdup( ipv6_unwrap( buf ) );83 } 84 } 85 86 if( host == NULL )87 host = g_strdup( "localhost.localdomain" );88 if( myhost == NULL )89 myhost = g_strdup( "localhost.localdomain" );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" ); 90 172 91 173 if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) 92 174 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" ); 93 177 94 178 irc_connection_list = g_slist_append( irc_connection_list, irc ); 95 179 96 b = irc->b = bee_new(); 97 b->ui_data = irc; 98 b->ui = &irc_ui_funcs; 99 100 s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc ); 101 s = set_add( &b->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); 102 s = set_add( &b->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); 103 s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); 104 //s = set_add( &b->set, "control_channel", irc->channel, NULL/*set_eval_control_channel*/, 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", "root", NULL, irc ); 109 s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); 110 s = set_add( &b->set, "ops", "both", NULL/*set_eval_ops*/, irc ); 111 s = set_add( &b->set, "private", "true", set_eval_bool, irc ); 112 s = set_add( &b->set, "query_order", "lifo", NULL, irc ); 113 s = set_add( &b->set, "root_nick", ROOT_NICK, NULL/*set_eval_root_nick*/, irc ); 114 s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc ); 115 s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc ); 116 s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc ); 117 s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc ); 118 119 irc->root = iu = irc_user_new( irc, ROOT_NICK ); 120 iu->host = g_strdup( myhost ); 121 iu->fullname = g_strdup( ROOT_FN ); 122 iu->f = &irc_user_root_funcs; 123 124 iu = irc_user_new( irc, NS_NICK ); 125 iu->host = g_strdup( myhost ); 126 iu->fullname = g_strdup( ROOT_FN ); 127 iu->f = &irc_user_root_funcs; 128 129 irc->user = g_new0( irc_user_t, 1 ); 130 irc->user->host = g_strdup( host ); 180 s = set_add( &irc->set, "away", NULL, set_eval_away_status, irc ); 181 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 ); 131 211 132 212 conf_loaddefaults( irc ); 133 213 134 214 /* Evaluator sets the iconv/oconv structures. */ 135 set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) ); 136 137 irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" ); 138 139 g_free( myhost ); 140 g_free( host ); 141 142 return irc; 215 set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) ); 216 217 return( irc ); 143 218 } 144 219 … … 161 236 162 237 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", 163 irc-> user->nick ? irc->user->nick : "(NONE)", irc->root->host, reason );238 irc->nick ? irc->nick : "(NONE)", irc->host, reason ); 164 239 165 240 g_free( reason ); … … 171 246 172 247 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", 173 irc-> user->nick ? irc->user->nick : "(NONE)", irc->root->host, "No reason given" );248 irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" ); 174 249 } 175 250 … … 192 267 } 193 268 194 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ); 195 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 */ 196 277 void irc_free( irc_t * irc ) 197 278 { 279 user_t *user, *usertmp; 280 198 281 log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); 199 282 200 if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc-> b->set, "save_on_quit" ) )283 if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) ) 201 284 if( storage_save( irc, NULL, TRUE ) != STORAGE_OK ) 202 log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick);285 irc_usermsg( irc, "Error while saving settings!" ); 203 286 204 287 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 else 299 /* Nasty hack, but account_del() doesn't work in this 300 case and we don't want infinite loops, do we? ;-) */ 301 irc->accounts = irc->accounts->next; 302 } 205 303 206 304 while( irc->queries != NULL ) 207 305 query_del( irc, irc->queries ); 208 306 209 /* This is a little bit messy: bee_free() frees all b->users which 210 calls us back to free the corresponding irc->users. So do this 211 before we clear the remaining ones ourselves. */ 212 bee_free( irc->b ); 213 214 while( irc->users ) 215 irc_user_free( irc, (irc_user_t *) irc->users->data ); 216 217 while( irc->channels ) 218 irc_channel_free( irc->channels->data ); 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 } 219 328 220 329 if( irc->ping_source_id > 0 ) … … 228 337 irc->fd = -1; 229 338 230 g_hash_table_foreach_remove( irc-> nick_user_hash, irc_free_hashkey, NULL );231 g_hash_table_destroy( irc-> nick_user_hash );339 g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL ); 340 g_hash_table_destroy( irc->userhash ); 232 341 233 342 g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL ); … … 241 350 g_free( irc->sendbuffer ); 242 351 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 ); 243 357 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 ); 244 365 245 366 g_free( irc ); … … 253 374 } 254 375 255 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )256 {257 g_free( key );258 259 return( TRUE );260 }261 262 376 /* USE WITH CAUTION! 263 377 Sets pass without checking */ 264 void irc_setpass (irc_t *irc, const char *pass) 378 void irc_setpass (irc_t *irc, const char *pass) 265 379 { 266 380 g_free (irc->password); … … 273 387 } 274 388 275 static char **irc_splitlines( char *buffer );276 277 389 void irc_process( irc_t *irc ) 278 390 { … … 282 394 if( irc->readbuffer != NULL ) 283 395 { 284 lines = irc_ splitlines( irc->readbuffer );396 lines = irc_tokenize( irc->readbuffer ); 285 397 286 398 for( i = 0; *lines[i] != '\0'; i ++ ) … … 319 431 "`help set charset' for more information. Your " 320 432 "message was ignored.", 321 set_getstr( &irc-> b->set, "charset" ) );433 set_getstr( &irc->set, "charset" ) ); 322 434 323 435 g_free( conv ); … … 326 438 else 327 439 { 328 irc_write( irc, ":%s NOTICE AUTH :%s", irc-> root->host,440 irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, 329 441 "Warning: invalid characters received at login time." ); 330 442 … … 364 476 } 365 477 366 /* Splits a long string into separate lines. The array is NULL-terminated 367 and, unless the string contains an incomplete line at the end, ends with 368 an empty string. Could use g_strsplit() but this one does it in-place. 369 (So yes, it's destructive.) */ 370 static char **irc_splitlines( char *buffer ) 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 ) 371 481 { 372 482 int i, j, n = 3; … … 499 609 } 500 610 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 501 641 void irc_write( irc_t *irc, char *format, ... ) 502 642 { … … 509 649 return; 510 650 } 511 512 void irc_write_all( int now, char *format, ... )513 {514 va_list params;515 GSList *temp;516 517 va_start( params, format );518 519 temp = irc_connection_list;520 while( temp != NULL )521 {522 irc_t *irc = temp->data;523 524 if( now )525 {526 g_free( irc->sendbuffer );527 irc->sendbuffer = g_strdup( "\r\n" );528 }529 irc_vawrite( temp->data, format, params );530 if( now )531 {532 bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );533 }534 temp = temp->next;535 }536 537 va_end( params );538 return;539 }540 651 541 652 void irc_vawrite( irc_t *irc, char *format, va_list params ) … … 594 705 } 595 706 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 ) 751 { 752 irc_reply( irc, 353, "= %s :%s", channel, namelist ); 753 *namelist = 0; 754 } 755 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 should 771 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 596 794 int irc_check_login( irc_t *irc ) 597 795 { 598 if( irc->user ->user && irc->user->nick )796 if( irc->user && irc->nick ) 599 797 { 600 798 if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) ) 601 799 { 602 irc_ send_num( irc, 464, ":This server is password-protected." );800 irc_reply( irc, 464, ":This server is password-protected." ); 603 801 return 0; 604 802 } 605 803 else 606 804 { 607 irc_channel_t *ic; 608 irc_user_t *iu = irc->user; 609 610 irc->user = irc_user_new( irc, iu->nick ); 611 irc->user->user = iu->user; 612 irc->user->host = iu->host; 613 irc->user->fullname = iu->fullname; 614 irc->user->f = &irc_user_self_funcs; 615 g_free( iu->nick ); 616 g_free( iu ); 617 618 if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) 619 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); 620 621 irc->status |= USTATUS_LOGGED_IN; 622 623 /* This is for bug #209 (use PASS to identify to NickServ). */ 624 if( irc->password != NULL ) 625 { 626 char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; 627 628 /*irc_setpass( irc, NULL );*/ 629 /*root_command( irc, send_cmd );*/ 630 g_free( send_cmd[1] ); 631 } 632 633 irc_send_login( irc ); 634 635 irc->umode[0] = '\0'; 636 irc_umode_set( irc, "+" UMODE, TRUE ); 637 638 ic = irc_channel_new( irc, ROOT_CHAN ); 639 irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root ); 640 irc_channel_add_user( ic, irc->user ); 641 642 if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 || 643 strcmp( set_getstr( &irc->b->set, "ops" ), "user" ) == 0 ) 644 irc_channel_user_set_mode( ic, irc->user, IRC_CHANNEL_USER_OP ); 645 646 irc->last_root_cmd = g_strdup( ROOT_CHAN ); 647 648 irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN, 649 "Welcome to the BitlBee gateway!\n\n" 650 "If you've never used BitlBee before, please do read the help " 651 "information using the \x02help\x02 command. Lots of FAQs are " 652 "answered there.\n" 653 "If you already have an account on this server, just use the " 654 "\x02identify\x02 command to identify yourself.", NULL ); 655 805 irc_login( irc ); 656 806 return 1; 657 807 } … … 664 814 } 665 815 666 void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv ) 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 ) 667 941 { 668 942 /* allow_priv: Set to 0 if s contains user input, 1 if you want 669 943 to set a "privileged" mode (+o, +R, etc). */ 670 char m[128], st = 1; 671 const char *t; 944 char m[256], st = 1, *t; 672 945 int i; 673 946 char changes[512], *p, st2 = 2; … … 677 950 678 951 for( t = irc->umode; *t; t ++ ) 679 if( *t < sizeof( m ) ) 680 m[(int)*t] = 1; 681 952 m[(int)*t] = 1; 953 682 954 p = changes; 683 955 for( t = s; *t; t ++ ) … … 685 957 if( *t == '+' || *t == '-' ) 686 958 st = *t == '+'; 687 else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) || 688 ( st == 1 && strchr( UMODES, *t ) ) || 689 ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) ) 959 else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) ) 690 960 { 691 961 if( m[(int)*t] != st) … … 704 974 memset( irc->umode, 0, sizeof( irc->umode ) ); 705 975 706 for( i = 'A'; i <= 'z'&& strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )976 for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ ) 707 977 if( m[i] ) 708 978 irc->umode[strlen(irc->umode)] = i; 709 979 710 980 if( badflag ) 711 irc_send_num( irc, 501, ":Unknown MODE flag" ); 981 irc_reply( irc, 501, ":Unknown MODE flag" ); 982 /* Deliberately no !user@host on the prefix here */ 712 983 if( *changes ) 713 irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick, 714 irc->user->user, irc->user->host, irc->user->nick, 715 changes ); 716 } 717 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 ); 1298 } 718 1299 719 1300 /* Returns 0 if everything seems to be okay, a number >0 when there was a … … 753 1334 } 754 1335 755 static char *set_eval_charset( set_t *set, char *value ) 756 { 757 irc_t *irc = (irc_t*) set->data; 758 char *test; 759 gsize test_bytes = 0; 760 GIConv ic, oc; 761 762 if( g_strcasecmp( value, "none" ) == 0 ) 763 value = g_strdup( "utf-8" ); 764 765 if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) 766 { 767 return NULL; 768 } 769 770 /* Do a test iconv to see if the user picked an IRC-compatible 771 charset (for example utf-16 goes *horribly* wrong). */ 772 if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL || 773 test_bytes > 1 ) 774 { 775 g_free( test ); 776 g_iconv_close( oc ); 777 irc_usermsg( irc, "Unsupported character set: The IRC protocol " 778 "only supports 8-bit character sets." ); 779 return NULL; 780 } 781 g_free( test ); 782 783 if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) 784 { 785 g_iconv_close( oc ); 786 return NULL; 787 } 788 789 if( irc->iconv != (GIConv) -1 ) 790 g_iconv_close( irc->iconv ); 791 if( irc->oconv != (GIConv) -1 ) 792 g_iconv_close( irc->oconv ); 793 794 irc->iconv = ic; 795 irc->oconv = oc; 796 797 return value; 798 } 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 }
Note: See TracChangeset
for help on using the changeset viewer.