blib/lib/SlideShow.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 18 | 240 | 7.5 |
branch | 0 | 98 | 0.0 |
condition | 0 | 9 | 0.0 |
subroutine | 6 | 14 | 42.8 |
pod | 4 | 7 | 57.1 |
total | 28 | 368 | 7.6 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package SlideShow; | ||||||
2 | |||||||
3 | 1 | 1 | 865 | use strict; | |||
1 | 2 | ||||||
1 | 47 | ||||||
4 | 1 | 1 | 5 | use vars qw($VERSION @ISA); | |||
1 | 2 | ||||||
1 | 94 | ||||||
5 | |||||||
6 | $VERSION = '2.0'; | ||||||
7 | |||||||
8 | @SlideShow::ISA = qw(HTML::Parser); | ||||||
9 | |||||||
10 | 1 | 1 | 2330 | use CGI qw/:standard/; | |||
1 | 17472 | ||||||
1 | 6 | ||||||
11 | 1 | 1 | 4937 | use LWP::UserAgent; | |||
1 | 59499 | ||||||
1 | 27 | ||||||
12 | 1 | 1 | 896 | use HTML::Parser; | |||
1 | 13045 | ||||||
1 | 45 | ||||||
13 | 1 | 1 | 10 | use URI::Escape; | |||
1 | 3 | ||||||
1 | 3389 | ||||||
14 | |||||||
15 | sub new { | ||||||
16 | 0 | 0 | 1 | my $class = shift; | |||
17 | |||||||
18 | 0 | my $self = HTML::Parser->new(); | |||||
19 | 0 | bless $self, $class; | |||||
20 | |||||||
21 | 0 | while (my $X = shift) { | |||||
22 | 0 | my $Y = shift; | |||||
23 | 0 | $self->{$X} = $Y; | |||||
24 | } | ||||||
25 | |||||||
26 | 0 | 0 | die "SlideShow::Master missing required parameter 'master_cgi'" | ||||
27 | unless exists $self->{master_cgi}; | ||||||
28 | |||||||
29 | 0 | 0 | $self->{start_title} = "SlideShow Startup" | ||||
30 | unless exists $self->{start_title}; | ||||||
31 | 0 | 0 | $self->{tmp_dir} = "/tmp" | ||||
32 | unless exists $self->{tmp_dir}; | ||||||
33 | 0 | 0 | $self->{view_file} = "$self->{tmp_dir}/viewfile.html" | ||||
34 | unless exists $self->{view_file}; | ||||||
35 | 0 | 0 | $self->{log_file} = "$self->{tmp_dir}/surflog.last" | ||||
36 | unless exists $self->{tmp_log}; | ||||||
37 | 0 | 0 | $self->{tmp_log} = "$self->{tmp_dir}/surf.tmp" | ||||
38 | unless exists $self->{tmp_log}; | ||||||
39 | |||||||
40 | 0 | 0 | $self->{url_list} = [ "http://www.perl.com/CPAN/" ] | ||||
41 | unless exists $self->{url_list}; | ||||||
42 | |||||||
43 | 0 | 0 | $self->{commentary} = "Presented using SlideShow, a Perl module" | ||||
44 | . " for remote browser control available at" | ||||||
45 | ." www.perl.com/CPAN/" | ||||||
46 | unless exists $self->{commentary}; | ||||||
47 | |||||||
48 | 0 | $self->{redirect} = "$self->{master_cgi}?URL="; | |||||
49 | 0 | $self->{inside} = 0; | |||||
50 | |||||||
51 | 0 | $self->{ua} = new LWP::UserAgent; | |||||
52 | 0 | $self->{ua}->agent("SlideShow/2.0"); | |||||
53 | 0 | $SlideShow::last_marker = ''; | |||||
54 | |||||||
55 | 0 | $self; | |||||
56 | } | ||||||
57 | |||||||
58 | sub run { | ||||||
59 | 0 | 0 | 0 | my $self = shift; | |||
60 | |||||||
61 | 0 | 0 | if (not param()) { | ||||
62 | # no URL given | ||||||
63 | 0 | ||||||
64 | header, | ||||||
65 | start_html(-title => $self->{start_title}, | ||||||
66 | -BGCOLOR => "#FFFFFF"), | ||||||
67 | h1($self->{start_title}), | ||||||
68 | start_form, | ||||||
69 | submit('Show URL'), | ||||||
70 | textfield('URL'), | ||||||
71 | end_form; | ||||||
72 | |||||||
73 | 0 | 0 | if ($self->{url_list}) { | ||||
74 | 0 | print hr; | |||||
75 | 0 | print h2('presets'); | |||||
76 | 0 | print "
|
|||||
77 | 0 | for my $url (@{$self->{url_list}}) { | |||||
0 | |||||||
78 | 0 | print " |
|||||
79 | } | ||||||
80 | 0 | print "\n"; | |||||
81 | } | ||||||
82 | |||||||
83 | 0 | 0 | if ($self->{commentary}) { | ||||
84 | 0 | print hr; | |||||
85 | 0 | print $self->{commentary}; | |||||
86 | } | ||||||
87 | |||||||
88 | 0 | print end_html; | |||||
89 | |||||||
90 | } else { | ||||||
91 | 0 | my $current_http_dir = param('URL'); | |||||
92 | 0 | $current_http_dir =~ s/\s+$//; | |||||
93 | |||||||
94 | 0 | 0 | if (substr($current_http_dir, -1, 1) ne '/') { | ||||
95 | 0 | $current_http_dir =~ s!^(http://.*/).*!$1!; | |||||
96 | } | ||||||
97 | |||||||
98 | 0 | $self->{current_dir} = $current_http_dir; | |||||
99 | |||||||
100 | 0 | print header; | |||||
101 | |||||||
102 | 0 | my $item = param('URL'); | |||||
103 | |||||||
104 | 0 | unlink($self->{view_file}); | |||||
105 | |||||||
106 | 0 | 0 | if (not open VIEWFILE, ">$self->{view_file}") { | ||||
107 | 0 | print " can't create $self->{view_file}: $! \n"; |
|||||
108 | } else { | ||||||
109 | 0 | print VIEWFILE $$."\n"; | |||||
110 | |||||||
111 | 0 | 0 | if ($item =~ /^\s*last\s*$/) { | ||||
112 | 0 | print VIEWFILE $SlideShow::last_marker."\n"; # send the termination | |||||
113 | 0 | close VIEWFILE; | |||||
114 | |||||||
115 | 0 | 0 | if (-e $self->{tmp_log}) { | ||||
116 | 0 | unlink ($self->{log_file}); | |||||
117 | 0 | rename ($self->{tmp_log}, $self->{log_file}); | |||||
118 | } | ||||||
119 | |||||||
120 | |||||||
121 | 0 | start_html(-title => "Session finished", | |||||
122 | -BGCOLOR => "#FFFFFF"), | ||||||
123 | h1("Session finished"); | ||||||
124 | |||||||
125 | 0 | print "{master_cgi}\">New Session",p; | |||||
126 | |||||||
127 | 0 | 0 | if ($self->{log_file}) { | ||||
128 | 0 | print " \n"; |
|||||
129 | 0 | print "The pages were visited in this order: \n"; |
|||||
130 | |||||||
131 | 0 | open(LOG, $self->{log_file}); | |||||
132 | |||||||
133 | 0 | while( |
|||||
134 | 0 | print " |
|||||
135 | } | ||||||
136 | 0 | close(LOG); | |||||
137 | 0 | print " \n"; |
|||||
138 | 0 | print "Save this document locally if you'd like to" | |||||
139 | . " keep a record.\n"; | ||||||
140 | } | ||||||
141 | |||||||
142 | 0 | sleep(4); # sleep longer than the update time for clients | |||||
143 | 0 | unlink ($self->{view_file}); | |||||
144 | 0 | exit 0; | |||||
145 | } | ||||||
146 | |||||||
147 | 0 | my $req = new HTTP::Request 'GET' => $item; | |||||
148 | 0 | my $res = $self->{ua}->request($req); | |||||
149 | |||||||
150 | 0 | 0 | if (not $res->is_success) { | ||||
151 | 0 | print "Error: " . $res->status_line . "\n"; | |||||
152 | } else { | ||||||
153 | 0 | my $item = $res->as_string; | |||||
154 | 0 | print $self->rewrite($item, "master"); | |||||
155 | 0 | print VIEWFILE $self->rewrite($item, "viewer"); | |||||
156 | 0 | 0 | if ($self->{log_file}) { | ||||
157 | 0 | 0 | open LOG, ">>$self->{tmp_log}" | ||||
158 | or die "can't open $self->{tmp_log}: $!"; | ||||||
159 | 0 | print LOG param('URL')."\n"; | |||||
160 | 0 | close LOG; | |||||
161 | } | ||||||
162 | } | ||||||
163 | 0 | close VIEWFILE; | |||||
164 | |||||||
165 | } | ||||||
166 | 0 | print end_html; | |||||
167 | } | ||||||
168 | } | ||||||
169 | |||||||
170 | sub start { | ||||||
171 | 0 | 0 | 1 | my $self = shift ; | |||
172 | 0 | my ($tag, $attr, $attrseq, $origtext) = @_; | |||||
173 | |||||||
174 | 0 | $self->{result} .= '<' . $tag; | |||||
175 | |||||||
176 | 0 | 0 | if (lc($tag) eq 'html') { | ||||
177 | 0 | $self->{inside} = 1; | |||||
178 | } | ||||||
179 | |||||||
180 | 0 | 0 | return unless $self->{inside}; | ||||
181 | 0 | my $dirpref = $self->{current_dir}; | |||||
182 | |||||||
183 | 0 | my $hostpref = $dirpref; | |||||
184 | 0 | $hostpref =~ s!^((?:ht|f)tp://[^\/]+).*!$1/!; | |||||
185 | |||||||
186 | 0 | 0 | if (defined $dirpref) { | ||||
187 | 0 | 0 | $dirpref .= '/' unless | ||||
188 | substr($dirpref, -1, 1) eq '/'; | ||||||
189 | } | ||||||
190 | |||||||
191 | 0 | 0 | if (defined $attr->{'src'}) { | ||||
192 | # fully qualify any relative IMG paths | ||||||
193 | # perhaps this should get the images locally and | ||||||
194 | # serve them up, too, but for now it still points | ||||||
195 | # to the original sites. | ||||||
196 | 0 | 0 | if ($attr->{'src'} !~ m!^(?:ht|f)tp://!i) { | ||||
197 | 0 | 0 | if (substr($attr->{'src'}, 0, 1) eq '/') { | ||||
198 | 0 | $attr->{'src'} = $hostpref . $attr->{'src'}; | |||||
199 | } else { | ||||||
200 | 0 | print STDERR "$attr->{'src'} -> "; | |||||
201 | 0 | $attr->{'src'} = $dirpref . $attr->{'src'}; | |||||
202 | 0 | print STDERR "$attr->{'src'}\n"; | |||||
203 | } | ||||||
204 | } | ||||||
205 | 0 | $attr->{'src'} =~ s|/+$|/|; | |||||
206 | } | ||||||
207 | |||||||
208 | 0 | 0 | if (defined $attr->{'href'}) { | ||||
209 | 0 | 0 | if ($attr->{'href'} !~ m!^(ht|f)tp://!i) { | ||||
210 | 0 | 0 | if (substr($attr->{'href'}, 0, 1) eq '/') { | ||||
211 | 0 | $attr->{'href'} = $hostpref . $attr->{'href'}; | |||||
212 | } else { | ||||||
213 | 0 | $attr->{'href'} = $dirpref . $attr->{'href'}; | |||||
214 | } | ||||||
215 | } | ||||||
216 | 0 | $attr->{'href'} =~ s!/+$!/!; | |||||
217 | } | ||||||
218 | |||||||
219 | 0 | 0 | if ($tag eq 'a') { | ||||
220 | 0 | 0 | if ($attr->{'href'}) { | ||||
221 | |||||||
222 | 0 | 0 | if ($self->{'which'} eq 'master') { | ||||
223 | # redirect HREFs on the master back into the CGI | ||||||
224 | 0 | my $h = $self->{redirect} . $attr->{'href'}; | |||||
225 | 0 | $attr->{'href'} = $h; | |||||
226 | } else { | ||||||
227 | # convert the viewer's HREFs to some style | ||||||
228 | # change so that aren't as easily tempted | ||||||
229 | # to click off the path | ||||||
230 | 0 | delete $attr->{'href'}; | |||||
231 | |||||||
232 | 0 | $attr->{'color'} = 'red'; | |||||
233 | |||||||
234 | 0 | 0 | push @$attrseq, 'color' | ||||
235 | unless grep $_ eq 'color', @$attrseq; | ||||||
236 | } | ||||||
237 | } | ||||||
238 | } | ||||||
239 | |||||||
240 | 0 | for my $m (@$attrseq) { | |||||
241 | 0 | $self->{result} .= " $m=\"$attr->{$m}\""; | |||||
242 | } | ||||||
243 | |||||||
244 | 0 | $self->{result} .= '>'; | |||||
245 | 0 | 0 | 0 | if ($tag eq 'body' and $self->{which} eq 'master') { | |||
246 | 0 | $self->{result} .= " \n"; |
|||||
247 | 0 | $self->{result} .= "\nSlideShow: "; | |||||
248 | 0 | $self->{result} .= "{master_cgi}\">Top | "; | |||||
249 | 0 | $self->{result} .= "{master_cgi}?URL=last\">Quit "; | |||||
250 | 0 | $self->{result} .= "\n"; | |||||
251 | } | ||||||
252 | |||||||
253 | 0 | $self->{result}; | |||||
254 | } | ||||||
255 | |||||||
256 | sub text { | ||||||
257 | 0 | 0 | 1 | my $self = shift ; | |||
258 | 0 | my $text = shift; | |||||
259 | |||||||
260 | 0 | 0 | return unless $self->{inside}; | ||||
261 | |||||||
262 | 0 | $self->{result} .= $text; | |||||
263 | } | ||||||
264 | |||||||
265 | sub comment { | ||||||
266 | 0 | 0 | my $self = shift ; | ||||
267 | 0 | my $comment = shift; | |||||
268 | 0 | 0 | return unless $self->{inside}; | ||||
269 | 0 | $self->{result} .= ""; | |||||
270 | } | ||||||
271 | |||||||
272 | sub end { | ||||||
273 | 0 | 0 | 1 | my $self = shift ; | |||
274 | 0 | 0 | return unless $self->{inside}; | ||||
275 | |||||||
276 | 0 | my ($tag, $origtext) = @_; | |||||
277 | 0 | 0 | if (lc($tag) eq 'html') { | ||||
278 | 0 | $self->{inside} = 0; | |||||
279 | } | ||||||
280 | 0 | 0 | 0 | if (lc($tag) eq 'body' and $self->{which} eq 'master') { | |||
281 | 0 | $self->{result} .= "\n"; | |||||
282 | |||||||
283 | 0 | $self->{result} .= start_form | |||||
284 | . submit('Next Slide:') | ||||||
285 | . textfield(-name => 'URL', -size=>40, -default=>param('URL')) | ||||||
286 | . end_form; | ||||||
287 | } | ||||||
288 | 0 | $self->{result} .= $origtext; | |||||
289 | } | ||||||
290 | |||||||
291 | sub rewrite { | ||||||
292 | 0 | 0 | 0 | my $self = shift; | |||
293 | 0 | my $html = shift; | |||||
294 | 0 | my $which = shift; | |||||
295 | |||||||
296 | 0 | $html =~ s/^.*?( | |||||
297 | |||||||
298 | 0 | 0 | if (lc($which) eq 'viewer') { | ||||
299 | 0 | $self->{which} = 'viewer'; | |||||
300 | } else { | ||||||
301 | 0 | $self->{which} = 'master'; | |||||
302 | } | ||||||
303 | |||||||
304 | 0 | $self->{result} = ''; | |||||
305 | |||||||
306 | 0 | $self->parse($html); | |||||
307 | |||||||
308 | 0 | return $self->{result}; | |||||
309 | } | ||||||
310 | |||||||
311 | sub client { | ||||||
312 | 0 | 0 | 0 | my %p; | |||
313 | 0 | while (shift) { | |||||
314 | 0 | $p{$_} = shift; | |||||
315 | } | ||||||
316 | |||||||
317 | 0 | 0 | $p{view_file} = "/tmp/viewfile.html" | ||||
318 | unless $p{view_file}; | ||||||
319 | |||||||
320 | 0 | 0 | $p{log_file} = "/tmp/surflog.last" | ||||
321 | unless $p{log_file}; | ||||||
322 | # $DEBUG = 1; | ||||||
323 | |||||||
324 | # Unbuffer STDOUT | ||||||
325 | 0 | $|=1; | |||||
326 | |||||||
327 | 0 | print "HTTP/1.0 200 OK\n"; | |||||
328 | 0 | print "Content-type: multipart/x-mixed-replace;boundary=ThisRandomString\n"; | |||||
329 | 0 | print "\n"; | |||||
330 | 0 | print "--ThisRandomString\n"; | |||||
331 | |||||||
332 | 0 | my $prev_surf_id = "-1"; | |||||
333 | 0 | my $done = 0; | |||||
334 | 0 | my $count = 0; | |||||
335 | 0 | my $TIMEOUT = 60 * 4; # (browser timeout is typically 5 minutes) | |||||
336 | |||||||
337 | # | ||||||
338 | # wait for the file to show up | ||||||
339 | # present the startup message until it does | ||||||
340 | # | ||||||
341 | 0 | while (!-r $p{view_file}) { | |||||
342 | 0 | print "Content-type: text/html\n\n"; | |||||
343 | |||||||
344 | 0 | print "\n"; | |||||
345 | 0 | print " |
|||||
346 | 0 | print "\n"; | |||||
347 | 0 | print "Presentation view\n"; |
|||||
348 | |||||||
349 | 0 | print "time: ".`date`." \n"; |
|||||
350 | 0 | print "Waiting for session to begin... \n\n"; |
|||||
351 | |||||||
352 | 0 | print "\n"; | |||||
353 | 0 | print "\n"; | |||||
354 | |||||||
355 | 0 | print "--ThisRandomString\n"; | |||||
356 | 0 | sleep(5); | |||||
357 | } | ||||||
358 | |||||||
359 | # | ||||||
360 | # Once we've seen the file, we're in a presentation. | ||||||
361 | # | ||||||
362 | 0 | my ($line, $surf_id); | |||||
363 | 0 | 0 | while(($count < $TIMEOUT) && !$done) { | ||||
364 | 0 | $count++; | |||||
365 | 0 | $line = 0; | |||||
366 | 0 | 0 | if (open(ITEM, $p{view_file})) { | ||||
367 | 0 | while (my $item = |
|||||
368 | 0 | $line++; | |||||
369 | 0 | 0 | if ($line == 1) { | ||||
370 | 0 | chop $item; | |||||
371 | 0 | $surf_id = $item; | |||||
372 | |||||||
373 | 0 | 0 | if ($prev_surf_id eq $surf_id) { | ||||
374 | 0 | last; | |||||
375 | } else { | ||||||
376 | 0 | $prev_surf_id = $surf_id; | |||||
377 | 0 | print "Content-type: text/html\n\n"; | |||||
378 | |||||||
379 | ## Reset the presentation counter | ||||||
380 | 0 | $count = 0; | |||||
381 | |||||||
382 | 0 | next; | |||||
383 | } | ||||||
384 | } | ||||||
385 | |||||||
386 | 0 | 0 | if ($item =~ /^$SlideShow::last_marker$/) { | ||||
387 | 0 | print "\n"; | |||||
388 | 0 | print " |
|||||
389 | 0 | print "\n"; | |||||
390 | |||||||
391 | 0 | print ' | |||||
392 | Thank you |
||||||
393 | |||||||
394 | The session has finished. |
||||||
395 | '; | ||||||
396 | 0 | 0 | if ($p{log_file}) { | ||||
397 | 0 | print " \n"; |
|||||
398 | 0 | print "The pages were visited in this order: \n"; |
|||||
399 | |||||||
400 | 0 | open(LOG, $p{log_file}); | |||||
401 | |||||||
402 | 0 | while( |
|||||
403 | 0 | print " |
|||||
404 | } | ||||||
405 | 0 | close(LOG); | |||||
406 | 0 | print " \n"; |
|||||
407 | 0 | print "Save this document locally if you'd like to" | |||||
408 | . " keep a record.\n"; | ||||||
409 | } | ||||||
410 | 0 | $done = 1; | |||||
411 | 0 | next; | |||||
412 | } | ||||||
413 | |||||||
414 | 0 | print $item; | |||||
415 | } | ||||||
416 | 0 | close(ITEM); | |||||
417 | 0 | 0 | print "\n--ThisRandomString\n" if ($line > 1); | ||||
418 | } | ||||||
419 | 0 | sleep(1); | |||||
420 | } | ||||||
421 | |||||||
422 | 0 | 0 | if ($count >= $TIMEOUT) { | ||||
423 | 0 | print "--ThisRandomString\n"; | |||||
424 | |||||||
425 | 0 | print "Content-type: text/html\n\n"; | |||||
426 | 0 | print "\n"; | |||||
427 | 0 | print '',"\n"; | |||||
428 | 0 | print "Timed out.\n"; |
|||||
429 | 0 | print " \n"; |
|||||
430 | 0 | print "This session has been active without update for too long. \n"; |
|||||
431 | 0 | print "Hit Reload if you think the session is still in progress.\n"; | |||||
432 | 0 | print "\n"; | |||||
433 | } | ||||||
434 | |||||||
435 | 0 | print "--ThisRandomString--\n"; | |||||
436 | } | ||||||
437 | |||||||
438 | |||||||
439 | 1; | ||||||
440 | __END__ |