1# 2# This script requires external perl module News::NNTPClient. You can download 3# sources of this module from: 4# http://www.cpan.org/authors/id/RVA/NNTPClient-0.37.tar.gz 5# http://derwan.irssi.pl/perl-modules/NNTPClient-0.37.tar.gz 6# Usage: 7# /ARTICLE [-s <server>] [-p <port>] [-P <password> -U <login>] [-l <group> <count>] [-a] [-L <index>] <Message-ID> 8# Settings: 9# /SET news_nntp_server [server] (default environment variable 'NNTPSERVER' is used (or news.tpi.pl if variable not set)) 10# /SET news_nntp_port [port] (default is 119) 11# /SET news_show_headers [headers] (default: from newsgroups subject message-id date lines) 12# /SET news_use_news_window [On/Off] (default is On) 13# /SET news_show_signature [On/Off] (default is On) 14# /SET news_use_body_colors [On/Off] (default is On) 15# /SET news_check_count [count] (default is 5) 16# /SET news_use_auth [On/Off] (default is Off) 17# /SET news_auth_user [login] 18# /SET news_auth_password [password] 19 20use strict; 21use Irssi; 22use 5.6.0; 23use POSIX; 24 25use vars qw($VERSION %IRSSI); 26$VERSION = "0.5.9"; 27%IRSSI = ( 28 'authors' => 'Marcin Rozycki, Mathieu Doidy', 29 'contact' => 'derwan@irssi.pl', 30 'name' => 'news', 31 'description' => 'News reader, usage: /article [-s <server>] [-p <port>] [-P <password> -U <login>] [-l <group> <count>] [-a] [-L <index>] <message-id>', 32 'url' => 'http://derwan.irssi.pl', 33 'license' => 'GNU GPL v2', 34 'changed' => 'Fri Feb 6 21:26:57 CET 2004', 35); 36 37use News::NNTPClient; 38 39my $debug_level = 0; 40my $nntp_server = $ENV{'NNTPSERVER'}; $nntp_server = 'news.tpi.pl' unless $nntp_server; 41my $nntp_port = '119'; 42my $default_headers = 'from newsgroups'; 43my $check_count = 5; 44my %pipe_tag = (); 45my $news_window_name = 'news'; 46my @colors = (15, 12, 03, 06, 05, 07, 14); 47my @articles = (); 48 49Irssi::command_bind article => sub { 50 my $usage = '/article [-s <server>] [-p <port>] [-P <password> -U <login>] [-l <group> <count>] [-a] [-L <index>] <Message-ID>'; 51 52 my $window; 53 if (Irssi::settings_get_bool('news_use_news_window')) { 54 $window = Irssi::window_find_name($news_window_name); 55 if (!$window) { 56 Irssi::command('^window new hide'); 57 Irssi::command('^window name '.$news_window_name); 58 $window = Irssi::window_find_name($news_window_name); 59 } 60 } else { 61 $window = Irssi::active_win(); 62 } 63 64 my $server = Irssi::settings_get_str('news_nntp_server'); 65 $server = $nntp_server unless $server; 66 my $port = Irssi::settings_get_int('news_nntp_port'); 67 $port = $nntp_port unless ($port > 0); 68 my $count = Irssi::settings_get_int('news_check_count'); 69 $count = $check_count unless ($count > 0); 70 71 my ($connection, $artid, $group, $strip, $showall, @article); 72 my ($auth, $user, $password); 73 my $yes = 0; 74 75 @_ = split(/ +/, $_[0]); 76 while ($_ = shift(@_)) 77 { 78 /^-a$/ and $showall = 1, next; 79 /^-s$/ and $server = shift(@_), next; 80 /^-p$/ and $port = shift(@_), next; 81 /^-P$/ and $password = shift(@_), next; 82 /^-U$/ and $user = shift(@_), next; 83 /^-l$/ and do { 84 $group = shift(@_); 85 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_group_missing', $usage), return unless ($group); 86 $_ = shift(@_); 87 $count = $_, next if ($_ =~ /^\d+$/ and $_ > 0); 88 }; 89 /^-yes$/i and ++$yes, next; 90 /^-L$/ and do { 91 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_no_artids'), return if ($#articles < 0); 92 if ($artid = shift(@_)) { 93 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_unknown_argument', $artid, $usage), return if ($artid !~ /^\d+/ or $artid < 0 or $artid > 10); 94 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_unknown_artid', ++$artid), return unless ($articles[--$artid]); 95 $_ = $articles[$artid]->[0]; 96 } else { 97 for (my $idx = 0; $idx <= $#articles; $idx++) { 98 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_artid_show', 99 ($idx + 1), $articles[$idx]->[0], $articles[$idx]->[1], $articles[$idx]->[2]); 100 } 101 return; 102 } 103 }; 104 /^-/ and $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_unknown_argument', $_, $usage), return; 105 $artid = ($_ =~ /^<.*>$/) ? $_ : '<'.$_.'>'; 106 last; 107 } 108 109 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_server_unknown', $server), return if (!$server or $server !~ /^..*\...*/); 110 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_port_unknown', $port), return if (!$port or $port !~ /^\d+$/ or $port == 0 or $port > 65535); 111 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_missing_argument', $usage), return if (!$group and !$artid); 112 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_article_unknown', $artid), return if (!$group and $artid !~ /^<..*\@..*>$/); 113 114 my ($rh, $wh); 115 pipe($rh, $wh); 116 117 my $pid = fork(); 118 unless (defined $pid) { 119 close($rh); close($wh); 120 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_cannot_fork'); 121 return; 122 123 } elsif ($pid) { 124 close ($wh); 125 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_server_connecting', $server, $port, $artid); 126 Irssi::pidwait_add($pid); 127 $pipe_tag{$rh} = Irssi::input_add(fileno($rh), INPUT_READ, \&news_fork, $rh); 128 return; 129 } 130 131 close($rh); 132 133 $connection = new News::NNTPClient($server, $port, $debug_level); 134 print($wh "not_connected $server $port\n"), goto END unless ($connection->{CODE} =~ /^(200|201)$/); 135 print($wh "connected ".$connection->{MESG}."\n"); 136 137 if ($user && $password or Irssi::settings_get_bool('news_use_auth')) { 138 $user = Irssi::settings_get_str('news_auth_user') unless defined $user; 139 $password = Irssi::settings_get_str('news_auth_password') unless defined $password; 140 $connection->authinfo($user,$password); 141 } 142 143 144 if ($group) { 145 print($wh "listgroup_yes $count\n"), goto END if ($count > 10 and !$yes); 146 print($wh "listgroup_request $server $group\n"); 147 my @list = $connection->listgroup($group); 148 print($wh "listgroup_error $server ".$connection->{MESG}."\n"), goto END if ($#list < 0); 149 150 my $num = $#list; 151 print($wh "listgroup_num $group $num $count\n"); 152 @list = @list[($num - $count) .. $num] if ($num > --$count); 153 154 N: while ($_ = shift(@list)) 155 { 156 chomp; 157 print($wh "space\n"); 158 foreach my $xhdr ("From", "Subject", "Message-ID") 159 { 160 my @reply = $connection->xhdr($xhdr, $_); 161 chomp $reply[0]; $reply[0] =~ s/^\d+ //; 162 goto N unless ($reply[0]); 163 if ($xhdr eq "Message-ID") { 164 print($wh "memo $reply[0]\,news.pl: listgroup,$group\n"); 165 } elsif ($xhdr eq "From") { 166 $reply[0] = "\002" . $reply[0] . "\002"; 167 } 168 print($wh "listgroup_header $xhdr $reply[0]\n"); 169 } 170 } 171 print($wh "space\n"); 172 173 } else { 174 my $show_headers = $default_headers .' '. Irssi::settings_get_str('news_show_headers'); 175 my ($head, $idx, $usecolors, $bodycolor) = (1, 0, Irssi::settings_get_bool('news_use_body_colors'), $colors[0]); 176 177 print($wh "article_request $server $artid\n"); 178 foreach ($connection->article($artid)) 179 { 180 unless ($idx++) { 181 print($wh "space\n"); 182 print($wh "memo $artid\,news.pl: read,$server\n"); 183 } 184 chomp; s/\t/ /g; 185 /^-- / and do { 186 last if (!$showall and !Irssi::settings_get_bool('news_show_signature')); 187 $bodycolor = $colors[6], $usecolors = 0 if $usecolors; 188 }; 189 unless ($head) { 190 /^$/ and next if (!$showall and $strip++); 191 /^..*$/ and $strip = 0; 192 if ($usecolors) { 193 $_ =~ /^[>| ]+/; 194 my $prefix = $&; $prefix =~ s/ //g; 195 $bodycolor = ($_ =~ /^[>]+/) ? $colors[(((length($prefix)-1) %5)+1)] : $colors[0]; 196 } 197 print($wh "article_body \003$bodycolor$_\n"); 198 next; 199 } 200 /^$/ and print($wh "space\n"), $head = 0, next; 201 my ($header, $text) = split(/: /, $_, 2); 202 print($wh "article_header $header $text\n") if ($showall or $show_headers =~ /\b$header\b/i); 203 } 204 print($wh "article_notexist $server $artid\n") unless ($idx); 205 print($wh "space\n") unless ($strip); 206 } 207 208 END: print($wh "close\n"); 209 close($wh); 210 POSIX::_exit(1); 211}; 212 213sub memo { 214 my ($text, $who, $where) = @_; 215 G: while ($text =~ /<[A-Za-z0-9\S]+\@[A-Za-z0-9\S]+>/g) 216 { 217 my $artid = $&; 218 foreach my $array (@articles) { goto G if ($artid eq $array->[0]); } 219 unshift @articles, [$artid, $who, $where]; 220 } 221 $#articles = 9 if ($#articles > 9); 222} 223 224sub news_fork { 225 my $rh = shift; 226 while (<$rh>) 227 { 228 chomp; 229 /^close/ and last; 230 /^memo / and memo(split(",", $', 3)), next; 231 my ($theme, @args) = split / +/, $_, 5; 232 my $window = Irssi::window_find_name($news_window_name); 233 $window = Irssi::active_win() unless $window; 234 $window->printformat(MSGLEVEL_CLIENTCRAP, 'news_' . $theme, @args); 235 } 236 237 Irssi::input_remove($pipe_tag{$rh}); 238 close($rh); 239} 240 241Irssi::signal_add_last 'message private' => sub { memo($_[1], $_[2], $_[3]); }; 242Irssi::signal_add_last 'message public' => sub { memo($_[1], $_[2], $_[4]); }; 243Irssi::signal_add_last 'dcc chat message' => sub { memo($_[1], $_[0]->{nick}, "chat"); }; 244 245Irssi::theme_register([ 246 'news_server_unknown', 'NNTP %_server unknown%_ or not defined, use: /set news_nntp_server [server], to set it', 247 'news_server_bad', '%_Bad%_ NNTP server {hilight $0} (bad hostname or addres)', 248 'news_port_unknown', '%_NNTP port%_ unknown or not defined, use: /set news_nntp_port [port], to set it', 249 'news_missing_argument', 'Missing argument, usage: $0-', 250 'news_unknown_argument', 'Unknown argument \'$0\', usage: $1-', 251 'news_server_connecting', 'Connecting to {hilight $0} on port {hilight $1}, wait...', 252 'news_not_connected', '%_Cannot connect%_ to NNTP server $0 on port $1', 253 'news_connected', '%_Connected%_; $0-', 254 'news_article_unknown', 'Unknown message-id {hilight $0}', 255 'news_article_notexist', 'No article {hilight $1} on $0', 256 'news_article_request', 'Sending query about article {hilight $1} to $0, wait...', 257 'news_article_body', '$0-', 258 'news_article_header', '%c$0:%n %_$1-%_', 259 'news_group_missing', 'Missing argument: newsgroup, usage: $0-', 260 'news_listgroup_request', 'Looking for %_new articles%_ in {hilight $1}, wait...', 261 'news_listgroup_error', 'Listgroup result: $1-', 262 'news_listgroup_num', '$1 articles in group $0; fetching headers (max in $2 articles), wait...', 263 'news_listgroup_header', '%c$0:%n $1-', 264 'news_listgroup_yes', 'Count > 10 ($0). Doing this is not a good idea. Add -YES option to command if you really mean it', 265 'news_no_artids', 'Sorry, list of logged message-id\'s is empty :/', 266 'news_cannot_fork', 'Cannot fork process', 267 'news_artid_show', '[%_$[!-2]0%_] article %c$1%n [by {hilight $2} ($3-)]', 268 'news_unknown_artid', 'Article {hilight $0} not found, type /article -L, to displays list of logged message-id\'s', 269 'news_space', ' ' 270]); 271 272# registering settings 273Irssi::settings_add_bool('misc', 'news_use_news_window', 1); 274Irssi::settings_add_str('misc', 'news_nntp_server', $nntp_server); 275Irssi::settings_add_int('misc', 'news_nntp_port', $nntp_port); 276Irssi::settings_add_str('misc', 'news_show_headers', $default_headers.' subject message-id date lines content-transfer-encoding'); 277Irssi::settings_add_bool('misc', 'news_show_signature', 1); 278Irssi::settings_add_bool('misc', 'news_use_body_colors', 1); 279Irssi::settings_add_bool('misc', 'news_use_auth', 0); 280Irssi::settings_add_str('misc', 'news_auth_user', ''); 281Irssi::settings_add_str('misc', 'news_auth_password', ''); 282Irssi::settings_add_int('misc', 'news_check_count', $check_count); 283