1 #include "main.h"
2 #include "utf8.h"
3 #include <stdarg.h>
4 
5 // $Id$
6 
7 /**
8  *
9  * display.cpp:
10  * Manages user output.
11  * All output is in UTF-8.
12  *
13  * If opt_escape8 is set, then non-ASCII UTF-8 characters are turned
14  * into U+XXXX notation.
15  *
16  * NOTE WITH MINGW GCC-4.3.0:
17  * You will get a warning from the format. IGNORE IT.
18  * See http://lists.gnu.org/archive/html/qemu-devel/2009-01/msg01979.html
19  *
20  * All output is threadsafe.
21  */
22 
23 /****************************************************************
24  ** Support routines
25  ****************************************************************/
26 
set_outfilename(std::string outfilename)27 void display::set_outfilename(std::string outfilename)
28 {
29     myoutstream.open(outfilename.c_str(),std::ios::out|std::ios::binary);
30     if(!myoutstream.is_open()){
31 	fatal_error("%s: Cannot open: ",opt_outfilename.c_str());
32     }
33     out = &myoutstream;
34 }
35 
writeln(std::ostream * os,const std::string & str)36 void display::writeln(std::ostream *os,const std::string &str)
37 {
38     lock();
39     (*os) << str;
40     if (opt_zero){
41 	(*os) << '\000';
42     }
43     else {
44 	(*os) << std::endl;
45     }
46     os->flush();
47     unlock();
48 }
49 
50 /* special handling for vasprintf under mingw.
51  * Sometimes the function prototype is missing. Sometimes the entire function is missing!
52  */
53 #if defined(HAVE_VASPRINTF) && defined(MINGW)
54 extern "C" {
55     int vasprintf(char **ret,const char *fmt,va_list ap);
56 }
57 #endif
58 
59 /*
60  * If we don't have vasprintf, bring it in.
61  */
62 /*
63 #if !defined(HAVE_VASPRINTF)
64 extern "C" {
65     //
66     // We do not have vasprintf.
67     // We have determined that vsnprintf() does not perform properly on windows.
68     // So we just allocate a huge buffer and then strdup() and hope!
69     //
70     int vasprintf(char **ret,const char *fmt,va_list ap)
71     {
72 	// Figure out how long the result will be
73 	char buf[65536];
74 	int size = vsnprintf(buf,sizeof(buf),fmt,ap);
75 	if(size<0) return size;
76 	// Now allocate the memory
77 	*ret = (char *)strdup(buf);
78 	return size;
79     }
80 }
81 #endif
82 */
83 
status(const char * fmt,...)84 void display::status(const char *fmt,...)
85 {
86     va_list(ap);
87     va_start(ap,fmt);
88 
89     char *ret = 0;
90     if(vasprintf(&ret,fmt,ap) < 0){
91 	(*out) << progname << ": " << strerror(errno);
92 	exit(EXIT_FAILURE);
93     }
94     writeln(out,ret);
95     free(ret);
96     va_end(ap);
97 }
98 
error(const char * fmt,...)99 void display::error(const char *fmt,...)
100 {
101     if(opt_silent) return;
102     va_list(ap);
103     va_start(ap,fmt);
104     char *ret = 0;
105     if (vasprintf(&ret,fmt,ap) < 0)    {
106       (*out) << progname << ": " << strerror(errno);
107       exit(EXIT_FAILURE);
108     }
109     lock();
110     std::cerr << progname << ": " << ret << std::endl;
111     unlock();
112     va_end(ap);
113 }
114 
115 
try_msg(void)116 void display::try_msg(void)
117 {
118     writeln(&std::cerr,std::string("Try `") + progname + " -h` for more information.");
119 }
120 
121 
fatal_error(const char * fmt,...)122 void display::fatal_error(const char *fmt,...)
123 {
124     va_list(ap);
125     va_start(ap,fmt);
126 
127     char *ret = 0;
128     if(vasprintf(&ret,fmt,ap) < 0){
129 	(*out) << progname << ": " << strerror(errno);
130 	exit(EXIT_FAILURE);
131     }
132     writeln(&std::cerr,progname + ": " + ret);
133     free(ret);
134     va_end(ap);
135     exit(1);
136 }
137 
internal_error(const char * fmt,...)138 void display::internal_error(const char *fmt,...)
139 {
140     va_list(ap);
141     va_start(ap,fmt);
142 
143     char *ret = 0;
144     if(vasprintf(&ret,fmt,ap) < 0){
145 	(*out) << progname << ": " << strerror(errno);
146 	exit(EXIT_FAILURE);
147     }
148     writeln(&std::cerr,ret);
149     writeln(&std::cerr,progname+": Internal error. Contact developer!");
150     va_end(ap);
151     free(ret);
152     exit(1);
153 }
154 
print_debug(const char * fmt,...)155 void display::print_debug(const char *fmt, ... )
156 {
157 #ifdef __DEBUG
158     va_list(ap);
159     va_start(ap,fmt);
160     char *ret = 0;
161     if(vasprintf(&ret,fmt,ap) < 0){
162 	(*out) << progname << ": " << strerror(errno);
163 	exit(EXIT_FAILURE);
164     }
165     writeln(&std::cerr,std::string("DEBUG:" ) + ret);
166     free(ret);
167     va_end(ap);
168 #endif
169 }
170 /**
171  * output the string, typically a fn, optionally performing unicode escaping
172  */
173 
fmt_filename(const std::string & fn) const174 std::string display::fmt_filename(const std::string &fn) const
175 {
176     if(opt_unicode_escape){
177 	return global::escape_utf8(fn);
178     } else {
179 	return fn;			// assumed to be utf8
180     }
181 }
182 
183 
184 #ifdef _WIN32
fmt_filename(const std::wstring & fn) const185 std::string display::fmt_filename(const std::wstring &fn) const
186 {
187     if(opt_unicode_escape){
188 	return global::escape_utf8(global::make_utf8(fn));
189     } else {
190 	return global::make_utf8(fn);
191     }
192 }
193 #endif
194 
195 
196 
error_filename(const std::string & fn,const char * fmt,...)197 void display::error_filename(const std::string &fn,const char *fmt, ...)
198 {
199     if(opt_silent) return;
200 
201     va_list(ap);
202     va_start(ap,fmt);
203     char *ret = 0;
204     if(vasprintf(&ret,fmt,ap) < 0){
205 	(*out) << progname << ": " << strerror(errno);
206 	exit(EXIT_FAILURE);
207     }
208     if(dfxml){
209 	lock();
210 	dfxml->push("fileobject");
211 	dfxml->xmlout("filename",fn);
212 	dfxml->xmlout("error",ret);
213 	dfxml->pop();
214 	unlock();
215     } else {
216 	writeln(&std::cerr,fmt_filename(fn) + ": " + ret);
217     }
218     free(ret);
219     va_end(ap);
220 }
221 
222 #ifdef _WIN32
error_filename(const std::wstring & fn,const char * fmt,...)223 void display::error_filename(const std::wstring &fn,const char *fmt, ...)
224 {
225     if(opt_silent) return;
226 
227     va_list(ap);
228     va_start(ap,fmt);
229     char *ret = 0;
230     if(vasprintf(&ret,fmt,ap) < 0){
231 	(*out) << progname << ": " << strerror(errno);
232 	exit(EXIT_FAILURE);
233     }
234     writeln(&std::cerr,fmt_filename(fn) + ": " + ret);
235     free(ret);
236     va_end(ap);
237 }
238 #endif
239 
clear_realtime_stats()240 void display::clear_realtime_stats()
241 {
242     lock();
243     std::cerr << "\r";
244     for(int i=0;i<LINE_LENGTH;i++){
245 	std::cerr << ' ';
246     }
247     std::cerr << "\r";
248     unlock();
249 }
250 
251 // At least one user has suggested changing update_display() to
252 // use human readable units (e.g. GB) when displaying the updates.
253 // The problem is that once the display goes above 1024MB, there
254 // won't be many updates. The counter doesn't change often enough
255 // to indicate progress. Using MB is a reasonable compromise.
256 
display_realtime_stats(const file_data_hasher_t * fdht,const hash_context_obj * hc,time_t elapsed)257 void display::display_realtime_stats(const file_data_hasher_t *fdht, const hash_context_obj *hc,time_t elapsed)
258 {
259     uint64_t mb_read=0;
260 
261     /*
262      * This is the only place where filenames are shortened.
263      * So do it explicitly here.
264      *
265      */
266 
267     std::stringstream ss;
268 
269     std::string fn = fdht->file_name;
270     if (fn.size() > MAX_FILENAME_LENGTH){
271 	/* Shorten from the middle */
272 	size_t half = (MAX_FILENAME_LENGTH)/2;
273 	fn = fn.substr(0,half) + "..." + fn.substr(fn.size()-half,half);
274     }
275 
276     ss << fmt_filename(fn) << " ";
277 
278     // If we've read less than one MB, then the computed value for mb_read
279     // will be zero. Later on we may need to divide the total file size,
280     // total_megs, by mb_read. Dividing by zero can create... problems
281 
282     if (hc->read_len < ONE_MEGABYTE){
283 	mb_read = 1;
284     }
285     else {
286 	mb_read = hc->read_len / ONE_MEGABYTE;
287     }
288 
289     if (fdht->stat_megs()==0 || opt_estimate==false)  {
290 	ss << mb_read << "MB done. Unable to estimate remaining time.";
291     }
292     else {
293 	// Estimate the number of seconds using only integer math.
294 	//
295 	// We now compute the number of bytes read per second and then
296 	// use that to determine how long the whole file should take.
297 	// By subtracting the number of elapsed seconds from that, we should
298 	// get a good estimate of how many seconds remain.
299 
300 	uint64_t seconds = (fdht->stat_bytes / (fdht->file_bytes / elapsed)) - elapsed;
301 
302 	// We don't care if the remaining time is more than one day.
303 	// If you're hashing something that big, to quote the movie Jaws:
304 	//
305 	//            "We're gonna need a bigger boat."
306 	uint64_t hour = seconds / 3600;
307 	seconds -= (hour * 3600);
308 
309 	uint64_t min = seconds/60;
310 	seconds -= min * 60;
311 
312 	ss << mb_read << "MB of " << fdht->stat_megs() << "MB done, ";
313 	char msg[64];
314 	snprintf(msg,sizeof(msg),"%02" PRIu64":%02" PRIu64":%02" PRIu64" left", hour, min, seconds);
315 	ss << msg;
316     }
317     ss << "\r";
318     lock();
319     std::cerr << ss.str() << "\r";
320     unlock();
321 }
322 
323 
display_banner_if_needed()324 void display::display_banner_if_needed()
325 {
326     if(this->dfxml!=0) return;		// output is in DFXML; no banner
327     lock();
328     if(banner_displayed==false){
329 	(*out) << utf8_banner;
330 	banner_displayed=true;
331     }
332     unlock();
333 }
334 
dfxml_write_hashes(std::string hex_hashes[],int indent)335 void file_data_hasher_t::dfxml_write_hashes(std::string hex_hashes[],int indent)
336 {
337     for(int i=0;i<NUM_ALGORITHMS;i++){
338 	if(hashes[i].inuse){
339 	    for(int j=0;j<indent;j++){
340 		this->dfxml_hash << ' ';
341 	    }
342 	    this->dfxml_hash << "<hashdigest type='" << makeupper(hashes[i].name) << "'>"
343 			     << hex_hashes[i] <<"</hashdigest>\n";
344 	}
345     }
346 }
347 
348 
compute_dfxml(bool known_hash,const hash_context_obj * hc)349 void file_data_hasher_t::compute_dfxml(bool known_hash,const hash_context_obj *hc)
350 {
351     int indent=0;
352     if(hc && this->ocb->piecewise_size>0){
353 	this->dfxml_hash << "<byte_run file_offset='" << hc->read_offset << "'"
354 			 << " len='" << hc->read_len << "'>   \n";
355 	indent=2;
356     }
357 
358     this->dfxml_write_hashes(this->hash_hex,indent);
359     if(known_hash){
360 	this->dfxml_hash << "<matched>1</matched>";
361     }
362     if(hc && this->ocb->piecewise_size){
363 	this->dfxml_hash << "</byte_run>\n";
364     }
365 }
366 
367 /**
368  * Return the number of entries in the hashlist that have used==0
369  * Optionally display them, optionally with additional output.
370  */
compute_unused(bool show_display,std::string annotation)371 uint64_t display::compute_unused(bool show_display, std::string annotation)
372 {
373     uint64_t count=0;
374 
375     /* This is the only place we iterate through known.
376      * We make a list of all the filenames so we can get out of the lock fast.
377      */
378 
379     std::vector<std::string> filelist;
380 
381     lock();
382     for(hashlist::const_iterator i = known.begin(); i != known.end(); i++){
383 	if((*i)->matched_file_number==0){
384 	    count++;
385 	    if (show_display || opt_verbose >= MORE_VERBOSE) {
386 		filelist.push_back((*i)->file_name);
387 	    }
388 	}
389     }
390     unlock();
391     for(std::vector<std::string>::const_iterator i = filelist.begin(); i!= filelist.end(); i++){
392 	std::string line = *i + annotation;
393 	writeln(out,line);
394     }
395     return count;
396 }
397 
398 
399 /**
400  * perform an audit
401  */
402 
403 
audit_check()404 int display::audit_check()
405 {
406     /* Count the number of unused */
407     match.unused = compute_unused(false,": Known file not used");
408     return (0 == this->match.unused  &&
409 	    0 == this->match.unknown &&
410 	    0 == this->match.moved);
411 }
412 
413 
414 
display_audit_results()415 void display::display_audit_results()
416 {
417     if (audit_check()==0) {
418 	status("%s: Audit failed", progname.c_str());
419 	set_return_code(status_t::status_EXIT_FAILURE);
420     }
421     else {
422 	status("%s: Audit passed", progname.c_str());
423     }
424 
425     if (opt_verbose)    {
426 	if(opt_verbose >= MORE_VERBOSE){
427 	    status("   Input files examined: %" PRIu64, this->match.total);
428 	    status("  Known files expecting: %" PRIu64, this->match.expect);
429 	}
430 	status("          Files matched: %" PRIu64, this->match.exact);
431 	status("Files partially matched: %" PRIu64, this->match.partial);
432 	status("            Files moved: %" PRIu64, this->match.moved);
433 	status("        New files found: %" PRIu64, this->match.unknown);
434 	status("  Known files not found: %" PRIu64, this->match.unused);
435     }
436 }
437 
438 
fmt_size(const file_data_t * fdt) const439 std::string display::fmt_size(const file_data_t *fdt) const
440 {
441     if (opt_display_size)  {
442 	std::stringstream ss;
443 	ss.width(10);
444 	ss.fill(' ');
445 	ss << fdt->file_bytes;
446 	ss.width(1);
447 	ss << (opt_csv ? ", " : "  ");
448 	return ss.str();
449     }
450     return std::string();
451 }
452 
453 
454 // The old display_match_result from md5deep
md5deep_display_match_result(file_data_hasher_t * fdht,const hash_context_obj * hc)455 void display::md5deep_display_match_result(file_data_hasher_t *fdht,
456 					   const hash_context_obj *hc)
457 {
458     lock();
459     const file_data_t *fs = known.find_hash(opt_md5deep_mode_algorithm,
460 					    fdht->hash_hex[opt_md5deep_mode_algorithm],
461 					    fdht->file_name,
462 					    fdht->file_number);
463     unlock();
464 
465     int known_hash = fs ? 1 : 0;
466     if ((known_hash && opt_mode_match) || (!known_hash && opt_mode_match_neg)) {
467 	if(dfxml){
468 	    fdht->compute_dfxml(opt_show_matched || known_hash,hc);
469 	    return ;
470 	}
471 
472 	std::stringstream line;
473 
474 	line << fmt_size(fdht);
475 	if (opt_display_hash) {
476 	    line << fdht->hash_hex[opt_md5deep_mode_algorithm];
477 	    if (opt_csv) {
478 		line << ",";
479 	    } else if (opt_asterisk) {
480 		line << " *";
481 	    } else {
482 		line << "  ";
483 	    }
484 	}
485 
486 	if (opt_show_matched) 	    {
487 	    if (known_hash && (opt_mode_match)) {
488 		line << fdht->file_name << " matched " << fs->file_name;
489 	    }
490 	    else {
491 		line << fdht->file_name << " does NOT match";
492 	    }
493 	}
494 	else{
495 	    line << fdht->file_name;
496 	    if(piecewise_size) {
497 		line << " offset " << hc->read_offset << "-" << hc->read_offset + hc->read_len - 1;
498 	    }
499 	}
500 	writeln(out,line.str());
501     }
502 }
503 
504 /* The original display_match_result from md5deep.
505  * This should probably be merged with the function above.
506  * This function is very similar to audit_update(), which follows
507  */
display_match_result(file_data_hasher_t * fdht,const hash_context_obj * hc)508 void display::display_match_result(file_data_hasher_t *fdht,const hash_context_obj *hc)
509 {
510     file_data_t *matched_fdt = NULL;
511     int should_display = (primary_match_neg == primary_function);
512 
513     lock();				// protects the search and the printing
514     hashlist::searchstatus_t m = known.search(fdht,
515 					      &matched_fdt,
516 					      opt_case_sensitive);
517     unlock();
518 
519     std::stringstream line1;
520 
521     switch(m){
522 	// If only the name is different, it's still really a match
523 	//  as far as we're concerned.
524     case hashlist::status_file_name_mismatch:
525     case hashlist::status_match:
526 	should_display = (primary_match_neg != primary_function);
527 	break;
528 
529     case hashlist::status_file_size_mismatch:
530 	line1 << fmt_filename(fdht) << ": Hash collision with " << fmt_filename(matched_fdt);
531 	writeln(&std::cerr,line1.str());
532 	break;
533 
534     case hashlist::status_partial_match:
535 	line1 << fmt_filename(fdht) << ": partial hash match with " << fmt_filename(matched_fdt);
536 	writeln(&std::cerr,line1.str());
537 	break;
538 
539     default:
540 	break;
541     }
542     if (should_display) {
543 	std::stringstream line2;
544 
545 	if (opt_display_hash){
546 	    display_hash_simple(fdht,hc);
547 	}
548 	else {
549 	    line2 << fmt_filename(fdht);
550 	    if (opt_show_matched && primary_match == primary_function) {
551 		line2 << " matches ";
552 		if (NULL == matched_fdt) {
553 		    line2 << "(unknown file)";
554 		} else {
555 		    line2 << fmt_filename(matched_fdt);
556 		}
557 	    }
558 	    writeln(out,line2.str());
559 	}
560     }
561 }
562 
563 
564 /**
565  * Called after every file is hashed by display_hash
566  * when primary_function==primary_audit
567  * Records every file seen in the 'seen' structure, referencing the 'known' structure.
568  */
569 
audit_update(file_data_hasher_t * fdht)570 int display::audit_update(file_data_hasher_t *fdht)
571 {
572     file_data_t *matched_fdht=0;
573     lock();				// protects the search and the printing
574     hashlist::searchstatus_t m = known.search(fdht,
575 					      &matched_fdht,
576 					      opt_case_sensitive);
577     unlock();
578     std::string line;
579     switch(m){
580     case hashlist::status_match:
581     case hashlist::searchstatus_ok:
582 	this->match.exact++;
583 	if (opt_verbose >= INSANELY_VERBOSE) {
584 	    line = fmt_filename(fdht) + ": Ok";
585 	}
586 	break;
587     case hashlist::status_no_match:
588 	this->match.unknown++;
589 	if (opt_verbose >= MORE_VERBOSE) {
590 	    line = fmt_filename(fdht) + ": No match";
591 	}
592 	break;
593     case hashlist::status_file_name_mismatch:
594 	this->match.moved++;
595 	if (opt_verbose >= MORE_VERBOSE) {
596 	    line = fmt_filename(fdht) + ": Moved from " + fmt_filename(matched_fdht);
597 	}
598 	break;
599     case hashlist::status_partial_match:
600     case hashlist::status_file_size_mismatch:
601 	this->match.partial++;
602 	// We only record the hash collision if it wasn't anything else.
603 	// At the same time, however, a collision is such a significant
604 	// event that we print it no matter what.
605 	line = fmt_filename(fdht) + ": Hash collision with " + fmt_filename(matched_fdht);
606     }
607     if(line.size()>0){
608 	writeln(&std::cout,line);
609     }
610     return FALSE;
611 }
612 
613 /**
614  * Examines the hash table and determines if any known hashes have not
615  * been used or if any input files did not match the known hashes. If
616  * requested, displays any unused known hashes. Returns a status variable
617  */
finalize_matching()618 void display::finalize_matching()
619 {
620     /* Could the total matched */
621     lock();
622     uint64_t total_matched = known.total_matched();
623     unlock();
624 
625     if (total_matched != known_size())
626     {
627       // were there any unmatched?
628       return_code.add(status_t::STATUS_UNUSED_HASHES);
629     }
630 
631     // RBF - Implement check for Input Did not Match
632     /*
633     if (this->match.unknown)
634     {
635       return_code.add(status_t::STATUS_INPUT_DID_NOT_MATCH);
636     }
637     */
638 
639     if (mode_not_matched)
640     {
641       // should we display those that were not matched?
642       compute_unused(true,"");
643     }
644 }
645 
646 
647 mutex_t    display::portable_gmtime_mutex;
648 
portable_gmtime(struct tm * my_time,const timestamp_t * t)649 struct tm  *display::portable_gmtime(struct tm *my_time,const timestamp_t *t)
650 {
651     memset(my_time,0,sizeof(*my_time)); // clear it out
652 #ifdef HAVE_GMTIME_R
653     gmtime_r(t,my_time);
654     return my_time;
655 #endif
656 #ifdef HAVE__GMTIME64
657     // This is not threadsafe, hence the lock
658     portable_gmtime_mutex.lock();
659     *my_time = *_gmtime64(t);
660     portable_gmtime_mutex.unlock();
661 
662     //we tried this:
663     //_gmtime64_s(&fdht->timestamp,&my_time);
664     //but it had problems on mingw64
665     return my_time;
666 #endif
667 #if !defined(HAVE__GMTIME64) && !defined(HAVE_GMTIME_R)
668 #error This program requires either _gmtime64 or gmtime_r for compilation
669 #endif
670 }
671 
672 /* The old display_hash from the md5deep program, with modifications
673  * to build the line before outputing it.
674  *
675  * needs hasher because of triage mode
676  */
677 
md5deep_display_hash(file_data_hasher_t * fdht,const hash_context_obj * hc)678 void  display::md5deep_display_hash(file_data_hasher_t *fdht,const hash_context_obj *hc)
679 {
680     /**
681      * We can't call display_size here because we don't know if we're
682      * going to display *anything* yet. If we're in matching mode, we
683      * have to evaluate if there was a match first.
684      */
685     if (opt_mode_match || opt_mode_match_neg){
686 	md5deep_display_match_result(fdht,hc);
687 	return;
688     }
689 
690     if(this->dfxml){
691 	fdht->compute_dfxml(opt_show_matched,hc);
692 	return;
693     }
694 
695     std::stringstream line;
696 
697     if (mode_triage) {
698 	line << fdht->triage_info << "\t";
699     }
700     line << fmt_size(fdht) << fdht->hash_hex[opt_md5deep_mode_algorithm];
701 
702     if (mode_quiet){
703 	line << "  ";
704     }
705     else  {
706 	if ((fdht->ocb->piecewise_size) || (fdht->is_stdin()==false))    {
707 	    if (mode_timestamp)      {
708 		struct tm my_time;
709 		portable_gmtime(&my_time,&fdht->ctime);
710 		char time_str[MAX_TIME_STRING_LENGTH];
711 
712 		// The format is four digit year, two digit month,
713 		// two digit hour, two digit minute, two digit second
714 		strftime(time_str, sizeof(time_str), "%Y:%m:%d:%H:%M:%S", &my_time);
715 		line << (opt_csv ? ",":" ") << time_str;
716 	    }
717 	    if (opt_csv) {
718 		line << ",";
719 	    } else if (opt_asterisk) {
720 		line << " *";
721 	    } else if (mode_triage) {
722 		line << "\t";
723 	    } else {
724 		line << "  ";
725 	    }
726 	    line << fmt_filename(fdht);
727 	}
728     }
729     if(hc && fdht->ocb->piecewise_size > 0){
730 	uint64_t len = (hc->read_offset+hc->read_len-1);
731 	if(hc->read_offset==0 && hc->read_len==0) len=0;
732 	line << " offset " << hc->read_offset << "-" << len;
733     }
734     writeln(out,line.str());
735 }
736 
737 
738 /*
739  * Externally called to display a simple hash
740  */
display_hash_simple(file_data_hasher_t * fdht,const hash_context_obj * hc)741 void display::display_hash_simple(file_data_hasher_t *fdht,
742 				  const hash_context_obj *hc)
743 {
744     if (this->dfxml)
745     {
746       fdht->compute_dfxml(opt_show_matched,hc);
747       return;
748     }
749 
750     /* Old comment:
751      * In piecewise mode the size of each 'file' is the size
752      * of the block it came from. This is important when doing an
753      * audit in piecewise mode. In all other cases we use the
754      * total number of bytes from the file we *actually* read
755      *
756      * New comment:
757      * read_len is always correct.
758      * In piecewise mode its the size of the piece,
759      * In normal mode it's the size of the file.
760      *
761      * NOTE: Ignore the warning in the format when running on mingw with GCC-4.3.0
762      * see http://lists.gnu.org/archive/html/qemu-devel/2009-01/msg01979.html
763      *
764      */
765 
766     display_banner_if_needed();
767     std::stringstream line;
768 
769     line << hc->read_len << ",";
770 
771     for (int i = 0 ; i < NUM_ALGORITHMS ; ++i)
772     {
773       if (hashes[i].inuse)
774       {
775 	line << fdht->hash_hex[i] << ",";
776       }
777     }
778     line << fmt_filename(fdht);
779     if(fdht->ocb->piecewise_size > 0){
780 	std::stringstream ss;
781 	uint64_t len = (hc->read_offset+hc->read_len-1);
782 	if(hc->read_offset==0 && hc->read_len==0) len=0;
783 	line << " offset " << hc->read_offset << "-" << len;
784     }
785     writeln(out,line.str());
786 }
787 
788 
789 /**
790  * Called by hash() in hash.c when the hashing operation is complete.
791  * Display the hash and perform any auditing steps.
792  */
display_hash(file_data_hasher_t * fdht,const hash_context_obj * hc)793 void display::display_hash(file_data_hasher_t *fdht,const hash_context_obj *hc)
794 {
795     switch (primary_function) {
796     case primary_compute:
797 	display_hash_simple(fdht,hc);
798 	break;
799     case primary_match:
800     case primary_match_neg:
801 	display_match_result(fdht,hc);
802 	break;
803     case primary_audit:
804 	audit_update(fdht);
805 	break;
806     }
807 }
808 
809 
810 
dfxml_startup(int argc,char ** argv)811 void display::dfxml_startup(int argc,char **argv)
812 {
813     if(dfxml){
814 	lock();
815 	dfxml->push("dfxml",
816 		       "\n  xmlns='http://www.forensicswiki.org/wiki/Category:Digital_Forensics_XML' "
817 		       "\n  xmlns:deep='http://md5deep.sourceforge.net/md5deep/' "
818 		       "\n  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
819 		       "\n  xmlns:dc='http://purl.org/dc/elements/1.1/' "
820 		       "version='1.0'");
821 	dfxml->push("deep:configuration");
822 	dfxml->push("algorithms");
823 	for(int i=0;i<NUM_ALGORITHMS;i++){
824 	    dfxml->make_indent();
825 	    dfxml->printf("<algorithm name='%s' enabled='%d'/>\n",
826 			   hashes[i].name.c_str(),hashes[i].inuse);
827 	}
828 	dfxml->pop();			// algorithms
829 	dfxml->pop();			// configuration
830 	dfxml->push("metadata", "");
831 	dfxml->xmlout("dc:type","Hash List","",false);
832 	dfxml->pop();
833 	dfxml->add_DFXML_creator(PACKAGE_NAME,PACKAGE_VERSION,XML::make_command_line(argc,argv));
834 	unlock();
835     }
836 }
837 
dfxml_timeout(const std::string & tag,const timestamp_t & val)838 void display::dfxml_timeout(const std::string &tag,const timestamp_t &val)
839 {
840     char buf[256];
841     struct tm tm;
842     strftime(buf,sizeof(buf),"%Y-%m-%dT%H:%M:%SZ",portable_gmtime(&tm,&val));
843     dfxml->xmlout(tag,buf);
844 }
845 
dfxml_write(file_data_hasher_t * fdht)846 void display::dfxml_write(file_data_hasher_t *fdht)
847 {
848     if(dfxml){
849 	std::string attrs;
850 	if(opt_verbose && fdht->workerid>=0){
851 	    std::stringstream ss;
852 	    ss << "workerid='" << fdht->workerid << "'";
853 	    attrs = ss.str();
854 	}
855 	lock();
856 	dfxml->push("fileobject",attrs);
857 	dfxml->xmlout("filename",fdht->file_name);
858 	dfxml->xmlout("filesize",(int64_t)fdht->stat_bytes);
859 	if(fdht->mtime) dfxml_timeout("mtime",fdht->mtime);
860 	if(fdht->ctime) dfxml_timeout("ctime",fdht->ctime);
861 	if(fdht->atime) dfxml_timeout("atime",fdht->atime);
862 	dfxml->writexml(fdht->dfxml_hash.str());
863 	dfxml->pop();
864 	unlock();
865     }
866 }
867 
dfxml_shutdown()868 void display::dfxml_shutdown()
869 {
870     if(dfxml){
871 	lock();
872 	dfxml->add_rusage();
873 	dfxml->pop();		// outermost
874 	dfxml->close();
875 	delete dfxml;
876 	dfxml = 0;
877 	unlock();
878     }
879 }
880 
881