1# OpenURL by Stefan'tommie' Tomanek
2#
3# 05.06.2002
4# * complete rewrite
5
6use strict;
7
8use vars qw($VERSION %IRSSI);
9$VERSION = "20030208";
10%IRSSI = (
11    authors     => "Stefan 'tommie' Tomanek",
12    contact     => "stefan\@pico.ruhr.de",
13    name        => "OpenURL",
14    description => "Stores URLs in a list and launches mail, web or ftp software",
15    license     => "GPLv2",
16    url         => "http://scripts.irssi.org",
17    changed     => "$VERSION",
18    commands	=> "openurl"
19);
20
21use Irssi 20020324;
22use Irssi::TextUI;
23use Irssi::UI;
24
25use vars qw(@urls %urltypes $recent);
26$recent = 1;
27
28# RegExp & defaultcommands
29%urltypes = ( http => { regexp => qr#((?:https?://[^\s<>"]+|www\.[-a-z0-9.]+)[^\s.,;<">\):])#, cmd => 'w3m "$1"' },
30              ftp  => { regexp => qr#((?:ftp://[^\s<>"]+|ftp\.[-a-z0-9.]+)[^\s.,;<">\):])#, cmd => 'ncftp "$1"' },
31	      mail => { regexp => qr#([-_a-z0-9.]+\@[-a-z0-9.]+\.[-a-z0-9.]+)#, cmd => 'mutt "$1" -s "$2"' },
32	    );
33
34sub draw_box ($$$$) {
35    my ($title, $text, $footer, $colour) = @_;
36    my $box = '';
37    $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
38    foreach (split(/\n/, $text)) {
39        $box .= '%R|%n '.$_."\n";
40    }                                                                               $box .= '%R`--<%n'.$footer.'%R>->%n';
41    $box =~ s/%.//g unless $colour;
42    return $box;
43}
44
45sub show_help() {
46    my $help=$IRSSI{name}." ".$VERSION."
47/openurl
48    List the saved URLs
49/openurl <number> <number>...
50    Load the corresponding URLs in your browser/mailer
51/openurl paste <number> <number>...
52    Paste the selected URLs to the current channel/query
53/openurl topics
54    Look for URLs in channel topics
55/openurl clear
56    Clear all URLs
57/openurl help
58    Display this help
59";
60    my $text = '';
61    foreach (split(/\n/, $help)) {
62        $_ =~ s/^\/(.*)$/%9\/$1%9/;
63	$text .= $_."\n";
64    }
65    print CLIENTCRAP draw_box($IRSSI{name}." help", $text, "help", 1) ;
66}
67
68sub list_urls {
69    my $string = '';
70    my $i = 1;
71    foreach (@urls) {
72	my $text = $_->{url};
73	my $url = $_->{url};
74	$text = $_->{text} if Irssi::settings_get_bool('openurl_display_context');
75	$url =~ s/%/%%/g;
76	$text =~ s/%/%%/g;
77	$text =~ s/\Q$url/%U$url%U/;
78	if ($recent-1 == $i) {
79	    $string .= '%B�%n';
80	} else {
81	    $string .= ' ';
82	}
83	$string .= '%r['.$i.']%n ';
84	$string .= '<'.$_->{channel};
85	$string .= '/'.$_->{nick} unless $_->{nick} eq "";
86	$string .= '> ';
87	$string .= $text." %n\n";
88	$i++;
89    }
90    print CLIENTCRAP draw_box("OpenURL", $string, "URLs", 1);
91}
92
93sub event_private_message {
94    my ($server, $text, $nick, $address) = @_;
95    process_line($server, $nick, $nick, $text);
96}
97sub event_public_message {
98    my ($server, $text, $nick, $address, $target) = @_;
99    process_line($server, $target, $nick, $text);
100}
101sub event_topic_changed {
102    my ($channel) = @_;
103    process_line($channel->{server}, $channel->{name}, "", $channel->{topic});
104}
105
106sub process_line ($$$$) {
107    my ($server, $target, $nick, $line) = @_;
108    my $url = get_url($line);
109    if ($url) {
110	my $type = url_type($url);
111	return unless Irssi::settings_get_bool('openurl_watch_'.$type);
112	new_url($server, $target, $nick, $line, $url);
113    }
114}
115
116sub get_url ($) {
117    my ($text) = @_;
118    foreach (keys %urltypes) {
119	return $1 if ($text =~ /$urltypes{$_}->{regexp}/);
120    }
121}
122
123sub url_type ($) {
124    my ($url) = @_;
125    foreach (keys %urltypes) {
126	return $_ if ($url =~ /$urltypes{$_}->{regexp}/);
127    }
128}
129
130sub launch_url ($) {
131    my ($url) = @_;
132    my $type = url_type($url);
133    my $address = $url;
134    my $suffix= "";
135    if ($type eq "mail") {
136	$address = $1 if $url =~ /(.*?@.*?)($|\?)/;
137	$suffix = $2 if $url =~ /(.*?@.*?)\?subject=(.*)/;
138    }
139    my $command = Irssi::settings_get_str("openurl_app_".$type);
140    $command =~ s/\$1/$address/;
141    $command =~ s/\$2/$suffix/;
142    system($command);
143}
144
145sub new_url ($$$$$) {
146    my ($server, $channel, $nick, $text, $url) = @_;
147    $recent = 1 if ($recent > Irssi::settings_get_int('openurl_max_urls'));
148    # Check for existance of URL
149    my $i = 1;
150    foreach (@urls) {
151	if ($text eq $_->{text} && $channel eq $_->{channel}) {
152	    my $note_id = add_note($server, $channel, $i);
153	    push @{$_->{notes}}, $note_id;
154	    return();
155	}
156	$i++;
157    }
158    if (defined $urls[$recent-1]) {
159	del_notes($recent);
160    }
161    $urls[$recent-1] = {channel => $channel,
162                        text    => $text,
163			nick    => $nick,
164			url     => $url,
165			notes   => [],
166			};
167    my $note_id = add_note($server, $channel, $recent);
168    push @{$urls[$recent-1]{notes}}, $note_id;
169    $recent++;
170}
171
172
173sub del_notes ($) {
174    my ($num) = @_;
175    my $view;
176    my $witem = Irssi::window_item_find($urls[$num-1]->{channel});
177    if (defined $witem) {
178	$view =  $witem->window()->view();
179    }
180    if (defined $view) {
181	foreach (@{$urls[$num-1]->{notes}}) {
182	    my $line = $view->get_bookmark($_);
183	    $view->remove_line($line) if defined $line;
184	    $view->set_bookmark($_, undef);
185	}
186	@{$urls[$num-1]->{notes}} = ();
187	$view->redraw();
188    }
189}
190
191sub add_note ($$$) {
192    my ($server, $target, $num) = @_;
193    my $witem;
194    if (defined $server) {
195	$witem = $server->window_item_find($target);
196    } else {
197	$witem = Irssi::window_item_find($target);
198    }
199    if (defined $witem) {
200	$witem->print("%R>>%n OpenURL ".$num, MSGLEVEL_CLIENTCRAP);
201	# create a unique ID for the mark
202	my $foo = time().'-'.int(rand()*1000);
203	$witem->window()->view()->set_bookmark_bottom("openurl_".$num.'-'.$foo);
204	return("openurl_".$num.'-'.$foo);
205    }
206    return(undef);
207}
208
209sub clear_urls {
210    del_notes($_) foreach (0..scalar(@urls)-1);
211    pop(@urls) foreach (1..scalar(@urls));
212    $recent = 1;
213    print CLIENTCRAP '%R>>%n URLs cleared';
214}
215
216sub cmd_openurl ($$$) {
217    my ($arg, $server, $witem) = @_;
218    my @args = split(/ /, $arg);
219    if (scalar(@args) == 0) {
220	list_urls;
221    } elsif ($args[0] eq 'clear') {
222	clear_urls;
223    } elsif ($args[0] eq 'topics') {
224    	event_topic_changed($_) foreach (Irssi::channels());
225    } elsif ($args[0] eq 'help') {
226	show_help();
227    } elsif ($args[0] eq 'open') {
228	launch_url($args[1]);
229    } else {
230	my $paste = 0;
231	if ($args[0] eq 'paste') {
232	    $paste = 1;
233	    shift(@args);
234	}
235	foreach (@args) {
236	    next unless /\d+/;
237	    next unless defined $urls[$_-1];
238	    my $url = $urls[$_-1]->{url};
239	    if ($paste == 1) {
240		if (ref $witem && ($witem->{type} eq "CHANNEL" || $witem->{type} eq "QUERY")) {
241		    $witem->command("MSG ".$witem->{name}." ".$url);
242		}
243	    } else {
244		launch_url($url);
245	    }
246	}
247    }
248}
249
250foreach (keys %urltypes) {
251    Irssi::settings_add_str($IRSSI{'name'}, 'openurl_app_'.$_, "screen ".$urltypes{$_}->{cmd});
252    Irssi::settings_add_bool($IRSSI{'name'}, 'openurl_watch_'.$_, 1);
253}
254Irssi::settings_add_int($IRSSI{'name'}, 'openurl_max_urls', 20);
255Irssi::settings_add_bool($IRSSI{'name'}, 'openurl_display_context', 1);
256
257Irssi::signal_add_last("message private", "event_private_message");
258Irssi::signal_add_last("message public", "event_public_message");
259Irssi::signal_add_last("channel topic changed", "event_topic_changed");
260
261#Irssi::signal_add('open url', \&launch_url);
262
263foreach my $cmd ('topics', 'clear', 'paste', 'help') {
264    Irssi::command_bind('openurl '.$cmd => sub {
265	cmd_openurl("$cmd ".$_[0], $_[1], $_[2]); });
266}
267Irssi::command_bind('openurl', 'cmd_openurl');
268
269print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /openurl help for help';
270