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