1 /*
2 
3     File: qphotorec.cpp
4 
5     Copyright (C) 2009-2014 Christophe GRENIER <grenier@cgsecurity.org>
6 
7     This software is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License along
18     with this program; if not, write the Free Software Foundation, Inc., 51
19     Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29 #include <stdarg.h>
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>	/* unlink, ftruncate */
32 #endif
33 #ifdef HAVE_STRING_H
34 #include <string.h>
35 #endif
36 #ifdef HAVE_TIME_H
37 #include <time.h>
38 #endif
39 #ifdef HAVE_SYS_TIME_H
40 #include <sys/time.h>
41 #endif
42 #ifdef HAVE_SYS_STAT_H
43 #include <sys/stat.h>
44 #endif
45 #include <ctype.h>      /* tolower */
46 #ifdef HAVE_LOCALE_H
47 #include <locale.h>	/* setlocale */
48 #endif
49 #ifdef HAVE_SIGNAL_H
50 #include <signal.h>
51 #endif
52 #ifdef HAVE_SYS_WAIT_H
53 #include <sys/wait.h>
54 #endif
55 #include <errno.h>
56 #ifdef HAVE_TIME_H
57 #include <time.h>
58 #endif
59 #include <QApplication>
60 #include <QLayoutItem>
61 #include <QLabel>
62 #include <QLayout>
63 #include <QTableView>
64 #include <QHeaderView>
65 #include <QStandardItemModel>
66 #include <QStandardItem>
67 #include <QDialogButtonBox>
68 #include <QSortFilterProxyModel>
69 #include <QGroupBox>
70 #include <QRadioButton>
71 #include <QFileDialog>
72 #include <QComboBox>
73 #include <QTimer>
74 #include <QMessageBox>
75 #include <QTextDocument>
76 #include "types.h"
77 #include "common.h"
78 #include "hdcache.h"
79 #include "hdaccess.h"
80 #include "fnctdsk.h"
81 #include "filegen.h"
82 #include "sessionp.h"
83 #include "intrf.h"
84 #include "partauto.h"
85 #include "phcfg.h"
86 #include "log.h"
87 #include "log_part.h"
88 #include "qphotorec.h"
89 
90 extern const arch_fnct_t arch_none;
91 extern file_enable_t list_file_enable[];
92 
QPhotorec(QWidget * my_parent)93 QPhotorec::QPhotorec(QWidget *my_parent) : QWidget(my_parent)
94 {
95   const int verbose=1;
96   const int testdisk_mode=TESTDISK_O_RDONLY|TESTDISK_O_READAHEAD_32K;
97   list_disk_t *element_disk;
98 
99   list_disk=NULL;
100   selected_disk=NULL;
101   list_part=NULL;
102   selected_partition=NULL;
103 
104   params=(struct ph_param *)MALLOC(sizeof(*params));
105   params->recup_dir=NULL;
106   params->cmd_device=NULL;
107   params->cmd_run=NULL;
108   params->carve_free_space_only=1;
109   params->disk=NULL;
110   params->partition=NULL;
111 
112   options=(struct ph_options *)MALLOC(sizeof(*options));
113   options->paranoid=1;
114   options->keep_corrupted_file=0;
115   options->mode_ext2=0;
116   options->expert=0;
117   options->lowmem=0;
118   options->verbose=0;
119   options->list_file_format=list_file_enable;
120   reset_list_file_enable(options->list_file_format);
121 
122   stop_the_recovery=false;
123 
124   setWindowIcon( QPixmap( ":res/photorec_64x64.png" ) );
125   this->setWindowTitle(tr("QPhotoRec"));
126   QVBoxLayout *mainLayout = new QVBoxLayout();
127   this->setLayout(mainLayout);
128 
129   list_disk=hd_parse(NULL, verbose, testdisk_mode);
130 
131   hd_update_all_geometry(list_disk, verbose);
132   /* Activate the cache, even if photorec has its own */
133   for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
134     element_disk->disk=new_diskcache(element_disk->disk,testdisk_mode);
135   if(list_disk==NULL)
136   {
137     no_disk_warning();
138   }
139   else
140     select_disk(list_disk->disk);
141   setupUI();
142 }
143 
~QPhotorec()144 QPhotorec::~QPhotorec()
145 {
146 //  session_save(list_search_space, params, options);
147   part_free_list(list_part);
148   delete_list_disk(list_disk);
149   free(options);
150   free(params);
151 }
152 
setExistingDirectory()153 void QPhotorec::setExistingDirectory()
154 {
155   QString directory = QFileDialog::getExistingDirectory(this,
156       tr("Please select a destination to save the recovered files to."),
157       directoryLabel->text(),
158       QFileDialog::ShowDirsOnly);
159   if (!directory.isEmpty())
160   {
161     directoryLabel->setText(directory);
162     buttons_updateUI();
163   }
164 }
165 
newSourceFile()166 void QPhotorec::newSourceFile()
167 {
168   const int testdisk_mode=TESTDISK_O_RDONLY|TESTDISK_O_READAHEAD_32K;
169   QString filename = QFileDialog::getOpenFileName(this,
170       tr("Please select a raw file"),
171       "",
172       tr("Raw Files (*.dd *.raw *.img)"));
173   if(!filename.isEmpty())
174   {
175     disk_t *new_disk=NULL;
176     QByteArray filenameArray= (filename).toUtf8();
177     list_disk=insert_new_disk_aux(list_disk, file_test_availability(filenameArray.constData(), options->verbose, testdisk_mode), &new_disk);
178     if(new_disk!=NULL)
179     {
180       select_disk(new_disk);
181       HDDlistWidget_updateUI();
182       PartListWidget_updateUI();
183     }
184   }
185 }
186 
partition_selected()187 void QPhotorec::partition_selected()
188 {
189   if(PartListWidget->selectedItems().count()<=0)
190     return;
191   list_part_t *tmp;
192   const QString& s = PartListWidget->selectedItems()[0]->text();
193   if(s.compare("")==0)
194   {
195     const QString& s2 = PartListWidget->selectedItems()[2]->text();
196     for(tmp=list_part; tmp!=NULL; tmp=tmp->next)
197     {
198       partition_t *part=tmp->part;
199       if(part->order==NO_ORDER && s2.compare(arch_none.get_partition_typename(part))==0)
200       {
201 	selected_partition=part;
202 	buttons_updateUI();
203 	return ;
204       }
205     }
206     if(list_part!=NULL)
207     {
208       selected_partition=list_part->part;
209       buttons_updateUI();
210       return ;
211     }
212     return ;
213   }
214   for(tmp=list_part; tmp!=NULL; tmp=tmp->next)
215   {
216     partition_t *part=tmp->part;
217     if(QString::number(part->order).compare(s)==0)
218     {
219       selected_partition=part;
220       buttons_updateUI();
221       return ;
222     }
223   }
224 }
225 
PartListWidget_updateUI()226 void QPhotorec::PartListWidget_updateUI()
227 {
228   list_part_t *element;
229   PartListWidget->setRowCount(0);
230   PartListWidget->setSortingEnabled(false);
231   for(element=list_part; element!=NULL; element=element->next)
232   {
233     const partition_t *partition=element->part;
234     if(partition->status!=STATUS_EXT_IN_EXT)
235     {
236       const arch_fnct_t *arch=partition->arch;
237       const int currentRow = PartListWidget->rowCount();
238       PartListWidget->setRowCount(currentRow + 1);
239       if(partition->order==NO_ORDER)
240       {
241 	QTableWidgetItem *item = new QTableWidgetItem();
242 	item->setData(0, "");
243 	PartListWidget->setItem(currentRow, 0, item);
244       }
245       else
246       {
247 	QTableWidgetItem *item = new QTableWidgetItem();
248 	item->setData(0, partition->order);
249 	PartListWidget->setItem(currentRow, 0, item);
250       }
251       {
252 	QTableWidgetItem *item=new QTableWidgetItem(QString(get_partition_status(partition)));
253 	item->setTextAlignment(Qt::AlignHCenter| Qt::AlignVCenter);
254 	PartListWidget->setItem(currentRow, 1, item);
255       }
256       if(arch->get_partition_typename(partition)!=NULL)
257 	PartListWidget->setItem(currentRow, 2, new QTableWidgetItem(QString(arch->get_partition_typename(partition))));
258       else if(arch->get_part_type)
259 	PartListWidget->setItem(currentRow, 2, new QTableWidgetItem(tr("Sys=") + QString::number(arch->get_part_type(partition))));
260       else
261 	PartListWidget->setItem(currentRow, 2, new QTableWidgetItem(tr("Unknown")));
262       if(partition->upart_type>0)
263       {
264 	QTableWidgetItem *item=new QTableWidgetItem(QString(arch_none.get_partition_typename(partition)));
265 	item->setToolTip(QString(partition->info));
266 	PartListWidget->setItem(currentRow, 3, item);
267       }
268       else
269       {
270 	PartListWidget->setItem(currentRow, 3, new QTableWidgetItem(""));
271       }
272       {
273 	char sizeinfo[32];
274 	QTableWidgetItem *item;
275 	size_to_unit(partition->part_size, &sizeinfo[0]);
276 	item=new QTableWidgetItem(QString(sizeinfo));
277 	item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
278 	PartListWidget->setItem(currentRow, 4, item);
279 	/* Select the partition if it's already known */
280 	if(selected_partition == partition)
281 	  PartListWidget->setCurrentItem(item);
282       }
283       {
284 	QString partname="";
285 	if(partition->partname[0]!='\0')
286 	{
287 	  partname.sprintf("[%s]", partition->partname);
288 	}
289 	if(partition->fsname[0]!='\0')
290 	{
291 	  QString fsname;
292 	  fsname.sprintf(" [%s]", partition->fsname);
293 	  partname.append(fsname);
294 	}
295 	PartListWidget->setItem(currentRow, 5, new QTableWidgetItem(partname));
296       }
297     }
298   }
299   PartListWidget->setSortingEnabled(true);
300   PartListWidget->sortByColumn(0, Qt::AscendingOrder);
301   PartListWidget->resizeColumnsToContents();
302 }
303 
select_disk(disk_t * disk)304 void QPhotorec::select_disk(disk_t *disk)
305 {
306   if(disk==NULL)
307     return ;
308   selected_disk=disk;
309   selected_partition=NULL;
310   autodetect_arch(selected_disk, &arch_none);
311   log_info("%s\n", selected_disk->description_short(selected_disk));
312   part_free_list(list_part);
313   list_part=init_list_part(selected_disk, NULL);
314   /* If only whole disk is listed, select it */
315   /* If there is the whole disk and only one partition, select the partition */
316   if(list_part!=NULL)
317   {
318     if(list_part->next==NULL)
319       selected_partition=list_part->part;
320     else if(list_part->next->next==NULL)
321       selected_partition=list_part->next->part;
322   }
323   log_all_partitions(selected_disk, list_part);
324 }
325 
disk_changed(int index)326 void QPhotorec::disk_changed(int index)
327 {
328   int i;
329   list_disk_t *element_disk;
330   for(element_disk=list_disk, i=0;
331       element_disk!=NULL;
332       element_disk=element_disk->next, i++)
333   {
334     if(i==index)
335     {
336       select_disk(element_disk->disk);
337       PartListWidget_updateUI();
338       return;
339     }
340   }
341   if(i==index)
342   {
343     newSourceFile();
344   }
345 }
346 
copyright(QWidget * qwparent)347 QWidget *QPhotorec::copyright(QWidget * qwparent)
348 {
349   QWidget *C_widget = new QWidget(qwparent);
350   QLabel *t_logo=new QLabel(C_widget);
351   QPixmap pixmap_img = QPixmap(":res/photorec_64x64.png");
352   t_logo->setPixmap(pixmap_img);
353 
354   QSizePolicy c_sizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
355   t_logo->setSizePolicy(c_sizePolicy);
356 
357   QLabel *t_copy=new QLabel(C_widget);
358 
359   t_copy->setText( "PhotoRec " + QString(VERSION) + ", Data Recovery Utility, " + QString(TESTDISKDATE) + "<br>\nCopyright (C) Christophe GRENIER &lt;<a href=\"mailto:grenier@cgsecurity.org\">grenier@cgsecurity.org</a>&gt;<br>\n<a href=\"https://www.cgsecurity.org/\">https://www.cgsecurity.org</a>");
360   t_copy->setTextFormat(Qt::RichText);
361   t_copy->setTextInteractionFlags(Qt::TextBrowserInteraction);
362   t_copy->setOpenExternalLinks(true);
363 
364   QHBoxLayout *C_layout = new QHBoxLayout(C_widget);
365   C_layout->addStretch(1);
366   C_layout->addWidget(t_logo);
367   C_layout->addWidget(t_copy);
368   C_layout->addStretch(1);
369   C_widget->setLayout(C_layout);
370   return C_widget;
371 }
372 
373 /* TODO replace by a warning */
no_disk_warning()374 int QPhotorec::no_disk_warning()
375 {
376   QString msg;
377   msg=tr("No harddisk found");
378 #if defined(__CYGWIN__) || defined(__MINGW32__)
379   msg=tr("No harddisk found\n"
380     "You need to be administrator to use this program.\n"
381     "Under Win9x, use the DOS version instead.\n"
382     "Under Vista or later, select this program, right-click and choose \"Run as administrator\".");
383 #elif defined(DJGPP)
384 #else
385 #ifdef HAVE_GETEUID
386   if(geteuid()!=0)
387   {
388     msg=tr("No harddisk found\n"
389       "You need to be root to use PhotoRec.");
390   }
391 #endif
392 #endif
393   return QMessageBox::warning(this,tr("No Disk!"), msg, QMessageBox::Ok);
394 }
395 
buttons_updateUI()396 void QPhotorec::buttons_updateUI()
397 {
398   if(selected_disk==NULL || selected_partition==NULL)
399   {
400     button_search->setEnabled(false);
401     qwholeRadioButton->setChecked(true);
402     qfreeRadioButton->setEnabled(false);
403     return ;
404   }
405   if(selected_partition->upart_type==UP_EXT2 || selected_partition->upart_type==UP_EXT3 || selected_partition->upart_type==UP_EXT4)
406     qextRadioButton->setChecked(true);
407   else
408     qfatRadioButton->setChecked(true);
409   switch(selected_partition->upart_type)
410   {
411     case UP_EXFAT:
412     case UP_FAT12:
413     case UP_FAT16:
414     case UP_FAT32:
415 #if defined(HAVE_LIBNTFS) || defined(HAVE_LIBNTFS3G)
416     case UP_NTFS:
417 #endif
418 #ifdef HAVE_LIBEXT2FS
419     case UP_EXT2:
420     case UP_EXT3:
421     case UP_EXT4:
422 #endif
423       qfreeRadioButton->setEnabled(true);
424       qfreeRadioButton->setChecked(true);
425       break;
426     default:
427       qwholeRadioButton->setChecked(true);
428       qfreeRadioButton->setEnabled(false);
429       break;
430   }
431   button_search->setEnabled(!directoryLabel->text().isEmpty());
432 }
433 
HDDlistWidget_updateUI()434 void QPhotorec::HDDlistWidget_updateUI()
435 {
436   list_disk_t *element_disk;
437   int i;
438   HDDlistWidget->clear();
439   for(element_disk=list_disk, i=0;
440       element_disk!=NULL;
441       element_disk=element_disk->next, i++)
442   {
443     disk_t *disk=element_disk->disk;
444     QString description=disk->description_short(disk);
445     if(disk->serial_no!=NULL)
446       description += ", S/N:" + QString(disk->serial_no);
447     HDDlistWidget->addItem(
448 	QIcon::fromTheme("drive-harddisk", QIcon(":res/gnome/drive-harddisk.png")),
449 	description);
450     if(disk==selected_disk)
451       HDDlistWidget->setCurrentIndex(i);
452   }
453   HDDlistWidget->addItem(
454       QIcon::fromTheme("application-x-cd-image", QIcon(":res/gnome/application-x-cd-image.png")),
455       tr("Add a raw disk image..."));
456 }
457 
setupUI()458 void QPhotorec::setupUI()
459 {
460   QWidget *t_copy = copyright(this);
461   QLabel *t_free_soft = new QLabel(tr("PhotoRec is free software, and comes with ABSOLUTELY NO WARRANTY."));
462   QLabel *t_select = new QLabel(tr("Please select a media to recover from"));
463 
464   HDDlistWidget = new QComboBox();
465   HDDlistWidget->setToolTip(tr("Disk capacity must be correctly detected for a successful recovery.\n"
466       "If a disk listed above has an incorrect size, check HD jumper settings and BIOS\n"
467       "detection, and install the latest OS patches and disk drivers.")
468   );
469 
470   QStringList oLabel;
471   oLabel.append("");
472   oLabel.append(tr("Flags"));
473   oLabel.append(tr("Type"));
474   oLabel.append(tr("File System"));
475   oLabel.append(tr("Size"));
476   oLabel.append(tr("Label"));
477 
478   PartListWidget= new QTableWidget();
479   PartListWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
480   PartListWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
481   PartListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
482   PartListWidget->verticalHeader()->hide();
483   PartListWidget->setShowGrid(false);
484   PartListWidget->setColumnCount( 6 );
485   PartListWidget->setHorizontalHeaderLabels( oLabel );
486   PartListWidget_updateUI();
487 
488   QGroupBox *groupBox1;
489   QGroupBox *groupBox2;
490 
491   groupBox1 = new QGroupBox(tr("File System type"));
492   qextRadioButton = new QRadioButton(tr("ext2/ext3/ext4 filesystem"));
493   qfatRadioButton = new QRadioButton(tr("FAT/NTFS/HFS+/ReiserFS/..."));
494   qfatRadioButton->setChecked(true);
495 
496   groupBox2 = new QGroupBox();
497   qfreeRadioButton = new QRadioButton(tr("Free: Scan for file from unallocated space only"));
498   qwholeRadioButton = new QRadioButton(tr("Whole: Extract files from whole partition"));
499   qfreeRadioButton->setEnabled(false);
500   qwholeRadioButton->setChecked(true);
501 
502 
503   QVBoxLayout *groupBox1Layout = new QVBoxLayout;
504   QVBoxLayout *groupBox2Layout = new QVBoxLayout;
505 
506   groupBox1Layout->addWidget(qextRadioButton);
507   groupBox1Layout->addWidget(qfatRadioButton);
508   groupBox1->setLayout(groupBox1Layout);
509 
510   groupBox2Layout->addWidget(qfreeRadioButton);
511   groupBox2Layout->addWidget(qwholeRadioButton);
512   groupBox2->setLayout(groupBox2Layout);
513 
514   QWidget *groupBox= new QWidget();
515   QHBoxLayout *groupBoxLayout = new QHBoxLayout;
516   groupBoxLayout->addWidget(groupBox1);
517   groupBoxLayout->addWidget(groupBox2);
518   groupBox->setLayout(groupBoxLayout);
519 
520 
521   QLabel *dstWidget= new QLabel(tr("Please select a destination to save the recovered files to."));
522   directoryLabel=new QLineEdit("");
523   QPushButton *dst_button = new QPushButton(
524       QIcon::fromTheme("folder", QIcon(":res/gnome/folder.png")),
525       tr("&Browse"));
526 
527   QWidget *dst_widget= new QWidget(this);
528   QWidget *dst_widget2= new QWidget(this);
529 
530   QHBoxLayout *dst_widgetLayout2 = new QHBoxLayout;
531   dst_widgetLayout2->addWidget(directoryLabel);
532   dst_widgetLayout2->addWidget(dst_button);
533   dst_widget2->setLayout(dst_widgetLayout2);
534 
535   QVBoxLayout *dst_widgetLayout = new QVBoxLayout;
536   dst_widgetLayout->addWidget(dstWidget);
537   dst_widgetLayout->addWidget(dst_widget2);
538   dst_widget->setLayout(dst_widgetLayout);
539 
540 
541   button_search = new QPushButton(QIcon::fromTheme("go-next", QIcon(":res/gnome/go-next.png")), tr("&Search"));
542   button_search->setEnabled(false);
543   QPushButton *button_quit= new QPushButton(QIcon::fromTheme("application-exit", QIcon(":res/gnome/application-exit.png")), tr("&Quit"));
544   QPushButton *button_about= new QPushButton(QIcon::fromTheme("help-about", QIcon(":res/gnome/help-about.png")), tr("&About"));
545   QPushButton *button_formats= new QPushButton(QIcon::fromTheme("image-x-generic.png", QIcon(":res/gnome/image-x-generic.png")),tr("&File Formats"));
546 
547   QWidget *B_widget = new QWidget(this);
548   QHBoxLayout *B_layout = new QHBoxLayout(B_widget);
549   B_layout->addWidget(button_about);
550   B_layout->addWidget(button_formats);
551   B_layout->addWidget(button_search);
552   B_layout->addWidget(button_quit);
553   B_widget->setLayout(B_layout);
554 
555   clearWidgets();
556 //  QLayout *mainLayout = this->layout();
557   delete this->layout();
558   QVBoxLayout *mainLayout = new QVBoxLayout();
559   mainLayout->addWidget(t_copy);
560   mainLayout->addWidget(t_free_soft);
561   mainLayout->addWidget(t_select);
562   mainLayout->addWidget(HDDlistWidget);
563   mainLayout->addWidget(PartListWidget);
564   mainLayout->addWidget(groupBox);
565   mainLayout->addWidget(dst_widget);
566   mainLayout->addWidget(B_widget);
567   this->setLayout(mainLayout);
568 
569   HDDlistWidget_updateUI();
570   buttons_updateUI();
571 
572   connect(button_about, SIGNAL(clicked()), this, SLOT(qphotorec_about()) );
573   connect(button_formats, SIGNAL(clicked()), this, SLOT(qphotorec_formats()) );
574   connect(button_search, SIGNAL(clicked()), this, SLOT(qphotorec_search()) );
575   connect(button_quit, SIGNAL(clicked()), qApp, SLOT(quit()) );
576   connect(HDDlistWidget, SIGNAL(activated(int)),this,SLOT(disk_changed(int)));
577   connect(PartListWidget, SIGNAL(itemSelectionChanged()), this, SLOT(partition_selected()));
578   connect(dst_button, SIGNAL(clicked()), this, SLOT(setExistingDirectory()));
579   connect(directoryLabel, SIGNAL(editingFinished()), this, SLOT(buttons_updateUI()));
580 }
581 
clearWidgets()582 void QPhotorec::clearWidgets()
583 {
584   while(1)
585   {
586     QLayoutItem *layoutwidget;
587     layoutwidget = this->layout()->takeAt(0);
588     if(layoutwidget==NULL)
589       return ;
590     layoutwidget->widget()->hide();
591     layoutwidget->widget()->deleteLater();
592   }
593 }
594 
photorec_info(const file_stat_t * file_stats)595 void QPhotorec::photorec_info(const file_stat_t *file_stats)
596 {
597   unsigned int i;
598   unsigned int nbr;
599   unsigned int others=0;
600   if(file_stats==NULL)
601     return ;
602   file_stat_t *new_file_stats;
603   filestatsWidget->setRowCount(0);
604   for(i=0;file_stats[i].file_hint!=NULL;i++);
605   nbr=i;
606   if(nbr==0)
607     return ;
608   new_file_stats=(file_stat_t*)MALLOC(nbr*sizeof(file_stat_t));
609   memcpy(new_file_stats, file_stats, nbr*sizeof(file_stat_t));
610   qsort(new_file_stats, nbr, sizeof(file_stat_t), sorfile_stat_ts);
611   for(i=0; i<10 && i<nbr && new_file_stats[i].recovered>0; i++)
612   {
613     QTableWidgetItem *item;
614     filestatsWidget->setRowCount(i+1);
615     if(new_file_stats[i].file_hint->extension!=NULL)
616     {
617       item = new QTableWidgetItem(new_file_stats[i].file_hint->extension);
618       filestatsWidget->setItem(i, 0, item);
619     }
620     item = new QTableWidgetItem();
621     item->setData(0, new_file_stats[i].recovered);
622     filestatsWidget->setItem(i, 1, item);
623   }
624   for(; i<nbr && new_file_stats[i].recovered>0; i++)
625     others+=new_file_stats[i].recovered;
626   if(others>0)
627   {
628     QTableWidgetItem *item;
629     filestatsWidget->setRowCount(11);
630     item = new QTableWidgetItem("others");
631     filestatsWidget->setItem(10, 0, item);
632     item = new QTableWidgetItem();
633     item->setData(0, others);
634     filestatsWidget->setItem(10, 1, item);
635   }
636   free(new_file_stats);
637 }
638 
qphotorec_search_updateUI()639 void QPhotorec::qphotorec_search_updateUI()
640 {
641   const partition_t *partition=params->partition;
642   const unsigned int sector_size=params->disk->sector_size;
643   QString tmp;
644   QString txt = QString(directoryLabel->text()).toHtmlEscaped();
645   folder_txt->setText(tr("Destination:")+" <a href=\"file://" + txt + "/" +
646       DEFAULT_RECUP_DIR + "." + QString::number(params->dir_num) + "\">" +
647       txt + "</a>");
648   if(params->status==STATUS_QUIT)
649   {
650     tmp=QString(tr("Recovery completed"));
651   }
652   else if(params->status==STATUS_EXT2_ON_BF || params->status==STATUS_EXT2_OFF_BF)
653   {
654     const unsigned long sectors_remaining=(params->offset-partition->part_offset)/sector_size;
655     tmp=QString(tr("Bruteforce %1 sectors remaining (test %2)").arg(sectors_remaining).arg(params->pass));
656   }
657   else
658   {
659     const unsigned long long sector_current=(params->offset>partition->part_offset && params->offset < partition->part_size ?
660 	((params->offset-partition->part_offset)/sector_size):
661 	0);
662     const unsigned long long sector_total=partition->part_size/sector_size;
663     tmp=QString(tr("Pass %1 - Reading sector %2/%3").arg(params->pass).arg(sector_current).arg(sector_total));
664   }
665   progress_info->setText(tmp);
666 
667   if(params->status==STATUS_FIND_OFFSET)
668     tmp=QString(tr("%1/10 headers found").arg(params->file_nbr));
669   else
670     tmp=QString(tr("%1 files found").arg(params->file_nbr));
671   progress_filefound->setText(tmp);
672 
673   if(params->status==STATUS_QUIT)
674   {
675     progress_bar->setMinimum(0);
676     progress_bar->setMaximum(100);
677     progress_bar->setValue(100);
678   }
679   else if(params->status==STATUS_FIND_OFFSET)
680   {
681     progress_bar->setMinimum(0);
682     progress_bar->setMaximum(10);
683     progress_bar->setValue(params->file_nbr);
684   }
685   else
686   {
687     progress_bar->setMinimum(0);
688     progress_bar->setMaximum(100);
689     progress_bar->setValue((params->offset-partition->part_offset)*100/ partition->part_size);
690   }
691   photorec_info(params->file_stats);
692 }
693 
qphotorec_search_setupUI()694 void QPhotorec::qphotorec_search_setupUI()
695 {
696   clearWidgets();
697   delete this->layout();
698   QVBoxLayout *mainLayout = new QVBoxLayout();
699   QWidget *t_copy = copyright(this);
700 
701   QSizePolicy c_sizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
702 
703   QLabel *disk_img=new QLabel();
704   QPixmap disk_pixmap = QPixmap(":res/gnome/drive-harddisk.png");
705   disk_img->setPixmap(disk_pixmap);
706   disk_img->setSizePolicy(c_sizePolicy);
707 
708   QLabel *disk_txt=new QLabel();
709   disk_txt->setText(selected_disk->description_short(selected_disk));
710 
711   QWidget *diskWidget = new QWidget();
712   QHBoxLayout *diskWidgetLayout = new QHBoxLayout(diskWidget);
713   diskWidgetLayout->addWidget(disk_img);
714   diskWidgetLayout->addWidget(disk_txt);
715   diskWidget->setLayout(diskWidgetLayout);
716 
717   QLabel *folder_img=new QLabel();
718   QPixmap *folder_pixmap = new QPixmap(":res/gnome/folder.png");
719   folder_img->setPixmap(*folder_pixmap);
720   folder_img->setSizePolicy(c_sizePolicy);
721 
722   folder_txt=new QLabel();
723   folder_txt->setTextFormat(Qt::RichText);
724   folder_txt->setTextInteractionFlags(Qt::TextBrowserInteraction);
725   folder_txt->setOpenExternalLinks(true);
726 
727   QWidget *folderWidget = new QWidget();
728   QHBoxLayout *folderWidgetLayout = new QHBoxLayout(folderWidget);
729   folderWidgetLayout->addWidget(folder_img);
730   folderWidgetLayout->addWidget(folder_txt);
731   folderWidget->setLayout(folderWidgetLayout);
732 
733 
734   progress_info=new QLabel();
735   progress_filefound=new QLabel();
736   progress_bar=new QProgressBar();
737 
738   QWidget *progressWidget = new QWidget();
739   QHBoxLayout *progressWidgetLayout = new QHBoxLayout(progressWidget);
740   progressWidgetLayout->addWidget(progress_info);
741   progressWidgetLayout->addWidget(progress_bar);
742   progressWidgetLayout->addWidget(progress_filefound);
743   progressWidget->setLayout(progressWidgetLayout);
744 
745   QWidget *progressWidget2 = new QWidget();
746   QHBoxLayout *progressWidgetLayout2 = new QHBoxLayout(progressWidget2);
747 // TODO
748 //  progressWidgetLayout2->addWidget(progress_elapsed);
749 //  progressWidgetLayout2->addWidget(progress_eta);
750   progressWidget2->setLayout(progressWidgetLayout2);
751 
752   QStringList oLabel;
753   oLabel.append(tr("File family"));
754   oLabel.append(tr("Number of files recovered"));
755 
756   filestatsWidget=new QTableWidget();
757   filestatsWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
758   filestatsWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
759   filestatsWidget->setSelectionMode(QAbstractItemView::SingleSelection);
760   filestatsWidget->verticalHeader()->hide();
761   filestatsWidget->setColumnCount( 2 );
762   filestatsWidget->setHorizontalHeaderLabels( oLabel );
763   filestatsWidget->resizeColumnsToContents();
764 
765   QPushButton *button_quit= new QPushButton(QIcon::fromTheme("application-exit", QIcon(":res/gnome/application-exit.png")), tr("&Quit"));
766   mainLayout->addWidget(t_copy);
767   mainLayout->addWidget(diskWidget);
768   mainLayout->addWidget(folderWidget);
769   mainLayout->addWidget(progressWidget);
770   mainLayout->addWidget(progressWidget2);
771   mainLayout->addWidget(filestatsWidget);
772   mainLayout->addWidget(button_quit);
773   this->setLayout(mainLayout);
774 
775   connect( button_quit, SIGNAL(clicked()), this, SLOT(stop_and_quit()) );
776   connect(this, SIGNAL(finished()), qApp, SLOT(quit()));
777 
778   timer = new QTimer(this);
779   timer->setInterval(500);
780   connect(timer, SIGNAL(timeout()), this, SLOT(qphotorec_search_updateUI()));
781 }
782 
stop_and_quit()783 void QPhotorec::stop_and_quit()
784 {
785   stop_the_recovery=true;
786   emit finished();
787 }
788 
photorec(alloc_data_t * list_search_space)789 int QPhotorec::photorec(alloc_data_t *list_search_space)
790 {
791   pstatus_t ind_stop=PSTATUS_OK;
792   const unsigned int blocksize_is_known=params->blocksize;
793   params_reset(params, options);
794   /* make the first recup_dir */
795   params->dir_num=photorec_mkdir(params->recup_dir, params->dir_num);
796   for(params->pass=0; params->status!=STATUS_QUIT; params->pass++)
797   {
798     timer->start();
799     switch(params->status)
800     {
801       case STATUS_UNFORMAT:
802 	/* FIXME */
803 	break;
804       case STATUS_FIND_OFFSET:
805 	{
806 	  uint64_t start_offset=0;
807 	  if(blocksize_is_known>0)
808 	  {
809 	    ind_stop=PSTATUS_OK;
810 	    if(!td_list_empty(&list_search_space->list))
811 	      start_offset=(td_list_entry(list_search_space->list.next, alloc_data_t, list))->start % params->blocksize;
812 	  }
813 	  else
814 	  {
815 	    ind_stop=photorec_find_blocksize(list_search_space);
816 	    params->blocksize=find_blocksize(list_search_space, params->disk->sector_size, &start_offset);
817 	  }
818 	  update_blocksize(params->blocksize, list_search_space, start_offset);
819 	}
820 	break;
821       case STATUS_EXT2_ON_BF:
822       case STATUS_EXT2_OFF_BF:
823 	/* FIXME */
824 	break;
825       default:
826 	ind_stop=photorec_aux(list_search_space);
827 	break;
828     }
829     timer->stop();
830     qphotorec_search_updateUI();
831     session_save(list_search_space, params, options);
832     switch(ind_stop)
833     {
834       case PSTATUS_EACCES:
835 	{
836 	  int ret=QMessageBox::warning(this,
837 	      tr("QPhotoRec: Failed to create file!"),
838 	      tr("Failed to create file! Please choose another destination"),
839 	      QMessageBox::Ok| QMessageBox::Cancel, QMessageBox::Ok);
840 	  if(ret==QMessageBox::Cancel)
841 	  {
842 	    params->status=STATUS_QUIT;
843 	  }
844 	  else
845 	  {
846 	    setExistingDirectory();
847 	    free(params->recup_dir);
848 	    QByteArray byteArray = (directoryLabel->text() + "/" + DEFAULT_RECUP_DIR).toUtf8();
849 	    params->recup_dir=strdup(byteArray.constData());
850 	    params->dir_num=photorec_mkdir(params->recup_dir, params->dir_num);
851 	  }
852 	}
853 	break;
854       case PSTATUS_ENOSPC:
855 	{
856 	  int ret=QMessageBox::warning(this,
857 	      tr("QPhotoRec: Not enough space!"),
858 	      tr("There is not enough space left! Please free disk space and/or choose another destination"),
859 	      QMessageBox::Ok| QMessageBox::Cancel, QMessageBox::Ok);
860 	  if(ret==QMessageBox::Cancel)
861 	  {
862 	    params->status=STATUS_QUIT;
863 	  }
864 	  else
865 	  {
866 	    setExistingDirectory();
867 	    free(params->recup_dir);
868 	    QByteArray byteArray = (directoryLabel->text() + "/" + DEFAULT_RECUP_DIR).toUtf8();
869 	    params->recup_dir=strdup(byteArray.constData());
870 	    params->dir_num=photorec_mkdir(params->recup_dir, params->dir_num);
871 	  }
872 	}
873 break;
874       case PSTATUS_OK:
875 	status_inc(params, options);
876 	if(params->status==STATUS_QUIT)
877 	  unlink("photorec.ses");
878 	break;
879       case PSTATUS_STOP:
880 	params->status=STATUS_QUIT;
881 	break;
882     }
883     update_stats(params->file_stats, list_search_space);
884     qphotorec_search_updateUI();
885   }
886   free_search_space(list_search_space);
887   free_header_check();
888   free(params->file_stats);
889   params->file_stats=NULL;
890   return 0;
891 }
892 
qphotorec_search()893 void QPhotorec::qphotorec_search()
894 {
895   if(selected_disk==NULL || selected_partition==NULL)
896     return;
897   alloc_data_t list_search_space;
898   TD_INIT_LIST_HEAD(&list_search_space.list);
899 
900   QByteArray byteArray = (directoryLabel->text() + "/" + DEFAULT_RECUP_DIR).toUtf8();
901   params->recup_dir=strdup(byteArray.constData());
902   params->carve_free_space_only=qfreeRadioButton->isChecked();
903   params->disk=selected_disk;
904   params->partition=selected_partition;
905   log_partition(selected_disk, selected_partition);
906 
907   options->mode_ext2=qextRadioButton->isChecked();
908 
909   qphotorec_search_setupUI();
910   if(td_list_empty(&list_search_space.list))
911   {
912     init_search_space(&list_search_space, params->disk, params->partition);
913   }
914   if(params->carve_free_space_only>0)
915   {
916     params->blocksize=remove_used_space(params->disk, params->partition, &list_search_space);
917   }
918   photorec(&list_search_space);
919   free(params->recup_dir);
920   params->recup_dir=NULL;
921 }
922 
qphotorec_about()923 void QPhotorec::qphotorec_about()
924 {
925   QPixmap pixmap_img = QPixmap(":res/photorec_64x64.png");
926   QMessageBox msg;
927   msg.setText(tr("QPhotoRec is is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.\n\nQPhotoRec is is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with QPhotoRec.  If not, see <http://www.gnu.org/licenses/>."));
928   msg.setWindowTitle(tr("QPhotoRec: About"));
929   msg.addButton(QMessageBox::Close);
930   msg.setIconPixmap(pixmap_img);
931   msg.exec();
932 }
933 
qphotorec_formats()934 void QPhotorec::qphotorec_formats()
935 {
936   file_enable_t *file_enable;
937   QStringList list;
938   formats=new QListWidget();
939   for(file_enable=list_file_enable;
940       file_enable->file_hint!=NULL;
941       file_enable++)
942   {
943     QListWidgetItem * item;
944     char descr[128];
945     sprintf(descr, "%-4s %s",
946 	    (file_enable->file_hint->extension!=NULL?
947 	     file_enable->file_hint->extension:""),
948 	    file_enable->file_hint->description);
949     item = new QListWidgetItem(descr, formats);
950     if(file_enable->enable)
951       item->setCheckState (Qt::Checked);
952     else
953       item->setCheckState (Qt::Unchecked);
954   }
955 
956   QDialog fenetre3;
957   fenetre3.setWindowTitle("QPhotoRec: "+tr("File Formats"));
958   QDialogButtonBox buttonBox(Qt::Horizontal);
959 
960   QPushButton *bt_reset= new QPushButton(tr("&Reset"));
961   QPushButton *bt_restore= new QPushButton(tr("Res&tore"));
962 
963   buttonBox.addButton(bt_reset, QDialogButtonBox::ResetRole);
964   buttonBox.addButton(bt_restore, QDialogButtonBox::ResetRole);
965   buttonBox.addButton(QDialogButtonBox::Ok);
966   QVBoxLayout vbox;
967   vbox.addWidget(formats);
968   vbox.addWidget(&buttonBox);
969   fenetre3.setLayout(&vbox);
970   connect(&buttonBox, SIGNAL(accepted()), &fenetre3, SLOT(accept()));
971   connect(bt_reset, SIGNAL(clicked()), this, SLOT(formats_reset()));
972   connect(bt_restore, SIGNAL(clicked()), this, SLOT(formats_restore()));
973   fenetre3.exec();
974   int i;
975   for (i = 0, file_enable=list_file_enable;
976       i < formats->count() && file_enable->file_hint!=NULL;
977       i++, file_enable++)
978   {
979     QListWidgetItem *item = formats->item(i);
980     file_enable->enable=(item->checkState()==Qt::Checked?1:0);
981   }
982 }
983 
formats_reset()984 void QPhotorec::formats_reset()
985 {
986   for (int i = 0; i < formats->count(); i++) {
987     QListWidgetItem *item = formats->item(i);
988     item->setCheckState (Qt::Unchecked);
989   }
990 }
991 
formats_restore()992 void QPhotorec::formats_restore()
993 {
994   file_enable_t *file_enable;
995   int i;
996   for (i = 0, file_enable=list_file_enable;
997       i < formats->count() && file_enable->file_hint!=NULL;
998       i++, file_enable++)
999   {
1000     QListWidgetItem *item = formats->item(i);
1001     if(file_enable->file_hint->enable_by_default)
1002       item->setCheckState (Qt::Checked);
1003     else
1004       item->setCheckState (Qt::Unchecked);
1005   }
1006 }
1007