ZzipArchivePlugin.cxx 4.71 KB
Newer Older
1
/*
2
 * Copyright (C) 2003-2013 The Music Player Daemon Project
3
 * http://www.musicpd.org
4 5 6 7 8 9 10 11 12 13
 *
 * This program 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.
 *
 * This program 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.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 19 20 21 22 23
 */

/**
  * zip archive handling (requires zziplib)
  */

24
#include "config.h"
25 26
#include "ZzipArchivePlugin.hxx"
#include "ArchivePlugin.hxx"
27
#include "ArchiveFile.hxx"
28
#include "ArchiveVisitor.hxx"
29
#include "InputInternal.hxx"
30
#include "InputStream.hxx"
31
#include "InputPlugin.hxx"
32
#include "util/RefCount.hxx"
33 34
#include "util/Error.hxx"
#include "util/Domain.hxx"
35 36

#include <zzip/zzip.h>
37

38 39
#include <string.h>

40
class ZzipArchiveFile final : public ArchiveFile {
41
public:
42
	RefCount ref;
43

44
	ZZIP_DIR *const dir;
45

46 47
	ZzipArchiveFile(ZZIP_DIR *_dir)
		:ArchiveFile(zzip_archive_plugin), dir(_dir) {}
48

49 50 51
	~ZzipArchiveFile() {
		zzip_dir_close(dir);
	}
52

53 54 55
	void Unref() {
		if (ref.Decrement())
			delete this;
56
	}
57

58 59 60 61 62 63 64 65
	virtual void Close() override {
		Unref();
	}

	virtual void Visit(ArchiveVisitor &visitor) override;

	virtual input_stream *OpenStream(const char *path,
					 Mutex &mutex, Cond &cond,
66
					 Error &error) override;
67
};
68

69
extern const InputPlugin zzip_input_plugin;
70

71
static constexpr Domain zzip_domain("zzip");
72

73 74
/* archive open && listing routine */

75
static ArchiveFile *
76
zzip_archive_open(const char *pathname, Error &error)
77
{
78 79
	ZZIP_DIR *dir = zzip_dir_open(pathname, NULL);
	if (dir == nullptr) {
80 81
		error.Format(zzip_domain, "Failed to open ZIP file %s",
			     pathname);
82 83 84
		return NULL;
	}

85
	return new ZzipArchiveFile(dir);
86 87
}

88 89
inline void
ZzipArchiveFile::Visit(ArchiveVisitor &visitor)
90
{
91 92 93 94 95 96 97
	zzip_rewinddir(dir);

	ZZIP_DIRENT dirent;
	while (zzip_dir_read(dir, &dirent))
		//add only files
		if (dirent.st_size > 0)
			visitor.VisitArchiveEntry(dirent.d_name);
98 99 100 101
}

/* single archive handling */

102
struct ZzipInputStream {
103 104
	struct input_stream base;

105
	ZzipArchiveFile *archive;
106 107

	ZZIP_FILE *file;
108 109 110 111

	ZzipInputStream(ZzipArchiveFile &_archive, const char *uri,
			Mutex &mutex, Cond &cond,
			ZZIP_FILE *_file)
112 113
		:base(zzip_input_plugin, uri, mutex, cond),
		 archive(&_archive), file(_file) {
114 115 116 117 118 119 120 121
		base.ready = true;
		//we are seekable (but its not recommendent to do so)
		base.seekable = true;

		ZZIP_STAT z_stat;
		zzip_file_stat(file, &z_stat);
		base.size = z_stat.st_size;

122
		archive->ref.Increment();
123 124 125 126 127 128
	}

	~ZzipInputStream() {
		zzip_file_close(file);
		archive->Unref();
	}
129 130
};

131 132 133
input_stream *
ZzipArchiveFile::OpenStream(const char *pathname,
			    Mutex &mutex, Cond &cond,
134
			    Error &error)
135
{
136
	ZZIP_FILE *_file = zzip_file_open(dir, pathname, 0);
137
	if (_file == nullptr) {
138 139
		error.Format(zzip_domain, "not found in the ZIP file: %s",
			     pathname);
140
		return NULL;
141 142
	}

143
	ZzipInputStream *zis =
144
		new ZzipInputStream(*this, pathname,
145 146
				    mutex, cond,
				    _file);
147
	return &zis->base;
148 149 150
}

static void
151
zzip_input_close(struct input_stream *is)
152
{
153
	ZzipInputStream *zis = (ZzipInputStream *)is;
154

155
	delete zis;
156 157 158
}

static size_t
159
zzip_input_read(struct input_stream *is, void *ptr, size_t size,
160
		Error &error)
161
{
162
	ZzipInputStream *zis = (ZzipInputStream *)is;
163
	int ret;
164 165

	ret = zzip_file_read(zis->file, ptr, size);
166
	if (ret < 0) {
167
		error.Set(zzip_domain, "zzip_file_read() has failed");
168 169
		return 0;
	}
170

171
	is->offset = zzip_tell(zis->file);
172

173 174 175 176
	return ret;
}

static bool
177
zzip_input_eof(struct input_stream *is)
178
{
179
	ZzipInputStream *zis = (ZzipInputStream *)is;
180

181
	return (InputPlugin::offset_type)zzip_tell(zis->file) == is->size;
182 183 184
}

static bool
185 186
zzip_input_seek(struct input_stream *is, InputPlugin::offset_type offset,
		int whence, Error &error)
187
{
188
	ZzipInputStream *zis = (ZzipInputStream *)is;
189
	zzip_off_t ofs = zzip_seek(zis->file, offset, whence);
190
	if (ofs != -1) {
191
		error.Set(zzip_domain, "zzip_seek() has failed");
192 193 194
		is->offset = ofs;
		return true;
	}
195 196 197 198 199
	return false;
}

/* exported structures */

200
static const char *const zzip_archive_extensions[] = {
201 202 203 204
	"zip",
	NULL
};

205
const InputPlugin zzip_input_plugin = {
206 207 208 209 210 211 212 213 214 215 216 217
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	zzip_input_close,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	zzip_input_read,
	zzip_input_eof,
	zzip_input_seek,
218 219
};

220
const struct archive_plugin zzip_archive_plugin = {
221 222 223 224 225
	"zzip",
	nullptr,
	nullptr,
	zzip_archive_open,
	zzip_archive_extensions,
226
};