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" <input type=\"radio\" name=\"sort\" value=\"$OTYPE_NORM\" CHECKED>Tabulated (normal)<br>\n" 670 . " <input type=\"radio\" name=\"sort\" value=\"$OTYPE_HOURLY\">Comma delimited with hourly summary<br>\n" 671 . " <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 . "<- $::d2m[$pmon] $pyear</a></td>\n" 1205 . "<td> </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> </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 -></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> </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 = "<$1 >" 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 $date $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 ->" 1421 . " 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