libarchivehandler.cpp 20.9 KB
Newer Older
1
2
/*
 * Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
3
 * Copyright (c) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "libarchivehandler.h"
#include "kerfuffle/archivefactory.h"
29
#include "kerfuffle/queries.h"
30
31
32
33

#include <archive.h>
#include <archive_entry.h>

34
#include <KDebug>
35
#include <KLocale>
36
#include <kde_file.h>
37

38
#include <QDateTime>
39
#include <QDir>
40
41
#include <QDirIterator>
#include <QFile>
42
43
44
45
#include <QList>
#include <QStringList>

LibArchiveInterface::LibArchiveInterface( const QString & filename, QObject *parent )
46
	: ReadWriteArchiveInterface( filename, parent ),
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
47
	m_cachedArchiveEntryCount(0),
48
49
	m_emitNoEntries(false),
	m_extractedFilesSize(0)
50
51
52
53
54
55
56
57
58
{
}

LibArchiveInterface::~LibArchiveInterface()
{
}

bool LibArchiveInterface::list()
{
59
	kDebug (1601);
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
	struct archive *arch;
	struct archive_entry *aentry;
	int result;

	arch = archive_read_new();
	if ( !arch )
		return false;

	result = archive_read_support_compression_all( arch );
	if ( result != ARCHIVE_OK ) return false;

	result = archive_read_support_format_all( arch );
	if ( result != ARCHIVE_OK ) return false;

	result = archive_read_open_filename( arch, QFile::encodeName( filename() ), 10240 );

	if ( result != ARCHIVE_OK )
	{
Yew Ming Chen's avatar
Yew Ming Chen committed
78
		error( i18n( "Could not open the file '%1', libarchive cannot handle it.", filename() ), QString() );
79
80
		return false;
	}
81
	
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
82
83
	m_cachedArchiveEntryCount = 0;
	m_extractedFilesSize = 0;
84
85
86

	while ( ( result = archive_read_next_header( arch, &aentry ) ) == ARCHIVE_OK )
	{
87
		if (!m_emitNoEntries) emitEntryFromArchiveEntry(aentry);
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
88
		m_extractedFilesSize += ( qlonglong ) archive_entry_size( aentry );
89

Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
90
		m_cachedArchiveEntryCount++;
91
		archive_read_data_skip( arch );
92
93
94
95
	}

	if ( result != ARCHIVE_EOF )
	{
96
		error( i18n("The archive reading failed with message: %1", archive_error_string(arch)) );
97
98
99
		return false;
	}

100
#if (ARCHIVE_API_VERSION>1)
101
	return archive_read_finish( arch ) == ARCHIVE_OK;
102
103
104
#else
	return true;
#endif
105
106
}

107
bool LibArchiveInterface::copyFiles( const QList<QVariant> & files, const QString & destinationDirectory, ExtractionOptions options )
108
{
109
	kDebug( 1601 ) << "Changing current directory to " << destinationDirectory;
110
111
112
	QDir::setCurrent( destinationDirectory );

	const bool extractAll = files.isEmpty();
113
	const bool preservePaths = options.value("PreservePaths").toBool();
114

115
116
	//TODO: don't leak these if the extraction fails with an error in the
	//middle
117
118
	struct archive *arch, *writer;
	struct archive_entry *entry;
119
	
120
121
	QString rootNode;
	if (options.contains("RootNode"))
122
	{
123
124
		rootNode = options.value("RootNode").toString();
		kDebug(1601) << "Set root node " << rootNode;
125
	}
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
	arch = archive_read_new();
	if ( !arch )
	{
		return false;
	}

	writer = archive_write_disk_new();
	archive_write_disk_set_options( writer, extractionFlags() );

	archive_read_support_compression_all( arch );
	archive_read_support_format_all( arch );
	int res = archive_read_open_filename( arch, QFile::encodeName( filename() ), 10240 );

	if ( res != ARCHIVE_OK )
	{
Andrew Coles's avatar
Andrew Coles committed
142
		error(i18n("Unable to open the file '%1', libarchive cannot handle it.", filename())) ;
143
144
145
		return false;
	}

146
	int entryNr = 0, totalCount = 0;
147
148
	if (extractAll)
	{
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
149
		if (!m_cachedArchiveEntryCount)
150
151
152
153
		{
			progress(0);
			//TODO: once information progress has been implemented, send
			//feedback here that the archive is being read
154
155
			kDebug( 1601 ) << "For getting progress information, the archive will be listed once";
			m_emitNoEntries = true;
156
			list();
157
			m_emitNoEntries = false;
158
		}
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
159
		totalCount = m_cachedArchiveEntryCount;
160
161
162
	}
	else
		totalCount = files.size();
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
163
	m_currentExtractedFilesSize = 0;
164

165
166
167
	bool overwriteAll = false; // Whether to overwrite all files
	bool skipAll = false; // Whether to skip all files

168
169
	while ( archive_read_next_header( arch, &entry ) == ARCHIVE_OK )
	{
170
171
        //retry with renamed entry, fire an overwrite query again if the new entry also exists
retry:
172
173
		const bool entryIsDir = S_ISDIR(archive_entry_mode( entry ));

174
		//we skip directories of not preserving paths
175
		if (!preservePaths && entryIsDir) {
176
177
178
179
			archive_read_data_skip( arch );
			continue;
		}

180
		//entryName is the name inside the archive, full path
181
		QString entryName = QDir::fromNativeSeparators(QFile::decodeName( archive_entry_pathname( entry ) ));
182

183
		if (entryName.startsWith('/')) {
184
185
186
187
188
189
			//for now we just can't handle absolute filenames in a tar archive.
			//TODO: find out what to do here!!
			error(i18n("This archive contains archive entries with absolute paths, which are not yet supported by ark."));
			return false;
		}

190
		if ( files.contains( entryName ) || extractAll )
191
		{
192
193
			// entryFI is the fileinfo pointing to where the file will be
			// written from the archive
194
			QFileInfo entryFI( entryName );
195
196
197
			//kDebug( 1601 ) << "setting path to " << archive_entry_pathname( entry );
			
			QString fileWithoutPath = entryFI.fileName();
198

199
200
			//if we DON'T preserve paths, we cut the path and set the entryFI
			//fileinfo to the one without the path
201
202
203
			if( !preservePaths ) {
				//empty filenames (ie dirs) should have been skipped already,
				//so asserting
204
				Q_ASSERT(!fileWithoutPath.isEmpty());
205

206
207
				archive_entry_copy_pathname( entry, QFile::encodeName(fileWithoutPath).constData() );
				entryFI = QFileInfo(fileWithoutPath);
208

209
210
				//OR, if the commonBase has been set, then we remove this
				//common base from the filename
211
			} else if (!rootNode.isEmpty()) {
212
				QString truncatedFilename;
213
				truncatedFilename = entryName.remove(0, rootNode.size());
214
				kDebug( 1601 ) << "Truncated filename: " << truncatedFilename;
215
				archive_entry_copy_pathname( entry, QFile::encodeName(truncatedFilename).constData() );
216
217

				entryFI = QFileInfo(truncatedFilename);
218
219
			}

220
			//now check if the file about to be written already exists
221
222
223
224
225
226
227
			if (!entryIsDir && entryFI.exists()) {
				if (skipAll) {
					archive_read_data_skip( arch );
					archive_entry_clear( entry );
					continue;
				}
				else if (!overwriteAll && !skipAll) {
228
					Kerfuffle::OverwriteQuery query(entryName);
229
					userQuery(&query);
230
231
232
233
					query.waitForResponse();

					if (query.responseCancelled()) {
						archive_read_data_skip( arch );
234
						archive_entry_clear( entry );
235
236
						break;
					}
237
					else if (query.responseSkip()) {
238
239
240
241
						archive_read_data_skip( arch );
						archive_entry_clear( entry );
						continue;
					}
242
					else if (query.responseAutoSkip()) {
243
244
						archive_read_data_skip( arch );
						archive_entry_clear( entry );
245
						skipAll = true;
246
247
						continue;
					}
248
					else if (query.responseRename()) {
249
250
						QString newName = query.newFilename();
						archive_entry_copy_pathname(entry, QFile::encodeName(newName).constData());
251
252
253
254
						goto retry;
					}
					else if (query.responseOverwriteAll()) {
						overwriteAll = true;
255
					}
256
257
				}
			}
Olivier Trichet's avatar
Olivier Trichet committed
258

259
			int header_response;
260
			kDebug(1601) << "Writing " << fileWithoutPath << " to " << archive_entry_pathname(entry);
261
			if ( (header_response = archive_write_header( writer, entry )) == ARCHIVE_OK )
262
263
				//if the whole archive is extracted and the total filesize is
				//available, we use partial progress
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
264
				copyData( arch, writer, (extractAll && m_extractedFilesSize) ); 
265
266
			else {
				kDebug( 1601 ) << "Writing header failed with error code " << header_response
267
				<< "While attempting to write " << fileWithoutPath;
268
			}
269
270
271
272

			//if we only partially extract the archive and the number of
			//archive entries is available we use a simple progress based on
			//number of items extracted
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
273
			if (!extractAll && m_cachedArchiveEntryCount)
274
275
276
277
			{
				++entryNr;
				progress(float(entryNr) / totalCount);
			}
Olivier Trichet's avatar
Olivier Trichet committed
278
			archive_entry_clear( entry );
279
280
281
282
283
284
		}
		else
		{
			archive_read_data_skip( arch );
		}
	}
285

Harald Hvaal's avatar
Harald Hvaal committed
286
287
	archive_write_finish( writer );

288
#if (ARCHIVE_API_VERSION>1)
289
	return archive_read_finish( arch ) == ARCHIVE_OK;
290
291
292
#else
	return true;
#endif
293
294
}

295
bool LibArchiveInterface::addFiles( const QStringList & files, const CompressionOptions& options )
296
{
297
	struct archive *arch_reader = NULL, *arch_writer = NULL;
298
	struct archive_entry *entry;
299
	int header_response;
300
	int ret;
301
	const bool creatingNewFile = !QFileInfo(filename()).exists();
302

303
	QString tempFilename = filename() + ".arkWriting";
304

305
	kDebug( 1601 ) << "Current path " << QDir::currentPath();
306

Harald Hvaal's avatar
Harald Hvaal committed
307
308
309
310
311
312
	QString globalWorkdir = options.value("GlobalWorkDir").toString();
	if (!globalWorkdir.isEmpty()) {
		kDebug( 1601 ) << "GlobalWorkDir is set, changing dir to " << globalWorkdir;
		QDir::setCurrent(globalWorkdir);
	}

313
	m_writtenFiles.clear();
Harald Hvaal's avatar
Harald Hvaal committed
314

315
316
317
318
319
320
321
322
	if (!creatingNewFile) {
		//*********initialize the reader
		arch_reader = archive_read_new();
		if ( !arch_reader )
		{
			error(i18n("The archive reader could not be initialized."));
			return false;
		}
323

324
325
326
327
328
329
330
331
		archive_read_support_compression_all( arch_reader );
		archive_read_support_format_all( arch_reader );
		ret = archive_read_open_filename( arch_reader, QFile::encodeName( filename() ), 10240 );
		if ( ret != ARCHIVE_OK )
		{
			error(i18n("The source file could not be read."));
			return false;
		}
332
	}
333
334
335
336
337

	//*********initialize the writer
	arch_writer = archive_write_new();
	if ( !arch_writer )
	{
338
339
340
341
		error(i18n("The archive writer could not be initialized."));
		return false;
	}

342
343
344
	//pax_restricted is the libarchive default, let's go with that.
	archive_write_set_format_pax_restricted(arch_writer);

345
346
347
	if (creatingNewFile) {
		if (filename().right(2).toUpper() == "GZ") {
			kDebug(1601) << "Detected gzip compression for new file";
348
			ret = archive_write_set_compression_gzip(arch_writer);
349
350
		} else if (filename().right(3).toUpper() == "BZ2") {
			kDebug(1601) << "Detected bzip2 compression for new file";
351
			ret = archive_write_set_compression_bzip2(arch_writer);
352
353
354
355
		} else {
			kDebug(1601) << "Falling back to gzip";
			ret = archive_write_set_compression_gzip(arch_writer);
		}
356

357
358
359
360
		if (ret != ARCHIVE_OK) {
			error(i18n("Setting compression failed with the error '%1'", QString(archive_error_string( arch_writer ))));
			return false;
		}
361

362
363
364
365
366
367
368
369
370
371
372
373
		if (ret != ARCHIVE_OK) {
			error(i18n("Setting format failed with the error '%1'", QString(archive_error_string( arch_writer ))));
			return false;
		}
	} else {
		switch (archive_compression(arch_reader)) {
			case ARCHIVE_COMPRESSION_GZIP:
				ret = archive_write_set_compression_gzip(arch_writer);
				break;
			case ARCHIVE_COMPRESSION_BZIP2:
				ret = archive_write_set_compression_bzip2(arch_writer);
				break;
374
375
376
			case ARCHIVE_COMPRESSION_NONE:
				ret = archive_write_set_compression_none(arch_writer);
				break;
377
378
			default:
				error(i18n("The compression type '%1' is not supported by Ark.", QString(archive_compression_name(arch_reader))));
379
				return false;
380
381
382
383
384
		}
		if (ret != ARCHIVE_OK) {
			error(i18n("Setting compression failed with the error '%1'", QString(archive_error_string( arch_writer ))));
			return false;
		}
385
	}
386

387
388
389
390
391
	ret = archive_write_open_filename(arch_writer, QFile::encodeName( tempFilename ));
	if (ret != ARCHIVE_OK) {
		error(i18n("Opening the archive for writing failed with error message '%1'", QString(archive_error_string( arch_writer ))));
		return false;
	}
392

393
394
395
396
	entry = archive_entry_new();

	//**************** first write the new files
	foreach(const QString& selectedFile, files) {
397
		bool success;
398

399
		success = writeFile(selectedFile, arch_writer, entry);
400

401
		if (!success)
402
			return false;
Harald Hvaal's avatar
Harald Hvaal committed
403

404
		if (QFileInfo(selectedFile).isDir()) {
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
405
			QDirIterator it(selectedFile, QDir::AllEntries | QDir::Readable | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
Harald Hvaal's avatar
Harald Hvaal committed
406
407
408
409
410

			while (it.hasNext()) {
				QString path = it.next();
				if (it.fileName() == ".." || it.fileName() == ".") continue;

Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
411
				success = writeFile(path +
Harald Hvaal's avatar
Harald Hvaal committed
412
						(it.fileInfo().isDir() ? "/" : "")
413
						, arch_writer, entry);
414
415

				if (!success)
416
					return false;
417
418
419
			}
		}
	}
420

421
422
423
	//and if we have old elements...
	if (!creatingNewFile) {
		//********** copy old elements from previous archive to new archive
424
425
		while ( archive_read_next_header( arch_reader, &entry ) == ARCHIVE_OK )
		{
426
			if (m_writtenFiles.contains(QFile::decodeName(archive_entry_pathname(entry)))) {
Harald Hvaal's avatar
Harald Hvaal committed
427
428
429
430
431
				archive_read_data_skip( arch_reader );
				kDebug( 1601 ) << "Entry already existing, will be refresh: ===> " << archive_entry_pathname(entry);
				continue;
			}

432
433
434
435
			//kDebug(1601) << "Writing entry " << fn;
			if ( (header_response = archive_write_header( arch_writer, entry )) == ARCHIVE_OK )
				//if the whole archive is extracted and the total filesize is
				//available, we use partial progress
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
436
				copyData( arch_reader, arch_writer, false);
437
438
			else {
				kDebug( 1601 ) << "Writing header failed with error code " << header_response;
439
440
441
				return false;
			}

442
			archive_entry_clear( entry );
443
444
445
446
		}
	}

	ret = archive_write_finish(arch_writer);
447

448
449
450
451
452
453
454
455
	if (!creatingNewFile) {
		archive_read_finish( arch_reader );

		//everything seems OK, so we remove the source file and replace it with
		//the new one.
		//TODO: do some extra checks to see if this is really OK
		QFile::remove(filename());
	}
456

457
	QFile::rename(tempFilename, filename());
458

459
	return true;
460
461
462
463
}

bool LibArchiveInterface::deleteFiles( const QList<QVariant> & files )
{
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
	struct archive *arch_reader = NULL, *arch_writer = NULL;
	struct archive_entry *entry;
	int header_response;
	int ret;

	QString tempFilename = filename() + ".arkWriting";

	arch_reader = archive_read_new();
	if ( !arch_reader )
	{
		error(i18n("The archive reader could not be initialized."));
		return false;
	}

	archive_read_support_compression_all( arch_reader );
	archive_read_support_format_all( arch_reader );
	ret = archive_read_open_filename( arch_reader, QFile::encodeName( filename() ), 10240 );
	if ( ret != ARCHIVE_OK )
	{
		error(i18n("The source file could not be read."));
		return false;
	}

	//*********initialize the writer
	arch_writer = archive_write_new();
	if ( !arch_writer )
	{
		error(i18n("The archive writer could not be initialized."));
		return false;
	}

	//pax_restricted is the libarchive default, let's go with that.
	archive_write_set_format_pax_restricted(arch_writer);

	switch (archive_compression(arch_reader)) {
		case ARCHIVE_COMPRESSION_GZIP:
			ret = archive_write_set_compression_gzip(arch_writer);
			break;
		case ARCHIVE_COMPRESSION_BZIP2:
			ret = archive_write_set_compression_bzip2(arch_writer);
			break;
505
506
507
		case ARCHIVE_COMPRESSION_NONE:
			ret = archive_write_set_compression_none(arch_writer);
			break;
508
509
510
511
		default:
			error(i18n("The compression type '%1' is not supported by Ark.", QString(archive_compression_name(arch_reader))));
			return false;
	}
512

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
	if (ret != ARCHIVE_OK) {
		error(i18n("Setting compression failed with the error '%1'", QString(archive_error_string( arch_writer ))));
		return false;
	}

	ret = archive_write_open_filename(arch_writer, QFile::encodeName( tempFilename ));
	if (ret != ARCHIVE_OK) {
		error(i18n("Opening the archive for writing failed with error message '%1'", QString(archive_error_string( arch_writer ))));
		return false;
	}

	entry = archive_entry_new();

	//********** copy old elements from previous archive to new archive
	while ( archive_read_next_header( arch_reader, &entry ) == ARCHIVE_OK )
	{
		if (files.contains(QFile::decodeName(archive_entry_pathname(entry)))) {
			archive_read_data_skip( arch_reader );
			kDebug( 1601 ) << "Entry to be deleted, skipping" << archive_entry_pathname(entry);
			entryRemoved(QFile::decodeName(archive_entry_pathname(entry)));
			continue;
		}

		//kDebug(1601) << "Writing entry " << fn;
		if ( (header_response = archive_write_header( arch_writer, entry )) == ARCHIVE_OK )

			//if the whole archive is extracted and the total filesize is
			//available, we use partial progress
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
541
			copyData( arch_reader, arch_writer, false);
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
		else {
			kDebug( 1601 ) << "Writing header failed with error code " << header_response;
			return false;
		}

		archive_entry_clear( entry );
	}

	ret = archive_write_finish(arch_writer);

	archive_read_finish( arch_reader );

	//everything seems OK, so we remove the source file and replace it with
	//the new one.
	//TODO: do some extra checks to see if this is really OK
	QFile::remove(filename());
	QFile::rename(tempFilename, filename());
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608

	return true;
}

void LibArchiveInterface::emitEntryFromArchiveEntry( struct archive_entry *aentry )
{
	ArchiveEntry e;
#ifdef _MSC_VER
	e[ FileName ] = QDir::fromNativeSeparators(QString::fromUtf16((ushort*)archive_entry_pathname_w( aentry )));
#else
	e[ FileName ] = QDir::fromNativeSeparators(QString::fromWCharArray(archive_entry_pathname_w( aentry )));
#endif
	e[ InternalID ] = e[ FileName ];

	QString owner = QString( archive_entry_uname( aentry ) );
	if (!owner.isEmpty()) e[ Owner ] = owner;

	QString group = QString( archive_entry_gname( aentry ) );
	if (!group.isEmpty()) e[ Group ] = group;

	e[ Size ] = ( qlonglong ) archive_entry_size( aentry );
	e[ IsDirectory ] = S_ISDIR( archive_entry_mode( aentry ) ); // see stat(2)
	if ( archive_entry_symlink( aentry ) )
	{
		e[ Link ] = archive_entry_symlink( aentry );
	}
	e[ Timestamp ] = QDateTime::fromTime_t( archive_entry_mtime( aentry ) );

	entry(e);
}

int LibArchiveInterface::extractionFlags() const
{
	int result = ARCHIVE_EXTRACT_TIME;
	result |= ARCHIVE_EXTRACT_SECURE_NODOTDOT;

	// TODO: Don't use arksettings here
	/*if ( ArkSettings::preservePerms() )
	{
		result &= ARCHIVE_EXTRACT_PERM;
	}

	if ( !ArkSettings::extractOverwrite() )
	{
		result &= ARCHIVE_EXTRACT_NO_OVERWRITE;
	}*/

	return result;
}

Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
609
void LibArchiveInterface::copyData( const QString& filename, struct archive *dest, bool partialprogress )
Raphael Kubo da Costa's avatar
Raphael Kubo da Costa committed
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
{
	char buff[ARCHIVE_DEFAULT_BYTES_PER_BLOCK];
	ssize_t readBytes;
	QFile file(filename);

	if (!file.open(QIODevice::ReadOnly))
		return;

	readBytes = file.read( buff, sizeof(buff) );
	while(readBytes > 0)
	{
	   /* int writeBytes = */ 
		archive_write_data( dest, buff, readBytes );
		if( archive_errno( dest ) != ARCHIVE_OK ) {
			kDebug() << "Error while writing..." << archive_error_string( dest ) << "(error nb =" << archive_errno( dest ) << ')';
			return;
		}

		if (partialprogress) {
			m_currentExtractedFilesSize += readBytes;
			progress(float(m_currentExtractedFilesSize) / m_extractedFilesSize);
		}

		readBytes = file.read( buff, sizeof(buff) );
	}

	file.close();
}

void LibArchiveInterface::copyData( struct archive *source, struct archive *dest, bool partialprogress )
{
	char buff[ARCHIVE_DEFAULT_BYTES_PER_BLOCK];
	ssize_t readBytes;

	readBytes = archive_read_data( source, buff, sizeof(buff) );
	while(readBytes > 0)
	{
	   /* int writeBytes = */ 
		archive_write_data( dest, buff, readBytes );
		if( archive_errno( dest ) != ARCHIVE_OK ) {
			kDebug() << "Error while extracting..." << archive_error_string( dest ) << "(error nb =" << archive_errno( dest ) << ')';
			return;
		}

		if (partialprogress) {
			m_currentExtractedFilesSize += readBytes;
			progress(float(m_currentExtractedFilesSize) / m_extractedFilesSize);
		}

		readBytes = archive_read_data( source, buff, sizeof(buff) );
	}
}

bool LibArchiveInterface::writeFile( const QString& fileName, struct archive* arch_writer, struct archive_entry* entry )
{
	KDE_struct_stat st;
	int header_response;

	bool trailingSlash = fileName.endsWith('/');
	QString relativeName = QDir::current().relativeFilePath(fileName) + (trailingSlash ? "/" : "");

	KDE_stat(QFile::encodeName(relativeName).constData(), &st);
	archive_entry_copy_stat(entry, &st);
	archive_entry_copy_pathname( entry, QFile::encodeName(relativeName).constData() );

	kDebug( 1601 ) << "Writing new entry " << archive_entry_pathname(entry);
	if ( (header_response = archive_write_header( arch_writer, entry )) == ARCHIVE_OK )
		//if the whole archive is extracted and the total filesize is
		//available, we use partial progress
		copyData( fileName, arch_writer, false); 
	else {
		kDebug( 1601 ) << "Writing header failed with error code " << header_response;
		kDebug() << "Error while writing..." << archive_error_string( arch_writer ) << "(error nb =" << archive_errno( arch_writer ) << ')';
		return false;
	}

	m_writtenFiles.push_back(relativeName);

	emitEntryFromArchiveEntry(entry);
	archive_entry_clear( entry );

691
	return true;
692
693
}

Pino Toscano's avatar
Pino Toscano committed
694
KERFUFFLE_PLUGIN_FACTORY( LibArchiveInterface )
695
696

#include "libarchivehandler.moc"