html/irssiq.pl


   1 #!/usr/bin/perl
   2 #
   3 # by Stefan "tommie" Tomanek
   4 #
   5 use strict;
   6 
   7 use vars qw($VERSION %IRSSI);
   8 $VERSION = "2003231101";
   9 %IRSSI = (
  10     authors     => "Stefan 'tommie' Tomanek",
  11     contact     => "stefan\@pico.ruhr.de",
  12     name        => "IrssiQ",
  13     description => "integrates ICQ instant-messaging into irssi",
  14     license     => "GPLv2",
  15     changed     => "$VERSION",
  16     modules     => "Net::vICQ Data::Dumper",
  17     sbitems     => "irssiq",
  18     commands	=> "irssiq"
  19 );
  20 
  21 use Irssi 20020324;
  22 use Irssi::TextUI;
  23 use Net::vICQ;
  24 use Data::Dumper;
  25 use vars qw($icq $timer $old_status %contacts @requested $want_connect);
  26 
  27 sub draw_box ($$$$) {
  28     my ($title, $text, $footer, $colour) = @_;
  29     my $box = '';
  30     $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
  31     foreach (split(/\n/, $text)) {
  32         $box .= '%R|%n '.$_."\n";
  33     }
  34     $box .= '%R`--<%n'.$footer.'%R>->%n';
  35     $box =~ s/%.//g unless $colour;
  36     return $box;
  37 }
  38 
  39 sub show_help() {
  40     my $help="IrssiQ $VERSION
  41 /irssiq
  42         List contact list
  43 /irssiq connect
  44         Connect to the ICQ network
  45 /irssiq disconnect
  46         Disconnect from ICQ network
  47 /irssiq add <uin>
  48         Add uin to contact list
  49 /irssiq del <uin>
  50         Delete uin from contact list
  51 /irssiq auth <uin>
  52         Authorize user to add your UIN to his contact list
  53 /irssiq info <uin1> <uin2>....
  54         Retrieve information about the uins
  55 /irssiq invisible <uin>
  56         Add or remove the UIN from your invisible list
  57 /irssiq visible <uin>
  58         Add or remove the UIN from your visible list
  59 /irssiq hidden <uin>
  60         Hide (or show) a uin in the statusbar
  61 /irssiq msg <uin>
  62         Send a message to uin
  63 /irssiq query <uin>
  64         Create a new query window with uin
  65 /irssiq email
  66         Send an email to uin
  67 /irssiq status (away|online|na|occupied|dnd|invisible)
  68         Change to the selected status
  69 /irssiq save
  70         Save contact list to file
  71 /irssiq load
  72         (Re-)Load contact list (reconnect afterwards)
  73 ";      
  74     my $text = '';
  75     foreach (split(/\n/, $help)) {
  76         $_ =~ s/^\/(.*)$/%9\/$1%9/;
  77         $text .= $_."\n";
  78     }
  79     print CLIENTCRAP draw_box($IRSSI{name}, $text, "help", 1);
  80 }
  81 
  82 sub call_openurl ($) {
  83     my ($url) = @_;
  84     no strict "refs";
  85     # check for a loaded openurl
  86     if (defined %{ "Irssi::Script::openurl::" }) {
  87         &{ "Irssi::Script::openurl::launch_url" }($url);
  88     } else {
  89         print CLIENTCRAP "%R>>%n Please install openurl.pl";
  90     }
  91 }
  92 
  93 sub store_openurl ($$$) {
  94     my ($uin, $text, $url) = @_;
  95     $url =~ s/\n/ /g;
  96     $text =~ s/\n/ /g;
  97     no strict "refs";
  98     if (defined %{ "Irssi::Script::openurl::" }) {
  99 	&{ "Irssi::Script::openurl::new_url" }(undef, "IrssiQ", $uin, $text, $url);
 100     } else {
 101 	print CLIENTCRAP "%R>>%n Please install openurl.pl";
 102     }
 103 }
 104 
 105 sub output ($) {
 106     print CLIENTCRAP $_ foreach split(/\n/, $_[0]);
 107 }
 108 
 109 sub icq_connect {
 110     my $uin = Irssi::settings_get_int('irssiq_uin');
 111     my $password = Irssi::settings_get_str('irssiq_password');
 112     $icq = Net::vICQ->new($uin, $password, 0);
 113     #$icq->{_Hide_IP} = 0;
 114     $icq->Add_Hook("Srv_Mes_Received", \&MessageHandler);
 115     $icq->Add_Hook("Srv_Srv_Message", \&MessageHandler);
 116     $icq->Add_Hook("Srv_BLM_Contact_Online", \&MessageHandler);
 117     $icq->Add_Hook("Srv_BLM_Contact_Offline", \&MessageHandler);
 118     $icq->{_Status} = 'Online';
 119     my $err;
 120     output "%R>>%n Trying to connect to ICQ server...";
 121     {
 122 	$icq->{_Auto_Login} = 1;
 123 	open FOO, '>>/dev/null';
 124 	my $oldfh = select(FOO);
 125 	$icq->Connect();
 126 	if(!($err = $icq->GetError())) {
 127 	    while(!$icq->{_LoggedIn} && !($err = $icq->GetError())) {
 128 	    	$icq->Execute_Once();
 129 	    }
 130     	}
 131 	select($oldfh);
 132     }
 133     if(!$err) {
 134         output "%R<<%n ..connected!";
 135 	#my ($details);
 136 	#$details->{Status} = 'Online';
 137 	#$icq->Send_Command("Cmd_GSC_Set_Status", $details);
 138 	add_uin($uin) unless $contacts{$uin};
 139 	send_contacts();
 140 	$timer = Irssi::timeout_add(2000, 'icq_cycle', undef);
 141         return 1;
 142     } else {
 143         output "%R<<%n ..failed!";
 144 	$want_connect = 0;
 145         my $s = $err;
 146         chomp($s);
 147         print("%R>>%n ".$s);
 148         return 0;
 149     }
 150 }
 151 
 152 sub write_to_log ($$$$) {
 153     my ($who, $direction, $type, $text) = @_;
 154     my $dir = Irssi::get_irssi_dir();
 155     mkdir $dir."/irssiq/" unless (-e $dir."/irssiq/");
 156     
 157     my $data = $type." ".$direction." ".$who.":\n";
 158     $data .= scalar(localtime())."\n";
 159     $data .= $text."\n\n";
 160 
 161     local *F;
 162     open(F, '>>'.$dir."/irssiq/".$who);
 163     print F $data;
 164     close(F);
 165 }
 166 
 167 sub MessageHandler ($$) {
 168     my ($icq, $details) = @_;
 169     if (Irssi::settings_get_bool('irssiq_debug')) {
 170 	my $text;
 171 	foreach (keys %$details) {
 172 	    my $content = $details->{$_};
 173 	    no warnings;
 174 	    #$content =~ s/\c.//g;
 175 	    #$content =~ s/\pC//g;
 176 	    $content =~ s/(?:(\n)|\pC)/$1/g;
 177 	    $content =~ s/%/%%/g;
 178 	    $text .=  $_." -> <".$content.">\n";
 179 	}
 180 	#print CLIENTCRAP $_." -> <".$details->{$_}.">" foreach keys %$details;
 181 	print CLIENTCRAP &draw_box('IrssiQ', $text, 'debug', 1);
 182     }
 183     my $type = $details->{MessageType};
 184     if ($type eq 'text_message' || $type eq 'offline_text_message' ) {
 185 	my $text = $details->{text};
 186 	# FIXME unicode stuff?!
 187 	no warnings;
 188 	#$text =~ s/\c.//g;
 189 	#$text =~ s/\pC//g;
 190 	$text =~ s/(?:(\n)|\pC)/$1/g;
 191 	write_to_log($details->{Sender}, 'from', 'msg', $text);
 192 	$text =~ s/%/%%/g;
 193 	my $output = draw_box("IrssiQ", $text, "msg from ".get_nick($details->{Sender}), 1);
 194 	# autocancels if there is already a window
 195 	start_query($details->{Sender}) if Irssi::settings_get_bool('irssiq_auto_open_query');
 196     	my $win = Irssi::window_find_name('<IrssiQ-'.$details->{Sender}.'>');
 197  	if (ref $win) {
 198 	    if (Irssi::settings_get_bool('irssiq_msg_border_in_query')) {
 199 		$win->print($output, MSGLEVEL_MSGS);
 200 	    } else {
 201 	     	$win->print("<".get_nick($details->{Sender})."> ".$text, MSGLEVEL_MSGS);
 202 	    }
 203 	} else {
 204 	    print MSGS $_ foreach split(/\n/, $output);
 205 	}
 206     } elsif ($type eq 'URL') {
 207 	write_to_log($details->{Sender}, 'from', 'URL', $details->{URL});
 208 	my $output = draw_box("IrssiQ", "%U".$details->{URL}."%U", "URL from ".get_nick($details->{Sender}), 1);
 209 	my $win = Irssi::window_find_name('<IrssiQ-'.$details->{Sender}.'>');
 210 	unless (ref $win) {
 211 	    print MSGS $_ foreach split(/\n/, $output);
 212 	} else {
 213 	    $win->print("<".get_nick($details->{Sender})."> ".$details->{URL}, MSGLEVEL_MSGS);
 214 	}
 215 	store_openurl($details->{Sender}, $details->{URL}, $details->{URL});
 216     } elsif ($type eq 'status_change') {
 217 	if ($details->{Sender} == Irssi::settings_get_int('irssiq_uin')) {
 218 	    unless ($contacts{$details->{Sender}}{status} eq $details->{Status}) {
 219 		output "%R<<%n Changed own status to '".$details->{Status}."'.";
 220 	    }
 221 	}
 222 	$contacts{$details->{Sender}}{status} = $details->{Status};
 223 	Irssi::statusbar_items_redraw('irssiq');
 224     } elsif ($type eq 'user_info_main') {
 225 	my $uin = shift(@requested);
 226 	if ($contacts{$uin}) {
 227 	    foreach (keys %$details) {
 228 		next if (/Ref|Our_UIN|MessageType|SubMessageType/);
 229 		$contacts{$uin}{user_info_main}{$_} = $details->{$_};
 230 		$contacts{$uin}{user_info_main}{$_} =~ s/ /_/g if $_ eq 'Nickname';
 231 	    }
 232 	}
 233 	show_short_info($uin, $details);
 234 	Irssi::statusbar_items_redraw('irssiq');
 235 	next_info();
 236     } elsif ($type eq 'user_info_not_found') {
 237 	my $uin = shift(@requested);
 238 	output "%R>>%n Information about UIN ".$uin." not found";
 239 	next_info();
 240     } elsif ($type eq 'add_message') {
 241 	output draw_box("IrssiQ", $details->{Sender}." added you to his/her contact list", "added by ".$details->{Sender}, 1);
 242     } elsif ($type eq 'auth_request') {
 243 	get_userinfo($details->{Sender});
 244 	output draw_box("IrssiQ", $details->{reason}, "auth-request from ".$details->{Sender}, 1);
 245     } elsif ($type eq 'Invalid tagged message') {
 246 	# Webmessage
 247 	my $string = $details->{TaggedDataString};
 248 	$string =~ s/\pC//g;
 249 	$string =~ /\d+\.\d+\.\d+\.\d+(.*)/;
 250 	write_to_log("Webmessage", 'from', 'msg', $1);
 251 	print CLIENTCRAP &draw_box("IrssiQ", $1, "WebMessage", 1);
 252     }
 253 }
 254 
 255 sub next_info {
 256     return unless defined $requested[0];
 257     my $uin = $requested[0];
 258     output "%B>>%n Requesting user information for UIN #$uin";
 259     my %details = (
 260         MessageType => "Get_WP_Info",
 261         TargetUIN => $uin,
 262     );
 263     $icq->Send_Command("Cmd_Srv_Message", \%details);
 264 }
 265 
 266 sub get_userinfo ($) {
 267     my ($uin) = @_;
 268     push @requested, $uin;
 269     next_info if (scalar(@requested) == 1);
 270 }
 271 
 272 sub icq_cycle {
 273     return unless ($icq);
 274     $icq->Send_Keep_Alive();
 275     $icq->Execute_Once();
 276     unless ($icq->{_Connected}) {
 277         Irssi::timeout_remove($timer);
 278 	output "%R>>%n IrssiQ disconnected";
 279 	$contacts{$_}{status} = 'Offline' foreach keys %contacts;
 280 	if (Irssi::settings_get_bool('irssiq_auto_reconnect') && $want_connect) {
 281 	    icq_connect();
 282 	}
 283     }
 284 }
 285 
 286 sub send_message ($$$) {
 287     my ($icq, $uin, $text) = @_;
 288     my $details = { uin => $uin,
 289                     MessageType => 'text',
 290                     text =>  $text
 291     };
 292     write_to_log($uin, 'to', 'msg', $text);
 293     $icq->Send_Command("Cmd_Send_Message", $details);
 294     my $win = Irssi::window_find_name('<IrssiQ-'.$uin.'>');
 295     my $output = draw_box("IrssiQ", $text, "msg to ".get_nick($uin), 1);
 296     unless (ref $win) {
 297 	print CLIENTCRAP $_ foreach split(/\n/, $output);
 298     } else {
 299 	my $my_uin = Irssi::settings_get_int('irssiq_uin');
 300 	$win->print("<".get_nick($my_uin)."> ".$text, MSGLEVEL_CLIENTCRAP);
 301     }
 302 }
 303 
 304 sub array2table {
 305     my (@array) = @_;
 306     my @width;
 307     foreach my $line (@array) {
 308         for (0..scalar(@$line)) {
 309             my $l = $line->[$_];
 310             $l =~ s/%[^%]//g;
 311             $l =~ s/%%/%/g;
 312             $width[$_] = length($l) if $width[$_]<length($l);
 313         }
 314     }   
 315     my $text;
 316     foreach my $line (@array) {
 317         for (0..scalar(@$line)) {
 318             my $l = $line->[$_];
 319             $text .= $line->[$_];
 320             $l =~ s/%[^%]//g;
 321             $l =~ s/%%/%/g;
 322             $text .= " "x($width[$_]-length($l)+1);
 323         }
 324         $text .= "\n";
 325     }
 326     return $text;
 327 }
 328 
 329 
 330 sub list_contacts {
 331     my $text;
 332     my @array;
 333     my $my_uin = Irssi::settings_get_int('irssiq_uin');
 334     foreach (sort {$contacts{$a}{status} cmp $contacts{$b}{status}} keys %contacts) {
 335 	my @line;
 336 	next if $_ eq $my_uin;
 337 	my $status = $contacts{$_}{status};
 338 	next if ($status eq 'Offline' && not Irssi::settings_get_bool('irssiq_list_show_offline'));
 339 	if ($status eq "Online") {
 340 	    push @line, "%go%n";
 341 	} else {
 342 	    push @line, "%ro%n";
 343 	}
 344 	push @line, $status;
 345 	if ($contacts{$_}{user_info_main} && $contacts{$_}{user_info_main}{Nickname}) {
 346 	    push @line, '['.$contacts{$_}{user_info_main}{Nickname}.']';
 347 	} else {
 348 	    push @line, '';
 349 	}
 350 	push @line, $_;
 351 	if ($contacts{$_}{invisible}) {
 352 	    push @line, '%B<Inv>%n';
 353 	} else {
 354 	    push @line, "";
 355 	}
 356 	if ($contacts{$_}{visible}) {
 357             push @line, '%G<Vis>%n';
 358         } else {
 359             push @line, "";
 360         }
 361 	if ($contacts{$_}{hide_in_sb}) {
 362 	    push @line, '%B<Hidden>%n';
 363 	} else {
 364 	    push @line, "";
 365 	}
 366 	push @array, \@line;
 367     }
 368     my %table = (Online           => '%G==Online==%n',
 369 		 Away             => '%R===Away===%n',
 370 		 'Do Not Disturb' => '%B===DnD====%n',
 371 		 Occupied         => '%Y=Occupied=%n',
 372 		 Invisible        => '%C===Inv====%n',
 373 		 'N/A'            => '%Y===N/A====%n',
 374 		 Offline          => '%R=Offline==%n'
 375 		 );
 376     $text = array2table(@array);
 377     $text .= $table{$contacts{$my_uin}{status}}."\n";
 378     output draw_box('IrssiQ', $text, 'contacts', 1);
 379 }
 380 
 381 sub add_uin ($) {
 382     my ($uin) = @_;
 383     $contacts{$uin} = { status => 'Offline' } unless defined $contacts{$uin};
 384     Irssi::statusbar_items_redraw('irssiq');
 385     get_userinfo($uin) if ($icq && $icq->{_Connected});
 386 }
 387 
 388 sub del_uin ($) {
 389     my ($uin) = @_;
 390     return unless defined $contacts{$uin};
 391     delete $contacts{$uin};
 392 }
 393 
 394 sub send_contacts {
 395     my ($details, $details2, $details3);
 396     my @uins;
 397     my @inv;
 398     my @vis;
 399     foreach (keys(%contacts)) {
 400 	push @uins, $_;
 401 	push @inv, $_ if $contacts{$_}{invisible}; 
 402 	push @vis, $_ if $contacts{$_}{visible}; 
 403     }
 404     $details->{ContactList} = \@uins;
 405     $icq->Send_Command("Cmd_Add_ContactList", $details);
 406     $icq->Send_Command("Cmd_CTL_UploadList", $details);
 407     $details2->{InVisibleList} = \@inv;
 408     $details3->{VisibleList} = \@vis;
 409     $icq->Send_Command("Cmd_BOS_Add_InVisibleList", $details2) if @inv;
 410     $icq->Send_Command("Cmd_BOS_Add_VisibleList", $details3) if @vis;
 411 }
 412 
 413 sub save_contacts {
 414     my $dir = Irssi::get_irssi_dir();
 415     my $dumper = Data::Dumper->new([\%contacts], ['contacts']);
 416     $dumper->Purity(1)->Deepcopy(1);
 417     my $data = $dumper->Dump;
 418     local *F;
 419     open(F, '>'.$dir.'/irssiq_contacts');
 420     print F $data;
 421     close(F);
 422     output "%R>>%n IrssiQ contacts saved";
 423 }
 424 
 425 sub load_contacts {
 426     my $text;
 427     my $dir = Irssi::get_irssi_dir();
 428     return unless (-e $dir.'/irssiq_contacts');
 429     local *F;
 430     open F, "<".$dir.'/irssiq_contacts';
 431     $text .= $_ foreach (<F>);
 432     close(F);
 433     if ($text) {
 434 	no strict;
 435 	my %friends = %{ eval "$text" };
 436 	foreach (keys %friends) {
 437 	    next if defined $contacts{$_};
 438 	    $contacts{$_} = $friends{$_};
 439 	    $contacts{$_}{status} = 'Offline';
 440 	}
 441     }
 442 }
 443 
 444 sub show_short_info ($$) {
 445     my ($uin, $details) = @_;
 446     my $text = "== ".$details->{Nickname}." ==\n";
 447     $text .= "Name : ".$details->{Firstname}." ".$details->{Lastname}."\n";
 448     $text .= "eMail: ".$details->{Email}."\n";
 449     output draw_box('IrssiQ', $text, $uin, 1);
 450 }
 451 
 452 sub set_status ($) {
 453     my ($status) = @_;
 454     $status =~ s/ /_/g;
 455     my %table = (online    => 'Online',
 456                  away      => 'Away',
 457 		 na        => 'Not_Available',
 458 		 occupied  => 'Occupied',
 459 		 dnd       => 'Do_Not_Disturb',
 460 		 invisible => 'Invisible',
 461 		 ffc       => 'Free_For_Chat'
 462 		 );
 463     my %options = %table;
 464     $options{'N/A'} = 'Not_Available';
 465     $options{ $table{$_}  } = $table{$_} foreach keys %table;
 466     unless (defined $options{$status}) {
 467 	output "%R>>%n '".$status."' is an invalid status";
 468 	output "%R>>%n Valid options are: ".join(" ", keys(%table));
 469 	return;
 470     }
 471     my ($details);
 472     $details->{Status} = $options{$status};
 473     $icq->Send_Command("Cmd_GSC_Set_Status", $details);
 474 }
 475 
 476 sub sig_away ($) {
 477     my ($server) = @_;
 478     return unless ($icq && $icq->{_Connected});
 479     my $away_status = Irssi::settings_get_str('irssiq_away_status');
 480     if ($server->{usermode_away}) {
 481         my $uin = Irssi::settings_get_str('irssiq_uin');
 482         $old_status = $contacts{$uin}{status};
 483 	set_status($away_status);
 484     } else {
 485 	set_status($old_status);
 486     }
 487 }
 488 
 489 sub sb_show ($$) {
 490     my ($item, $get_size_only) = @_;
 491     my $line = "";
 492     my %users;
 493     my $more = 0;
 494     foreach my $uin (sort {$contacts{$a}{status} cmp $contacts{$b}{status}} keys %contacts) {
 495 	next if $uin eq Irssi::settings_get_str('irssiq_uin');
 496 	my $status = $contacts{$uin}{status};
 497 	next if $status eq '';
 498 	next if ($status eq 'Online' && not Irssi::settings_get_bool('irssiq_statusbar_show_online'));
 499 	next if ($status eq 'Offline' && not Irssi::settings_get_bool('irssiq_statusbar_show_offline'));
 500 	next if ($status eq 'Away' && not Irssi::settings_get_bool('irssiq_statusbar_show_away'));
 501 	next if ($status eq 'Do Not Disturb' && not Irssi::settings_get_bool('irssiq_statusbar_show_dnd'));
 502 	next if ($status eq 'Occupied' && not Irssi::settings_get_bool('irssiq_statusbar_show_occupied'));
 503 	next if ($status eq 'Invisible' && not Irssi::settings_get_bool('irssiq_statusbar_show_invisible'));
 504 	next if ($status eq 'N/A' && not Irssi::settings_get_bool('irssiq_statusbar_show_not_available'));
 505 	if ($contacts{$uin}{hide_in_sb}) { $more = 1; next; }
 506 	# FIXME Irssi bug?!
 507 	my %table = (Online           => '%gO%n',
 508 		    Away             => '%rA%n',
 509 		    'Do Not Disturb' => '%bD%n',
 510 		    Occupied         => '%yOc%n',
 511 		    Invisible        => '%cI%n',
 512 		    'N/A'            => '%yN%n',
 513 		    'Offline'        => '%RO%n'
 514 		    );
 515 	unless (Irssi::settings_get_bool('irssiq_statusbar_compact')) {
 516 	    $line .= '<';
 517 	    if (defined $table{$status}) {
 518 		$line .= $table{$status};
 519 	    } else {
 520 		$line .= substr($status, 0, 1);
 521 	    }
 522 	    $line .= '%bI%n' if $contacts{$uin}{invisible};
 523 	    $line .= '%gV%n' if $contacts{$uin}{visible};
 524 	    $line .= '>';
 525 	    if ($contacts{$uin}{user_info_main} && $contacts{$uin}{user_info_main}{Nickname}) {
 526 		$line .= $contacts{$uin}{user_info_main}{Nickname}." ";
 527 	    } else {
 528 		$line .= $uin." ";
 529 	    }
 530 	} else {
 531 	    push @{ $users{$table{$status}} }, $uin;
 532 	}
 533     }
 534     if (Irssi::settings_get_bool('irssiq_statusbar_compact')) {
 535 	foreach (keys %users) {
 536 	    $line .= '<'.$_;
 537 	    foreach my $uin (@{ $users{$_} }) {
 538 		$line .= ' '.get_nick($uin);
 539 	    }
 540 	    $line .= '>';
 541 	}
 542     }
 543     my %table = (Online           => '%G==Online==%n',
 544                  Away             => '%R===Away===%n',
 545                  'Do Not Disturb' => '%B===DnD====%n',
 546                  Occupied         => '%Y=Occupied=%n',
 547                  Invisible        => '%C===Inv====%n',
 548                  'N/A'            => '%Y===N/A====%n',
 549 		 Offline          => '%R=Offline==%n'
 550                  );
 551     if (Irssi::settings_get_bool('irssiq_statusbar_short_status')) {
 552 	%table = (Online           => '%G(On)%n',
 553 		  Away             => '%R(Aw)%n',
 554 		  'Do Not Disturb' => '%B(DnD)%n',
 555 		  Occupied         => '%Y(Inv)%n',
 556 		  'N/A'            => '%Y(NA)%n',
 557 		  Offline          => '%R(Off)%n'
 558 		 );
 559     }
 560     my $my_uin = Irssi::settings_get_int('irssiq_uin');
 561     $line .= '...' if $more;
 562     $line .= $table{$contacts{$my_uin}{status}};
 563 
 564     my $format = "{sb ".$line."}";
 565     $item->{min_size} = $item->{max_size} = length($line);
 566     $item->default_handler($get_size_only, $format, 0, 1);
 567 }
 568 
 569 sub get_uin ($) {
 570     my ($input) = @_;
 571     return $input if $input =~ /^[0-9]+$/;
 572     
 573     foreach (keys %contacts) {
 574 	if ($contacts{$_}{user_info_main} && $contacts{$_}{user_info_main}{Nickname}) {
 575 	    return $_ if lc($contacts{$_}{user_info_main}{Nickname}) eq lc($input);
 576 	}
 577     }
 578     return undef;
 579 }
 580 
 581 sub get_nick ($) {
 582     my ($uin) = @_;
 583     if ($contacts{$uin} && $contacts{$uin}{user_info_main}) {
 584 	return $contacts{$uin}{user_info_main}{Nickname};
 585     }
 586     # Fallback
 587     return $uin;
 588 }
 589 
 590 sub send_auth ($) {
 591     my ($uin) = @_;
 592     return unless $uin =~ /^[0-9]+$/;
 593     my ($details);
 594     $details->{uin} = $uin;
 595     $icq->Send_Command("Cmd_Authorize", $details);
 596     output "%R>>%n Authorization sent to ".$uin;
 597 }
 598 
 599 sub start_query ($) {
 600     my ($uin) = @_;
 601     return if ref Irssi::window_find_name('<IrssiQ-'.$uin.'>');
 602     Irssi::command("window new hide");
 603     my $win = Irssi::active_win;
 604     $win->set_name('<IrssiQ-'.$uin.'>');
 605     $win->set_history('<IrssiQ-'.$uin.'>');
 606     $win->print('Starting IrssiQ query with '.get_nick($uin).' ('.$uin.')');
 607 }
 608 
 609 sub sig_send_text ($) {
 610     my ($text, $foo1, $foo2) = @_;
 611     my $win = Irssi::active_win;
 612     return unless (ref $win && $win->{name} =~ /<IrssiQ-(\d+)>/);
 613     my $uin = $1;
 614     if ($icq && $icq->{_Connected}) {
 615 	send_message($icq, $uin, $text);
 616     } else {
 617 	$win->print("%R>>%n You are not connected to ICQ", MSGLEVEL_CLIENTCRAP);
 618     }
 619 }
 620 
 621 sub send_url ($$$) {
 622     my ($uin, $url, $description) = @_;
 623     return unless ($icq && $icq->{_Connected});
 624     my %details = ( uin => $uin,
 625 		    MessageType => 'url',
 626 		    URL =>  $url,
 627 		    Description => $description
 628 	    	    );
 629     write_to_log($uin, 'to', 'url', $url."\n".$description);
 630     $icq->Send_Command("Cmd_Send_Message", \%details);
 631     my $win = Irssi::window_find_name('<IrssiQ-'.$uin.'>');
 632     my $output = draw_box("IrssiQ", $url, "url to ".get_nick($uin), 1);
 633     unless (ref $win) {
 634         output $output;
 635     } else {
 636 	my $my_uin = Irssi::settings_get_int('irssiq_uin');
 637 	$win->print("<".get_nick($my_uin)."> ".$url, MSGLEVEL_CRAP);
 638     }
 639 
 640 }
 641 # calles by scriptassist on reload
 642 sub pre_unload {
 643     save_contacts();
 644     return unless $icq->{_Connected};
 645     $icq->Disconnect() if ($icq && $icq->{_Connected});
 646     while ($icq->{_Connected}) {
 647 	$icq->Execute_Once();
 648     }
 649     $contacts{$_}{status} = 'Offline' foreach keys %contacts;
 650     output "%R>>%n IrssiQ disconnected";
 651 }
 652 
 653 sub sig_complete_word ($$$$$) {
 654     my ($list, $window, $word, $linestart, $want_space) = @_;
 655     return unless $linestart =~ /^.irssiq (\w+)/;
 656     my @newlist;
 657     if ($1 eq 'status') {
 658 	foreach (('online', 'away', 'na', 'occupied', 'dnd', 'invisible', 'ffc')) {
 659 	    push @newlist, $_ if /^(\Q$word\E.*)?$/i;
 660 	}
 661     } else {
 662 	foreach (keys %contacts) {
 663 	    push @newlist, $_ if /^(\Q$word\E.*)?$/;
 664 	    if ($contacts{$_}{user_info_main} && $contacts{$_}{user_info_main}{Nickname}) {
 665 		push @newlist, $contacts{$_}{user_info_main}{Nickname} if $contacts{$_}{user_info_main}{Nickname} =~ /^(\Q$word\E.*)?$/i;
 666 	    }
 667 	}
 668     }
 669     $want_space = 0;
 670     push @$list, $_ foreach @newlist;
 671 
 672 }
 673 
 674 sub toggle_inv_list ($) {
 675     my ($uin) = @_;
 676     return unless defined $contacts{$uin};
 677     $contacts{$uin}{invisible} = not $contacts{$uin}{invisible};
 678     my ($details);
 679     $details->{InVisibleList} = [$uin];
 680     if ($contacts{$uin}{invisible}) {
 681 	$icq->Send_Command("Cmd_BOS_Add_InVisibleList", $details) if $icq->{_Connected};
 682 	output "%B>>%n You are now invisible for ".get_nick($uin)." (".$uin.")";
 683     } else {
 684 	$icq->Send_Command("Cmd_BOS_Remove_InVisibleList", $details) if $icq->{_Connected};
 685 	output "%B>>%n You are no longer invisible for ".get_nick($uin)." (".$uin.")";
 686     }
 687 }
 688 
 689 sub toggle_vis_list ($) {
 690     my ($uin) = @_;
 691     return unless defined $contacts{$uin};
 692     $contacts{$uin}{visible} = not $contacts{$uin}{visible};
 693     my ($details);
 694     $details->{VisibleList} = [$uin];
 695     if ($contacts{$uin}{visible}) {
 696         $icq->Send_Command("Cmd_BOS_Add_VisibleList", $details) if $icq->{_Connected};
 697         output "%B>>%n You are now visible for ".get_nick($uin)." (".$uin.")";
 698     } else {
 699         $icq->Send_Command("Cmd_BOS_Remove_VisibleList", $details) if $icq->{_Connected};
 700         output "%B>>%n You are no longer visible for ".get_nick($uin)." (".$uin.")";
 701     }
 702 }
 703 
 704 sub toggle_hidden ($) {
 705     my ($uin) = @_;
 706     return unless defined $contacts{$uin};
 707     $contacts{$uin}{hide_in_sb} = not $contacts{$uin}{hide_in_sb};
 708     if ($contacts{$uin}{hide_in_sb}) {
 709         output "%B>>%n ".get_nick($uin)." (".$uin.") is now hidden";
 710     } else {
 711         output "%B>>%n ".get_nick($uin)." (".$uin.") is no longer hidden";
 712     }
 713     Irssi::statusbar_items_redraw('irssiq');
 714 }       
 715 
 716 sub cmd_irssiq ($$$) {
 717     my ($args, $server, $witem) = @_;
 718     my @arg = split / +/, $args;
 719     if (scalar(@arg) == 0) {
 720 	list_contacts();
 721     } elsif ($arg[0] eq 'connect') {
 722 	$want_connect = 1;
 723 	icq_connect();
 724     } elsif ($arg[0] eq 'disconnect') {
 725 	$want_connect = 0;
 726 	$icq->Disconnect() if ($icq && $icq->{_Connected});
 727     } elsif ($arg[0] eq 'msg' && defined $arg[1] && defined $arg[2]) {
 728 	my $uin = get_uin($arg[1]);
 729 	return unless $uin;
 730 	shift @arg;
 731 	shift @arg;
 732 	send_message($icq, $uin, join(" ", @arg));
 733     } elsif ($arg[0] eq 'url' && defined $arg[1] && defined $arg[2]) {
 734 	my $uin = get_uin($arg[1]);
 735 	return unless $uin;
 736 	my $url = $arg[2];
 737 	shift @arg;
 738 	shift @arg;
 739 	send_url($uin, $url, join(" ", @arg));
 740     } elsif ($arg[0] eq 'auth' && defined $arg[1]) {
 741 	send_auth($arg[1]) if ($icq && $icq->{_Connected});
 742     } elsif ($arg[0] eq 'email' && defined $arg[1]) {
 743 	my $uin = get_uin($arg[1]);
 744 	return unless $uin;
 745 	if ($contacts{$uin} && $contacts{$uin}{user_info_main}) {
 746 	    call_openurl($contacts{$uin}{user_info_main}{Email}) if $contacts{$uin}{user_info_main}{Email};
 747 	}
 748     } elsif ($arg[0] eq 'add' && defined $arg[1]) {
 749 	shift @arg;
 750 	foreach (@arg) {
 751 	    next unless $_ =~ /^[0-9]+$/;
 752 	    add_uin($_);
 753 	    output "%B>>%n Added UIN ".$_." to contact list";
 754 	}
 755 	send_contacts() if ($icq && $icq->{_Connected});
 756     } elsif ($arg[0] eq 'del' && defined $arg[1]) {
 757 	shift @arg;
 758 	foreach (@arg) {
 759 	    next unless $_ =~ /^[0-9]+$/;
 760 	    del_uin($_);
 761 	    output "%B>>%n Removed UIN ".$_." from contact list";
 762 	}
 763 	send_contacts() if ($icq && $icq->{_Connected});
 764     } elsif ($arg[0] eq 'save') {
 765 	save_contacts();
 766     } elsif ($arg[0] eq 'load') {
 767 	load_contacts();
 768 	send_contacts if ($icq && $icq->{_Connected});
 769     } elsif ($arg[0] eq 'info') {
 770 	shift @arg;
 771 	foreach (@arg) {
 772 	    my $uin = get_uin($_);
 773 	    get_userinfo($uin) if $uin;
 774 	}
 775     } elsif ($arg[0] eq 'status' && defined $arg[1]) {
 776 	set_status($arg[1]) if ($icq && $icq->{_Connected});
 777     } elsif ($arg[0] eq 'query' && defined $arg[1]) {
 778 	my $uin = get_uin($arg[1]);
 779 	start_query($uin) if $uin;
 780     } elsif ($arg[0] eq 'invisible') {
 781 	my $uin = get_uin($arg[1]);
 782 	toggle_inv_list($uin);
 783     } elsif ($arg[0] eq 'visible') {
 784         my $uin = get_uin($arg[1]);
 785         toggle_vis_list($uin);
 786     } elsif ($arg[0] eq 'hidden') {
 787 	my $uin = get_uin($arg[1]);
 788 	toggle_hidden($uin);
 789     } elsif ($arg[0] eq 'help') {
 790 	show_help();
 791     }
 792 }
 793 
 794 Irssi::settings_add_int($IRSSI{name}, 'irssiq_uin', '');
 795 Irssi::settings_add_str($IRSSI{name}, 'irssiq_password', '');
 796 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_debug', 0);
 797 
 798 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_show_online', 1);
 799 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_show_offline', 0);
 800 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_show_away', 1);
 801 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_show_dnd', 1);
 802 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_show_occupied', 1);
 803 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_show_invisible', 1);
 804 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_show_not_available', 1);
 805 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_short_status', 0);
 806 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_list_show_offline', 1);
 807 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_statusbar_compact', 0);
 808 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_auto_open_query', 0);
 809 
 810 Irssi::settings_add_str($IRSSI{name}, 'irssiq_away_status', 'away');
 811 
 812 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_msg_border_in_query', 0);
 813 Irssi::settings_add_bool($IRSSI{name}, 'irssiq_auto_reconnect', 1);
 814 
 815 Irssi::signal_add_first('complete word', \&sig_complete_word);
 816 Irssi::signal_add('setup saved', \&save_contacts);
 817 Irssi::signal_add('away mode changed', \&sig_away);
 818 Irssi::signal_add('send text', \&sig_send_text);
 819 
 820 Irssi::statusbar_item_register('irssiq', 0, 'sb_show');
 821 
 822 Irssi::command_bind('irssiq', \&cmd_irssiq);
 823 
 824 foreach my $cmd ('help', 'connect', 'disconnect', 'msg', 'auth', 'email', 'save', 'load', 'add', 'del', 'info', 'status', 'query', 'url', 'invisible', 'visible', 'hidden' ) {
 825 Irssi::command_bind('irssiq '.$cmd => sub {
 826 		    cmd_scripassist("$cmd ".$_[0], $_[1], $_[2]); });
 827 }
 828 
 829 load_contacts();
 830 
 831 print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /irssiq help for help';