1#
2# Timeline functions
3#
4# Brian Carrier [carrier@sleuthkit.org]
5# Copyright (c) 2001-2005 by Brian Carrier.  All rights reserved
6#
7# This file is part of the Autopsy Forensic Browser (Autopsy)
8#
9# Autopsy is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# Autopsy is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with Autopsy; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22#
23# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
24# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
25# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE.
26# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR
29# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34package Timeline;
35
36use POSIX;    # needed for tzset
37
38# Changing the order of this may affect the main function ordering
39
40$Timeline::BLANK      = 0;
41$Timeline::FRAME      = 1;
42$Timeline::TABS       = 2;
43$Timeline::BODY_ENTER = 3;
44$Timeline::BODY_RUN   = 4;
45$Timeline::TL_ENTER   = 5;
46$Timeline::TL_RUN     = 6;
47$Timeline::VIEW_FR    = 7;
48$Timeline::VIEW_MENU  = 8;
49$Timeline::VIEW_IDX   = 9;
50$Timeline::VIEW_SUM   = 10;
51$Timeline::VIEW       = 11;
52
53# Types of modes for fname (i.e. can we overwrite it if it exists)
54my $FNAME_MODE_INIT = 0;
55my $FNAME_MODE_OVER = 1;
56
57sub main {
58
59    return if ($::LIVE == 1);
60
61    # By default, show the main frame
62    $Args::args{'view'} = $Args::enc_args{'view'} = $Timeline::FRAME
63      unless (exists $Args::args{'view'});
64
65    Args::check_view();
66    my $view = Args::get_view();
67
68    if ($view < $Timeline::VIEW_FR) {
69        if ($view == $Timeline::BLANK) {
70            return blank();
71        }
72        elsif ($view == $Timeline::FRAME) {
73            return frame();
74        }
75        elsif ($view == $Timeline::TABS) {
76            return tabs();
77        }
78        elsif ($view == $Timeline::BODY_ENTER) {
79            return body_enter();
80        }
81        elsif ($view == $Timeline::BODY_RUN) {
82            return body_run();
83        }
84        elsif ($view == $Timeline::TL_ENTER) {
85            return tl_enter();
86        }
87        elsif ($view == $Timeline::TL_RUN) {
88            return tl_run();
89        }
90    }
91    else {
92        if ($view == $Timeline::VIEW_FR) {
93            return view_fr();
94        }
95        elsif ($view == $Timeline::VIEW_MENU) {
96            return view_menu();
97        }
98        elsif ($view == $Timeline::VIEW_IDX) {
99            return view_idx();
100        }
101        elsif ($view == $Timeline::VIEW_SUM) {
102            return view_sum();
103        }
104        elsif ($view == $Timeline::VIEW) {
105            return view();
106        }
107    }
108    Print::print_check_err("Invalid Timeline View");
109}
110
111# Call the appropriate function based on the value of sort
112sub frame {
113    Print::print_html_header_frameset(
114        "Timeline: $Args::args{'case'}:$Args::args{'host'}");
115
116    print "<frameset rows=\"38,*\">\n";
117
118    my $submod = $Timeline::BLANK;
119    $submod = Args::get_submod() if (exists $Args::args{'submod'});
120
121    # Listing
122    print "<frame src=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::TABS&"
123      . "$Args::baseargs&submod=$submod\">\n";
124
125    my $str = "";
126
127    # Contents
128    if ($submod == $Timeline::BLANK) {
129        print
130"<frame src=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::BLANK&$Args::baseargs\" "
131          . "name=\"content\">\n</frameset>\n";
132        return;
133    }
134    elsif ($submod == $Timeline::TL_ENTER) {
135        $str .= "&body=$Args::args{'body'}" if (exists $Args::args{'body'});
136    }
137    elsif ($submod == $Timeline::VIEW_FR) {
138        $str .= "&tl=$Args::args{'tl'}" if (exists $Args::args{'tl'});
139    }
140
141    print
142"<frame src=\"$::PROGNAME?mod=$::MOD_TL&view=$submod&$Args::baseargs$str\" "
143      . "name=\"content\">\n</frameset>\n";
144
145    Print::print_html_footer_frameset();
146    return 0;
147}
148
149# The tabs / button images in timeline view
150sub tabs {
151    Args::check_submod();
152    Print::print_html_header_tabs("Timeline Mode Tabs");
153
154    my $submod = Args::get_submod();
155
156    print "<center><table width=\"800\" border=\"0\" "
157      . "cellspacing=\"0\" cellpadding=\"0\"><tr>\n";
158
159    # Create Datafile
160    print "<td align=\"center\" width=174>"
161      . "<a href=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::FRAME&"
162      . "submod=$Timeline::BODY_ENTER&$Args::baseargs\" target=\"_top\">";
163
164    if ($submod == $Timeline::BODY_ENTER) {
165        print "<img border=0 "
166          . "src=\"pict/tl_t_data_cur.jpg\" "
167          . "width=174 height=38 "
168          . "alt=\"Create Data File (Current Mode)\"></a>\n";
169    }
170    else {
171        print "<img border=0 "
172          . "src=\"pict/tl_t_data_link.jpg\" "
173          . "width=174 height=38 "
174          . "alt=\"Create Data File\"></a>\n";
175    }
176
177    print "</td>\n"
178      . "<td align=\"center\" width=174>"
179      . "<a href=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::FRAME&"
180      . "submod=$Timeline::TL_ENTER&$Args::baseargs\" "
181      . "target=\"_top\">";
182
183    # Create Timeline
184    if ($submod == $Timeline::TL_ENTER) {
185        print "<img border=0 "
186          . "src=\"pict/tl_t_tl_cur.jpg\" "
187          . "width=174 height=38 "
188          . "alt=\"Create Timeline (Current Mode)\"></a>\n";
189    }
190    else {
191        print "<img border=0 "
192          . "src=\"pict/tl_t_tl_link.jpg\" "
193          . "width=174 height=38 "
194          . "alt=\"Create Timeline\"></a>\n";
195    }
196    print "</td>\n"
197      . "<td align=\"center\" width=174>"
198      . "<a href=\"$::PROGNAME?mod=$::MOD_TL&view=$Timeline::FRAME&"
199      . "submod=$Timeline::VIEW_MENU&$Args::baseargs\" "
200      . "target=\"_top\">";
201
202    # View Timeline
203    if (($submod == $Timeline::VIEW_FR) || ($submod == $Timeline::VIEW_MENU)) {
204        print "<img border=0 "
205          . "src=\"pict/tl_t_view_cur.jpg\" "
206          . "width=174 height=38 "
207          . "alt=\"View Timeline (Current Mode)\"></a>\n";
208    }
209    else {
210        print "<img border=0 "
211          . "src=\"pict/tl_t_view_link.jpg\" "
212          . "width=174 height=38 "
213          . "alt=\"View Timeline\"></a>\n";
214    }
215
216    # Notes
217    print "</td>\n" . "<td align=\"center\" width=174>";
218    if ($::USE_NOTES == 1) {
219        print
220"<a href=\"$::PROGNAME?mod=$::MOD_NOTES&view=$Notes::READ_NORM&$Args::baseargs_novol\" "
221          . "target=\"_blank\">"
222          . "<img border=0 "
223          . "src=\"pict/tl_t_notes_link.jpg\" "
224          . "width=174 height=38 "
225          . "alt=\"View Notes\"></a></td>\n";
226    }
227    else {
228        print "<img border=0 "
229          . "src=\"pict/tl_t_notes_org.jpg\" "
230          . "width=174 height=38 "
231          . "alt=\"View Notes\"></a></td>\n";
232    }
233
234    # Help - set to current submod
235    print "<td align=\"center\" width=52>"
236      . "<a href=\"$::HELP_URL\" target=\"_blank\">"
237      . "<img border=0 src=\"pict/tab_help.jpg\" width=52 "
238      . "alt=\"Help\"></a></td>\n";
239
240    # Close
241    print "<td align=\"center\" width=52>"
242      . "<a href=\"$::PROGNAME?mod=$::MOD_CASEMAN&"
243      . "view=$Caseman::VOL_OPEN&$Args::baseargs\" target=\"_top\">"
244      . "<img border=0 src=\"pict/tab_close.jpg\" width=52 "
245      . "alt=\"Exit to Host Manager\"></a></td>\n"
246      . "</tr></table>\n";
247
248    Print::print_html_footer_tabs();
249    return 0;
250}
251
252sub body_enter {
253    Print::print_html_header("Enter Data to Make Body File");
254
255    my $i;
256    my %mnt2img;
257
258    # Cycle through each image we read from fsmorgue
259    foreach $i (keys %Caseman::vol2mnt) {
260        next
261          unless ($Caseman::vol2cat{$i} eq "part");
262        next
263          if ( ($Caseman::vol2ftype{$i} eq "swap")
264            || ($Caseman::vol2ftype{$i} eq "raw"));
265        $mnt2vol{"$Caseman::vol2mnt{$i}--$i"} = $i;
266    }
267
268# sort via parent volume, then starting location, and then mount point (which includes the name)
269    my @mnt = sort {
270        ($Caseman::vol2par{$mnt2vol{$a}} cmp $Caseman::vol2par{$mnt2vol{$b}})
271          or ($Caseman::vol2start{$mnt2vol{$a}} <=>
272            $Caseman::vol2start{$mnt2vol{$b}})
273          or (lc($a) cmp lc($b))
274    } keys %mnt2vol;
275
276    print "<form action=\"$::PROGNAME\" method=\"get\">\n"
277      . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
278      . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::BODY_RUN\">\n"
279      . Args::make_hidden()
280      . "<p>Here  we will process the file system images, collect the temporal data, and save the data to a single file."
281      . "<p>1.  Select one or more of the following images to collect data from:\n"
282      . "<table cellspacing=\"8\" cellpadding=\"2\">";
283
284    for (my $i = 0; $i <= $#mnt; $i++) {
285        my $vol = $mnt2vol{$mnt[$i]};
286
287        print "<tr><td><input type=\"checkbox\" name=\"$vol\" value=\"1\">"
288          . "</td><td><tt>$Caseman::vol2mnt{$vol}</tt></td><td><tt>$Caseman::vol2sname{$vol}</tt></td>"
289          . "<td>$Caseman::vol2ftype{$vol}</td>\n";
290    }
291
292    print "</table><p>2.  Select the data types to gather:<br>\n"
293      . "<table cellspacing=\"8\" cellpadding=\"2\"><tr>"
294      . "<td><input type=\"checkbox\" name=\"al_file\" value=\"1\" CHECKED>"
295      . "</td><td>Allocated Files</td>"
296      . "<td><input type=\"checkbox\" name=\"unal_file\" value=\"1\" CHECKED>"
297      . "</td><td>Unallocated Files</td>"
298      . "</tr></table>\n"
299      . "<p>3.   Enter name of output file (<tt>body</tt>):<br>"
300      . "<tt>$::DATADIR/</tt>"
301      . "<input type=\"text\" name=\"fname\" value=\"body\">\n"
302      . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n"
303      . "<p>4.   Generate MD5 Value? "
304      . "<input type=\"checkbox\" name=\"md5\" value=\"1\" CHECKED>";
305
306    print "<p><input type=\"image\" src=\"pict/but_ok.jpg\" "
307      . "width=43 height=20 alt=\"Ok\" border=\"0\"></form>\n";
308
309    Print::print_html_footer();
310    return 0;
311}
312
313sub body_run {
314    Args::check_fname();
315    Args::check_fname_mode();
316    Print::print_html_header("Make Body File");
317
318    my $fname_rel = Args::get_fname_rel();
319    my $fname     = Args::get_fname();
320
321    my $fname_mode = $Args::args{'fname_mode'};
322
323    if ((-e "$fname") && ($FNAME_MODE_INIT == $fname_mode)) {
324        print "File Already Exists: $fname_rel\n";
325
326        my $hidden = Args::make_hidden();
327
328        $hidden .=
329            "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
330          . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::BODY_RUN\">\n";
331
332        my $i;
333        foreach $i (%Caseman::vol2mnt) {
334            $hidden .= "<input type=\"hidden\" name=\"$i\" value=\"1\">\n"
335              if (exists $Args::args{$i});
336        }
337
338        $hidden .=
339            "<input type=\"hidden\" name=\"al_file\" "
340          . "value=\"$Args::args{'al_file'}\">\n"
341          if (exists $Args::args{'al_file'});
342        $hidden .=
343            "<input type=\"hidden\" name=\"unal_file\" "
344          . "value=\"$Args::args{'unal_file'}\">\n"
345          if (exists $Args::args{'unal_file'});
346        $hidden .=
347            "<input type=\"hidden\" name=\"md5\" "
348          . "value=\"$Args::args{'md5'}\">\n"
349          if (exists $Args::args{'md5'});
350
351        # Make a new name
352        print "<form action=\"$::PROGNAME\" method=\"get\">\n"
353          . "New Name: <input type=\"text\" name=\"fname\">"
354          . "<table cellspacing=\"30\" cellpadding=\"2\"><tr><td>"
355          . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n"
356          . "$hidden"
357          . "<input type=\"image\" src=\"pict/but_new_name.jpg\" "
358          . "width=79 height=20 alt=\"Use New Name\" border=\"0\">\n"
359          . "</form></td>\n";
360
361        # Overwrite it
362        print "<td><form action=\"$::PROGNAME\" method=\"get\">\n"
363          . "<input type=\"image\" src=\"pict/but_replace.jpg\" "
364          . "width=66 height=20 alt=\"Replace\" border=\"0\"><br>\n"
365          . "<input type=\"hidden\" name=\"fname\" value=\"$Args::args{'fname'}\">\n"
366          . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_OVER\">\n"
367          . "$hidden"
368          . "</form></td></tr></table>";
369
370        return 0;
371    }
372
373    # we will be appending to the file so we should del it now
374    if (-e "$fname") {
375        unlink($fname);
376    }
377
378    my $log_files = "";
379    my $log_type  = "";
380
381    # What kind of data are we collecting?
382    my $al_file = 0;
383    if (exists $Args::args{'al_file'}) {
384        $al_file = $Args::args{'al_file'};
385        $log_type .= "Allocated Files";
386    }
387
388    my $unal_file = 0;
389    if (exists $Args::args{'unal_file'}) {
390        $unal_file = $Args::args{'unal_file'};
391        $log_type .= ", " if ($log_type ne "");
392        $log_type .= "Unallocated Files";
393    }
394
395    if (($unal_file == 0) && ($al_file == 0)) {
396        print
397          "No data types were selected.  You must select at least one.<br>\n";
398        return 1;
399    }
400
401    my $tz = "";
402    $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq "");
403
404    my $i;
405    my $found = 0;
406    local *OUT;
407
408    # Analyze each image - the image names are passed as an argument
409    foreach $i (keys %Caseman::vol2mnt) {
410        if (exists $Args::args{$i}) {
411
412            $found = 1;
413            my $ftype   = $Caseman::vol2ftype{$i};
414            my $img     = $Caseman::vol2path{$i};
415            my $offset  = $Caseman::vol2start{$i};
416            my $imgtype = $Caseman::vol2itype{$i};
417            my $mnt     = $Caseman::vol2mnt{$i};
418
419            $log_files .= ", " if ($log_files ne "");
420            $log_files .= "$i";
421
422            if (($al_file) && ($unal_file)) {
423                print "Running <tt>fls -r -m</tt> on <tt>$i</tt><br>\n";
424                Exec::exec_pipe(*OUT,
425"'$::TSKDIR/fls' $tz -s $Caseman::ts -m '$mnt' -f $ftype -r -o $offset -i $imgtype $img >> '$fname'"
426                );
427                print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT));
428                close(OUT);
429            }
430            elsif ($al_file) {
431                print "Running <tt>fls -ru -m</tt> on <tt>$i</tt><br>\n";
432                Exec::exec_pipe(*OUT,
433"'$::TSKDIR/fls' $tz -s $Caseman::ts -m '$mnt' -f $ftype -ru -o $offset -i $imgtype $img >> '$fname'"
434                );
435                print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT));
436                close(OUT);
437            }
438            elsif ($unal_file) {
439                print "Running <tt>fls -rd -m</tt> on <tt>$i</tt><br>\n";
440                Exec::exec_pipe(*OUT,
441"'$::TSKDIR/fls' $tz -s $Caseman::ts -m '$mnt' -f $ftype -rd -o $offset -i $imgtype $img >> '$fname'"
442                );
443                print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT));
444                close(OUT);
445            }
446        }
447    }
448
449    unless ($found) {
450        print
451"No images were given for analysis.  At least one must be selected.<br>\n";
452        return 1;
453    }
454
455    Print::log_host_inv(
456        "Saving timeline data for $log_type for $log_files to $fname_rel");
457
458    # append to  host config
459    my $bod_vol = Caseman::add_vol_host_config("body", $fname_rel);
460    $Caseman::vol2cat{$bod_vol}   = "timeline";
461    $Caseman::vol2ftype{$bod_vol} = "body";
462    $Caseman::vol2itype{$bod_vol} = "raw";
463    $Caseman::vol2path{$bod_vol}  = $fname;
464    $Caseman::vol2start{$bod_vol} = 0;
465    $Caseman::vol2end{$bod_vol}   = 0;
466    $Caseman::vol2sname{$bod_vol} = $fname_rel;
467
468    print "<br>Body file saved to <tt>$fname</tt><br><br>\n"
469      . "Entry added to host config file<br><br>\n";
470
471    # Calculate MD5
472    if ((exists $Args::args{'md5'}) && ($Args::args{'md5'} == 1)) {
473        print "Calculating MD5 Value<br><br>\n";
474        my $m = Hash::int_create_wrap($bod_vol);
475        print "MD5 Value: <tt>$m</tt><br><br>\n";
476    }
477
478    print "<p>The next step is to sort the data into a timeline."
479      . "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n"
480      . "<input type=\"hidden\" name=\"body\" value=\"$bod_vol\">\n"
481      . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
482      . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::FRAME\">\n"
483      . "<input type=\"hidden\" name=\"submod\" value=\"$Timeline::TL_ENTER\">\n"
484      . Args::make_hidden()
485      . "<input type=\"image\" src=\"pict/but_ok.jpg\" "
486      . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n";
487
488    Print::print_html_footer();
489    return 0;
490}
491
492my $OTYPE_NORM   = 1;
493my $OTYPE_HOURLY = 2;
494my $OTYPE_DAILY  = 3;
495
496sub tl_enter {
497    Print::print_html_header("Enter data for timeline");
498
499    my @body;
500
501    # Find the body files if we will be looking for them
502    unless ((exists $Args::args{'body'})
503        && (exists $Caseman::vol2cat{$Args::args{'body'}}))
504    {
505        foreach my $k (keys %Caseman::vol2cat) {
506            if (   ($Caseman::vol2cat{$k} eq "timeline")
507                && ($Caseman::vol2ftype{$k} eq "body"))
508            {
509                push @body, $k;
510            }
511        }
512
513        if (scalar(@body) == 0) {
514            print "There are currently no <tt>body</tt> files "
515              . "for this host.<br>You must create the intermediate "
516              . "data file before you can perform this step<br>\n"
517              . "<p><a target=\"_top\"  "
518              . "href=\"$::PROGNAME?$Args::baseargs&"
519              . "mod=$::MOD_TL&view=$Timeline::FRAME&"
520              . "submod=$Timeline::BODY_ENTER\">"
521              . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" "
522              . "width=\"43\" height=20 border=\"0\">"
523              . "</a>\n";
524            return 1;
525        }
526    }
527    print "Now we will sort the data and save it to a timeline.<p>\n"
528      . "<form action=\"$::PROGNAME\" method=\"get\">\n"
529      . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
530      . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::TL_RUN\">\n"
531      . Args::make_hidden()
532      . "1.  Select the data input file (<tt>body</tt>):\n"
533      . "<table cellspacing=\"0\" cellpadding=\"2\">";
534
535    # if the body file was specified then just print it
536    if (exists $Args::args{'body'}) {
537        print "<tr><td><input type=\"radio\" name=\"body\" "
538          . "value=\"$Args::args{'body'}\" CHECKED>"
539          . "</td><td>$Caseman::vol2sname{$Args::args{'body'}}</td>\n";
540    }
541    else {
542        my @body_sort = sort { lc($a) cmp lc($b) } @body;
543        my $chk = " CHECKED";
544        for (my $i = 0; $i <= $#body_sort; $i++) {
545            print "<tr><td><input type=\"radio\" name=\"body\" "
546              . "value=\"$body_sort[$i]\" $chk></td><td>$Caseman::vol2sname{$body_sort[$i]}</td>\n";
547            $chk = "";
548        }
549    }
550
551    my $cur_mon  = 1 +    (localtime())[4];
552    my $cur_year = 1900 + (localtime())[5];
553
554    # STARTING DATE
555    print "</table>\n"
556      . "<p>2.  Enter the starting date:<br>\n"
557      . "None: <input type=\"radio\" name=\"st_none\" value=\"1\" CHECKED><br>"
558      . "Specify: <input type=\"radio\" name=\"st_none\" value=\"0\">  "
559      . "<select name=\"st_mon\" size=\"1\">";
560
561    for my $i (1 .. 12) {
562        if ($i == $cur_mon) {
563            print "<option selected value=\"$i\">$::d2m[$i]</option>\n";
564        }
565        else {
566            print "<option value=\"$i\">$::d2m[$i]</option>\n";
567        }
568    }
569
570    print "</select>"
571      . "<select name=\"st_day\" size=\"1\">"
572      . "<option selected>1</option>\n";
573
574    for my $i (2 .. 31) {
575        print "<option value=\"$i\">$i</option>\n";
576    }
577
578    print "</select>"
579      . "<input type=\"text\" name=\"st_year\" size=\"6\" value=\"$cur_year\">\n";
580
581    # END DATE
582    print "<p>3.   Enter the ending date:<br>\n"
583      . "None: <input type=\"radio\" name=\"end_none\" value=\"1\" CHECKED><br>\n"
584      . "Specify: <input type=\"radio\" name=\"end_none\" value=\"0\">  \n"
585      . "<select name=\"end_mon\" size=\"1\">\n";
586
587    for my $i (1 .. 12) {
588        if ($i == $cur_mon) {
589            print "<option selected value=\"$i\">$::d2m[$i]</option>\n";
590        }
591        else {
592            print "<option value=\"$i\">$::d2m[$i]</option>\n";
593        }
594    }
595
596    print "</select>\n"
597      . "<select name=\"end_day\" size=\"1\">\n"
598      . "<option selected value=\"1\">1</option>\n";
599
600    for my $i (2 .. 31) {
601        print "<option value=\"$i\">$i</option>\n";
602    }
603
604    print "</select>"
605      . "<input type=\"text\" name=\"end_year\" size=\"6\" value=\"$cur_year\">\n";
606
607    # FILE NAME
608    print "<p>4.   Enter the file name to save as:<br>"
609      . "<tt>$::DATADIR/</tt><input type=\"text\" size=36 name=\"fname\" value=\"timeline.txt\"><br>\n"
610      . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n";
611
612    # Get only the UNIX images - since only they have /etc/passwd and group
613    my @unix_imgs;
614    my $root_vol = "";
615    foreach my $i (keys %Caseman::vol2ftype) {
616        my $f = $Caseman::vol2ftype{$i};
617
618        next
619          unless (($f =~ /^ext/)
620            || ($f =~ /^ufs/)
621            || ($f =~ /^linux/)
622            || ($f =~ /bsd$/)
623            || ($f =~ /^solaris$/)
624            || ($f =~ /^bsdi$/));
625
626        push @unix_vols, $i;
627
628        # Keep a reference to an image with '/' as the mounting point
629        $root_vol = $i
630          if ($Caseman::vol2mnt{$i} eq '/');
631    }
632
633    my $cnt = 5;
634    if (scalar @unix_vols > 0) {
635
636        print
637"<p>$cnt.  Select the UNIX image that contains the /etc/passwd and /etc/group files:<br>\n";
638        $cnt++;
639
640        print "<select name=\"pw_vol\">\n";
641
642        # If we did not find an image that has a / mounting point, then
643        # we will use none as the default.
644        if ($root_vol eq "") {
645            print "<option value=\"\" selected>None</option>\n";
646        }
647        else {
648            print "<option value=\"\">None</option>\n";
649        }
650
651        foreach my $vol (@unix_vols) {
652            if ($root_vol eq $vol) {
653                print
654"<option value=\"$vol\" selected>$Caseman::vol2sname{$vol} ($Caseman::vol2mnt{$vol})"
655                  . "</option>\n";
656            }
657            else {
658                print
659"<option value=\"$vol\">$Caseman::vol2sname{$vol} ($Caseman::vol2mnt{$vol})</option>\n";
660            }
661        }
662
663        print "</select>\n";
664    }
665
666    print "<p>$cnt. Choose the output format:<br>\n";
667    $cnt++;
668    print
669"&nbsp;&nbsp;<input type=\"radio\" name=\"sort\" value=\"$OTYPE_NORM\" CHECKED>Tabulated (normal)<br>\n"
670      . "&nbsp;&nbsp;<input type=\"radio\" name=\"sort\" value=\"$OTYPE_HOURLY\">Comma delimited with hourly summary<br>\n"
671      . "&nbsp;&nbsp;<input type=\"radio\" name=\"sort\" value=\"$OTYPE_DAILY\">Comma delimited with daily summary<br>\n";
672
673    print "<p>$cnt.  Generate MD5 Value? ";
674    $cnt++;
675
676    print "<input type=\"checkbox\" name=\"md5\" value=\"1\" CHECKED>\n";
677
678    # Create Button
679    print "<p><input type=\"image\" src=\"pict/but_ok.jpg\" "
680      . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n";
681
682    Print::print_html_footer();
683    return 0;
684}
685
686sub tl_run {
687    Args::check_fname();
688    Args::check_body();
689    Args::check_sort();
690
691    Print::print_html_header("Make Timeline");
692
693    my $body      = Args::get_body();
694    my $fname     = Args::get_fname();
695    my $fname_rel = Args::get_fname_rel();
696    my $otype     = Args::get_sort();
697
698    my $fname_mode = $Args::args{'fname_mode'};
699
700    if ((-e "$fname") && ($FNAME_MODE_INIT == $fname_mode)) {
701        print "File Already Exists: <tt>$fname_rel</tt><br>\n";
702
703        my $hidden =
704          "<input type=\"hidden\" name=\"body\" value=\"$Args::args{'body'}\">"
705          . Args::make_hidden();
706
707        $hidden .=
708            "<input type=\"hidden\" name=\"st_none\" "
709          . "value=\"$Args::args{'st_none'}\">\n"
710          if (exists $Args::args{'st_none'});
711        $hidden .=
712            "<input type=\"hidden\" name=\"st_year\" "
713          . "value=\"$Args::args{'st_year'}\">\n"
714          if (exists $Args::args{'st_year'});
715        $hidden .=
716            "<input type=\"hidden\" name=\"st_day\" "
717          . "value=\"$Args::args{'st_day'}\">\n"
718          if (exists $Args::args{'st_day'});
719        $hidden .=
720            "<input type=\"hidden\" name=\"st_mon\" "
721          . "value=\"$Args::args{'st_mon'}\">\n"
722          if (exists $Args::args{'st_mon'});
723        $hidden .=
724            "<input type=\"hidden\" name=\"end_none\" "
725          . "value=\"$Args::args{'end_none'}\">\n"
726          if (exists $Args::args{'end_none'});
727        $hidden .=
728            "<input type=\"hidden\" name=\"end_year\" "
729          . "value=\"$Args::args{'end_year'}\">\n"
730          if (exists $Args::args{'end_year'});
731        $hidden .=
732            "<input type=\"hidden\" name=\"end_day\" "
733          . "value=\"$Args::args{'end_day'}\">\n"
734          if (exists $Args::args{'end_day'});
735        $hidden .=
736            "<input type=\"hidden\" name=\"end_mon\" "
737          . "value=\"$Args::args{'end_mon'}\">\n"
738          if (exists $Args::args{'end_mon'});
739        $hidden .=
740            "<input type=\"hidden\" name=\"tz\" "
741          . "value=\"$Args::args{'tz'}\">\n"
742          if (exists $Args::args{'tz'});
743        $hidden .=
744            "<input type=\"hidden\" name=\"pw_vol\" "
745          . "value=\"$Args::args{'pw_vol'}\">\n"
746          if (exists $Args::args{'pw_vol'});
747        $hidden .=
748            "<input type=\"hidden\" name=\"md5\" "
749          . "value=\"$Args::args{'md5'}\">\n"
750          if (exists $Args::args{'md5'});
751        $hidden .=
752            "<input type=\"hidden\" name=\"sort\" "
753          . "value=\"$Args::args{'sort'}\">\n"
754          if (exists $Args::args{'sort'});
755
756        # Make a new name
757        print "<form action=\"$::PROGNAME\" method=\"get\">\n"
758          . "New Name: <input type=\"text\" name=\"fname\">"
759          . "<table cellspacing=\"30\" cellpadding=\"2\"><tr><td>"
760          . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
761          . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::TL_RUN\">\n"
762          . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_INIT\">\n"
763          . "$hidden\n"
764          . "<input type=\"image\" src=\"pict/but_new_name.jpg\" "
765          . "width=79 height=20 alt=\"Use New Name\" border=\"0\">\n"
766          . "</form></td>\n";
767
768        # Overwrite it
769        print "<td><form action=\"$::PROGNAME\" method=\"get\">\n"
770          . "<input type=\"image\" src=\"pict/but_replace.jpg\" "
771          . "width=66 height=20 alt=\"Replace\" border=\"0\"><br>\n"
772          . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
773          . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::TL_RUN\">\n"
774          . "<input type=\"hidden\" name=\"fname\" value=\"$Args::args{'fname'}\">\n"
775          . "<input type=\"hidden\" name=\"fname_mode\" value=\"$FNAME_MODE_OVER\"
776>\n" . "$hidden\n" . "</form></td></tr></table>";
777
778        return 0;
779    }
780
781    my $mon;
782    my $day;
783    my $year;
784
785    my $date = "";
786
787    # Get the start date
788    unless ((exists $Args::args{'st_none'}) && ($Args::args{'st_none'} == 1)) {
789
790        if (exists $Args::args{'st_mon'}) {
791            Args::check_st_mon();
792            $mon = Args::get_st_mon();
793            if (($mon < 1) || ($mon > 12)) {
794                print("Invalid starting month\n");
795                return 1;
796            }
797            if ($mon < 10) {
798                $mon = "0" . $mon;
799            }
800        }
801        if (exists $Args::args{'st_year'}) {
802            Args::check_st_year();
803            $year = Args::get_st_year();
804            if (($year < 1970) || ($year > 2020)) {
805                print("Invalid starting year\n");
806                return 1;
807            }
808        }
809        if (   (exists $Args::args{'st_day'})
810            && ($Args::args{'st_day'} =~ /^(\d\d?)$/))
811        {
812            $day = $1;
813            if (($day < 1) || ($day > 31)) {
814                print("Invalid starting day\n");
815                return 1;
816            }
817            if ($day < 10) {
818                $day = "0" . $day;
819            }
820        }
821        else {
822            print("Invalid start day\n");
823            return 1;
824        }
825
826        $date = "$year-$mon-$day";
827    }
828
829    unless ((exists $Args::args{'end_none'}) && ($Args::args{'end_none'} == 1))
830    {
831
832        if ($date eq "") {
833            print "Begin date must be given if ending date is given<br>";
834            return 1;
835        }
836
837        if (   (exists $Args::args{'end_mon'})
838            && ($Args::args{'end_mon'} =~ /^(\d\d?)$/))
839        {
840            $mon = $1;
841            if (($mon < 1) || ($mon > 12)) {
842                print("Invalid end month\n");
843                return 1;
844            }
845            if ($mon < 10) {
846                $mon = "0" . $mon;
847            }
848        }
849        else {
850            print("Invalid end month\n");
851            return 1;
852        }
853        if (   (exists $Args::args{'end_year'})
854            && ($Args::args{'end_year'} =~ /^(\d\d\d\d)$/))
855        {
856            $year = $1;
857            if (($year < 1970) || ($year > 2020)) {
858                print("Invalid ending year\n");
859                return 1;
860            }
861
862        }
863        else {
864            print("Invalid end year\n");
865            return 1;
866        }
867        if (   (exists $Args::args{'end_day'})
868            && ($Args::args{'end_day'} =~ /^(\d\d?)$/))
869        {
870            $day = $1;
871            if (($day < 1) || ($day > 31)) {
872                print("Invalid end day\n");
873                return 1;
874            }
875            if ($day < 10) {
876                $day = "0" . $day;
877            }
878        }
879        else {
880            print("Invalid end day\n");
881            return 1;
882        }
883
884        $date .= "..$year-$mon-$day";
885    }
886
887    # temp strings for the password and group files
888    my $pw_tmp   = "";
889    my $gr_tmp   = "";
890    my $mac_args = "";
891    my $log      = "";
892
893    local *OUT;
894
895    # Password and Group Files
896    if ((exists $Args::args{'pw_vol'}) && ($Args::args{'pw_vol'} ne "")) {
897        Args::check_vol('pw_vol');
898        my $pw_vol = Args::get_vol('pw_vol');
899
900        my $ftype   = $Caseman::vol2ftype{$pw_vol};
901        my $img     = $Caseman::vol2path{$pw_vol};
902        my $offset  = $Caseman::vol2start{$pw_vol};
903        my $imgtype = $Caseman::vol2itype{$pw_vol};
904
905        $log .= "Password & Group File ($pw_vol) ";
906
907        # Get the passwd file meta and copy the file
908        Exec::exec_pipe(*OUT,
909"'$::TSKDIR/ifind' -f $ftype -n 'etc/passwd' -o $offset -i $imgtype $img"
910        );
911        my $pwi = Exec::read_pipe_line(*OUT);
912        close(OUT);
913
914        $pwi = "Error getting meta for passwd"
915          if ((!defined $pwi) || ($pwi eq ""));
916
917        # Do the Taint Checking
918        if ($pwi =~ /^($::REG_META)$/) {
919            $pwi = $1;
920
921            $log .= "Password Meta Address ($pwi) ";
922
923            # Find a temp name that we can call it
924            my $i;
925            for ($i = 0;; $i++) {
926                unless (-e "$fname.pw-$i") {
927                    $pw_tmp = "$fname.pw-$i";
928                    last;
929                }
930            }
931
932            Exec::exec_sys(
933"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $pwi > '$pw_tmp'"
934            );
935            $mac_args .= " -p \'$pw_tmp\' ";
936
937        }
938        else {
939            print(
940"Error finding /etc/passwd meta in $Caseman::vol2sname{$pw_vol} ($pwi)<br>"
941            );
942            Print::log_host_inv(
943"$Caseman::vol2sname{$pw_vol}: /etc/passwd file not found for timeline"
944            );
945        }
946
947        # Get the group file meta and copy the file
948        Exec::exec_pipe(*OUT,
949"'$::TSKDIR/ifind' -f $ftype -n 'etc/group' -o $offset -i $imgtype $img"
950        );
951        my $gri = Exec::read_pipe_line(*OUT);
952        close(OUT);
953
954        $gri = "Error getting meta for group"
955          if ((!defined $gri) || ($gri eq ""));
956
957        # Do the Taint Checking
958        if ($gri =~ /^($::REG_META)$/) {
959            $gri = $1;
960
961            $log .= "Group Meta Address ($gri) ";
962
963            # Find a temp name that we can call it
964            my $i;
965            for ($i = 0;; $i++) {
966                unless (-e "$fname.gr-$i") {
967                    $gr_tmp = "$fname.gr-$i";
968                    last;
969                }
970            }
971            Exec::exec_sys(
972"'$::TSKDIR/icat' -f $ftype -o $offset -i $imgtype $img $gri > '$gr_tmp'"
973            );
974            $mac_args .= " -g \'$gr_tmp\' ";
975        }
976        else {
977            print(
978"Error finding /etc/group meta in $Caseman::vol2sname{$pw_vol} ($gri)<br>"
979            );
980            Print::log_host_inv(
981"$Caseman::vol2sname{$pw_vol}: /etc/group file not found for timeline"
982            );
983        }
984    }
985
986    if ($date eq "") {
987        print
988          "Creating Timeline using all dates (Time Zone: $Caseman::tz)<br>\n";
989        Print::log_host_inv(
990"$Caseman::vol2sname{$body}: Creating timeline using all dates (TZ: $Caseman::tz) ${log}to $fname_rel"
991        );
992    }
993    else {
994        print "Creating Timeline for $date (Time Zone: $Caseman::tz)<br>\n";
995        Print::log_host_inv(
996"$Caseman::vol2sname{$body}: Creating timeline for $date (TZ: $Caseman::tz) ${log}to $fname_rel"
997        );
998    }
999
1000    my $tz = "";
1001    $tz = "-z '$Caseman::tz'" unless ("$Caseman::tz" eq "");
1002
1003    # mactime needs the path to run the 'date' command
1004    $ENV{PATH} = "/bin:/usr/bin";
1005    local *OUT;
1006    if ($otype == $OTYPE_NORM) {
1007        Exec::exec_pipe(*OUT,
1008"LANG=C LC_ALL=C '$::TSKDIR/mactime' -b $Caseman::vol2path{$body} $tz -i day '${fname}.sum' $mac_args $date > '$fname'"
1009        );
1010    }
1011    elsif ($otype == $OTYPE_HOURLY) {
1012        Exec::exec_pipe(*OUT,
1013"LANG=C LC_ALL=C '$::TSKDIR/mactime' -b $Caseman::vol2path{$body} $tz  -d -i hour '${fname}.sum' $mac_args $date > '$fname'"
1014        );
1015    }
1016    elsif ($otype == $OTYPE_DAILY) {
1017        Exec::exec_pipe(*OUT,
1018"LANG=C LC_ALL=C '$::TSKDIR/mactime' -b $Caseman::vol2path{$body} $tz -d -i day '${fname}.sum' $mac_args $date > '$fname'"
1019        );
1020    }
1021    else {
1022        Print::print_err("Unknown output type");
1023    }
1024
1025    print "$_<br>\n" while ($_ = Exec::read_pipe_line(*OUT));
1026    close(OUT);
1027    $ENV{PATH} = "";
1028
1029    # Remove the password and group files
1030    unlink("$pw_tmp") if ($pw_tmp ne "");
1031    unlink("$gr_tmp") if ($gr_tmp ne "");
1032
1033    print "<br>Timeline saved to <tt>$fname</tt><br><br>\n";
1034
1035    # append to fsmorgue if a normal timeline
1036    if ($otype == $OTYPE_NORM) {
1037        my $tl_vol = Caseman::add_vol_host_config("timeline", $fname_rel);
1038        print "Entry added to host config file<br><br>\n";
1039
1040        $Caseman::vol2cat{$tl_vol}   = "timeline";
1041        $Caseman::vol2ftype{$tl_vol} = "timeline";
1042        $Caseman::vol2itype{$tl_vol} = "raw";
1043        $Caseman::vol2path{$tl_vol}  = "$fname";
1044        $Caseman::vol2start{$tl_vol} = 0;
1045        $Caseman::vol2end{$tl_vol}   = 0;
1046        $Caseman::vol2sname{$tl_vol} = $fname_rel;
1047
1048        # Calculate MD5
1049        if ((exists $Args::args{'md5'}) && ($Args::args{'md5'} == 1)) {
1050            print "Calculating MD5 Value<br><br>\n";
1051            my $m = Hash::int_create_wrap($tl_vol);
1052            print "MD5 Value: <tt>$m</tt><br><br>\n";
1053        }
1054
1055        print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_top\">\n"
1056          . "<input type=\"hidden\" name=\"tl\" value=\"$tl_vol\">\n"
1057          . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
1058          . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::FRAME\">\n"
1059          . "<input type=\"hidden\" name=\"submod\" value=\"$Timeline::VIEW_FR\">\n"
1060          . Args::make_hidden()
1061          . "<input type=\"image\" src=\"pict/but_ok.jpg\" "
1062          . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n"
1063          . "(NOTE: It is easier to view the timeline in a text editor than here)";
1064    }
1065    else {
1066        print
1067          "Comma delimited files cannot be viewed from within Autopsy.<br>\n"
1068          . "Open it in a spreadsheet or other data processing tool.<br>\n";
1069    }
1070    Print::print_html_footer();
1071    return 0;
1072}
1073
1074sub view_menu {
1075    Print::print_html_header("View Timeline Menu");
1076
1077    my @tl;
1078
1079    # Find the timelines in the images hash
1080    foreach my $k (keys %Caseman::vol2cat) {
1081        if (   ($Caseman::vol2cat{$k} eq "timeline")
1082            && ($Caseman::vol2ftype{$k} eq "timeline"))
1083        {
1084            push @tl, $k;
1085        }
1086    }
1087
1088    if (scalar(@tl) == 0) {
1089        print "There are currently no timeline files in the "
1090          . "host config file.<br>One must first be created before you "
1091          . "can view it<br>\n";
1092
1093        print "<p><a target=\"_top\"  "
1094          . "href=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&view=$Timeline::FRAME&"
1095          . "submod=$Timeline::TL_ENTER\">"
1096          . "<img src=\"pict/but_ok.jpg\" alt=\"Ok\" "
1097          . "width=\"43\" height=20 border=\"0\">"
1098          . "</a>\n";
1099
1100        return 1;
1101    }
1102
1103    print "<form action=\"$::PROGNAME\" method=\"get\">\n"
1104      . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
1105      . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::VIEW_FR\">\n"
1106      . Args::make_hidden()
1107      . "1.  Select the timeline file:\n"
1108      . "<table cellspacing=\"0\" cellpadding=\"2\">\n";
1109
1110    my @tl_sort = sort { lc($a) cmp lc($b) } @tl;
1111    for (my $i = 0; $i <= $#tl_sort; $i++) {
1112        print "<tr><td><input type=\"radio\" name=\"tl\" "
1113          . "value=\"$tl_sort[$i]\"></td><td>$Caseman::vol2sname{$tl_sort[$i]}</td>\n";
1114    }
1115
1116    print "</table>\n"
1117      . "<input type=\"image\" src=\"pict/but_ok.jpg\" "
1118      . "width=43 height=20 alt=\"Ok\" border=\"0\">\n</form>\n";
1119
1120    Print::print_html_footer();
1121    return 0;
1122}
1123
1124sub view_fr {
1125    Args::check_tl();
1126
1127    Print::print_html_header_frameset("");
1128    my $tl_vol = Args::get_tl();
1129    my $tl     = $Caseman::vol2path{$tl_vol};
1130    my $url    = "";
1131
1132    unless (exists $Args::args{'st_mon'}) {
1133
1134        unless (open(TL, $tl)) {
1135            print("Error opening $tl");
1136            return (1);
1137        }
1138
1139        my $beg_mon;
1140        my $beg_year;
1141        my $cnt = 0;
1142        while (<TL>) {
1143            $cnt++;
1144            if (/^(?:\w\w\w )?(\w\w\w)\s+\d\d\s+(\d\d\d\d)\s+\d\d:\d\d:\d\d/) {
1145                $url = "tl=$tl_vol&st_mon=$::m2d{$1}&st_year=$2";
1146
1147            }
1148            last;
1149        }
1150        close(TL);
1151
1152        if ($cnt == 0) {
1153            print "Empty timeline<br>\n";
1154            return 1;
1155        }
1156        if ($url eq "") {
1157            print "Invalid Timeline<br>\n";
1158            return 1;
1159        }
1160    }
1161    else {
1162        $url =
1163            "tl=$tl_vol&st_mon=$Args::enc_args{'st_mon'}&"
1164          . "st_year=$Args::enc_args{'st_year'}";
1165    }
1166
1167    print "<frameset rows=\"65,*\">\n"
1168      . "<frame src=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&"
1169      . "view=$Timeline::VIEW_IDX&$url\">\n"
1170      . "<frame src=\"$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&"
1171      . "view=$Timeline::VIEW&$url\">\n</frameset>";
1172
1173    Print::print_html_footer();
1174    return 0;
1175}
1176
1177sub view_idx {
1178    Args::check_st_mon();
1179    Args::check_st_year();
1180    Args::check_tl();
1181
1182    Print::print_html_header("View Timeline Index");
1183
1184    my $mon    = Args::get_st_mon();
1185    my $year   = Args::get_st_year();
1186    my $tl_vol = Args::get_tl();
1187    my $tl     = $Caseman::vol2path{$tl_vol};
1188
1189    print "<center>";
1190    my $url =
1191        "$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&view=$Timeline::VIEW_FR&"
1192      . "tl=$tl_vol";
1193
1194    # Next and Previous pointers
1195    my $pyear = $year;
1196    my $pmon  = $mon - 1;
1197    if ($pmon == 0) {
1198        $pmon = 12;
1199        $pyear--;
1200    }
1201    print "<table cellspacing=\"0\" cellpadding=\"2\">\n"
1202      . "<tr><td align=\"center\">"
1203      . "<a href=\"$url&st_mon=$pmon&st_year=$pyear\" target=\"_parent\">"
1204      . "&lt;- $::d2m[$pmon] $pyear</a></td>\n"
1205      . "<td>&nbsp;</td>\n";
1206
1207    if (-e "${tl}.sum") {
1208        print "<td><a href=\"$::PROGNAME?$Args::baseargs&"
1209          . "mod=$::MOD_TL&view=$Timeline::VIEW_SUM&"
1210          . "tl=$tl_vol\" target=\"_parent\">"
1211          . "Summary</td>\n"
1212          . "<td>&nbsp;</td>\n";
1213    }
1214
1215    my $nyear = $year;
1216    my $nmon  = $mon + 1;
1217    if ($nmon == 13) {
1218        $nmon = 1;
1219        $nyear++;
1220    }
1221
1222    print "<td align=\"center\">"
1223      . "<a href=\"$url&st_mon=$nmon&st_year=$nyear\" target=\"_parent\">"
1224      . "$::d2m[$nmon] $nyear -&gt</a></td></tr></table>\n";
1225
1226    # Make a form to enter the next month and year to show.
1227    # it defaults to the current location
1228    print "<form action=\"$::PROGNAME\" method=\"get\" target=\"_parent\">\n"
1229      . "<input type=\"hidden\" name=\"mod\" value=\"$::MOD_TL\">\n"
1230      . "<input type=\"hidden\" name=\"view\" value=\"$Timeline::VIEW_FR\">\n"
1231      . "<input type=\"hidden\" name=\"tl\" value=\"$tl_vol\">\n"
1232      . Args::make_hidden()
1233      .
1234
1235      "<table cellspacing=\"0\" cellpadding=\"2\">\n"
1236      . "<tr><td align=\"center\"><select name=\"st_mon\" size=\"1\">\n";
1237
1238    for my $i (1 .. 12) {
1239        if ($i == $mon) {
1240            print "<option selected value=\"$i\">$::d2m[$i]</option>\n";
1241        }
1242        else {
1243            print "<option value=\"$i\">$::d2m[$i]</option>\n";
1244        }
1245    }
1246
1247    print "</select></td>"
1248      . "<td align=\"center\">"
1249      . "<input type=\"text\" name=\"st_year\" size=\"6\" value=\"$year\">"
1250      . "</td>"
1251      . "<td align=\"center\">"
1252      . "<input type=\"image\" src=\"pict/but_ok.jpg\" alt=\"Ok\" "
1253      . "width=43 height=20 border=\"0\"></td>\n"
1254      . "</tr></table></form>\n";
1255
1256    Print::print_html_footer();
1257    return 0;
1258}
1259
1260# Display the contents of the summary file (hits per day) and show
1261# it as hits per month
1262sub view_sum {
1263    Args::check_tl();
1264
1265    Print::print_html_header("View Timeline Summary");
1266
1267    my $tl_vol = Args::get_tl();
1268    my $tl     = $Caseman::vol2path{$tl_vol};
1269
1270    $tl .= ".sum";
1271
1272    open(TL, "<$tl") or die "Can not open $tl";
1273    my $url =
1274        "$::PROGNAME?$Args::baseargs&mod=$::MOD_TL&"
1275      . "view=$Timeline::VIEW_FR&tl=$tl_vol";
1276
1277    print "<p>This page provides a monthly summary of activity.<br>"
1278      . "Each day that has activity is noted with the number of events<br>\n";
1279
1280    my $p_year = "";
1281    my $p_mon  = "";
1282
1283    print "<p><table cellspacing=2 border=0>\n";
1284
1285    while (<TL>) {
1286        my @a = split(/ /, $_);
1287        next unless (scalar(@a) == 5);
1288        my $mon  = $::m2d{$a[1]};
1289        my $year = $a[3];
1290        $year = $1 if ($year =~ /^(\d{4,4}):$/);
1291
1292        if (($p_year ne $year) || ($p_mon ne $mon)) {
1293            print "<tr><td colspan=6 align=left>"
1294              . "<a href=\"$url&st_mon=$mon&st_year=$year\">"
1295              . "$a[1] $year</a></td></tr>\n";
1296
1297            $p_year = $year;
1298            $p_mon  = $mon;
1299        }
1300        print "<tr><td>&nbsp;&nbsp;</td><td>$a[0]</td>"
1301          . "<td>$a[1]</td><td>$a[2]</td><td>$year</td><td>($a[4])</td></tr>\n";
1302    }
1303    print "</table>\n";
1304
1305    close(TL);
1306
1307    Print::print_html_footer();
1308    return 0;
1309}
1310
1311# display a given month of the timeline
1312sub view {
1313    Args::check_tl();
1314    Args::check_st_mon();
1315    Args::check_st_year();
1316
1317    my $tl_vol  = Args::get_tl();
1318    my $tl      = $Caseman::vol2path{$tl_vol};
1319    my $st_mon  = Args::get_st_mon();
1320    my $st_year = Args::get_st_year();
1321
1322    Print::print_html_header("View $st_mon, $st_year of Timeline");
1323
1324    unless (open(TL, "$tl")) {
1325        print("Error opening $tl");
1326        return (1);
1327    }
1328
1329    Print::log_host_inv(
1330        "$Args::args{'tl'}: Viewing timeline for $::d2m[$st_mon] $st_year");
1331
1332    print "<table cellspacing=\"5\" cellpadding=\"2\" width=100%>\n";
1333
1334    # zone identifies if we should be printing or not
1335    my $zone = 0;
1336    my $row  = 0;
1337    while (<TL>) {
1338        if (
1339/^(?:(\w\w\w\s+)?(\w\w\w\s+\d\d\s+\d\d\d\d)\s+(\d\d:\d\d:\d\d))?\s+(\d+)\s+([macb\.]+)\s+([-\/\?\w]+)\s+([\d\w\/]+)\s+([\d\w\/]+)\s+($::REG_META)\s+(.*)$/o
1340          )
1341        {
1342
1343            my $day = "";
1344            $day = $1 if (defined $1);
1345            my $date = "";
1346            $date = $2 if (defined $2);
1347            my $time = "";
1348            $time = $3 if (defined $3);
1349            my $sz  = $4;
1350            my $mac = $5;
1351            my $p   = $6;
1352            my $u   = $7;
1353            my $g   = $8;
1354            my $i   = $9;
1355            my $f   = $10;
1356
1357            # we must break this down to see if we can skip it or not
1358            if ($date ne "") {
1359                if ($date =~ /^(\w\w\w)\s+\d\d\s+(\d\d\d\d)$/) {
1360                    if ($2 < $st_year) {
1361                        next;
1362                    }
1363                    elsif (($2 == $st_year) && ($::m2d{$1} < $st_mon)) {
1364                        next;
1365                    }
1366                    elsif ($2 > $st_year) {
1367                        last;
1368                    }
1369                    elsif (($2 == $st_year) && ($::m2d{$1} > $st_mon)) {
1370                        last;
1371                    }
1372                    else {
1373                        $zone = 1;
1374                    }
1375                }
1376            }
1377
1378            # we need to print this entry
1379            if ($zone) {
1380
1381                # the deleted meta <blah-dead-2> entries screw up in HTML
1382                $f = "&lt;$1 &gt" if ($f =~ /^<(.*?)>$/);
1383
1384                if (($row % 2) == 0) {
1385                    print "<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR\">\n";
1386                }
1387                else {
1388                    print
1389                      "<tr valign=\"TOP\" bgcolor=\"$::BACK_COLOR_TABLE\">\n";
1390                }
1391
1392                print "<td>$day&nbsp;$date&nbsp;$time</td>"
1393                  . "<td>$sz</td><td>$mac</td><td>$p</td>"
1394                  . "<td>$u</td><td>$g</td><td>$i</td><td>"
1395                  . Print::html_encode($f)
1396                  . "</td></tr>\n";
1397
1398                $row++;
1399            }
1400        }
1401        else {
1402            print "Error parsing timeline: "
1403              . Print::html_encode($_)
1404              . "<br>\n";
1405        }
1406    }
1407    close(TL);
1408    print "</table>";
1409
1410    Print::print_html_footer();
1411    return 0;
1412}
1413
1414# Blank Page
1415sub blank {
1416    Print::print_html_header("");
1417    print "<center><h3>File Activity Timelines</h3>\n"
1418      . "Here you can create a timeline of file activity.<br>\n"
1419      . "This process requires two steps:<p>\n"
1420      . "1.  <b>Create Data File</b> from file system data&nbsp;&nbsp;->"
1421      . "&nbsp;&nbsp;2.  <b>Create Timeline</b> from the data file\n"
1422      . "<p>Use the tabs above to start.\n";
1423    Print::print_html_footer();
1424    return 0;
1425}
1426